pax_global_header00006660000000000000000000000064125721107370014517gustar00rootroot0000000000000052 comment=a67d9c8da2090be95d14450a5de2ba6c0f64c8b6 libtorrent-0.13.6/000077500000000000000000000000001257211073700137725ustar00rootroot00000000000000libtorrent-0.13.6/.gitignore000066400000000000000000000014431257211073700157640ustar00rootroot00000000000000# Compiled source # ################### *.o *.lo *.a *.la *.so *.in *.sub *.pc *.orig *.rej # Autoconf files # ################## .deps .libs Makefile aclocal.m4 autom4te.cache compile config.h config.guess config.status confdefs.h conftest.dir configure depcomp install-sh libtool ltmain.sh missing stamp-h1 scripts/libtool.m4 scripts/lt*.m4 test # Editor poo # ############## .#* \#*# *~ # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.gz *.tar *.zip # Logs and databases # ###################### *.log # OS generated files # ###################### .DS_Store? ehthumbs.db Icon? Thumbs.db TAGS # LibTorrent specific files ########################### test/LibTorrentTest test/LibTorrentTest.trs test-driver libtorrent-0.13.6/AUTHORS000066400000000000000000000000401257211073700150340ustar00rootroot00000000000000Jari Sundell libtorrent-0.13.6/BUGS000066400000000000000000000002071257211073700144540ustar00rootroot00000000000000FIXME LATER: Caught exception from libtorrent: Connection write fd(68,0,6) "ProtocolBuffer tried to write beyond scope of the buffer" libtorrent-0.13.6/COPYING000066400000000000000000000431311257211073700150270ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libtorrent-0.13.6/ChangeLog000066400000000000000000000000001257211073700155320ustar00rootroot00000000000000libtorrent-0.13.6/INSTALL000066400000000000000000000220301257211073700150200ustar00rootroot00000000000000Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. libtorrent-0.13.6/Makefile.am000066400000000000000000000011411257211073700160230ustar00rootroot00000000000000SUBDIRS = src test pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libtorrent.pc EXTRA_DIST= \ autogen.sh \ scripts/checks.m4 \ scripts/common.m4 \ scripts/attributes.m4 \ doc/main.xml \ doc/http.xml \ doc/torrent.xml \ rak/address_info.h \ rak/algorithm.h \ rak/allocators.h \ rak/error_number.h \ rak/file_stat.h \ rak/fs_stat.h \ rak/functional.h \ rak/path.h \ rak/partial_queue.h \ rak/priority_queue.h \ rak/priority_queue_default.h \ rak/regex.h \ rak/socket_address.h \ rak/string_manip.h \ rak/timer.h \ rak/unordered_vector.h ACLOCAL_AMFLAGS = -I scripts libtorrent-0.13.6/NEWS000066400000000000000000000000521257211073700144660ustar00rootroot00000000000000If you want news, watch CNN or something. libtorrent-0.13.6/README000066400000000000000000000022251257211073700146530ustar00rootroot00000000000000LibTorrent LICENSE GNU GPL, see COPYING. "libtorrent/src/utils/sha_fast.{cc,h}" is originally from the Mozilla NSS and is under a triple license; MPL, LGPL and GPL. An exception to non-NSS code has been added for linking to OpenSSL as requested by Debian, though the author considers that library to be part of the Operative System and thus linking is allowed according to the GPL. Use whatever fits your purpose, the code required to compile with Mozilla's NSS implementation of SHA1 has been retained and can be compiled if the user wishes to avoid using OpenSSL. CONTACT Send bug reports, suggestions and patches to or to the mailinglist. LIBRARY DEPENDENCIES g++ >= 4.2.1 SIGC++ The API will use sigc++ signals to give the client a simple, yet powerful way of reacting to events from the library. The client can hook multiple slots on each signal and modify the parameters to suit the functions. This avoids lots of unnecessary code in the client. POLLING "libtorrent/src/torrent/poll.h" provides an abstract class for implementing any kind of polling the client wishes to use. Currently epoll and select based polling is included. libtorrent-0.13.6/TODO_LONGTERM000066400000000000000000000077571257211073700160110ustar00rootroot00000000000000== API changes == Add macro for asserting with internal_error exception. A disk setting manager, allow change of read-ahead etc. Each download should also include slots for monitoring syncing, etc. The seed count and scrape leech/seed. Rename FileListIterator. Allow the client to set the priority of chunks directly, just add some helper functions. Remove the old non-const rate functions in Download. Allow flags to be passed during creation of download. This would be used to f.ex deciding if a download should be considered a multi-file torrent or not. === torrent::Object == Ways of checking that they are a value and between a range. Make an union of two bencode streams. This will allow rtorrent to save much smaller files with recent changes, and thus not require re-constructing torrent files of ~100KB in some cases. Add a variable that has can have an off state, plus a value range. Add a bit for modified entries, or perhaps just freeze lists/maps. Need to make this cheap for copying, so that we don't need to rely on an inherently borked design in the use of Variable::m_cache. It doesn't work if we call the same variable several times. === Session management == Saving session torrents should be lazy, perhaps only for those who are not open. Or do session torrent saving as needed, when seeding is stopped etc. Perhaps a dirty flag. == Configurable keybindings == ... == Efficiently handle torrents in watched directories == Currently a load is triggered every time for a bad torrent/duplicate torrent. Fix this. Consider tieing new files that match old torrents without a tied_to_file. == Delegator rework == Keep multiple downloads of the same block seperate. When done, and hash checking fails, compare pieces and merge/mark equal pieces. Those would have higher priority when comparing, but not absolute. The anonymous mmap'ed regions would contain different dtor functor than file chunks. We need to keep around PeerInfo's after peers disconnect, so we store information for longer that the immediate connection. This also eases the delegator since we can use pointers to identify the peer we downloaded the piece from. Get rid of affinity, perhaps each peer saves the pointer to the delegator chunk they are currently using, in addition to the delegator piece. Re-enable randomized selector position. Do the range-disable and position moving in d selector. == Sockets == Look into increasing the socket buffers. This would allow us to queue more data for each poll event. But make sure we only get polled when the buffer can take a nice amount of data. The goal would be to push as much data into the buffer per event as possible if it is uploading fast. == Improve logging == ... == Disk worker thread == A worker thread that gets activated to do stuff like hashing and syncing to disk. This would have the benefit of avoiding SIGBUS due to full disk as msync can be called blockingly. Before work can start on this, a threading library needs to be available. Possibly the TR2 stuff? == Tracker scrape == Add tracker scraping and display connected/not-connected seeds and leechers. In the mean time, parse the extra information the official tracker transmits on requests. == typedefs or more restrictive types for chunk indices, ports and such == These would include defines for invalid states etc. == Validate the presence of files for session torrents == Make sure that when session torrents are opened, it ensures that the downloaded/ing files are where we last saw them. Libtorrent shouldn't create the files when opened, but perhaps a call can be added for trying to make them? Also, when starting torrents, bork on incorrect sizes so as to not overwrite files created by other torrents. == Added initialization stuff to sections in the elf file? == Stuff like adding stuff to the ClientInfo object could be done from with a resource file embedded in the executable. == Extensions == * Send max block size on connection initialization. * Don't send the whole bitfield when you're a seeder. libtorrent-0.13.6/autogen.sh000077500000000000000000000020131257211073700157670ustar00rootroot00000000000000#! /bin/sh echo aclocal... (aclocal --version) < /dev/null > /dev/null 2>&1 || { echo aclocal not found exit 1 } aclocal -I ./scripts -I . ${ACLOCAL_FLAGS} || exit 1 echo autoheader... (autoheader --version) < /dev/null > /dev/null 2>&1 || { echo autoheader not found exit 1 } autoheader || exit 1 echo -n "libtoolize... " if ( (glibtoolize --version) < /dev/null > /dev/null 2>&1 ); then echo "using glibtoolize" glibtoolize --automake --copy --force || exit 1 elif ( (libtoolize --version) < /dev/null > /dev/null 2>&1 ) ; then echo "using libtoolize" libtoolize --automake --copy --force || exit 1 else echo "libtoolize nor glibtoolize not found" exit 1 fi echo automake... (automake --version) < /dev/null > /dev/null 2>&1 || { echo automake not found exit 1 } automake --add-missing --copy --gnu || exit 1 echo autoconf... (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo autoconf not found exit 1 } autoconf || exit 1 echo ready to configure exit 0 libtorrent-0.13.6/configure.ac000066400000000000000000000065371257211073700162730ustar00rootroot00000000000000AC_INIT(libtorrent, 0.13.6, sundell.software@gmail.com) LT_INIT([disable-static]) dnl Find a better way to do this AC_DEFINE(PEER_NAME, "-lt0D60-", Identifier that is part of the default peer id) AC_DEFINE(PEER_VERSION, "lt\x0D\x60", 4 byte client and version identifier for DHT) LIBTORRENT_CURRENT=19 LIBTORRENT_REVISION=0 LIBTORRENT_AGE=0 LIBTORRENT_INTERFACE_VERSION_INFO=$LIBTORRENT_CURRENT:$LIBTORRENT_REVISION:$LIBTORRENT_AGE LIBTORRENT_INTERFACE_VERSION_NO=$LIBTORRENT_CURRENT.$LIBTORRENT_AGE.$LIBTORRENT_REVISION AC_SUBST(LIBTORRENT_CURRENT) AC_SUBST(LIBTORRENT_INTERFACE_VERSION_INFO) AC_SUBST(LIBTORRENT_INTERFACE_VERSION_NO) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS(config.h) AM_PATH_CPPUNIT(1.9.6) AC_PROG_CXX AC_C_BIGENDIAN( AC_DEFINE(IS_BIG_ENDIAN, 1, Big endian), AC_DEFINE(IS_LITTLE_ENDIAN, 1, Little endian), AC_MSG_ERROR([Could not determine endianness]) ) TORRENT_CHECK_CXXFLAGS TORRENT_ENABLE_ALIGNED TORRENT_ENABLE_INTERRUPT_SOCKET TORRENT_ENABLE_DEBUG TORRENT_ENABLE_EXTRA_DEBUG TORRENT_ENABLE_WERROR TORRENT_ENABLE_TR1 TORRENT_ENABLE_CXX11 AC_SYS_LARGEFILE TORRENT_ENABLE_ARCH TORRENT_WITH_SYSROOT dnl TORRENT_WITH_XFS TORRENT_WITHOUT_KQUEUE TORRENT_WITHOUT_EPOLL TORRENT_CHECK_FALLOCATE TORRENT_WITH_POSIX_FALLOCATE TORRENT_WITH_ADDRESS_SPACE TORRENT_WITHOUT_STATVFS TORRENT_WITHOUT_STATFS CC_ATTRIBUTE_VISIBILITY AX_PTHREAD AX_CHECK_ZLIB CFLAGS="$PTHREAD_CFLAGS $CFLAGS" CXXFLAGS="$PTHREAD_CFLAGS $CXXFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_ARG_ENABLE(openssl, [ --disable-openssl Don't use OpenSSL's SHA1 implementation.], [ if test "$enableval" = "yes"; then PKG_CHECK_MODULES(OPENSSL, libcrypto, CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS"; LIBS="$LIBS $OPENSSL_LIBS") AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.) AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.) else AC_DEFINE(USE_NSS_SHA, 1, Using Mozilla's SHA1 implementation.) fi ],[ PKG_CHECK_MODULES(OPENSSL, libcrypto, CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS"; LIBS="$LIBS $OPENSSL_LIBS") AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.) AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.) ] ) AC_ARG_ENABLE(cyrus-rc4, [ --enable-cyrus-rc4=PFX Use Cyrus RC4 implementation.], [ CXXFLAGS="$CXXFLAGS -I${enableval}/include"; LIBS="$LIBS -lrc4 -L${enableval}/lib" AC_DEFINE(USE_CYRUS_RC4, 1, Using Cyrus RC4 implementation.) ] ) AC_CHECK_FUNCS(posix_memalign) TORRENT_CHECK_MADVISE() TORRENT_CHECK_CACHELINE() TORRENT_CHECK_POPCOUNT() TORRENT_CHECK_EXECINFO() TORRENT_CHECK_PTHREAD_SETNAME_NP() TORRENT_MINCORE() TORRENT_OTFD() TORRENT_DISABLE_IPV6 TORRENT_DISABLE_INSTRUMENTATION LIBTORRENT_LIBS="-ltorrent" AC_SUBST(LIBTORRENT_LIBS) LIBTORRENT_CFLAGS="" AC_SUBST(LIBTORRENT_CFLAGS) AC_DEFINE(HAVE_CONFIG_H, 1, true if config.h was included) CC_ATTRIBUTE_UNUSED( AC_DEFINE([__UNUSED], [__attribute__((unused))], [Wrapper around unused attribute]), AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported]) ) AC_OUTPUT([ libtorrent.pc Makefile src/Makefile src/torrent/Makefile src/torrent/peer/Makefile src/torrent/data/Makefile src/torrent/download/Makefile src/torrent/utils/Makefile src/data/Makefile src/dht/Makefile src/download/Makefile src/net/Makefile src/protocol/Makefile src/tracker/Makefile src/utils/Makefile test/Makefile ]) libtorrent-0.13.6/doc/000077500000000000000000000000001257211073700145375ustar00rootroot00000000000000libtorrent-0.13.6/doc/RELEASE_CHECKLIST000066400000000000000000000005371257211073700171400ustar00rootroot00000000000000Just some personal notes on what needs to be done when making a release. * Download a couple torrents using Valgrind. * Check both client and library version number. * Commit any changes. * Package releases. * Upload to host. * Update frontpage. * Download, compile and test. * Tag the release in SVN. * Send mail to the ML, update ChangeLog page. libtorrent-0.13.6/doc/http.xml000066400000000000000000000047711257211073700162510ustar00rootroot00000000000000 Http handler
Introduction LibTorrent depends on the client to handle http downloads, thus the library does not have a dependency on any specific http library. The library provides a base class named torrent::Http with virtual member functions that the client must implement, and a sigc++ slot which must be set to create an instance of the derived torrent::Http class when called. The torrent::Http class and the factory slot related functions can be found in the header "torrent/http.h". The http handler should have reasonable connection timeouts, be non-blocking and not do reconnects on failed downloads.
Factory Slot The client registers the desired factory slot with the static torrent::Http::set_factory member function. Using sigc++ the client may bind values to the arguments of their function to avoid depending on globals. The factory slot must return a pointer to a new instance with the base type torrent::Http, and the caller takes responsibility of deleting the object. (Note: consider making the cleanup a slot)
Output Stream The data downloaded by the http handler is to be written to torrent::Http::m_stream which is a pointer to an std::iostream. The http handler must not change any of the flags on the stream.
Start Http::start is called by the library when it wishes to initiate a http download. Your Http derived class must implement this function. It must be non-blocking and thread-safe. This means that if a seperate thread is used for downloading then it must not emit any signal while the main thread is inside the library.
close Http::close is used bu the library to stop and close a download. No signals may be emited after this. Http::m_data should not be cleared. The library may clear the Http::m_data pointer after this.
Signals There are two mutually exclusive signals that are called when the download has stopped. The signal torrent::Http::m_signalDone is called if the download was successful and torrent::Http::m_stream contains the complete data. Or if the download was unsuccessful for some reason, then torrent::Http::m_signalFailed is called with an error message.
libtorrent-0.13.6/doc/main.xml000066400000000000000000000003541257211073700162070ustar00rootroot00000000000000 ]> libTorrent Manual &torrent_section; &http_section; libtorrent-0.13.6/doc/multitracker-spec.txt000066400000000000000000000045461257211073700207470ustar00rootroot00000000000000 MULTITRACKER METADATA ENTRY SPECIFICATION ========================================= This specification is for John Hoffman's proposed extension to the BitTorrent metadata format. This extension is not official as of this writing. * "announce-list" In addition to the standard "announce" key, in the main area of the metadata file and not part of the "info" section, will be a new key, "announce-list". This key will refer to a list of lists of URLs, and will contain a list of tiers of announces. If the client is compatible with the multitracker specification, and if the "announce-list" key is present, the client will ignore the "announce" key and only use the URLs in "announce-list". * order of processing The tiers of announces will be processed sequentially; all URLs in each tier must be checked before the client goes on to the next tier. URLs within each tier will be processed in a randomly chosen order; in other words, the list will be shuffled when first read, and then parsed in order. In addition, if a connection with a tracker is successful, it will be moved to the front of the tier. * examples. d['announce-list'] = [ [tracker1], [backup1], [backup2] ] On each announce, first try tracker1, then if that cannot be reached, try backup1 and backup2 respectively. On the next announce, repeat in the same order. This is meant for when the trackers are standard and can not share information. d['announce-list'] = [[ tracker1, tracker2, tracker3 ]] First, shuffle the list. (For argument's sake, we'll say the list has already been shuffled.) Then, if tracker1 cannot be reached, try tracker2. If tracker2 can be reached, the list is now: tracker2,tracker1,tracker3. From then on, this will be the order the client tries. If later neither tracker2 nor tracker1 can be reached, but tracker3 responds, then the list will be changed to: tracker3,tracker2,tracker1, and will be tried in that order in the future. This form is meant for trackers which can trade peer information and will cause the clients to help balance the load between the trackers. d['announce-list'] = [ [ tracker1, tracker2 ], [backup1] ] The first tier, consisting of tracker1 and tracker2, is shuffled. Both trackers 1 and 2 will be tried on each announce (though perhaps in varying order) before the client tries to reach backup1.libtorrent-0.13.6/doc/torrent.xml000066400000000000000000000063651257211073700167700ustar00rootroot00000000000000 Torrent
State
Closed This is the initial state of a download. When switching to this mode, all tracker requests are closed and the bitfield of completed chunks is cleared. File paths can only be changed in this state. Functions for getting information on bitfields, chunk count and various others will return size 0 in this state. (TODO: Check which)
torrent::Download::is_open() == false; torrent::Download::is_active() == false; torrent::Download::is_tracker_busy() == false;
Open This is the state after a successfull call to torrent::Download::open(). This function throws torrent::local_error if the download could not be opened. All files in the download have been created and are open. The initial hash check must be done to get a valid bitfield of completed chunks.
torrent::Download::is_open() == true; torrent::Download::is_active() == false;
Active A download is active after calling torrent::Download::start(). Only downloads that are in an open state and has a valid bitfield of completed chunks can be activated.
torrent::Download::is_open() == true; torrent::Download::is_active() == true; torrent::Download::is_hash_checked() == true;
A tracker request will be made when torrent::Download::stop() is called on an active download. It is not required to wait for the tracker request to finish before calling torrent::Download::close(), but it is recommened so the tracker knows this client is not available.
File Paths The paths of files in a Download consists of two parts, the root directory and the paths of each file. The file paths are read from the torrent file and the files usually reside in the root directory. The root directory is by default "./" for single file torrents and "./[torrent_name]/" for multi-file torrents.
// Get and set the root directory. std::string torrent::Download::get_root_dir(); void torrent::Download::set_root_dir(const std::string& dir); // Get the torrent::Entry class for each file in the download. torrent::Entry torrent::Download::get_entry(uint32_t index); uint32_t torrent::Download::get_entry_size(); typedef std::list<std::string> torrent::Entry::Path; // Get and set the file path. std::string torrent::Entry::get_path(); const Path& torrent::Entry::get_path_list(); void torrent::Entry::set_path_list(const Path& l);
The modifications can only be done while the download is in a closed state. Modifying the file paths will not change the "info hash" part of the bencode'd torrent associated with the download. (TODO: When exporting, save root directory and file paths to another section of the torrent)
libtorrent-0.13.6/extra/000077500000000000000000000000001257211073700151155ustar00rootroot00000000000000libtorrent-0.13.6/extra/bt/000077500000000000000000000000001257211073700155225ustar00rootroot00000000000000libtorrent-0.13.6/extra/bt/bt.sh000077500000000000000000000001741257211073700164700ustar00rootroot00000000000000#!/bin/sh mkdir $1 cd $1 nice -n11 btdownloadheadless.py --max_upload_rate $1 --url file:///tmp/bt/t.torrent > /dev/null & libtorrent-0.13.6/extra/bt/run.sh000077500000000000000000000000761257211073700166700ustar00rootroot00000000000000#!/bin/sh for ((i = 0; i < 10; i++)); do bt.sh down$i ; done libtorrent-0.13.6/extra/compile_object.sh000077500000000000000000000005711257211073700204350ustar00rootroot00000000000000STUFF="-Wall -O2 -I.. -I../src/ -lcrypto `pkg-config --libs-only-L openssl` `pkg-config --cflags openssl`" g++ $STUFF -DNEW_OBJECT -o test_object test_object.cc object.cc ../src/torrent/object_stream.cc ../src/torrent/exceptions.cc g++ $STUFF -DOLD_OBJECT -o test_object_old test_object.cc ../src/torrent/object.cc ../src/torrent/object_stream.cc ../src/torrent/exceptions.cc libtorrent-0.13.6/extra/compile_partial_queue.sh000077500000000000000000000001021257211073700220150ustar00rootroot00000000000000g++ -Wall -O2 -g -I.. -o test_partial_queue test_partial_queue.cc libtorrent-0.13.6/extra/corrupt_file.cc000066400000000000000000000020541257211073700201220ustar00rootroot00000000000000#include #include #include #include #include #include #include "../rak/file_stat.h" void corrupt_region(int fd, int pos, int length) { char buf[length]; for (char *first = buf, *last = buf + length; first != last; ++first) *first = '\0'; if (write(fd, buf, length) == -1) throw std::runtime_error("Could not write data to file."); } int main(int argc, char** argv) { if (argc != 4) throw std::runtime_error("Too few arguments."); int seed; int length; if (sscanf(argv[1], "%i", &seed) != 1) throw std::runtime_error("Could not parse seed."); if (sscanf(argv[2], "%i", &length) != 1) throw std::runtime_error("Could not parse length."); int fd = open(argv[3], O_RDWR); if (fd == -1) throw std::runtime_error("Could not open file."); rak::file_stat fileStat; if (!fileStat.update(fd)) throw std::runtime_error("Could not read fs stat."); srand(seed); corrupt_region(fd, rand() % (fileStat.size() - length), length); return 0; } libtorrent-0.13.6/extra/object.cc000066400000000000000000000117741257211073700167040ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "object.h" namespace torrent { Object::Object(const Object& b) : m_state(b.type()) { switch (type()) { case type_none: break; case type_value: m_value = b.m_value; break; case type_string: m_string = new string_type(*b.m_string); break; case type_list: m_list = new list_type(*b.m_list); break; case type_map: m_map = new map_type(*b.m_map); break; } } Object& Object::operator = (const Object& src) { if (&src == this) return *this; clear(); m_state = src.m_state; switch (type()) { case type_none: break; case type_value: m_value = src.m_value; break; case type_string: m_string = new string_type(*src.m_string); break; case type_list: m_list = new list_type(*src.m_list); break; case type_map: m_map = new map_type(*src.m_map); break; } return *this; } void Object::clear() { switch (type()) { case type_none: case type_value: break; case type_string: delete m_string; break; case type_list: delete m_list; break; case type_map: delete m_map; break; } m_state = type_none; } Object& Object::get_key(const std::string& k) { check_throw(type_map); map_type::iterator itr = m_map->find(k); if (itr == m_map->end()) throw bencode_error("Object operator [" + k + "] could not find element"); return itr->second; } const Object& Object::get_key(const std::string& k) const { check_throw(type_map); map_type::const_iterator itr = m_map->find(k); if (itr == m_map->end()) throw bencode_error("Object operator [" + k + "] could not find element"); return itr->second; } Object& Object::move(Object& src) { if (this == &src) return *this; clear(); std::memcpy(this, &src, sizeof(Object)); std::memset(&src, 0, sizeof(Object)); return *this; } Object& Object::swap(Object& src) { char tmp[sizeof(Object)]; std::memcpy(tmp, &src, sizeof(Object)); std::memcpy(&src, this, sizeof(Object)); std::memcpy(this, tmp, sizeof(Object)); return *this; } Object& Object::merge_copy(const Object& object, uint32_t maxDepth) { if (maxDepth == 0) return (*this = object); if (object.is_map()) { if (!is_map()) *this = Object(type_map); map_type& dest = as_map(); map_type::iterator destItr = dest.begin(); map_type::const_iterator srcItr = object.as_map().begin(); map_type::const_iterator srcLast = object.as_map().end(); while (srcItr != srcLast) { destItr = std::find_if(destItr, dest.end(), rak::less_equal(srcItr->first, rak::mem_ref(&map_type::value_type::first))); if (srcItr->first < destItr->first) // destItr remains valid and pointing to the next possible // position. dest.insert(destItr, *srcItr); else destItr->second.merge_copy(srcItr->second, maxDepth - 1); srcItr++; } } else if (object.is_list()) { if (!is_list()) *this = Object(type_list); list_type& dest = as_list(); list_type::iterator destItr = dest.begin(); list_type::const_iterator srcItr = object.as_list().begin(); list_type::const_iterator srcLast = object.as_list().end(); while (srcItr != srcLast) { if (destItr == dest.end()) destItr = dest.insert(destItr, *srcItr); else destItr->merge_copy(*srcItr, maxDepth - 1); destItr++; } } else { *this = object; } return *this; } } libtorrent-0.13.6/extra/object.h000066400000000000000000000210431257211073700165340ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_OBJECT_H #define LIBTORRENT_OBJECT_H #include #include #include #include #include #include namespace torrent { // Add support for the GCC move semantics. // // Not today? class ObjectRefRef; class LIBTORRENT_EXPORT Object { public: typedef int64_t value_type; typedef std::string string_type; typedef std::list list_type; typedef std::map map_type; typedef map_type::key_type key_type; // Figure a better name for this? typedef uint32_t state_type; enum type_type { TYPE_NONE, TYPE_VALUE, TYPE_STRING, TYPE_LIST, TYPE_MAP }; // The users of the library should only ever set/get the bits in the // public mask, else risk waking dragons. // // Consider if part of the mask should be made to clear upon copy, // and do so for both private and public? static const state_type mask_private = 0x0000ffff; static const state_type mask_public = 0xffff0000; static const state_type mask_type = 0x000000ff; // static const state_type flag_const = 0x000000; static const state_type flag_reference = 0x100; static const state_type type_none = TYPE_NONE; static const state_type type_value = TYPE_VALUE; static const state_type type_string = TYPE_STRING; static const state_type type_list = TYPE_LIST; static const state_type type_map = TYPE_MAP; // Add ctors that take a use_copy, use_move and use_internal_move // parameters. Object() : m_state(type_none) {} Object(const value_type v) : m_state(type_value), m_value(v) {} // Don't inline these. Object(const char* s) : m_state(type_string), m_string(new string_type(s)) {} Object(const string_type& s) : m_state(type_string), m_string(new string_type(s)) {} Object(const Object& b); // Hmm... reconsider this. explicit Object(type_type t); ~Object() { clear(); } void clear(); type_type type() const { return (type_type)(m_state & mask_type); } bool is_value() const { return type() == type_value; } bool is_string() const { return type() == type_string; } bool is_list() const { return type() == type_list; } bool is_map() const { return type() == type_map; } bool is_reference() const { return m_state & flag_reference; } // Add _mutable_ to non-const access. value_type& as_value() { check_throw(type_value); return m_value; } const value_type& as_value() const { check_throw(type_value); return m_value; } string_type& as_string() { check_throw(type_string); return *m_string; } const string_type& as_string() const { check_throw(type_string); return *m_string; } list_type& as_list() { check_throw(type_list); return *m_list; } const list_type& as_list() const { check_throw(type_list); return *m_list; } map_type& as_map() { check_throw(type_map); return *m_map; } const map_type& as_map() const { check_throw(type_map); return *m_map; } // Map operations: bool has_key(const key_type& k) const { check_throw(type_map); return m_map->find(k) != m_map->end(); } bool has_key_value(const key_type& k) const { check_throw(type_map); return check(m_map->find(k), type_value); } bool has_key_string(const key_type& k) const { check_throw(type_map); return check(m_map->find(k), type_string); } bool has_key_list(const key_type& k) const { check_throw(type_map); return check(m_map->find(k), type_list); } bool has_key_map(const key_type& k) const { check_throw(type_map); return check(m_map->find(k), type_map); } Object& get_key(const key_type& k); const Object& get_key(const key_type& k) const; value_type& get_key_value(const key_type& k) { return get_key(k).as_value(); } const value_type& get_key_value(const key_type& k) const { return get_key(k).as_value(); } string_type& get_key_string(const key_type& k) { return get_key(k).as_string(); } const string_type& get_key_string(const key_type& k) const { return get_key(k).as_string(); } list_type& get_key_list(const key_type& k) { return get_key(k).as_list(); } const list_type& get_key_list(const key_type& k) const { return get_key(k).as_list(); } map_type& get_key_map(const key_type& k) { return get_key(k).as_map(); } const map_type& get_key_map(const key_type& k) const { return get_key(k).as_map(); } Object& insert_key(const key_type& k, const Object& b) { check_throw(type_map); return (*m_map)[k] = b; } void erase_key(const key_type& k) { check_throw(type_map); m_map->erase(k); } // List operations: Object& insert_front(const Object& b) { check_throw(type_list); return *m_list->insert(m_list->begin(), b); } Object& insert_back(const Object& b) { check_throw(type_list); return *m_list->insert(m_list->end(), b); } // Copy and merge operations: Object& move(Object& b); Object& swap(Object& b); // Only map entries are merged. Object& merge_move(Object object, uint32_t maxDepth = ~uint32_t()); Object& merge_copy(const Object& object, uint32_t maxDepth = ~uint32_t()); Object& operator = (const Object& b); private: inline bool check(map_type::const_iterator itr, state_type t) const; inline void check_throw(state_type t) const; state_type m_state; union { int64_t m_value; string_type* m_string; list_type* m_list; map_type* m_map; }; }; // Inline? // // Or just replace with specific ctors? inline Object::Object(type_type t) : m_state(t) { switch (t) { case TYPE_NONE: break; case TYPE_VALUE: m_value = value_type(); break; case TYPE_STRING: m_string = new string_type(); break; case TYPE_LIST: m_list = new list_type(); break; case TYPE_MAP: m_map = new map_type(); break; } } inline bool Object::check(map_type::const_iterator itr, state_type t) const { return itr != m_map->end() && itr->second.type() == t; } inline void Object::check_throw(state_type t) const { if (t != type()) throw bencode_error("Wrong object type."); } } #endif libtorrent-0.13.6/extra/posix_fallocate.cc000066400000000000000000000010771257211073700206050ustar00rootroot00000000000000#include #include #include "../src/utils/timer.h" namespace torrent { int64_t Timer::m_cache; } #define FILE_SIZE ((off_t)1 << 20) int main() { int fd = open("./posix_fallocate.out", O_CREAT | O_RDWR); if (ftruncate(fd, FILE_SIZE)) { std::cout << "truncate failed" << std::endl; return -1; } torrent::Timer t1 = torrent::Timer::current(); int v = posix_fallocate(fd, 0, FILE_SIZE); torrent::Timer t2 = torrent::Timer::current(); std::cout << "Return: " << v << " Time: " << (t2 - t1).usec() << std::endl; return 0; } libtorrent-0.13.6/extra/test_object.cc000066400000000000000000000077241257211073700177430ustar00rootroot00000000000000#include #include #include #include "../src/torrent/object_stream.h" #ifdef NEW_OBJECT #include "object.h" typedef torrent::Object return_type; //#define OBJECTREF_MOVE(x) torrent::ObjectRef::move(x) #define OBJECTREF_MOVE(x) x #else #include "../src/torrent/object.h" typedef torrent::Object return_type; #define OBJECTREF_MOVE(x) x #endif #define TIME_WRAPPER(name, body) \ rak::timer \ time_##name(unsigned int n) { \ rak::timer started = rak::timer::current(); \ \ for (unsigned int i = 0; i < n; i++) { \ body; \ } \ \ return rak::timer::current() - started; \ } typedef std::list std_list_type; void f() {} torrent::Object func_create_string_20() { return torrent::Object("12345678901234567890"); } std::string func_create_std_string_20() { return "12345678901234567890"; } return_type func_create_string_list_20() { torrent::Object tmp(torrent::Object::TYPE_LIST); torrent::Object::list_type& listRef = tmp.as_list(); for (int i = 0; i < 10; i++) listRef.push_back(torrent::Object("12345678901234567890")); return OBJECTREF_MOVE(tmp); } std_list_type func_create_std_string_list_20() { std_list_type tmp(torrent::Object::TYPE_LIST); for (int i = 0; i < 10; i++) tmp.push_back("12345678901234567890"); return tmp; } torrent::Object stringList20(func_create_string_list_20()); // return_type // func_copy_string_list_20_f() { // torrent::Object tmp(stringList20); // return OBJECTREF_MOVE(tmp); // } torrent::Object tmp1; return_type func_copy_string_list_20() { tmp1 = stringList20; return OBJECTREF_MOVE(tmp1); } TIME_WRAPPER(dummy, f(); ) TIME_WRAPPER(string, torrent::Object s("12345678901234567890"); ) TIME_WRAPPER(std_string, std::string s("12345678901234567890"); ) TIME_WRAPPER(return_string, torrent::Object s = func_create_string_20(); ) TIME_WRAPPER(return_std_string, std::string s = func_create_std_string_20(); ) TIME_WRAPPER(return_string_list, torrent::Object s(func_create_string_list_20()); ) TIME_WRAPPER(return_std_string_list, std_list_type s(func_create_std_string_list_20()); ) TIME_WRAPPER(copy_string_list, torrent::Object s(func_copy_string_list_20()); ) int main(int argc, char** argv) { // std::cout << "sizeof(torrent::Object): " << sizeof(torrent::Object) << std::endl; // std::cout << "sizeof(torrent::Object::value_type): " << sizeof(torrent::Object::value_type) << std::endl; // std::cout << "sizeof(torrent::Object::string_type): " << sizeof(torrent::Object::string_type) << std::endl; // std::cout << "sizeof(torrent::Object::map_type): " << sizeof(torrent::Object::map_type) << std::endl; // std::cout << "sizeof(torrent::Object::list_type): " << sizeof(torrent::Object::list_type) << std::endl; std::cout.setf(std::ios::right, std::ios::adjustfield); std::cout << "time_dummy: " << std::setw(8) << time_dummy(100000).usec() << std::endl; std::cout << "time_string: " << std::setw(8) << time_string(100000).usec() << std::endl; std::cout << "time_std_string: " << std::setw(8) << time_std_string(100000).usec() << std::endl; std::cout << "time_return_string: " << std::setw(8) << time_return_string(100000).usec() << std::endl; std::cout << "time_return_std_string: " << std::setw(8) << time_return_std_string(100000).usec() << std::endl; std::cout << std::endl; std::cout << "time_return_string_list: " << std::setw(8) << time_return_string_list(100000).usec() << std::endl; std::cout << "time_return_std_string_list: " << std::setw(8) << time_return_std_string_list(100000).usec() << std::endl; std::cout << "time_copy_string_list: " << std::setw(8) << time_copy_string_list(100000).usec() << std::endl; return 0; } libtorrent-0.13.6/extra/test_partial_queue.cc000066400000000000000000000032031257211073700213210ustar00rootroot00000000000000#include #include #include #include #include void test_fill() { rak::partial_queue queue; queue.enable(8); queue.clear(); std::cout << "test_fill()" << std::endl; std::cout << " inserting: "; int i = 0; while (true) { uint8_t k = rand() % 256; if (queue.insert(k, k)) std::cout << '[' << (uint32_t)k << ']' << ' '; // else // std::cout << '<' << (uint32_t)k << '>' << ' '; if (queue.is_full() && ++i == 100) break; } std::cout << std::endl << " popping: "; while (queue.prepare_pop()) { std::cout << queue.pop() << ' '; } std::cout << std::endl; } void test_random() { rak::partial_queue queue; queue.enable(8); queue.clear(); std::cout << "test_random()" << std::endl; std::cout << " inserting: "; for (int i = 0; i < 10 && !queue.is_full(); ++i) { uint8_t k = rand() % 256; if (queue.insert(k, k)) std::cout << '[' << (uint32_t)k << ']' << ' '; else std::cout << '<' << (uint32_t)k << '>' << ' '; } std::cout << std::endl << " popping: "; while (queue.prepare_pop()) { if (rand() % 2) { std::cout << queue.pop() << ' '; } else { uint8_t k = rand() % 128; if (queue.insert(k, k)) std::cout << '[' << (uint32_t)k << ']' << ' '; else std::cout << '<' << (uint32_t)k << '>' << ' '; } } std::cout << std::endl; } int main(int argc, char** argv) { srand(rak::timer::current().usec()); std::cout << "sizeof(rak::partial_queue): " << sizeof(rak::partial_queue) << std::endl; test_fill(); test_random(); return 0; } libtorrent-0.13.6/extra/test_queue.cc000066400000000000000000000031371257211073700176130ustar00rootroot00000000000000#include #include #include #include rak::priority_queue_default queue;//(priority_compare(), priority_equal(), priority_erase()); rak::priority_item items[100]; int last = 0; class test { public: test() {} void f() { std::cout << "Called: " << std::endl; } }; void print_item(rak::priority_item* p) { std::cout << (p - items) << ' ' << p->time().usec() << std::endl; if (p->time().usec() < last) { std::cout << "order bork." << std::endl; exit(-1); } last = p->time().usec(); p->clear_time(); if (std::rand() % 5) { int i = rand() % 100; std::cout << "erase " << i << ' ' << items[i].time().usec() << std::endl; priority_queue_erase(&queue, items + i); } } int main() { try { test t; for (rak::priority_item* first = items, *last = items + 100; first != last; ++first) { first->set_slot(rak::mem_fn(&t, &test::f)); priority_queue_insert(&queue, first, (std::rand() % 50) + 1); } // std::vector due; // std::copy(rak::queue_popper(queue, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), 20)), // rak::queue_popper(queue, rak::bind2nd(std::mem_fun(&rak::priority_item::compare), rak::timer())), // std::back_inserter(due)); // std::for_each(due.begin(), due.end(), std::ptr_fun(&print_item)); while (!queue.empty()) { rak::priority_item* i = queue.top(); queue.pop(); print_item(i); } } catch (std::logic_error& e) { std::cout << "Exception: " << e.what() << std::endl; } return 0; } libtorrent-0.13.6/extra/test_ranges.cc000066400000000000000000000015461257211073700177500ustar00rootroot00000000000000#include #include #include "../rak/ranges.h" void print_ranges(rak::ranges& r) { rak::ranges::iterator itr = r.begin(); std::cout << std::endl; while (itr != r.end()) { std::cout << itr->first << ' ' << itr->second << std::endl; ++itr; } } int main() { rak::ranges r; r.insert(10, 20); r.insert(30, 40); r.insert(50, 60); assert(r.has(10) && !r.has(20) && r.has(30) && !r.has(40)); r.insert(0, 5); assert(r.has(2) && !r.has(5) && r.has(10)); r.insert(5, 30); assert(r.has(0) && r.has(7) && r.has(29) && r.has(30) && !r.has(40)); print_ranges(r); // r.erase(15, 55); // print(r); // assert(r.size() == 3); // assert(r.has(14)); // assert(!r.has(15)); // assert(!r.has(54)); // assert(r.has(55)); // r.insert(5, 60); // print(r); return 0; } libtorrent-0.13.6/extra/test_sockaddr.cc000066400000000000000000000041441257211073700202600ustar00rootroot00000000000000#include #include #include "../src/torrent/object.h" void print_addr(const char* name, const rak::socket_address_inet& sa) { std::cout << name << ": " << sa.family() << ' ' << sa.address_str() << ':' << sa.port() << std::endl; } bool lookup_address(const char* name) { rak::address_info* result; std::cout << "Lookup: " << name << std::endl; // int errcode = rak::address_info::get_address_info(name, 0, 0, &result); int errcode = rak::address_info::get_address_info(name, PF_INET6, 0, &result); if (errcode != 0) { std::cout << "Failed: " << rak::address_info::strerror(errcode) << std::endl; return false; } for (rak::address_info* itr = result; itr != NULL; itr = itr->next()) { std::cout << "Flags: " << itr->flags() << std::endl; std::cout << "Family: " << itr->family() << std::endl; std::cout << "Socket Type: " << itr->socket_type() << std::endl; std::cout << "Protocol: " << itr->protocol() << std::endl; std::cout << "Length: " << itr->length() << std::endl; std::cout << "Address: " << itr->address()->family() << ' ' << itr->address()->address_str() << ':' << itr->address()->port() << std::endl; } // Release. freeaddrinfo(reinterpret_cast(result)); return true; } int main(int argc, char** argv) { std::cout << "sizeof(sockaddr_in): " << sizeof(sockaddr_in) << std::endl; std::cout << "sizeof(sockaddr_in6): " << sizeof(sockaddr_in6) << std::endl; rak::socket_address saNone; saNone.set_family(); print_addr("none", *saNone.sa_inet()); rak::socket_address_inet sa1; sa1.set_family(); sa1.set_port(0); sa1.set_address_any(); print_addr("sa1", sa1); rak::socket_address_inet sa2; sa2.set_family(); sa2.set_port(12345); if (!sa2.set_address_str("123.45.67.255")) return -1; print_addr("sa2", sa2); rak::socket_address sa3; sa3.sa_inet()->set_family(); sa3.sa_inet()->set_port(6999); sa3.sa_inet()->set_address_str("127.0.0.2"); print_addr("sa3", *sa3.sa_inet()); lookup_address("www.uio.no"); lookup_address("www.ipv6.org"); lookup_address("lampedusa"); return 0; } libtorrent-0.13.6/libtorrent.pc.in000066400000000000000000000003151257211073700171060ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libtorrent Description: A BitTorrent library Version: @VERSION@ Libs: -L${libdir} -ltorrent Cflags: -I${includedir} libtorrent-0.13.6/rak/000077500000000000000000000000001257211073700145475ustar00rootroot00000000000000libtorrent-0.13.6/rak/address_info.h000066400000000000000000000075041257211073700173660ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Wrapper for addrinfo with focus on zero-copy conversion to and from // the c-type and wrapper. // // Do use the wrapper on a pre-existing struct addrinfo, cast the // pointer rather than the base type. #ifndef RAK_ADDRESS_INFO_H #define RAK_ADDRESS_INFO_H #include #include namespace rak { class address_info { public: void clear() { std::memset(this, 0, sizeof(address_info)); } int flags() const { return m_addrinfo.ai_flags; } void set_flags(int f) { m_addrinfo.ai_flags = f; } int family() const { return m_addrinfo.ai_family; } void set_family(int f) { m_addrinfo.ai_family = f; } int socket_type() const { return m_addrinfo.ai_socktype; } void set_socket_type(int t) { m_addrinfo.ai_socktype = t; } int protocol() const { return m_addrinfo.ai_protocol; } void set_protocol(int p) { m_addrinfo.ai_protocol = p; } size_t length() const { return m_addrinfo.ai_addrlen; } socket_address* address() { return reinterpret_cast(m_addrinfo.ai_addr); } addrinfo* c_addrinfo() { return &m_addrinfo; } const addrinfo* c_addrinfo() const { return &m_addrinfo; } address_info* next() { return reinterpret_cast(m_addrinfo.ai_next); } static int get_address_info(const char* node, int domain, int type, address_info** ai); static void free_address_info(address_info* ai) { ::freeaddrinfo(ai->c_addrinfo()); } static const char* strerror(int err) { return gai_strerror(err); } private: addrinfo m_addrinfo; }; inline int address_info::get_address_info(const char* node, int pfamily, int stype, address_info** ai) { address_info hints; hints.clear(); hints.set_family(pfamily); hints.set_socket_type(stype); return ::getaddrinfo(node, NULL, hints.c_addrinfo(), reinterpret_cast(ai)); } } #endif libtorrent-0.13.6/rak/algorithm.h000066400000000000000000000116461257211073700167160ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_ALGORITHM_H #define RAK_ALGORITHM_H #include #include #include namespace rak { template _Function for_each_pre(_InputIter __first, _InputIter __last, _Function __f) { _InputIter __tmp; while (__first != __last) { __tmp = __first++; __f(*__tmp); } return __f; } // Return a range with a distance of no more than __distance and // between __first and __last, centered on __middle1. template std::pair<_InputIter, _InputIter> advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) { _InputIter __middle2 = __middle1; do { if (!__distance) break; if (__middle2 != __last) { ++__middle2; --__distance; } else if (__middle1 == __first) { break; } if (!__distance) break; if (__middle1 != __first) { --__middle1; --__distance; } else if (__middle2 == __last) { break; } } while (true); return std::make_pair(__middle1, __middle2); } template _InputIter advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) { while (__first != __last && __distance != 0) { __first++; __distance--; } return __first; } template _InputIter advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) { while (__first != __last && __distance != 0) { __first--; __distance--; } return __first; } template struct compare_base : public std::binary_function<_Value, _Value, bool> { bool operator () (const _Value& complete, const _Value& base) const { return !complete.compare(0, base.size(), base); } }; // Count the number of elements from the start of the containers to // the first inequal element. template typename std::iterator_traits<_InputIter1>::difference_type count_base(_InputIter1 __first1, _InputIter1 __last1, _InputIter2 __first2, _InputIter2 __last2) { typename std::iterator_traits<_InputIter1>::difference_type __n = 0; for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n) if (*__first1 != *__first2) return __n; return __n; } template _Return make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) { if (__first == __last) return ""; _Return __base = __ftor(*__first++); for ( ;__first != __last; ++__first) { typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(), __ftor(*__first).begin(), __ftor(*__first).end()); if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size()) __base.resize(__pos); } return __base; } template inline int popcount_wrapper(T t) { #if USE_BUILTIN_POPCOUNT if (std::numeric_limits::digits <= std::numeric_limits::digits) return __builtin_popcount(t); else return __builtin_popcountll(t); #else #error __builtin_popcount not found. unsigned int count = 0; while (t) { count += t & 0x1; t >> 1; } return count; #endif } } #endif libtorrent-0.13.6/rak/allocators.h000066400000000000000000000071131257211073700170650ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Some allocators for cacheline aligned chunks of memory, etc. #ifndef RAK_ALLOCATORS_H #define RAK_ALLOCATORS_H #include #include #include #include namespace rak { template class cacheline_allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef const void* const_void_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; cacheline_allocator() throw() { } cacheline_allocator(const cacheline_allocator&) throw() { } template cacheline_allocator(const cacheline_allocator&) throw() { } ~cacheline_allocator() throw() { } template struct rebind { typedef cacheline_allocator other; }; // return address of values pointer address (reference value) const { return &value; } const_pointer address (const_reference value) const { return &value; } size_type max_size () const throw() { return std::numeric_limits::max() / sizeof(T); } pointer allocate(size_type num, const_void_pointer hint = 0) { return alloc_size(num*sizeof(T)); } static pointer alloc_size(size_type size) { pointer ptr = NULL; int __UNUSED result = posix_memalign((void**)&ptr, LT_SMP_CACHE_BYTES, size); return ptr; } void construct (pointer p, const T& value) { new((void*)p)T(value); } void destroy (pointer p) { p->~T(); } void deallocate (pointer p, size_type num) { free((void*)p); } }; template bool operator== (const cacheline_allocator&, const cacheline_allocator&) throw() { return true; } template bool operator!= (const cacheline_allocator&, const cacheline_allocator&) throw() { return false; } } // // Operator new with custom allocators: // template void* operator new(size_t s, rak::cacheline_allocator a) { return a.alloc_size(s); } #endif // namespace rak libtorrent-0.13.6/rak/error_number.h000066400000000000000000000063701257211073700174270ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_ERROR_NUMBER_H #define RAK_ERROR_NUMBER_H #include #include namespace rak { class error_number { public: static const int e_access = EACCES; static const int e_again = EAGAIN; static const int e_connreset = ECONNRESET; static const int e_connaborted = ECONNABORTED; static const int e_deadlk = EDEADLK; static const int e_noent = ENOENT; static const int e_nomem = ENOMEM; static const int e_notdir = ENOTDIR; static const int e_isdir = EISDIR; static const int e_intr = EINTR; error_number() : m_errno(0) {} error_number(int e) : m_errno(e) {} bool is_valid() const { return m_errno != 0; } int value() const { return m_errno; } const char* c_str() const { return std::strerror(m_errno); } bool is_blocked_momentary() const { return m_errno == e_again || m_errno == e_intr; } bool is_blocked_prolonged() const { return m_errno == e_deadlk; } bool is_closed() const { return m_errno == e_connreset || m_errno == e_connaborted; } bool is_bad_path() const { return m_errno == e_noent || m_errno == e_notdir || m_errno == e_access; } static error_number current() { return errno; } static void clear_global() { errno = 0; } static void set_global(error_number err) { errno = err.m_errno; } bool operator == (const error_number& e) const { return m_errno == e.m_errno; } private: int m_errno; }; } #endif libtorrent-0.13.6/rak/file_stat.h000066400000000000000000000065461257211073700167050ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_FILE_STAT_H #define RAK_FILE_STAT_H #include #include #include namespace rak { class file_stat { public: // Consider storing rak::error_number. bool update(int fd) { return fstat(fd, &m_stat) == 0; } bool update(const char* filename) { return stat(filename, &m_stat) == 0; } bool update(const std::string& filename) { return update(filename.c_str()); } bool update_link(const char* filename) { return lstat(filename, &m_stat) == 0; } bool update_link(const std::string& filename) { return update_link(filename.c_str()); } bool is_regular() const { return S_ISREG(m_stat.st_mode); } bool is_directory() const { return S_ISDIR(m_stat.st_mode); } bool is_character() const { return S_ISCHR(m_stat.st_mode); } bool is_block() const { return S_ISBLK(m_stat.st_mode); } bool is_fifo() const { return S_ISFIFO(m_stat.st_mode); } bool is_link() const { return S_ISLNK(m_stat.st_mode); } bool is_socket() const { return S_ISSOCK(m_stat.st_mode); } off_t size() const { return m_stat.st_size; } time_t access_time() const { return m_stat.st_atime; } time_t change_time() const { return m_stat.st_ctime; } time_t modified_time() const { return m_stat.st_mtime; } private: struct stat m_stat; }; } #endif libtorrent-0.13.6/rak/fs_stat.h000066400000000000000000000054421257211073700163700ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_FS_STAT_H #define RAK_FS_STAT_H #include #include #include #if HAVE_SYS_VFS_H #include #endif #if HAVE_SYS_STATVFS_H #include #endif #if HAVE_SYS_STATFS_H #include #endif #if HAVE_SYS_PARAM_H #include #endif #if HAVE_SYS_MOUNT_H #include #endif namespace rak { class fs_stat { public: typedef FS_STAT_SIZE_TYPE blocksize_type; typedef FS_STAT_COUNT_TYPE blockcount_type; typedef FS_STAT_STRUCT fs_stat_type; bool update(int fd) { return FS_STAT_FD; } bool update(const char* fn) { return FS_STAT_FN; } bool update(const std::string& filename) { return update(filename.c_str()); } blocksize_type blocksize() { return FS_STAT_BLOCK_SIZE; } blockcount_type blocks_avail() { return m_stat.f_bavail; } int64_t bytes_avail() { return (int64_t) blocksize() * m_stat.f_bavail; } private: fs_stat_type m_stat; }; } #endif libtorrent-0.13.6/rak/functional.h000066400000000000000000000412141257211073700170640ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_FUNCTIONAL_H #define RAK_FUNCTIONAL_H #include #include namespace rak { template struct reference_fix { typedef Type type; }; template struct reference_fix { typedef Type type; }; template struct value_t { value_t(Type v) : m_v(v) {} Type operator () () const { return m_v; } Type m_v; }; template inline value_t value(Type v) { return value_t(v); } template struct accumulate_t { accumulate_t(Type t, Ftor f) : result(t), m_f(f) {} template void operator () (const Arg& a) { result += m_f(a); } Type result; Ftor m_f; }; template inline accumulate_t accumulate(Type t, Ftor f) { return accumulate_t(t, f); } // Operators: template struct equal_t { typedef bool result_type; equal_t(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t == m_f(a); } Type m_t; Ftor m_f; }; template inline equal_t equal(Type t, Ftor f) { return equal_t(t, f); } template struct equal_ptr_t { typedef bool result_type; equal_ptr_t(Type* t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (const Arg& a) { return *m_t == *m_f(a); } Type* m_t; Ftor m_f; }; template inline equal_ptr_t equal_ptr(Type* t, Ftor f) { return equal_ptr_t(t, f); } template struct not_equal_t { typedef bool result_type; not_equal_t(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t != m_f(a); } Type m_t; Ftor m_f; }; template inline not_equal_t not_equal(Type t, Ftor f) { return not_equal_t(t, f); } template struct less_t { typedef bool result_type; less_t(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t < m_f(a); } Type m_t; Ftor m_f; }; template inline less_t less(Type t, Ftor f) { return less_t(t, f); } template struct less2_t : public std::binary_function { less2_t(FtorA f_a, FtorB f_b) : m_f_a(f_a), m_f_b(f_b) {} bool operator () (typename FtorA::argument_type a, typename FtorB::argument_type b) { return m_f_a(a) < m_f_b(b); } FtorA m_f_a; FtorB m_f_b; }; template inline less2_t less2(FtorA f_a, FtorB f_b) { return less2_t(f_a,f_b); } template struct _greater { typedef bool result_type; _greater(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t > m_f(a); } Type m_t; Ftor m_f; }; template inline _greater greater(Type t, Ftor f) { return _greater(t, f); } template struct greater2_t : public std::binary_function { greater2_t(FtorA f_a, FtorB f_b) : m_f_a(f_a), m_f_b(f_b) {} bool operator () (typename FtorA::argument_type a, typename FtorB::argument_type b) { return m_f_a(a) > m_f_b(b); } FtorA m_f_a; FtorB m_f_b; }; template inline greater2_t greater2(FtorA f_a, FtorB f_b) { return greater2_t(f_a,f_b); } template struct less_equal_t { typedef bool result_type; less_equal_t(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t <= m_f(a); } Type m_t; Ftor m_f; }; template inline less_equal_t less_equal(Type t, Ftor f) { return less_equal_t(t, f); } template struct greater_equal_t { typedef bool result_type; greater_equal_t(Type t, Ftor f) : m_t(t), m_f(f) {} template bool operator () (Arg& a) { return m_t >= m_f(a); } Type m_t; Ftor m_f; }; template inline greater_equal_t greater_equal(Type t, Ftor f) { return greater_equal_t(t, f); } template struct invert : public std::unary_function { Tp operator () (const Tp& x) const { return ~x; } }; template struct on_t : public std::unary_function { typedef typename Dest::result_type result_type; on_t(Src s, Dest d) : m_dest(d), m_src(s) {} result_type operator () (typename reference_fix::type arg) { return m_dest(m_src(arg)); } Dest m_dest; Src m_src; }; template inline on_t on(Src s, Dest d) { return on_t(s, d); } template struct on2_t : public std::binary_function { typedef typename Dest::result_type result_type; on2_t(Src s, Dest d) : m_dest(d), m_src(s) {} result_type operator () (typename reference_fix::type first, typename reference_fix::type second) { return m_dest(m_src(first), second); } Dest m_dest; Src m_src; }; template inline on2_t on2(Src s, Dest d) { return on2_t(s, d); } // Creates a functor for accessing a member. template struct mem_ptr_t : public std::unary_function { mem_ptr_t(Member Class::*m) : m_member(m) {} Member& operator () (Class* c) { return c->*m_member; } const Member& operator () (const Class* c) { return c->*m_member; } Member Class::*m_member; }; template inline mem_ptr_t mem_ptr(Member Class::*m) { return mem_ptr_t(m); } template struct mem_ref_t : public std::unary_function { mem_ref_t(Member Class::*m) : m_member(m) {} Member& operator () (Class& c) { return c.*m_member; } Member Class::*m_member; }; template struct const_mem_ref_t : public std::unary_function { const_mem_ref_t(const Member Class::*m) : m_member(m) {} const Member& operator () (const Class& c) { return c.*m_member; } const Member Class::*m_member; }; template inline mem_ref_t mem_ref(Member Class::*m) { return mem_ref_t(m); } template inline const_mem_ref_t const_mem_ref(const Member Class::*m) { return const_mem_ref_t(m); } template struct if_then_t { if_then_t(Cond c, Then t) : m_cond(c), m_then(t) {} template void operator () (Arg& a) { if (m_cond(a)) m_then(a); } Cond m_cond; Then m_then; }; template inline if_then_t if_then(Cond c, Then t) { return if_then_t(c, t); } template struct call_delete : public std::unary_function { void operator () (T* t) { delete t; } }; template inline void call_delete_func(T* t) { delete t; } template class bind1st_t : public std::unary_function { public: typedef typename reference_fix::type value_type; typedef typename reference_fix::type argument_type; bind1st_t(const Operation& op, const value_type v) : m_op(op), m_value(v) {} typename Operation::result_type operator () (const argument_type arg) { return m_op(m_value, arg); } protected: Operation m_op; value_type m_value; }; template inline bind1st_t bind1st(const Operation& op, const Type& val) { return bind1st_t(op, val); } template class bind2nd_t : public std::unary_function { public: typedef typename reference_fix::type argument_type; typedef typename reference_fix::type value_type; bind2nd_t(const Operation& op, const value_type v) : m_op(op), m_value(v) {} typename Operation::result_type operator () (argument_type arg) { return m_op(arg, m_value); } protected: Operation m_op; value_type m_value; }; template inline bind2nd_t bind2nd(const Operation& op, const Type& val) { return bind2nd_t(op, val); } // Lightweight callback function including pointer to object. Should // be replaced by TR1 stuff later. Requires an object to bind, instead // of using a seperate functor for that. template class ptr_fun0 { public: typedef Ret result_type; typedef Ret (*Function)(); ptr_fun0() {} ptr_fun0(Function f) : m_function(f) {} bool is_valid() const { return m_function; } Ret operator () () { return m_function(); } private: Function m_function; }; template class mem_fun0 { public: typedef Ret result_type; typedef Ret (Object::*Function)(); mem_fun0() : m_object(NULL) {} mem_fun0(Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } Ret operator () () { return (m_object->*m_function)(); } private: Object* m_object; Function m_function; }; template class const_mem_fun0 { public: typedef Ret result_type; typedef Ret (Object::*Function)() const; const_mem_fun0() : m_object(NULL) {} const_mem_fun0(const Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } Ret operator () () const { return (m_object->*m_function)(); } private: const Object* m_object; Function m_function; }; template class mem_fun1 { public: typedef Ret result_type; typedef Ret (Object::*Function)(Arg1); mem_fun1() : m_object(NULL) {} mem_fun1(Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } Ret operator () (Arg1 a1) { return (m_object->*m_function)(a1); } private: Object* m_object; Function m_function; }; template class const_mem_fun1 { public: typedef Ret result_type; typedef Ret (Object::*Function)(Arg1) const; const_mem_fun1() : m_object(NULL) {} const_mem_fun1(const Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } Ret operator () (Arg1 a1) const { return (m_object->*m_function)(a1); } private: const Object* m_object; Function m_function; }; template class mem_fun2 : public std::binary_function { public: typedef Ret result_type; typedef Ret (Object::*Function)(Arg1, Arg2); typedef Object object_type; mem_fun2() : m_object(NULL) {} mem_fun2(Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } object_type* object() { return m_object; } const object_type* object() const { return m_object; } Ret operator () (Arg1 a1, Arg2 a2) { return (m_object->*m_function)(a1, a2); } private: Object* m_object; Function m_function; }; template class mem_fun3 { public: typedef Ret result_type; typedef Ret (Object::*Function)(Arg1, Arg2, Arg3); mem_fun3() : m_object(NULL) {} mem_fun3(Object* o, Function f) : m_object(o), m_function(f) {} bool is_valid() const { return m_object; } Ret operator () (Arg1 a1, Arg2 a2, Arg3 a3) { return (m_object->*m_function)(a1, a2, a3); } private: Object* m_object; Function m_function; }; template inline ptr_fun0 ptr_fun(Ret (*f)()) { return ptr_fun0(f); } template inline mem_fun0 make_mem_fun(Object* o, Ret (Object::*f)()) { return mem_fun0(o, f); } template inline const_mem_fun0 make_mem_fun(const Object* o, Ret (Object::*f)() const) { return const_mem_fun0(o, f); } template inline mem_fun1 make_mem_fun(Object* o, Ret (Object::*f)(Arg1)) { return mem_fun1(o, f); } template inline const_mem_fun1 make_mem_fun(const Object* o, Ret (Object::*f)(Arg1) const) { return const_mem_fun1(o, f); } template inline mem_fun2 make_mem_fun(Object* o, Ret (Object::*f)(Arg1, Arg2)) { return mem_fun2(o, f); } template inline mem_fun3 make_mem_fun(Object* o, Ret (Object::*f)(Arg1, Arg2, Arg3)) { return mem_fun3(o, f); } template inline void slot_list_call(const Container& slot_list) { if (slot_list.empty()) return; typename Container::const_iterator first = slot_list.begin(); typename Container::const_iterator next = slot_list.begin(); while (++next != slot_list.end()) { (*first)(); first = next; } (*first)(); } template inline void slot_list_call(const Container& slot_list, Arg1 arg1) { if (slot_list.empty()) return; typename Container::const_iterator first = slot_list.begin(); typename Container::const_iterator next = slot_list.begin(); while (++next != slot_list.end()) { (*first)(arg1); first = next; } (*first)(arg1); } template inline void slot_list_call(const Container& slot_list, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { if (slot_list.empty()) return; typename Container::const_iterator first = slot_list.begin(); typename Container::const_iterator next = slot_list.begin(); while (++next != slot_list.end()) { (*first)(arg1, arg2, arg3, arg4); first = next; } (*first)(arg1, arg2, arg3, arg4); } } #endif libtorrent-0.13.6/rak/partial_queue.h000066400000000000000000000136731257211073700175720ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_PARTIAL_QUEUE_H #define RAK_PARTIAL_QUEUE_H #include #include #include namespace rak { // First step, don't allow overflowing to the next layer. Only disable // the above layers for now. // We also include 0 in a single layer as some chunk may be available // only through seeders. class partial_queue { public: typedef uint8_t key_type; typedef uint32_t mapped_type; typedef uint16_t size_type; typedef std::pair size_pair_type; static const size_type num_layers = 8; partial_queue() : m_data(NULL), m_maxLayerSize(0) {} ~partial_queue() { disable(); } bool is_full() const { return m_ceiling == 0; } bool is_layer_full(size_type l) const { return m_layers[l].second >= m_maxLayerSize; } bool is_enabled() const { return m_data != NULL; } // Add check to see if we can add more. Also make it possible to // check how full we are in the lower parts so the caller knows when // he can stop searching. // // Though propably not needed, as we must continue til the first // layer is full. size_type max_size() const { return m_maxLayerSize * num_layers; } size_type max_layer_size() const { return m_maxLayerSize; } // Must be less that or equal to (max size_type) / num_layers. void enable(size_type ls); void disable(); void clear(); // Safe to call while pop'ing and it will not reuse pop'ed indices // so it is guaranteed to reach max_size at some point. This will // ensure that the user needs to refill with new data at regular // intervals. bool insert(key_type key, mapped_type value); // Only call this when pop'ing as it moves the index. bool prepare_pop(); mapped_type pop(); private: partial_queue(const partial_queue&); void operator = (const partial_queue&); static size_type ceiling(size_type layer) { return (2 << layer) - 1; } void find_non_empty(); mapped_type* m_data; size_type m_maxLayerSize; size_type m_index; size_type m_ceiling; size_pair_type m_layers[num_layers]; }; inline void partial_queue::enable(size_type ls) { if (ls == 0) throw std::logic_error("partial_queue::enable(...) ls == 0."); delete [] m_data; m_data = new mapped_type[ls * num_layers]; m_maxLayerSize = ls; } inline void partial_queue::disable() { delete [] m_data; m_data = NULL; m_maxLayerSize = 0; } inline void partial_queue::clear() { if (m_data == NULL) return; m_index = 0; m_ceiling = ceiling(num_layers - 1); std::memset(m_layers, 0, num_layers * sizeof(size_pair_type)); } inline bool partial_queue::insert(key_type key, mapped_type value) { if (key >= m_ceiling) return false; size_type idx = 0; // Hmm... since we already check the 'm_ceiling' above, we only need // to find the target layer. Could this be calculated directly? while (key >= ceiling(idx)) ++idx; m_index = std::min(m_index, idx); // Currently don't allow overflow. if (is_layer_full(idx)) throw std::logic_error("partial_queue::insert(...) layer already full."); //return false; m_data[m_maxLayerSize * idx + m_layers[idx].second] = value; m_layers[idx].second++; if (is_layer_full(idx)) // Set the ceiling to 0 when layer 0 is full so no more values can // be inserted. m_ceiling = idx > 0 ? ceiling(idx - 1) : 0; return true; } // is_empty() will iterate to the first layer with un-popped elements // and return true, else return false when it reaches a overflowed or // the last layer. inline bool partial_queue::prepare_pop() { while (m_layers[m_index].first == m_layers[m_index].second) { if (is_layer_full(m_index) || m_index + 1 == num_layers) return false; m_index++; } return true; } inline partial_queue::mapped_type partial_queue::pop() { if (m_index >= num_layers || m_layers[m_index].first >= m_layers[m_index].second) throw std::logic_error("partial_queue::pop() bad state."); return m_data[m_index * m_maxLayerSize + m_layers[m_index].first++]; } } #endif libtorrent-0.13.6/rak/path.h000066400000000000000000000054321257211073700156600ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Various functions for manipulating file paths. Also consider making // a directory iterator. #ifndef RAK_PATH_H #define RAK_PATH_H #include #include namespace rak { inline std::string path_expand(const std::string& path) { if (path.empty() || path[0] != '~') return path; char* home = std::getenv("HOME"); if (home == NULL) return path; return home + path.substr(1); } // Don't inline this... // // Same strlcpy as found in *bsd. inline size_t strlcpy(char *dest, const char *src, size_t size) { size_t n = size; const char* first = src; if (n != 0) { while (--n != 0) if ((*dest++ = *src++) == '\0') break; } if (n == 0) { if (size != 0) *dest = '\0'; while (*src++) ; } return src - first - 1; } inline char* path_expand(const char* src, char* first, char* last) { if (*src == '~') { char* home = std::getenv("HOME"); if (home == NULL) return first; first += strlcpy(first, home, std::distance(first, last)); if (first > last) return last; src++; } return std::max(first + strlcpy(first, src, std::distance(first, last)), last); } } #endif libtorrent-0.13.6/rak/priority_queue.h000066400000000000000000000112531257211073700200070ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // priority_queue is a priority queue implemented using a binary // heap. It can contain multiple instances of a value. #ifndef RAK_PRIORITY_QUEUE_H #define RAK_PRIORITY_QUEUE_H #include #include #include namespace rak { template > class priority_queue : public std::vector { public: typedef std::vector base_type; typedef typename base_type::reference reference; typedef typename base_type::const_reference const_reference; typedef typename base_type::iterator iterator; typedef typename base_type::const_iterator const_iterator; typedef typename base_type::value_type value_type; using base_type::begin; using base_type::end; using base_type::size; using base_type::empty; using base_type::clear; priority_queue(Compare l = Compare(), Equal e = Equal()) : m_compare(l), m_equal(e) {} const_reference top() const { return base_type::front(); } void pop() { std::pop_heap(begin(), end(), m_compare); base_type::pop_back(); } void push(const value_type& value) { base_type::push_back(value); std::push_heap(begin(), end(), m_compare); } template iterator find(const Key& key) { return std::find_if(begin(), end(), std::bind2nd(m_equal, key)); } template bool erase(const Key& key) { iterator itr = find(key); if (itr == end()) return false; erase(itr); return true; } // Removes 'itr' from the queue. This assumes 'itr' has been // modified such that it has a higher priority than any other // element in the queue. void erase(iterator itr) { // std::push_heap(begin(), ++itr, m_compare); // pop(); base_type::erase(itr); std::make_heap(begin(), end(), m_compare); } private: Compare m_compare; Equal m_equal; }; // Iterate while the top node has higher priority, as 'Compare' // returns false. template class queue_pop_iterator : public std::iterator { public: typedef Queue container_type; queue_pop_iterator() : m_queue(NULL) {} queue_pop_iterator(Queue* q, Compare c) : m_queue(q), m_compare(c) {} queue_pop_iterator& operator ++ () { m_queue->pop(); return *this; } queue_pop_iterator& operator ++ (int) { m_queue->pop(); return *this; } typename container_type::const_reference operator * () { return m_queue->top(); } bool operator != (const queue_pop_iterator& itr) { return !m_queue->empty() && !m_compare(m_queue->top()); } bool operator == (const queue_pop_iterator& itr) { return m_queue->empty() || m_compare(m_queue->top()); } private: Queue* m_queue; Compare m_compare; }; template inline queue_pop_iterator queue_popper(Queue& queue, Compare comp) { return queue_pop_iterator(&queue, comp); } } #endif libtorrent-0.13.6/rak/priority_queue_default.h000066400000000000000000000112431257211073700215120ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_PRIORITY_QUEUE_DEFAULT_H #define RAK_PRIORITY_QUEUE_DEFAULT_H #include #include #include #include #include "torrent/exceptions.h" namespace rak { class priority_item { public: typedef std::tr1::function slot_void; priority_item() {} ~priority_item() { if (is_queued()) throw torrent::internal_error("priority_item::~priority_item() called on a queued item."); m_time = timer(); m_slot = slot_void(); } bool is_valid() const { return (bool)m_slot; } bool is_queued() const { return m_time != timer(); } slot_void& slot() { return m_slot; } const timer& time() const { return m_time; } void clear_time() { m_time = timer(); } void set_time(const timer& t) { m_time = t; } bool compare(const timer& t) const { return m_time > t; } private: priority_item(const priority_item&); void operator = (const priority_item&); timer m_time; slot_void m_slot; }; struct priority_compare { bool operator () (const priority_item* const p1, const priority_item* const p2) const { return p1->time() > p2->time(); } }; typedef std::equal_to priority_equal; typedef priority_queue > priority_queue_default; inline void priority_queue_perform(priority_queue_default* queue, timer t) { while (!queue->empty() && queue->top()->time() <= t) { priority_item* v = queue->top(); queue->pop(); v->clear_time(); v->slot()(); } } inline void priority_queue_insert(priority_queue_default* queue, priority_item* item, timer t) { if (t == timer()) throw torrent::internal_error("priority_queue_insert(...) received a bad timer."); if (!item->is_valid()) throw torrent::internal_error("priority_queue_insert(...) called on an invalid item."); if (item->is_queued()) throw torrent::internal_error("priority_queue_insert(...) called on an already queued item."); if (queue->find(item) != queue->end()) throw torrent::internal_error("priority_queue_insert(...) item found in queue."); item->set_time(t); queue->push(item); } inline void priority_queue_erase(priority_queue_default* queue, priority_item* item) { if (!item->is_queued()) return; // Check is_valid() after is_queued() so that it is safe to call // erase on untouched instances. if (!item->is_valid()) throw torrent::internal_error("priority_queue_erase(...) called on an invalid item."); // Clear time before erasing to force it to the top. item->clear_time(); if (!queue->erase(item)) throw torrent::internal_error("priority_queue_erase(...) could not find item in queue."); if (queue->find(item) != queue->end()) throw torrent::internal_error("priority_queue_erase(...) item still in queue."); } } #endif libtorrent-0.13.6/rak/regex.h000066400000000000000000000066171257211073700160440ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // This is a hacked up whole string pattern matching. Replace with // TR1's regex when that becomes widely available. It is intended for // small strings. #ifndef RAK_REGEX_H #define RAK_REGEX_H #include #include #include #include #include namespace rak { class regex : public std::unary_function { public: regex() {} regex(const std::string& p) : m_pattern(p) {} const std::string& pattern() const { return m_pattern; } bool operator () (const std::string& p) const; private: std::string m_pattern; }; // This isn't optimized, or very clean. A simple hack that should work. inline bool regex::operator () (const std::string& text) const { if (m_pattern.empty() || text.empty() || (m_pattern[0] != '*' && m_pattern[0] != text[0])) return false; // Replace with unordered_vector? std::list paths; paths.push_front(0); for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) { for (std::list::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) { unsigned int next = *itrPaths + 1; if (m_pattern[*itrPaths] != '*') itrPaths = paths.erase(itrPaths); else itrPaths++; // When we reach the end of 'm_pattern', we don't have a whole // match of 'text'. if (next == m_pattern.size()) continue; // Push to the back so that '*' will match zero length strings. if (m_pattern[next] == '*') paths.push_back(next); if (m_pattern[next] == *itrText) paths.push_front(next); } if (paths.empty()) return false; } return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end(); } } #endif libtorrent-0.13.6/rak/socket_address.h000066400000000000000000000327401257211073700177230ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Wrappers for the various sockaddr types with focus on zero-copy // casting between the original type and the wrapper class. // // The default ctor does not initialize any data. // // _n suffixes indicate that the argument or return value is in // network byte order, _h that they are in hardware byte order. // Add define for inet6 scope id? #ifndef RAK_SOCKET_ADDRESS_H #define RAK_SOCKET_ADDRESS_H #include #include #include #include #include #include #include namespace rak { class socket_address_inet; class socket_address_inet6; class socket_address { public: static const sa_family_t af_inet = AF_INET; static const int pf_inet = PF_INET; static const sa_family_t af_inet6 = AF_INET6; static const int pf_inet6 = PF_INET6; static const sa_family_t af_unspec = AF_UNSPEC; static const int pf_unspec = PF_UNSPEC; #ifdef AF_LOCAL static const sa_family_t af_local = AF_LOCAL; static const int pf_local = PF_LOCAL; #else static const sa_family_t af_local = AF_UNIX; static const int pf_local = PF_UNIX; #endif bool is_valid() const; bool is_bindable() const; bool is_address_any() const; // Should we need to set AF_UNSPEC? void clear() { std::memset(this, 0, sizeof(socket_address)); set_family(); } sa_family_t family() const { return m_sockaddr.sa_family; } void set_family() { m_sockaddr.sa_family = af_unspec; } uint16_t port() const; void set_port(uint16_t p); std::string address_str() const; bool address_c_str(char* buf, socklen_t size) const; // Attemts to set it as an inet, then an inet6 address. It will // never set anything but net addresses, no local/unix. bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); } bool set_address_c_str(const char* a); uint32_t length() const; socket_address_inet* sa_inet() { return reinterpret_cast(this); } const socket_address_inet* sa_inet() const { return reinterpret_cast(this); } sockaddr* c_sockaddr() { return &m_sockaddr; } sockaddr_in* c_sockaddr_inet() { return &m_sockaddrInet; } const sockaddr* c_sockaddr() const { return &m_sockaddr; } const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddrInet; } #ifdef RAK_USE_INET6 socket_address_inet6* sa_inet6() { return reinterpret_cast(this); } const socket_address_inet6* sa_inet6() const { return reinterpret_cast(this); } sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddrInet6; } const sockaddr_in6* c_sockaddr_inet6() const { return &m_sockaddrInet6; } #endif // Copy a socket address which has the length 'length. Zero out any // extranous bytes and ensure it does not go beyond the size of this // struct. void copy(const socket_address& src, size_t length); static socket_address* cast_from(sockaddr* sa) { return reinterpret_cast(sa); } static const socket_address* cast_from(const sockaddr* sa) { return reinterpret_cast(sa); } // The different families will be sorted according to the // sa_family_t's numeric value. bool operator == (const socket_address& rhs) const; bool operator < (const socket_address& rhs) const; bool operator == (const sockaddr& rhs) const { return *this == *cast_from(&rhs); } bool operator == (const sockaddr* rhs) const { return *this == *cast_from(rhs); } bool operator < (const sockaddr& rhs) const { return *this == *cast_from(&rhs); } bool operator < (const sockaddr* rhs) const { return *this == *cast_from(rhs); } private: union { sockaddr m_sockaddr; sockaddr_in m_sockaddrInet; #ifdef RAK_USE_INET6 sockaddr_in6 m_sockaddrInet6; #endif }; }; // Remeber to set the AF_INET. class socket_address_inet { public: bool is_any() const { return is_port_any() && is_address_any(); } bool is_valid() const { return !is_port_any() && !is_address_any(); } bool is_port_any() const { return port() == 0; } bool is_address_any() const { return m_sockaddr.sin_addr.s_addr == htonl(INADDR_ANY); } void clear() { std::memset(this, 0, sizeof(socket_address_inet)); set_family(); } uint16_t port() const { return ntohs(m_sockaddr.sin_port); } uint16_t port_n() const { return m_sockaddr.sin_port; } void set_port(uint16_t p) { m_sockaddr.sin_port = htons(p); } void set_port_n(uint16_t p) { m_sockaddr.sin_port = p; } // Should address() return the uint32_t? in_addr address() const { return m_sockaddr.sin_addr; } uint32_t address_h() const { return ntohl(m_sockaddr.sin_addr.s_addr); } uint32_t address_n() const { return m_sockaddr.sin_addr.s_addr; } std::string address_str() const; bool address_c_str(char* buf, socklen_t size) const; void set_address(in_addr a) { m_sockaddr.sin_addr = a; } void set_address_h(uint32_t a) { m_sockaddr.sin_addr.s_addr = htonl(a); } void set_address_n(uint32_t a) { m_sockaddr.sin_addr.s_addr = a; } bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); } bool set_address_c_str(const char* a); void set_address_any() { set_port(0); set_address_h(INADDR_ANY); } sa_family_t family() const { return m_sockaddr.sin_family; } void set_family() { m_sockaddr.sin_family = AF_INET; } sockaddr* c_sockaddr() { return reinterpret_cast(&m_sockaddr); } sockaddr_in* c_sockaddr_inet() { return &m_sockaddr; } const sockaddr* c_sockaddr() const { return reinterpret_cast(&m_sockaddr); } const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; } bool operator == (const socket_address_inet& rhs) const; bool operator < (const socket_address_inet& rhs) const; private: struct sockaddr_in m_sockaddr; }; // Unique key for the address, excluding port numbers etc. class socket_address_key { public: // socket_address_host_key() {} socket_address_key(const socket_address& sa) { *this = sa; } socket_address_key& operator = (const socket_address& sa) { if (sa.family() == 0) { std::memset(this, 0, sizeof(socket_address_key)); } else if (sa.family() == socket_address::af_inet) { // Using hardware order as we use operator < to compare when // using inet only. m_addr.s_addr = sa.sa_inet()->address_h(); } else { // When we implement INET6 handling, embed the ipv4 address in // the ipv6 address. throw std::logic_error("socket_address_key(...) received an unsupported protocol family."); } return *this; } // socket_address_key& operator = (const socket_address_key& sa) { // } bool operator < (const socket_address_key& sa) const { // Compare the memory area instead. return m_addr.s_addr < sa.m_addr.s_addr; } private: union { in_addr m_addr; // #ifdef RAK_USE_INET6 // in_addr6 m_addr6; // #endif }; }; inline bool socket_address::is_valid() const { switch (family()) { case af_inet: return sa_inet()->is_valid(); // case af_inet6: // return sa_inet6().is_valid(); default: return false; } } inline bool socket_address::is_bindable() const { switch (family()) { case af_inet: return !sa_inet()->is_address_any(); default: return false; } } inline bool socket_address::is_address_any() const { switch (family()) { case af_inet: return sa_inet()->is_address_any(); default: return true; } } inline uint16_t socket_address::port() const { switch (family()) { case af_inet: return sa_inet()->port(); default: return 0; } } inline void socket_address::set_port(uint16_t p) { switch (family()) { case af_inet: return sa_inet()->set_port(p); default: break; } } inline std::string socket_address::address_str() const { switch (family()) { case af_inet: return sa_inet()->address_str(); default: return std::string(); } } inline bool socket_address::address_c_str(char* buf, socklen_t size) const { switch (family()) { case af_inet: return sa_inet()->address_c_str(buf, size); default: return false; } } inline bool socket_address::set_address_c_str(const char* a) { if (sa_inet()->set_address_c_str(a)) { sa_inet()->set_family(); return true; } else { return false; } } // Is the zero length really needed, should we require some length? inline uint32_t socket_address::length() const { switch(family()) { case af_inet: return sizeof(sockaddr_in); default: return 0; } } inline void socket_address::copy(const socket_address& src, size_t length) { length = std::min(length, sizeof(socket_address)); // Does this get properly optimized? std::memset(this, 0, sizeof(socket_address)); std::memcpy(this, &src, length); } // Should we be able to compare af_unspec? inline bool socket_address::operator == (const socket_address& rhs) const { if (family() != rhs.family()) return false; switch (family()) { case af_inet: return *sa_inet() == *rhs.sa_inet(); // case af_inet6: // return *sa_inet6() == *rhs.sa_inet6(); default: throw std::logic_error("socket_address::operator == (rhs) invalid type comparison."); } } inline bool socket_address::operator < (const socket_address& rhs) const { if (family() != rhs.family()) return family() < rhs.family(); switch (family()) { case af_inet: return *sa_inet() < *rhs.sa_inet(); // case af_inet6: // return *sa_inet6() < *rhs.sa_inet6(); default: throw std::logic_error("socket_address::operator < (rhs) invalid type comparison."); } } inline std::string socket_address_inet::address_str() const { char buf[INET_ADDRSTRLEN]; if (!address_c_str(buf, INET_ADDRSTRLEN)) return std::string(); return std::string(buf); } inline bool socket_address_inet::address_c_str(char* buf, socklen_t size) const { return inet_ntop(family(), &m_sockaddr.sin_addr, buf, size); } inline bool socket_address_inet::set_address_c_str(const char* a) { return inet_pton(AF_INET, a, &m_sockaddr.sin_addr); } inline bool socket_address_inet::operator == (const socket_address_inet& rhs) const { return m_sockaddr.sin_addr.s_addr == rhs.m_sockaddr.sin_addr.s_addr && m_sockaddr.sin_port == rhs.m_sockaddr.sin_port; } inline bool socket_address_inet::operator < (const socket_address_inet& rhs) const { return m_sockaddr.sin_addr.s_addr < rhs.m_sockaddr.sin_addr.s_addr || (m_sockaddr.sin_addr.s_addr == rhs.m_sockaddr.sin_addr.s_addr && m_sockaddr.sin_port < rhs.m_sockaddr.sin_port); } } #endif libtorrent-0.13.6/rak/string_manip.h000066400000000000000000000224401257211073700174140ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_STRING_MANIP_H #define RAK_STRING_MANIP_H #include #include #include #include #include namespace rak { // Use these trim functions until n1872 is widely supported. template Sequence trim_begin(const Sequence& seq) { if (seq.empty() || !std::isspace(*seq.begin())) return seq; typename Sequence::size_type pos = 0; while (pos != seq.length() && std::isspace(seq[pos])) pos++; return seq.substr(pos, seq.length() - pos); } template Sequence trim_end(const Sequence& seq) { if (seq.empty() || !std::isspace(*(--seq.end()))) return seq; typename Sequence::size_type pos = seq.size(); while (pos != 0 && std::isspace(seq[pos - 1])) pos--; return seq.substr(0, pos); } template Sequence trim(const Sequence& seq) { return trim_begin(trim_end(seq)); } template Sequence trim_begin_classic(const Sequence& seq) { if (seq.empty() || !std::isspace(*seq.begin(), std::locale::classic())) return seq; typename Sequence::size_type pos = 0; while (pos != seq.length() && std::isspace(seq[pos], std::locale::classic())) pos++; return seq.substr(pos, seq.length() - pos); } template Sequence trim_end_classic(const Sequence& seq) { if (seq.empty() || !std::isspace(*(--seq.end()), std::locale::classic())) return seq; typename Sequence::size_type pos = seq.size(); while (pos != 0 && std::isspace(seq[pos - 1], std::locale::classic())) pos--; return seq.substr(0, pos); } template Sequence trim_classic(const Sequence& seq) { return trim_begin_classic(trim_end_classic(seq)); } // Consider rewritting such that m_seq is replaced by first/last. template class split_iterator_t { public: typedef typename Sequence::const_iterator const_iterator; typedef typename Sequence::value_type value_type; split_iterator_t() {} split_iterator_t(const Sequence& seq, value_type delim) : m_seq(&seq), m_delim(delim), m_pos(seq.begin()), m_next(std::find(seq.begin(), seq.end(), delim)) { } Sequence operator * () { return Sequence(m_pos, m_next); } split_iterator_t& operator ++ () { m_pos = m_next; if (m_pos == m_seq->end()) return *this; m_pos++; m_next = std::find(m_pos, m_seq->end(), m_delim); return *this; } bool operator == (__UNUSED const split_iterator_t& itr) const { return m_pos == m_seq->end(); } bool operator != (__UNUSED const split_iterator_t& itr) const { return m_pos != m_seq->end(); } private: const Sequence* m_seq; value_type m_delim; const_iterator m_pos; const_iterator m_next; }; template inline split_iterator_t split_iterator(const Sequence& seq, typename Sequence::value_type delim) { return split_iterator_t(seq, delim); } template inline split_iterator_t split_iterator(__UNUSED const Sequence& seq) { return split_iterator_t(); } // Could optimize this abit. inline char hexchar_to_value(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'A' && c <= 'F') return 10 + c - 'A'; else return 10 + c - 'a'; } template inline char value_to_hexchar(Value v) { v >>= pos * 4; v &= 0xf; if (v < 0xA) return '0' + v; else return 'A' + v - 0xA; } template OutputIterator copy_escape_html(InputIterator first, InputIterator last, OutputIterator dest) { while (first != last) { if (std::isalpha(*first, std::locale::classic()) || std::isdigit(*first, std::locale::classic()) || *first == '-') { *(dest++) = *first; } else { *(dest++) = '%'; *(dest++) = value_to_hexchar<1>(*first); *(dest++) = value_to_hexchar<0>(*first); } ++first; } return dest; } template OutputIterator copy_escape_html(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) { while (first1 != last1) { if (std::isalpha(*first1, std::locale::classic()) || std::isdigit(*first1, std::locale::classic()) || *first1 == '-') { if (first2 == last2) break; else *(first2++) = *first1; } else { if (first2 == last2) break; else *(first2++) = '%'; if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1); if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1); } ++first1; } return first2; } template inline std::string copy_escape_html(Iterator first, Iterator last) { std::string dest; copy_escape_html(first, last, std::back_inserter(dest)); return dest; } template inline Sequence copy_escape_html(const Sequence& src) { Sequence dest; copy_escape_html(src.begin(), src.end(), std::back_inserter(dest)); return dest; } template inline std::string copy_escape_html_str(const Sequence& src) { std::string dest; copy_escape_html(src.begin(), src.end(), std::back_inserter(dest)); return dest; } // Consider support for larger than char type. template OutputIterator transform_hex(InputIterator first, InputIterator last, OutputIterator dest) { while (first != last) { *(dest++) = value_to_hexchar<1>(*first); *(dest++) = value_to_hexchar<0>(*first); ++first; } return dest; } template OutputIterator transform_hex(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) { while (first1 != last1) { if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1); if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1); ++first1; } return first2; } template inline Sequence transform_hex(const Sequence& src) { Sequence dest; transform_hex(src.begin(), src.end(), std::back_inserter(dest)); return dest; } template inline std::string transform_hex(Iterator first, Iterator last) { std::string dest; transform_hex(first, last, std::back_inserter(dest)); return dest; } template inline std::string transform_hex_str(const Sequence& seq) { std::string dest; transform_hex(seq.begin(), seq.end(), std::back_inserter(dest)); return dest; } template Sequence generate_random(size_t length) { Sequence s; s.reserve(length); std::generate_n(std::back_inserter(s), length, &::random); return s; } template inline bool is_all_alpha(Iterator first, Iterator last) { while (first != last) if (!std::isalpha(*first++, std::locale::classic())) return false; return true; } template inline bool is_all_alpha(const Sequence& src) { return is_all_alpha(src.begin(), src.end()); } template inline bool is_all_alnum(Iterator first, Iterator last) { while (first != last) if (!std::isalnum(*first++, std::locale::classic())) return false; return true; } template inline bool is_all_alnum(const Sequence& src) { return is_all_alnum(src.begin(), src.end()); } template inline bool is_all_name(Iterator first, Iterator last) { while (first != last) { if (!std::isalnum(*first, std::locale::classic()) && *first != '_') return false; first++; } return true; } template inline bool is_all_name(const Sequence& src) { return is_all_name(src.begin(), src.end()); } } #endif libtorrent-0.13.6/rak/timer.h000066400000000000000000000114161257211073700160430ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_TIMER_H #define RAK_TIMER_H #include #include #include namespace rak { // Don't convert negative Timer to timeval and then back to Timer, that will bork. class timer { public: timer(int64_t usec = 0) : m_time(usec) {} timer(timeval tv) : m_time((int64_t)(uint32_t)tv.tv_sec * 1000000 + (int64_t)(uint32_t)tv.tv_usec % 1000000) {} bool is_zero() const { return m_time == 0; } bool is_not_zero() const { return m_time != 0; } int32_t seconds() const { return m_time / 1000000; } int32_t seconds_ceiling() const { return (m_time + 1000000 - 1) / 1000000; } int64_t usec() const { return m_time; } timer round_seconds() const { return (m_time / 1000000) * 1000000; } timer round_seconds_ceiling() const { return ((m_time + 1000000 - 1) / 1000000) * 1000000; } timeval tval() const { timeval val; val.tv_sec = m_time / 1000000; val.tv_usec = m_time % 1000000; return val; } static timer current(); static int64_t current_seconds() { return current().seconds(); } static int64_t current_usec() { return current().usec(); } static timer from_minutes(uint32_t minutes) { return rak::timer((uint64_t)minutes * 60 * 1000000); } static timer from_seconds(uint32_t seconds) { return rak::timer((uint64_t)seconds * 1000000); } static timer from_milliseconds(uint32_t msec) { return rak::timer((uint64_t)msec * 1000); } static timer max() { return std::numeric_limits::max(); } bool operator < (const timer& t) const { return m_time < t.m_time; } bool operator > (const timer& t) const { return m_time > t.m_time; } bool operator <= (const timer& t) const { return m_time <= t.m_time; } bool operator >= (const timer& t) const { return m_time >= t.m_time; } bool operator == (const timer& t) const { return m_time == t.m_time; } bool operator != (const timer& t) const { return m_time != t.m_time; } timer operator - (const timer& t) const { return timer(m_time - t.m_time); } timer operator + (const timer& t) const { return timer(m_time + t.m_time); } timer operator * (int64_t t) const { return timer(m_time * t); } timer operator / (int64_t t) const { return timer(m_time / t); } timer operator -= (int64_t t) { m_time -= t; return *this; } timer operator -= (const timer& t) { m_time -= t.m_time; return *this; } timer operator += (int64_t t) { m_time += t; return *this; } timer operator += (const timer& t) { m_time += t.m_time; return *this; } private: int64_t m_time; }; inline timer timer::current() { timeval t; gettimeofday(&t, 0); return timer(t); } } #endif libtorrent-0.13.6/rak/unordered_vector.h000066400000000000000000000066411257211073700203000ustar00rootroot00000000000000// rak - Rakshasa's toolbox // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef RAK_UNORDERED_VECTOR_H #define RAK_UNORDERED_VECTOR_H #include namespace rak { template class unordered_vector : private std::vector<_Tp> { public: typedef std::vector<_Tp> Base; typedef typename Base::value_type value_type; typedef typename Base::pointer pointer; typedef typename Base::const_pointer const_pointer; typedef typename Base::reference reference; typedef typename Base::const_reference const_reference; typedef typename Base::size_type size_type; typedef typename Base::difference_type difference_type; typedef typename Base::allocator_type allocator_type; typedef typename Base::iterator iterator; typedef typename Base::reverse_iterator reverse_iterator; typedef typename Base::const_iterator const_iterator; typedef typename Base::const_reverse_iterator const_reverse_iterator; using Base::clear; using Base::empty; using Base::size; using Base::reserve; using Base::front; using Base::back; using Base::begin; using Base::end; using Base::rbegin; using Base::rend; using Base::push_back; using Base::pop_back; // Use the range erase function, the single element erase gets // overloaded. using Base::erase; iterator insert(iterator position, const value_type& x); iterator erase(iterator position); private: }; template typename unordered_vector<_Tp>::iterator unordered_vector<_Tp>::insert(iterator position, const value_type& x) { Base::push_back(x); return --end(); } template typename unordered_vector<_Tp>::iterator unordered_vector<_Tp>::erase(iterator position) { // We don't need to check if position == end - 1 since we then copy // to the position we pop later. *position = Base::back(); Base::pop_back(); return position; } } #endif libtorrent-0.13.6/scripts/000077500000000000000000000000001257211073700154615ustar00rootroot00000000000000libtorrent-0.13.6/scripts/attributes.m4000066400000000000000000000076501257211073700201210ustar00rootroot00000000000000# Functions to check for attributes support in compiler AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ AC_CACHE_CHECK([if compiler supports __attribute__((constructor))], [cc_cv_attribute_constructor], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void ctor() __attribute__((constructor)); void ctor() { }; ])], [cc_cv_attribute_constructor=yes], [cc_cv_attribute_constructor=no]) ]) if test "x$cc_cv_attribute_constructor" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_CONSTRUCTOR], 1, [Define this if the compiler supports the constructor attribute]) $1 else true $2 fi ]) AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ AC_CACHE_CHECK([if compiler supports __attribute__((format(printf, n, n)))], [cc_cv_attribute_format], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { } ])], [cc_cv_attribute_format=yes], [cc_cv_attribute_format=no]) ]) if test "x$cc_cv_attribute_format" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_FORMAT], 1, [Define this if the compiler supports the format attribute]) $1 else true $2 fi ]) AC_DEFUN([CC_ATTRIBUTE_INTERNAL], [ AC_CACHE_CHECK([if compiler supports __attribute__((visibility("internal")))], [cc_cv_attribute_internal], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((visibility("internal"))) internal_function() { } ])], [cc_cv_attribute_internal=yes], [cc_cv_attribute_internal=no]) ]) if test "x$cc_cv_attribute_internal" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_INTERNAL], 1, [Define this if the compiler supports the internal visibility attribute]) $1 else true $2 fi ]) AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ AC_LANG_PUSH(C++) tmp_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS -fvisibility=hidden" AC_CACHE_CHECK([if compiler supports __attribute__((visibility("default")))], [cc_cv_attribute_visibility], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void __attribute__((visibility("default"))) visibility_function() { } ])], [cc_cv_attribute_visibility=yes], [cc_cv_attribute_visibility=no]) ]) CXXFLAGS=$tmp_CXXFLAGS AC_LANG_POP(C++) if test "x$cc_cv_attribute_visibility" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_VISIBILITY], 1, [Define this if the compiler supports the visibility attributes.]) CXXFLAGS="$CXXFLAGS -fvisibility=hidden" $1 else true $2 fi ]) AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ AC_CACHE_CHECK([if compiler supports __attribute__((nonnull()))], [cc_cv_attribute_nonnull], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void some_function(void *foo, void *bar) __attribute__((nonnull())); void some_function(void *foo, void *bar) { } ])], [cc_cv_attribute_nonnull=yes], [cc_cv_attribute_nonnull=no]) ]) if test "x$cc_cv_attribute_nonnull" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_NONNULL], 1, [Define this if the compiler supports the nonnull attribute]) $1 else true $2 fi ]) AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ AC_CACHE_CHECK([if compiler supports __attribute__((unused))], [cc_cv_attribute_unused], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ void some_function(void *foo, __attribute__((unused)) void *bar); ])], [cc_cv_attribute_unused=yes], [cc_cv_attribute_unused=no]) ]) if test "x$cc_cv_attribute_unused" = "xyes"; then AC_DEFINE([SUPPORT_ATTRIBUTE_UNUSED], 1, [Define this if the compiler supports the unused attribute]) $1 else true $2 fi ]) AC_DEFUN([CC_FUNC_EXPECT], [ AC_CACHE_CHECK([if compiler has __builtin_expect function], [cc_cv_func_expect], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ int some_function() { int a = 3; return (int)__builtin_expect(a, 3); } ])], [cc_cv_func_expect=yes], [cc_cv_func_expect=no]) ]) if test "x$cc_cv_func_expect" = "xyes"; then AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, [Define this if the compiler supports __builtin_expect() function]) $1 else true $2 fi ]) libtorrent-0.13.6/scripts/ax_check_zlib.m4000066400000000000000000000124501257211073700205120ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_zlib.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_ZLIB([action-if-found], [action-if-not-found]) # # DESCRIPTION # # This macro searches for an installed zlib library. If nothing was # specified when calling configure, it searches first in /usr/local and # then in /usr, /opt/local and /sw. If the --with-zlib=DIR is specified, # it will try to find it in DIR/include/zlib.h and DIR/lib/libz.a. If # --without-zlib is specified, the library is not searched at all. # # If either the header file (zlib.h) or the library (libz) is not found, # shell commands 'action-if-not-found' is run. If 'action-if-not-found' is # not specified, the configuration exits on error, asking for a valid zlib # installation directory or --without-zlib. # # If both header file and library are found, shell commands # 'action-if-found' is run. If 'action-if-found' is not specified, the # default action appends '-I${ZLIB_HOME}/include' to CPFLAGS, appends # '-L$ZLIB_HOME}/lib' to LDFLAGS, prepends '-lz' to LIBS, and calls # AC_DEFINE(HAVE_LIBZ). You should use autoheader to include a definition # for this symbol in a config.h file. Sample usage in a C/C++ source is as # follows: # # #ifdef HAVE_LIBZ # #include # #endif /* HAVE_LIBZ */ # # LICENSE # # Copyright (c) 2008 Loic Dachary # Copyright (c) 2010 Bastien Chevreux # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 14 AU_ALIAS([CHECK_ZLIB], [AX_CHECK_ZLIB]) AC_DEFUN([AX_CHECK_ZLIB], # # Handle user hints # [AC_MSG_CHECKING(if zlib is wanted) zlib_places="/usr/local /usr /opt/local /sw" AC_ARG_WITH([zlib], [ --with-zlib=DIR root directory path of zlib installation @<:@defaults to /usr/local or /usr if not found in /usr/local@:>@ --without-zlib to disable zlib usage completely], [if test "$withval" != no ; then AC_MSG_RESULT(yes) if test -d "$withval" then zlib_places="$withval $zlib_places" else AC_MSG_WARN([Sorry, $withval does not exist, checking usual places]) fi else zlib_places= AC_MSG_RESULT(no) fi], [AC_MSG_RESULT(yes)]) # # Locate zlib, if wanted # if test -n "${zlib_places}" then # check the user supplied or any other more or less 'standard' place: # Most UNIX systems : /usr/local and /usr # MacPorts / Fink on OSX : /opt/local respectively /sw for ZLIB_HOME in ${zlib_places} ; do if test -f "${ZLIB_HOME}/include/zlib.h"; then break; fi ZLIB_HOME="" done ZLIB_OLD_LDFLAGS=$LDFLAGS ZLIB_OLD_CPPFLAGS=$CPPFLAGS if test -n "${ZLIB_HOME}"; then LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib" CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include" fi AC_LANG_SAVE AC_LANG_C AC_CHECK_LIB([z], [inflateEnd], [zlib_cv_libz=yes], [zlib_cv_libz=no]) AC_CHECK_HEADER([zlib.h], [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no]) AC_LANG_RESTORE if test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes" then # # If both library and header were found, action-if-found # m4_ifblank([$1],[ CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include" LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib" LIBS="-lz $LIBS" AC_DEFINE([HAVE_LIBZ], [1], [Define to 1 if you have `z' library (-lz)]) ],[ # Restore variables LDFLAGS="$ZLIB_OLD_LDFLAGS" CPPFLAGS="$ZLIB_OLD_CPPFLAGS" $1 ]) else # # If either header or library was not found, action-if-not-found # m4_default([$2],[ AC_MSG_ERROR([either specify a valid zlib installation with --with-zlib=DIR or disable zlib usage with --without-zlib]) ]) fi fi ]) libtorrent-0.13.6/scripts/ax_pthread.m4000066400000000000000000000304451257211073700200500ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 17 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="none pthreads -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; *-darwin*) ax_pthread_flags="none -pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *-osf* | *-hpux*) flag="-D_REENTRANT";; *solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], ax_cv_PTHREAD_PRIO_INHERIT, [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD libtorrent-0.13.6/scripts/checks.m4000066400000000000000000000336431257211073700171740ustar00rootroot00000000000000AC_DEFUN([TORRENT_CHECK_XFS], [ AC_MSG_CHECKING(for XFS support) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include int main() { struct xfs_flock64 l; ioctl(0, XFS_IOC_RESVSP64, &l); return 0; } ])], [ AC_DEFINE(USE_XFS, 1, Use XFS filesystem stuff.) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_WITHOUT_XFS], [ AC_ARG_WITH(xfs, AC_HELP_STRING([--without-xfs], [do not check for XFS filesystem support]), [ if test "$withval" = "yes"; then TORRENT_CHECK_XFS fi ], [ TORRENT_CHECK_XFS ]) ]) AC_DEFUN([TORRENT_WITH_XFS], [ AC_ARG_WITH(xfs, AC_HELP_STRING([--with-xfs], [check for XFS filesystem support]), [ if test "$withval" = "yes"; then TORRENT_CHECK_XFS fi ]) ]) AC_DEFUN([TORRENT_CHECK_EPOLL], [ AC_MSG_CHECKING(for epoll support) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main() { int fd = epoll_create(100); return 0; } ])], [ AC_DEFINE(USE_EPOLL, 1, Use epoll.) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_WITHOUT_EPOLL], [ AC_ARG_WITH(epoll, AC_HELP_STRING([--without-epoll], [do not check for epoll support]), [ if test "$withval" = "yes"; then TORRENT_CHECK_EPOLL fi ], [ TORRENT_CHECK_EPOLL ]) ]) AC_DEFUN([TORRENT_CHECK_KQUEUE], [ AC_MSG_CHECKING(for kqueue support) AC_LINK_IFELSE([AC_LANG_SOURCE([ #include /* Because OpenBSD's sys/event.h fails to compile otherwise. Yeah... */ #include int main() { int fd = kqueue(); return 0; } ])], [ AC_DEFINE(USE_KQUEUE, 1, Use kqueue.) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_CHECK_KQUEUE_SOCKET_ONLY], [ AC_MSG_CHECKING(whether kqueue supports pipes and ptys) AC_RUN_IFELSE([AC_LANG_SOURCE([ #include #include #include #include #include int main() { struct kevent ev@<:@2@:>@, ev_out@<:@2@:>@; struct timespec ts = { 0, 0 }; int pfd@<:@2@:>@, pty@<:@2@:>@, kfd, n; char buffer@<:@9001@:>@; if (pipe(pfd) == -1) return 1; if (fcntl(pfd@<:@1@:>@, F_SETFL, O_NONBLOCK) == -1) return 2; while ((n = write(pfd@<:@1@:>@, buffer, sizeof(buffer))) == sizeof(buffer)); if ((pty@<:@0@:>@=posix_openpt(O_RDWR | O_NOCTTY)) == -1) return 3; if ((pty@<:@1@:>@=grantpt(pty@<:@0@:>@)) == -1) return 4; EV_SET(ev+0, pfd@<:@1@:>@, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL); EV_SET(ev+1, pty@<:@1@:>@, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); if ((kfd = kqueue()) == -1) return 5; if ((n = kevent(kfd, ev, 2, NULL, 0, NULL)) == -1) return 6; if (ev_out@<:@0@:>@.flags & EV_ERROR) return 7; if (ev_out@<:@1@:>@.flags & EV_ERROR) return 8; read(pfd@<:@0@:>@, buffer, sizeof(buffer)); if ((n = kevent(kfd, NULL, 0, ev_out, 2, &ts)) < 1) return 9; return 0; } ])], [ AC_MSG_RESULT(yes) ], [ AC_DEFINE(KQUEUE_SOCKET_ONLY, 1, kqueue only supports sockets.) AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_WITH_KQUEUE], [ AC_ARG_WITH(kqueue, AC_HELP_STRING([--with-kqueue], [enable kqueue [[default=no]]]), [ if test "$withval" = "yes"; then TORRENT_CHECK_KQUEUE TORRENT_CHECK_KQUEUE_SOCKET_ONLY fi ]) ]) AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [ AC_ARG_WITH(kqueue, AC_HELP_STRING([--without-kqueue], [do not check for kqueue support]), [ if test "$withval" = "yes"; then TORRENT_CHECK_KQUEUE TORRENT_CHECK_KQUEUE_SOCKET_ONLY fi ], [ TORRENT_CHECK_KQUEUE TORRENT_CHECK_KQUEUE_SOCKET_ONLY ]) ]) AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [ AC_ARG_WITH(variable-fdset, AC_HELP_STRING([--without-variable-fdset], [do not use non-portable variable sized fd_set's]), [ if test "$withval" = "yes"; then AC_DEFINE(USE_VARIABLE_FDSET, 1, defined when we allow the use of fd_set's of any size) fi ], [ AC_DEFINE(USE_VARIABLE_FDSET, 1, defined when we allow the use of fd_set's of any size) ]) ]) AC_DEFUN([TORRENT_CHECK_FALLOCATE], [ AC_MSG_CHECKING(for fallocate) AC_TRY_LINK([#include #include ],[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0; ], [ AC_DEFINE(HAVE_FALLOCATE, 1, Linux's fallocate supported.) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [ AC_MSG_CHECKING(for posix_fallocate) AC_TRY_LINK([#include ],[ posix_fallocate(0, 0, 0); ], [ AC_DEFINE(USE_POSIX_FALLOCATE, 1, posix_fallocate supported.) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [ AC_ARG_WITH(posix-fallocate, AC_HELP_STRING([--with-posix-fallocate], [check for and use posix_fallocate to allocate files]), [ if test "$withval" = "yes"; then TORRENT_CHECK_POSIX_FALLOCATE fi ]) ]) AC_DEFUN([TORRENT_CHECK_STATVFS], [ AC_CHECK_HEADERS(sys/vfs.h sys/statvfs.h sys/statfs.h) AC_MSG_CHECKING(for statvfs) AC_TRY_LINK( [ #if HAVE_SYS_VFS_H #include #endif #if HAVE_SYS_STATVFS_H #include #endif #if HAVE_SYS_STATFS_H #include #endif ],[ struct statvfs s; fsblkcnt_t c; statvfs("", &s); fstatvfs(0, &s); ], [ AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd) AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename) AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function) AC_DEFINE(FS_STAT_SIZE_TYPE, [unsigned long], Type of block size member in stat struct) AC_DEFINE(FS_STAT_COUNT_TYPE, [fsblkcnt_t], Type of block count member in stat struct) AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_frsize)], Determine the block size) AC_MSG_RESULT(ok) have_stat_vfs=yes ], [ AC_MSG_RESULT(no) have_stat_vfs=no ]) ]) AC_DEFUN([TORRENT_CHECK_STATFS], [ AC_CHECK_HEADERS(sys/statfs.h sys/param.h sys/mount.h) AC_MSG_CHECKING(for statfs) AC_TRY_LINK( [ #if HAVE_SYS_STATFS_H #include #endif #if HAVE_SYS_PARAM_H #include #endif #if HAVE_SYS_MOUNT_H #include #endif ],[ struct statfs s; statfs("", &s); fstatfs(0, &s); ], [ AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd) AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename) AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function) AC_DEFINE(FS_STAT_SIZE_TYPE, [long], Type of block size member in stat struct) AC_DEFINE(FS_STAT_COUNT_TYPE, [long], Type of block count member in stat struct) AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_bsize)], Determine the block size) AC_MSG_RESULT(ok) have_stat_vfs=yes ], [ AC_MSG_RESULT(no) have_stat_vfs=no ]) ]) AC_DEFUN([TORRENT_DISABLED_STATFS], [ AC_DEFINE(FS_STAT_FD, [(errno = ENOSYS) == 0], Function to determine filesystem stats from fd) AC_DEFINE(FS_STAT_FN, [(errno = ENOSYS) == 0], Function to determine filesystem stats from filename) AC_DEFINE(FS_STAT_STRUCT, [struct {blocksize_type f_bsize; blockcount_type f_bavail;}], Type of second argument to statfs function) AC_DEFINE(FS_STAT_SIZE_TYPE, [int], Type of block size member in stat struct) AC_DEFINE(FS_STAT_COUNT_TYPE, [int], Type of block count member in stat struct) AC_DEFINE(FS_STAT_BLOCK_SIZE, [(4096)], Determine the block size) AC_MSG_RESULT(No filesystem stats available) ]) AC_DEFUN([TORRENT_WITHOUT_STATVFS], [ AC_ARG_WITH(statvfs, AC_HELP_STRING([--without-statvfs], [don't try to use statvfs to find free diskspace]), [ if test "$withval" = "yes"; then TORRENT_CHECK_STATVFS else have_stat_vfs=no fi ], [ TORRENT_CHECK_STATVFS ]) ]) AC_DEFUN([TORRENT_WITHOUT_STATFS], [ AC_ARG_WITH(statfs, AC_HELP_STRING([--without-statfs], [don't try to use statfs to find free diskspace]), [ if test "$have_stat_vfs" = "no"; then if test "$withval" = "yes"; then TORRENT_CHECK_STATFS else TORRENT_DISABLED_STATFS fi fi ], [ if test "$have_stat_vfs" = "no"; then TORRENT_CHECK_STATFS if test "$have_stat_vfs" = "no"; then TORRENT_DISABLED_STATFS fi fi ]) ]) AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [ AC_ARG_WITH(address-space, AC_HELP_STRING([--with-address-space=MB], [change the default address space size [[default=1024mb]]]), [ if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval]) else AC_MSG_ERROR(--with-address-space requires a parameter.) fi ], [ AC_CHECK_SIZEOF(long) if test $ac_cv_sizeof_long = 8; then AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 4096, Default address space size.) else AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.) fi ]) ]) AC_DEFUN([TORRENT_CHECK_TR1], [ AC_LANG_PUSH(C++) AC_MSG_CHECKING(for TR1 support) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include class Foo; typedef std::tr1::unordered_map Bar; ])], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_TR1, 1, Define to 1 if your C++ library supports the extensions from Technical Report 1) ], [ AC_MSG_RESULT(no) ] ) AC_LANG_POP(C++) ]) AC_DEFUN([TORRENT_CHECK_CXX11], [ AC_LANG_PUSH(C++) AC_MSG_CHECKING(for C++11 support) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include class Foo; typedef std::unordered_map Bar; union test { Bar b1; }; ])], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_CXX11, 1, Define to 1 if your C++ compiler has support for C++11.) ], [ AC_MSG_RESULT(no) ] ) AC_LANG_POP(C++) ]) AC_DEFUN([TORRENT_WITH_FASTCGI], [ AC_ARG_WITH(fastcgi, AC_HELP_STRING([--with-fastcgi=PATH], [enable FastCGI RPC support (DO NOT USE)]), [ AC_MSG_CHECKING([for FastCGI (DO NOT USE)]) if test "$withval" = "no"; then AC_MSG_RESULT(no) elif test "$withval" = "yes"; then CXXFLAGS="$CXXFLAGS" LIBS="$LIBS -lfcgi" AC_TRY_LINK( [ #include ],[ FCGX_Init(); ], [ AC_MSG_RESULT(ok) ], [ AC_MSG_RESULT(not found) AC_MSG_ERROR(Could not compile FastCGI test.) ]) AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.) else CXXFLAGS="$CXXFLAGS -I$withval/include" LIBS="$LIBS -lfcgi -L$withval/lib" AC_TRY_LINK( [ #include ],[ FCGX_Init(); ], [ AC_MSG_RESULT(ok) ], [ AC_MSG_RESULT(not found) AC_MSG_ERROR(Could not compile FastCGI test.) ]) AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.) fi ]) ]) AC_DEFUN([TORRENT_WITH_XMLRPC_C], [ AC_MSG_CHECKING(for XMLRPC-C) AC_ARG_WITH(xmlrpc-c, AC_HELP_STRING([--with-xmlrpc-c=PATH], [enable XMLRPC-C support]), [ if test "$withval" = "no"; then AC_MSG_RESULT(no) else if test "$withval" = "yes"; then xmlrpc_cc_prg="xmlrpc-c-config" else xmlrpc_cc_prg="$withval" fi if eval $xmlrpc_cc_prg --version 2>/dev/null >/dev/null; then CXXFLAGS="$CXXFLAGS `$xmlrpc_cc_prg --cflags server-util`" LIBS="$LIBS `$xmlrpc_cc_prg server-util --libs`" AC_TRY_LINK( [ #include ],[ xmlrpc_registry_new(NULL); ], [ AC_MSG_RESULT(ok) ], [ AC_MSG_RESULT(failed) AC_MSG_ERROR(Could not compile XMLRPC-C test.) ]) AC_DEFINE(HAVE_XMLRPC_C, 1, Support for XMLRPC-C.) else AC_MSG_RESULT(failed) AC_MSG_ERROR(Could not compile XMLRPC-C test.) fi fi ],[ AC_MSG_RESULT(ignored) ]) ]) AC_DEFUN([TORRENT_WITH_INOTIFY], [ AC_LANG_PUSH(C++) AC_CHECK_HEADERS([sys/inotify.h mcheck.h]) AC_MSG_CHECKING([whether sys/inotify.h actually works]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main(int,const char**) { return (-1 == inotify_init()); }]) ],[ AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly]) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(failed)] ) AC_LANG_POP(C++) ]) AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [ AC_CHECK_HEADERS(pthread.h) AC_MSG_CHECKING(for pthread_setname_np type) AC_TRY_LINK([ #include #include ],[ pthread_t t; pthread_setname_np(t, "foo"); ],[ AC_DEFINE(HAS_PTHREAD_SETNAME_NP_GENERIC, 1, The function to set pthread name has a pthread_t argumet.) AC_MSG_RESULT(generic) ],[ AC_TRY_LINK([ #include #include ],[ pthread_t t; pthread_setname_np("foo"); ],[ AC_DEFINE(HAS_PTHREAD_SETNAME_NP_DARWIN, 1, The function to set pthread name has no pthread argument.) AC_MSG_RESULT(darwin) ],[ AC_MSG_RESULT(no) ]) ]) ]) libtorrent-0.13.6/scripts/common.m4000066400000000000000000000216301257211073700172150ustar00rootroot00000000000000AC_DEFUN([TORRENT_CHECK_CXXFLAGS], [ AC_MSG_CHECKING([for user-defined CXXFLAGS]) if test -n "$CXXFLAGS"; then AC_MSG_RESULT([user-defined "$CXXFLAGS"]) else CXXFLAGS="-O2 -Wall" AC_MSG_RESULT([default "$CXXFLAGS"]) fi ]) AC_DEFUN([TORRENT_ENABLE_DEBUG], [ AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable debug information [[default=yes]]]), [ if test "$enableval" = "yes"; then CXXFLAGS="$CXXFLAGS -g -DDEBUG" else CXXFLAGS="$CXXFLAGS -DNDEBUG" fi ],[ CXXFLAGS="$CXXFLAGS -g -DDEBUG" ]) ]) AC_DEFUN([TORRENT_ENABLE_WERROR], [ AC_ARG_ENABLE(werror, AC_HELP_STRING([--enable-werror], [enable the -Werror and -Wall flag [[default=no]]]), [ if test "$enableval" = "yes"; then CXXFLAGS="$CXXFLAGS -Werror -Wall" fi ]) ]) AC_DEFUN([TORRENT_ENABLE_EXTRA_DEBUG], [ AC_ARG_ENABLE(extra-debug, AC_HELP_STRING([--enable-extra-debug], [enable extra debugging checks [[default=no]]]), [ if test "$enableval" = "yes"; then AC_DEFINE(USE_EXTRA_DEBUG, 1, Enable extra debugging checks.) fi ]) ]) AC_DEFUN([TORRENT_WITH_SYSROOT], [ AC_ARG_WITH(sysroot, AC_HELP_STRING([--with-sysroot=PATH], [compile and link with a specific sysroot]), [ AC_MSG_CHECKING(for sysroot) if test "$withval" = "no"; then AC_MSG_RESULT(no) elif test "$withval" = "yes"; then AC_MSG_RESULT(not a path) AC_MSG_ERROR(The sysroot option must point to a directory, like f.ex "/Developer/SDKs/MacOSX10.4u.sdk".) else AC_MSG_RESULT($withval) CXXFLAGS="$CXXFLAGS -isysroot $withval" LDFLAGS="$LDFLAGS -Wl,-syslibroot,$withval" fi ]) ]) AC_DEFUN([TORRENT_ENABLE_ARCH], [ AC_ARG_ENABLE(arch, AC_HELP_STRING([--enable-arch=ARCH], [comma seprated list of architectures to compile for]), [ AC_MSG_CHECKING(for target architectures) if test "$enableval" = "yes"; then AC_MSG_ERROR(no arch supplied) elif test "$enableval" = "no"; then AC_MSG_RESULT(using default) else AC_MSG_RESULT($enableval) for i in `IFS=,; echo $enableval`; do CFLAGS="$CFLAGS -march=$i" CXXFLAGS="$CXXFLAGS -march=$i" LDFLAGS="$LDFLAGS -march=$i" done fi ]) ]) AC_DEFUN([TORRENT_OTFD], [ AC_LANG_PUSH(C++) AC_MSG_CHECKING(for proper overloaded template function disambiguation) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ template void f(T&) {} template void f(T*) {} int main() { int *i = 0; f(*i); f(i); } ])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) AC_MSG_ERROR([your compiler does not properly handle overloaded template function disambiguation]) ]) AC_LANG_POP(C++) ]) AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [ AC_LANG_PUSH(C++) AC_MSG_CHECKING(signedness of mincore parameter) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include #include void f() { mincore((char*)0, 0, (unsigned char*)0); } ])], [ AC_DEFINE(USE_MINCORE, 1, Use mincore) AC_DEFINE(USE_MINCORE_UNSIGNED, 1, use unsigned char* in mincore) AC_MSG_RESULT(unsigned) ], [ AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include #include void f() { mincore((char*)0, 0, (char*)0); } ])], [ AC_DEFINE(USE_MINCORE, 1, Use mincore) AC_DEFINE(USE_MINCORE_UNSIGNED, 0, use char* in mincore) AC_MSG_RESULT(signed) ], [ AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32.]) ]) ]) AC_LANG_POP(C++) ]) AC_DEFUN([TORRENT_MINCORE], [ AC_ARG_ENABLE(mincore, AC_HELP_STRING([--disable-mincore], [disable mincore check [[default=enable]]]), [ if test "$enableval" = "yes"; then TORRENT_MINCORE_SIGNEDNESS() else AC_MSG_CHECKING(for mincore) AC_MSG_RESULT(disabled) fi ],[ TORRENT_MINCORE_SIGNEDNESS() ]) ]) AC_DEFUN([TORRENT_CHECK_MADVISE], [ AC_MSG_CHECKING(for madvise) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include void f() { static char test@<:@1024@:>@; madvise((void *)test, sizeof(test), MADV_NORMAL); } ])], [ AC_MSG_RESULT(yes) AC_DEFINE(USE_MADVISE, 1, Use madvise) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_CHECK_POPCOUNT], [ AC_MSG_CHECKING(for __builtin_popcount) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ int f() { return __builtin_popcount(0); } ])], [ AC_MSG_RESULT(yes) AC_DEFINE(USE_BUILTIN_POPCOUNT, 1, Use __builtin_popcount.) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_CHECK_CACHELINE], [ AC_MSG_CHECKING(for cacheline) AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include #include void* vptr __cacheline_aligned; void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); } ])], [ AC_MSG_RESULT(found builtin) dnl AC_DEFINE(LT_SMP_CACHE_BYTES, SMP_CACHE_BYTES, Largest L1 cache size we know of, should work on all archs.) dnl AC_DEFINE(lt_cacheline_aligned, __cacheline_aligned, LibTorrent defined cacheline aligned.) dnl Need to fix this so that it uses the stuff defined by the system. AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) AC_DEFINE(lt_cacheline_aligned, __attribute__((__aligned__(LT_SMP_CACHE_BYTES))), LibTorrent defined cacheline aligned.) ], [ AC_MSG_RESULT(using default 128 bytes) AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) AC_DEFINE(lt_cacheline_aligned, __attribute__((__aligned__(LT_SMP_CACHE_BYTES))), LibTorrent defined cacheline aligned.) ]) ]) AC_DEFUN([TORRENT_CHECK_EXECINFO], [ AC_MSG_CHECKING(for execinfo.h) AC_RUN_IFELSE([AC_LANG_SOURCE([ #include int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;} ])], [ AC_MSG_RESULT(yes) AC_DEFINE(USE_EXECINFO, 1, Use execinfo.h) ], [ AC_MSG_RESULT(no) ]) ]) AC_DEFUN([TORRENT_CHECK_ALIGNED], [ AC_MSG_CHECKING(the byte alignment) AC_RUN_IFELSE([AC_LANG_SOURCE([ #include int main() { char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 }; int i; for (i = 1; i < 4; ++i) if (*(uint32_t*)(buf + i) == 0) return -1; return 0; } ])], [ AC_MSG_RESULT(none needed) ], [ AC_DEFINE(USE_ALIGNED, 1, Require byte alignment) AC_MSG_RESULT(required) ]) ]) AC_DEFUN([TORRENT_ENABLE_ALIGNED], [ AC_ARG_ENABLE(aligned, AC_HELP_STRING([--enable-aligned], [enable alignment safe code [[default=check]]]), [ if test "$enableval" = "yes"; then AC_DEFINE(USE_ALIGNED, 1, Require byte alignment) fi ],[ TORRENT_CHECK_ALIGNED ]) ]) AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [ AC_MSG_CHECKING([if instrumentation should be included]) AC_ARG_ENABLE(instrumentation, AC_HELP_STRING([--disable-instrumentation], [disable instrumentation [[default=enabled]]]), [ if test "$enableval" = "yes"; then AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi ],[ AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_MSG_RESULT(yes) ]) ]) AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [ AC_ARG_ENABLE(interrupt-socket, AC_HELP_STRING([--enable-interrupt-socket], [enable interrupt socket [[default=no]]]), [ if test "$enableval" = "yes"; then AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill) fi ] ) ]) AC_DEFUN([TORRENT_DISABLE_IPV6], [ AC_ARG_ENABLE(ipv6, AC_HELP_STRING([--enable-ipv6], [enable ipv6 [[default=no]]]), [ if test "$enableval" = "yes"; then AC_DEFINE(RAK_USE_INET6, 1, enable ipv6 stuff) fi ]) ]) AC_DEFUN([TORRENT_ENABLE_TR1], [ AC_ARG_ENABLE(std_tr1, AC_HELP_STRING([--disable-std_tr1], [disable check for support for TR1 [[default=enable]]]), [ if test "$enableval" = "yes"; then TORRENT_CHECK_TR1() else AC_MSG_CHECKING(for TR1 support) AC_MSG_RESULT(disabled) fi ],[ TORRENT_CHECK_TR1() ]) ]) AC_DEFUN([TORRENT_ENABLE_CXX11], [ AC_ARG_ENABLE(std_c++11, AC_HELP_STRING([--disable-std_c++11], [disable check for support for C++11 [[default=enable]]]), [ if test "$enableval" = "yes"; then TORRENT_CHECK_CXX11() else AC_MSG_CHECKING(for C++11 support) AC_MSG_RESULT(disabled) fi ],[ TORRENT_CHECK_CXX11() ] ) ]) libtorrent-0.13.6/src/000077500000000000000000000000001257211073700145615ustar00rootroot00000000000000libtorrent-0.13.6/src/Makefile.am000066400000000000000000000014171257211073700166200ustar00rootroot00000000000000SUBDIRS = \ torrent \ data \ dht \ download \ net \ protocol \ tracker \ utils lib_LTLIBRARIES = libtorrent.la libtorrent_la_LDFLAGS = -version-info $(LIBTORRENT_INTERFACE_VERSION_INFO) libtorrent_la_LIBADD = \ torrent/libsub_torrent.la \ torrent/data/libsub_torrentdata.la \ torrent/download/libsub_torrentdownload.la \ torrent/peer/libsub_torrentpeer.la \ torrent/utils/libsub_torrentutils.la \ data/libsub_data.la \ dht/libsub_dht.la \ download/libsub_download.la \ net/libsub_net.la \ protocol/libsub_protocol.la \ tracker/libsub_tracker.la \ utils/libsub_utils.la libtorrent_la_SOURCES = \ globals.cc \ globals.h \ manager.cc \ manager.h \ thread_disk.cc \ thread_disk.h \ thread_main.cc \ thread_main.h AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) libtorrent-0.13.6/src/data/000077500000000000000000000000001257211073700154725ustar00rootroot00000000000000libtorrent-0.13.6/src/data/Makefile.am000066400000000000000000000010231257211073700175220ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_data.la libsub_data_la_SOURCES = \ chunk.cc \ chunk.h \ chunk_handle.h \ chunk_iterator.h \ chunk_list.cc \ chunk_list.h \ chunk_list_node.h \ chunk_part.cc \ chunk_part.h \ hash_check_queue.cc \ hash_check_queue.h \ hash_chunk.cc \ hash_chunk.h \ hash_queue.cc \ hash_queue.h \ hash_queue_node.cc \ hash_queue_node.h \ hash_torrent.cc \ hash_torrent.h \ memory_chunk.cc \ memory_chunk.h \ socket_file.cc \ socket_file.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/data/chunk.cc000066400000000000000000000173171257211073700171220ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include "torrent/exceptions.h" #include "chunk.h" #include "chunk_iterator.h" namespace torrent { bool Chunk::is_all_valid() const { return !empty() && std::find_if(begin(), end(), std::not1(std::mem_fun_ref(&ChunkPart::is_valid))) == end(); } void Chunk::clear() { std::for_each(begin(), end(), std::mem_fun_ref(&ChunkPart::clear)); m_chunkSize = 0; m_prot = ~0; base_type::clear(); } // Each add calls vector's reserve adding 1. This should keep // the size of the vector at exactly what we need. Though it // will require a few more cycles, it won't matter as we only // rarely have more than 1 or 2 nodes. // // If the user knows how many chunk parts he is going to add, then he // may call reserve prior to this. void Chunk::push_back(value_type::mapped_type mapped, const MemoryChunk& c) { m_prot &= c.get_prot(); // Gcc starts the reserved size at 1 for the first insert, so we // won't be wasting any space in the general case. base_type::insert(end(), ChunkPart(mapped, c, m_chunkSize)); m_chunkSize += c.size(); } Chunk::iterator Chunk::at_position(uint32_t pos) { if (pos >= m_chunkSize) throw internal_error("Chunk::at_position(...) tried to get Chunk position out of range."); iterator itr = std::find_if(begin(), end(), std::bind2nd(std::mem_fun_ref(&ChunkPart::is_contained), pos)); if (itr == end()) throw internal_error("Chunk::at_position(...) might be mangled, at_position failed horribly"); if (itr->size() == 0) throw internal_error("Chunk::at_position(...) tried to return a node with length 0"); return itr; } Chunk::data_type Chunk::at_memory(uint32_t offset, iterator part) { if (part == end()) throw internal_error("Chunk::at_memory(...) reached end."); if (!part->chunk().is_valid()) throw internal_error("Chunk::at_memory(...) chunk part isn't valid."); if (offset < part->position() || offset >= part->position() + part->size()) throw internal_error("Chunk::at_memory(...) out of range."); offset -= part->position(); return data_type(part->chunk().begin() + offset, part->size() - offset); } bool Chunk::is_incore(uint32_t pos, uint32_t length) { iterator itr = at_position(pos); if (itr == end()) throw internal_error("Chunk::incore_length(...) at end()"); uint32_t last = pos + std::min(length, chunk_size() - pos); while (itr->is_incore(pos, last - pos)) { if (++itr == end() || itr->position() >= last) return true; pos = itr->position(); } return false; } // TODO: Buggy, hitting internal_error. Likely need to fix // ChunkPart::incore_length's length align. uint32_t Chunk::incore_length(uint32_t pos, uint32_t length) { uint32_t result = 0; iterator itr = at_position(pos); if (itr == end()) throw internal_error("Chunk::incore_length(...) at end()"); length = std::min(length, chunk_size() - pos); do { uint32_t incore_len = itr->incore_length(pos, length); if (incore_len > length) throw internal_error("Chunk::incore_length(...) incore_len > length."); pos += incore_len; length -= incore_len; result += incore_len; } while (pos == itr->position() + itr->size() && ++itr != end()); return result; } bool Chunk::sync(int flags) { bool success = true; for (iterator itr = begin(), last = end(); itr != last; ++itr) if (!itr->chunk().sync(0, itr->chunk().size(), flags)) success = false; return success; } void Chunk::preload(uint32_t position, uint32_t length, bool useAdvise) { if (position >= m_chunkSize) throw internal_error("Chunk::preload(...) position > m_chunkSize."); if (length == 0) return; Chunk::data_type data; ChunkIterator itr(this, position, position + std::min(length, m_chunkSize - position)); do { data = itr.data(); // Don't do preloading for zero-length chunks. if (useAdvise) { itr.memory_chunk()->advise(itr.memory_chunk_first(), data.second, MemoryChunk::advice_willneed); } else { for (char* first = (char*)data.first, *last = (char*)data.first + data.second; first < last; first += 4096) volatile char __UNUSED touchChunk = *(char*)data.first; // Make sure we touch the last page in the range. volatile char __UNUSED touchChunk = *((char*)data.first + data.second - 1); } } while (itr.next()); } // Consider using uint32_t returning first mismatch or length if // matching. bool Chunk::to_buffer(void* buffer, uint32_t position, uint32_t length) { if (position + length > m_chunkSize) throw internal_error("Chunk::to_buffer(...) position + length > m_chunkSize."); if (length == 0) return true; Chunk::data_type data; ChunkIterator itr(this, position, position + length); do { data = itr.data(); std::memcpy(buffer, data.first, data.second); buffer = static_cast(buffer) + data.second; } while (itr.next()); return true; } // Consider using uint32_t returning first mismatch or length if // matching. bool Chunk::from_buffer(const void* buffer, uint32_t position, uint32_t length) { if (position + length > m_chunkSize) throw internal_error("Chunk::from_buffer(...) position + length > m_chunkSize."); if (length == 0) return true; Chunk::data_type data; ChunkIterator itr(this, position, position + length); do { data = itr.data(); std::memcpy(data.first, buffer, data.second); buffer = static_cast(buffer) + data.second; } while (itr.next()); return true; } // Consider using uint32_t returning first mismatch or length if // matching. bool Chunk::compare_buffer(const void* buffer, uint32_t position, uint32_t length) { if (position + length > m_chunkSize) throw internal_error("Chunk::compare_buffer(...) position + length > m_chunkSize."); if (length == 0) return true; Chunk::data_type data; ChunkIterator itr(this, position, position + length); do { data = itr.data(); if (std::memcmp(data.first, buffer, data.second) != 0) return false; buffer = static_cast(buffer) + data.second; } while (itr.next()); return true; } } libtorrent-0.13.6/src/data/chunk.h000066400000000000000000000104331257211073700167540ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_STORAGE_CHUNK_H #define LIBTORRENT_STORAGE_CHUNK_H #include #include #include #include "chunk_part.h" namespace torrent { class lt_cacheline_aligned Chunk : private std::vector { public: typedef std::vector base_type; typedef std::pair data_type; using base_type::value_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::const_iterator; using base_type::empty; using base_type::reserve; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::front; using base_type::back; Chunk() : m_chunkSize(0), m_prot(~0) {} ~Chunk() { clear(); } bool is_all_valid() const; // All permissions are set for empty chunks. bool is_readable() const { return m_prot & MemoryChunk::prot_read; } bool is_writable() const { return m_prot & MemoryChunk::prot_write; } bool has_permissions(int prot) const { return !(prot & ~m_prot); } uint32_t chunk_size() const { return m_chunkSize; } void clear(); void push_back(value_type::mapped_type mapped, const MemoryChunk& c); // The at_position functions only returns non-zero length iterators // or end. iterator at_position(uint32_t pos); iterator at_position(uint32_t pos, iterator itr); data_type at_memory(uint32_t offset, iterator part); iterator find_address(void* ptr); // Check how much of the chunk is incore from pos. bool is_incore(uint32_t pos, uint32_t length = ~uint32_t()); uint32_t incore_length(uint32_t pos, uint32_t length = ~uint32_t()); bool sync(int flags); void preload(uint32_t position, uint32_t length, bool useAdvise); bool to_buffer(void* buffer, uint32_t position, uint32_t length); bool from_buffer(const void* buffer, uint32_t position, uint32_t length); bool compare_buffer(const void* buffer, uint32_t position, uint32_t length); private: Chunk(const Chunk&); void operator = (const Chunk&); uint32_t m_chunkSize; int m_prot; }; inline Chunk::iterator Chunk::at_position(uint32_t pos, iterator itr) { while (itr != end() && itr->position() + itr->size() <= pos) itr++; return itr; } inline Chunk::iterator Chunk::find_address(void* ptr) { return std::find_if(begin(), end(), std::bind2nd(std::mem_fun_ref(&ChunkPart::has_address), ptr)); } } #endif libtorrent-0.13.6/src/data/chunk_handle.h000066400000000000000000000061471257211073700202760ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_CHUNK_HANDLE_H #define LIBTORRENT_DATA_CHUNK_HANDLE_H #include #include "chunk_list_node.h" namespace torrent { class ChunkListNode; class ChunkHandle { public: ChunkHandle(ChunkListNode* c = NULL, bool wr = false, bool blk = false) : m_node(c), m_writable(wr), m_blocking(blk) {} bool is_valid() const { return m_node != NULL; } bool is_loaded() const { return m_node != NULL && m_node->is_valid(); } bool is_writable() const { return m_writable; } bool is_blocking() const { return m_blocking; } void clear() { m_node = NULL; m_writable = false; m_blocking = false; } rak::error_number error_number() const { return m_errorNumber; } void set_error_number(rak::error_number e) { m_errorNumber = e; } ChunkListNode* object() const { return m_node; } Chunk* chunk() const { return m_node->chunk(); } uint32_t index() const { return m_node->index(); } static ChunkHandle from_error(rak::error_number e) { ChunkHandle h; h.set_error_number(e); return h; } private: ChunkListNode* m_node; bool m_writable; bool m_blocking; rak::error_number m_errorNumber; }; } #endif libtorrent-0.13.6/src/data/chunk_iterator.h000066400000000000000000000071071257211073700206710ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_CHUNK_ITERATOR_H #define LIBTORRENT_DATA_CHUNK_ITERATOR_H #include "chunk.h" namespace torrent { class ChunkIterator { public: ChunkIterator(Chunk* chunk, uint32_t first, uint32_t last); bool empty() const { return m_iterator == m_chunk->end() || m_first >= m_last; } // Only non-zero length ranges will be returned. Chunk::data_type data(); MemoryChunk* memory_chunk() { return &m_iterator->chunk(); } uint32_t memory_chunk_first() const { return m_first - m_iterator->position(); } uint32_t memory_chunk_last() const { return m_last - m_iterator->position(); } bool next(); bool forward(uint32_t length); private: Chunk* m_chunk; Chunk::iterator m_iterator; uint32_t m_first; uint32_t m_last; }; inline ChunkIterator::ChunkIterator(Chunk* chunk, uint32_t first, uint32_t last) : m_chunk(chunk), m_iterator(chunk->at_position(first)), m_first(first), m_last(last) { } inline Chunk::data_type ChunkIterator::data() { Chunk::data_type data = m_chunk->at_memory(m_first, m_iterator); data.second = std::min(data.second, m_last - m_first); return data; } inline bool ChunkIterator::next() { m_first = m_iterator->position() + m_iterator->size(); while (++m_iterator != m_chunk->end()) { if (m_iterator->size() != 0) return m_first < m_last; } return false; } // Returns true if the new position is on a file boundary while not at // the edges of the chunk. // // Do not return true if the length was zero, in order to avoid // getting stuck looping when no data is being read/written. inline bool ChunkIterator::forward(uint32_t length) { m_first += length; if (m_first >= m_last) return false; do { if (m_first < m_iterator->position() + m_iterator->size()) return true; m_iterator++; } while (m_iterator != m_chunk->end()); return false; } } #endif libtorrent-0.13.6/src/data/chunk_list.cc000066400000000000000000000355211257211073700201520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include "torrent/exceptions.h" #include "torrent/chunk_manager.h" #include "torrent/data/download_data.h" #include "torrent/utils/log.h" #include "utils/instrumentation.h" #include "chunk_list.h" #include "chunk.h" #include "globals.h" #define LT_LOG_THIS(log_level, log_fmt, ...) \ lt_log_print_data(LOG_STORAGE_##log_level, m_data, "chunk_list", log_fmt, __VA_ARGS__); namespace torrent { struct chunk_list_earliest_modified { chunk_list_earliest_modified() : m_time(cachedTime) {} void operator () (ChunkListNode* node) { if (node->time_modified() < m_time && node->time_modified() != rak::timer()) m_time = node->time_modified(); } rak::timer m_time; }; struct chunk_list_sort_index { bool operator () (ChunkListNode* node1, ChunkListNode* node2) { return node1->index() < node2->index(); } }; inline bool ChunkList::is_queued(ChunkListNode* node) { return std::find(m_queue.begin(), m_queue.end(), node) != m_queue.end(); } bool ChunkList::has_chunk(size_type index, int prot) const { return base_type::at(index).is_valid() && base_type::at(index).chunk()->has_permissions(prot); } void ChunkList::resize(size_type to_size) { LT_LOG_THIS(INFO, "Resizing: from:%" PRIu32 " to:%" PRIu32 ".", size(), to_size); if (!empty()) throw internal_error("ChunkList::resize(...) called on an non-empty object."); base_type::resize(to_size); uint32_t index = 0; for (iterator itr = begin(), last = end(); itr != last; ++itr, ++index) itr->set_index(index); } void ChunkList::clear() { LT_LOG_THIS(INFO, "Clearing.", 0); // Don't do any sync'ing as whomever decided to shut down really // doesn't care, so just de-reference all chunks in queue. for (Queue::iterator itr = m_queue.begin(), last = m_queue.end(); itr != last; ++itr) { if ((*itr)->references() != 1 || (*itr)->writable() != 1) throw internal_error("ChunkList::clear() called but a node in the queue is still referenced."); (*itr)->dec_rw(); clear_chunk(*itr); } m_queue.clear(); if (std::find_if(begin(), end(), std::mem_fun_ref(&ChunkListNode::chunk)) != end()) throw internal_error("ChunkList::clear() called but a node with a valid chunk was found."); if (std::find_if(begin(), end(), std::mem_fun_ref(&ChunkListNode::references)) != end()) throw internal_error("ChunkList::clear() called but a node with references != 0 was found."); if (std::find_if(begin(), end(), std::mem_fun_ref(&ChunkListNode::writable)) != end()) throw internal_error("ChunkList::clear() called but a node with writable != 0 was found."); if (std::find_if(begin(), end(), std::mem_fun_ref(&ChunkListNode::blocking)) != end()) throw internal_error("ChunkList::clear() called but a node with blocking != 0 was found."); base_type::clear(); } ChunkHandle ChunkList::get(size_type index, int flags) { LT_LOG_THIS(DEBUG, "Get: index:%" PRIu32 " flags:%#x.", index, flags); rak::error_number::clear_global(); ChunkListNode* node = &base_type::at(index); int allocate_flags = (flags & get_dont_log) ? ChunkManager::allocate_dont_log : 0; int prot_flags = MemoryChunk::prot_read | ((flags & get_writable) ? MemoryChunk::prot_write : 0); if (!node->is_valid()) { if (!m_manager->allocate(m_chunk_size, allocate_flags)) { LT_LOG_THIS(DEBUG, "Could not allocate: memory:%" PRIu64 " block:%" PRIu32 ".", m_manager->memory_usage(), m_manager->memory_block_count()); return ChunkHandle::from_error(rak::error_number::e_nomem); } Chunk* chunk = m_slot_create_chunk(index, prot_flags); if (chunk == NULL) { rak::error_number current_error = rak::error_number::current(); LT_LOG_THIS(DEBUG, "Could not create: memory:%" PRIu64 " block:%" PRIu32 " errno:%i errmsg:%s.", m_manager->memory_usage(), m_manager->memory_block_count(), current_error.value(), current_error.c_str()); m_manager->deallocate(m_chunk_size, allocate_flags | ChunkManager::allocate_revert_log); return ChunkHandle::from_error(current_error.is_valid() ? current_error : rak::error_number::e_noent); } node->set_chunk(chunk); node->set_time_modified(rak::timer()); } else if (flags & get_writable && !node->chunk()->is_writable()) { if (node->blocking() != 0) { if ((flags & get_nonblock)) return ChunkHandle::from_error(rak::error_number::e_again); throw internal_error("No support yet for getting write permission for blocked chunk."); } Chunk* chunk = m_slot_create_chunk(index, prot_flags); if (chunk == NULL) return ChunkHandle::from_error(rak::error_number::current().is_valid() ? rak::error_number::current() : rak::error_number::e_noent); delete node->chunk(); node->set_chunk(chunk); node->set_time_modified(rak::timer()); } node->inc_references(); if (flags & get_writable) { node->inc_writable(); // Make sure that periodic syncing uses async on any subsequent // changes even if it was triggered before this get. node->set_sync_triggered(false); } if (flags & get_blocking) { node->inc_blocking(); } return ChunkHandle(node, flags & get_writable, flags & get_blocking); } // The chunks in 'm_queue' have been modified and need to be synced // when appropriate. Hopefully keeping the chunks mmap'ed for a while // will allow us to schedule writes at more resonable intervals. void ChunkList::release(ChunkHandle* handle, int release_flags) { if (!handle->is_valid()) throw internal_error("ChunkList::release(...) received an invalid handle."); if (handle->object() < &*begin() || handle->object() >= &*end()) throw internal_error("ChunkList::release(...) received an unknown handle."); LT_LOG_THIS(DEBUG, "Release: index:%" PRIu32 " flags:%#x.", handle->index(), release_flags); if (handle->object()->references() <= 0 || (handle->is_writable() && handle->object()->writable() <= 0) || (handle->is_blocking() && handle->object()->blocking() <= 0)) throw internal_error("ChunkList::release(...) received a node with bad reference count."); if (handle->is_blocking()) { handle->object()->dec_blocking(); } if (handle->is_writable()) { if (handle->object()->writable() == 1) { if (is_queued(handle->object())) throw internal_error("ChunkList::release(...) tried to queue an already queued chunk."); // Only add those that have a modification time set? // // Only chunks that are not already in the queue will execute // this branch. m_queue.push_back(handle->object()); } else { handle->object()->dec_rw(); } } else { if (handle->object()->dec_references() == 0) { if (is_queued(handle->object())) throw internal_error("ChunkList::release(...) tried to unmap a queued chunk."); clear_chunk(handle->object(), release_flags); } } handle->clear(); } void ChunkList::clear_chunk(ChunkListNode* node, int flags) { if (!node->is_valid()) throw internal_error("ChunkList::clear_chunk(...) !node->is_valid()."); delete node->chunk(); node->set_chunk(NULL); m_manager->deallocate(m_chunk_size, (flags & get_dont_log) ? ChunkManager::allocate_dont_log : 0); } inline bool ChunkList::sync_chunk(ChunkListNode* node, std::pair options) { if (node->references() <= 0 || node->writable() <= 0) throw internal_error("ChunkList::sync_chunk(...) got a node with invalid reference count."); if (!node->chunk()->sync(options.first)) return false; node->set_sync_triggered(true); // When returning here we're not properly deallocating the piece. // // Only release the chunk after a blocking sync. if (!options.second) return true; node->dec_rw(); if (node->references() == 0) clear_chunk(node); return true; } uint32_t ChunkList::sync_chunks(int flags) { LT_LOG_THIS(DEBUG, "Sync chunks: flags:%#x.", flags); Queue::iterator split; if (flags & sync_all) split = m_queue.begin(); else split = std::stable_partition(m_queue.begin(), m_queue.end(), rak::not_equal(1, std::mem_fun(&ChunkListNode::writable))); // Allow a flag that does more culling, so that we only get large // continous sections. // // How does this interact with timers, should be make it so that // only areas with timers are (preferably) synced? std::sort(split, m_queue.end()); // If we got enough diskspace and have not requested safe syncing, // then sync all chunks with MS_ASYNC. if (!(flags & (sync_safe | sync_sloppy))) { if (m_manager->safe_sync() || m_slot_free_diskspace() <= m_manager->safe_free_diskspace()) flags |= sync_safe; else flags |= sync_force; } // TODO: This won't trigger for default sync_force. if ((flags & sync_use_timeout) && !(flags & sync_force)) split = partition_optimize(split, m_queue.end(), 50, 5, false); uint32_t failed = 0; for (Queue::iterator itr = split, last = m_queue.end(); itr != last; ++itr) { // We can easily skip pieces by swap_iter, so there should be no // problem being selective about the ranges we sync. // Use a function for checking the next few chunks and see how far // we want to sync. When we want to sync everything use end. Call // before the loop, or add a check. // if we don't want to sync, swap and break. std::pair options = sync_options(*itr, flags); if (!sync_chunk(*itr, options)) { std::iter_swap(itr, split++); failed++; continue; } if (!options.second) std::iter_swap(itr, split++); } if (lt_log_is_valid(LOG_INSTRUMENTATION_MINCORE)) { instrumentation_update(INSTRUMENTATION_MINCORE_SYNC_SUCCESS, std::distance(split, m_queue.end())); instrumentation_update(INSTRUMENTATION_MINCORE_SYNC_FAILED, failed); instrumentation_update(INSTRUMENTATION_MINCORE_SYNC_NOT_SYNCED, std::distance(m_queue.begin(), split)); instrumentation_update(INSTRUMENTATION_MINCORE_SYNC_NOT_DEALLOCATED, std::count_if(split, m_queue.end(), std::mem_fun(&ChunkListNode::is_valid))); } m_queue.erase(split, m_queue.end()); // The caller must either make sure that it is safe to close the // download or set the sync_ignore_error flag. if (failed && !(flags & sync_ignore_error)) m_slot_storage_error("Could not sync chunk: " + std::string(rak::error_number::current().c_str())); return failed; } std::pair ChunkList::sync_options(ChunkListNode* node, int flags) { // Using if statements since some linkers have problem with static // const int members inside the ?: operators. The compiler should // be optimizing this anyway. if (flags & sync_force) { if (flags & sync_safe) return std::make_pair(MemoryChunk::sync_sync, true); else return std::make_pair(MemoryChunk::sync_async, true); } else if (flags & sync_safe) { if (node->sync_triggered()) return std::make_pair(MemoryChunk::sync_sync, true); else return std::make_pair(MemoryChunk::sync_async, false); } else { return std::make_pair(MemoryChunk::sync_async, true); } } // Using a rather simple algorithm for now. This should really be more // robust against holes withing otherwise compact ranges and take into // consideration chunk size. inline ChunkList::Queue::iterator ChunkList::seek_range(Queue::iterator first, Queue::iterator last) { uint32_t prevIndex = (*first)->index(); while (++first != last) { if ((*first)->index() - prevIndex > 5) break; prevIndex = (*first)->index(); } return first; } inline bool ChunkList::check_node(ChunkListNode* node) { return node->time_modified() != rak::timer() && node->time_modified() + rak::timer::from_seconds(m_manager->timeout_sync()) < cachedTime; } // Optimize the selection of chunks to sync. Continuous regions are // preferred, while if too fragmented or if too few chunks are // available it skips syncing of all chunks. ChunkList::Queue::iterator ChunkList::partition_optimize(Queue::iterator first, Queue::iterator last, int weight, int maxDistance, bool dontSkip) { for (Queue::iterator itr = first; itr != last;) { Queue::iterator range = seek_range(itr, last); bool required = std::find_if(itr, range, std::bind1st(std::mem_fun(&ChunkList::check_node), this)) != range; dontSkip = dontSkip || required; if (!required && std::distance(itr, range) < maxDistance) { // Don't sync this range. unsigned int l = std::min(range - itr, itr - first); std::swap_ranges(first, first + l, range - l); first += l; } else { // This probably increases too fast. weight -= std::distance(itr, range) * std::distance(itr, range); } itr = range; } // These values are all arbritrary... if (!dontSkip && weight > 0) return last; return first; } ChunkList::chunk_address_result ChunkList::find_address(void* ptr) { iterator first = begin(); iterator last = end(); for (; first != last; first++) { if (!first->is_valid()) continue; Chunk::iterator partition = first->chunk()->find_address(ptr); if (partition != first->chunk()->end()) return chunk_address_result(first, partition); first++; } return chunk_address_result(end(), Chunk::iterator()); } } libtorrent-0.13.6/src/data/chunk_list.h000066400000000000000000000135531257211073700200150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_CHUNK_LIST_H #define LIBTORRENT_DATA_CHUNK_LIST_H #include #include #include #include "chunk.h" #include "chunk_handle.h" #include "chunk_list_node.h" namespace torrent { class ChunkManager; class Content; class download_data; class DownloadWrapper; class FileList; class ChunkList : private std::vector { public: typedef uint32_t size_type; typedef std::vector base_type; typedef std::vector Queue; typedef std::tr1::function slot_chunk_index; typedef std::tr1::function slot_value; typedef std::tr1::function slot_string; using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::const_iterator; using base_type::begin; using base_type::end; using base_type::size; using base_type::empty; using base_type::operator[]; static const int sync_all = (1 << 0); static const int sync_force = (1 << 1); static const int sync_safe = (1 << 2); static const int sync_sloppy = (1 << 3); static const int sync_use_timeout = (1 << 4); static const int sync_ignore_error = (1 << 5); static const int get_writable = (1 << 0); static const int get_blocking = (1 << 1); static const int get_dont_log = (1 << 2); static const int get_nonblock = (1 << 3); static const int flag_active = (1 << 0); ChunkList() : m_data(NULL), m_manager(NULL), m_flags(0), m_chunk_size(0) {} ~ChunkList() { clear(); } int flags() const { return m_flags; } void set_flags(int flags) { m_flags |= flags; } void unset_flags(int flags) { m_flags &= ~flags; } void change_flags(int flags, bool state) { if (state) set_flags(flags); else unset_flags(flags); } uint32_t chunk_size() const { return m_chunk_size; } size_type queue_size() const { return m_queue.size(); } download_data* data() { return m_data; } void set_data(download_data* data) { m_data = data; } void set_manager(ChunkManager* manager) { m_manager = manager; } void set_chunk_size(uint32_t cs) { m_chunk_size = cs; } bool has_chunk(size_type index, int prot) const; void resize(size_type to_size); void clear(); ChunkHandle get(size_type index, int flags = 0); void release(ChunkHandle* handle, int flags = 0); // Replace use_timeout with something like performance related // keyword. Then use that flag to decide if we should skip // non-continious regions. // Returns the number of failed syncs. uint32_t sync_chunks(int flags); slot_string& slot_storage_error() { return m_slot_storage_error; } slot_chunk_index& slot_create_chunk() { return m_slot_create_chunk; } slot_value& slot_free_diskspace() { return m_slot_free_diskspace; } typedef std::pair chunk_address_result; chunk_address_result find_address(void* ptr); private: inline bool is_queued(ChunkListNode* node); inline void clear_chunk(ChunkListNode* node, int flags = 0); inline bool sync_chunk(ChunkListNode* node, std::pair options); Queue::iterator partition_optimize(Queue::iterator first, Queue::iterator last, int weight, int maxDistance, bool dontSkip); inline Queue::iterator seek_range(Queue::iterator first, Queue::iterator last); inline bool check_node(ChunkListNode* node); std::pair sync_options(ChunkListNode* node, int flags); download_data* m_data; ChunkManager* m_manager; Queue m_queue; int m_flags; uint32_t m_chunk_size; slot_string m_slot_storage_error; slot_chunk_index m_slot_create_chunk; slot_value m_slot_free_diskspace; }; } #endif libtorrent-0.13.6/src/data/chunk_list_node.h000066400000000000000000000104541257211073700210170ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_CHUNK_LIST_NODE_H #define LIBTORRENT_DATA_CHUNK_LIST_NODE_H #include #include namespace torrent { class Chunk; // ChunkNode can contain information like how long since it was last // used, last synced, last checked with mincore and how many // references there are to it. // // ChunkList will make sure all the nodes are cleaned up properly, so // no dtor is needed. class lt_cacheline_aligned ChunkListNode { public: static const uint32_t invalid_index = ~uint32_t(); ChunkListNode() : m_index(invalid_index), m_chunk(NULL), m_references(0), m_writable(0), m_blocking(0), m_asyncTriggered(false) {} bool is_valid() const { return m_chunk != NULL; } uint32_t index() const { return m_index; } void set_index(uint32_t idx) { m_index = idx; } Chunk* chunk() const { return m_chunk; } void set_chunk(Chunk* c) { m_chunk = c; } const rak::timer& time_modified() const { return m_timeModified; } void set_time_modified(rak::timer t) { m_timeModified = t; } const rak::timer& time_preloaded() const { return m_timePreloaded; } void set_time_preloaded(rak::timer t) { m_timePreloaded = t; } bool sync_triggered() const { return m_asyncTriggered; } void set_sync_triggered(bool v) { m_asyncTriggered = v; } int references() const { return m_references; } int dec_references() { return --m_references; } int inc_references() { return ++m_references; } int writable() const { return m_writable; } int dec_writable() { return --m_writable; } int inc_writable() { return ++m_writable; } int blocking() const { return m_blocking; } int dec_blocking() { return --m_blocking; } int inc_blocking() { return ++m_blocking; } void inc_rw() { inc_writable(); inc_references(); } void dec_rw() { dec_writable(); dec_references(); } private: uint32_t m_index; Chunk* m_chunk; int m_references; int m_writable; int m_blocking; bool m_asyncTriggered; rak::timer m_timeModified; rak::timer m_timePreloaded; }; } #endif libtorrent-0.13.6/src/data/chunk_part.cc000066400000000000000000000060541257211073700201440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "torrent/exceptions.h" #include "chunk_part.h" namespace torrent { void ChunkPart::clear() { switch (m_mapped) { case MAPPED_MMAP: m_chunk.unmap(); break; default: case MAPPED_STATIC: throw internal_error("ChunkPart::clear() only MAPPED_MMAP supported."); break; } m_chunk.clear(); } bool ChunkPart::is_incore(uint32_t pos, uint32_t length) { length = std::min(length, remaining_from(pos)); pos = pos - m_position; if (pos > size()) throw internal_error("ChunkPart::is_incore(...) got invalid position."); if (length > size() || pos + length > size()) throw internal_error("ChunkPart::is_incore(...) got invalid length."); return m_chunk.is_incore(pos, length); } // TODO: Buggy. uint32_t ChunkPart::incore_length(uint32_t pos, uint32_t length) { // Do we want to use this? length = std::min(length, remaining_from(pos)); pos = pos - m_position; if (pos >= size()) throw internal_error("ChunkPart::incore_length(...) got invalid position"); uint32_t touched = m_chunk.pages_touched(pos, length); char buf[touched]; m_chunk.incore(buf, pos, length); uint32_t dist = std::distance(buf, std::find(buf, buf + touched, 0)); // This doesn't properly account for alignment when calculating the length. return std::min(dist ? (dist * m_chunk.page_size() - m_chunk.page_align()) : 0, length); } } libtorrent-0.13.6/src/data/chunk_part.h000066400000000000000000000072301257211073700200030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_STORAGE_CHUNK_PART_H #define LIBTORRENT_DATA_STORAGE_CHUNK_PART_H #include "memory_chunk.h" namespace torrent { class File; class lt_cacheline_aligned ChunkPart { public: typedef enum { MAPPED_MMAP, MAPPED_STATIC } mapped_type; ChunkPart(mapped_type mapped, const MemoryChunk& c, uint32_t pos) : m_mapped(mapped), m_chunk(c), m_position(pos), m_file(NULL), m_file_offset(0) {} bool is_valid() const { return m_chunk.is_valid(); } bool is_contained(uint32_t p) const { return p >= m_position && p < m_position + size(); } bool has_address(void* p) const { return (char*)p >= m_chunk.begin() && p < m_chunk.end(); } void clear(); mapped_type mapped() const { return m_mapped; } MemoryChunk& chunk() { return m_chunk; } const MemoryChunk& chunk() const { return m_chunk; } uint32_t size() const { return m_chunk.size(); } uint32_t position() const { return m_position; } uint32_t remaining_from(uint32_t pos) const { return size() - (pos - m_position); } File* file() const { return m_file; } uint64_t file_offset() const { return m_file_offset; } void set_file(File* f, uint64_t f_offset) { m_file = f; m_file_offset = f_offset; } bool is_incore(uint32_t pos, uint32_t length = ~uint32_t()); uint32_t incore_length(uint32_t pos, uint32_t length = ~uint32_t()); private: mapped_type m_mapped; MemoryChunk m_chunk; uint32_t m_position; // Currently used to figure out what file and where a SIGBUS // occurred. Can also be used in the future to indicate if a part is // temporary storage, etc. File* m_file; uint64_t m_file_offset; }; } #endif libtorrent-0.13.6/src/data/hash_check_queue.cc000066400000000000000000000106721257211073700212730ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "hash_check_queue.h" #include "data/hash_chunk.h" #include "torrent/hash_string.h" #include "utils/instrumentation.h" namespace torrent { HashCheckQueue::HashCheckQueue() { pthread_mutex_init(&m_lock, NULL); } HashCheckQueue::~HashCheckQueue() { pthread_mutex_destroy(&m_lock); } // Always poke thread_disk after calling this. void HashCheckQueue::push_back(HashChunk* hash_chunk) { if (hash_chunk == NULL || !hash_chunk->chunk()->is_loaded() || !hash_chunk->chunk()->is_blocking()) throw internal_error("Invalid hash chunk passed to HashCheckQueue."); pthread_mutex_lock(&m_lock); // Set blocking...(? this needs to be possible to do after getting // the chunk) When doing this make sure we verify that the handle is // not previously blocked. base_type::push_back(hash_chunk); int64_t size = hash_chunk->chunk()->chunk()->chunk_size(); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_COUNT, 1); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_USAGE, size); pthread_mutex_unlock(&m_lock); } // erase... // // The erasing function should call slot, perhaps return a bool if we // deleted, or in the rare case it has already been hashed and will // arraive in the near future? // // We could handle this by polling the done chunks on false return // values. // Lock the chunk list and increment blocking only when starting the // checking. // No removal of entries, only clearing. bool HashCheckQueue::remove(HashChunk* hash_chunk) { pthread_mutex_lock(&m_lock); bool result; iterator itr = std::find(begin(), end(), hash_chunk); if (itr != end()) { base_type::erase(itr); result = true; int64_t size = hash_chunk->chunk()->chunk()->chunk_size(); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_COUNT, -1); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_USAGE, -size); } else { result = false; } pthread_mutex_unlock(&m_lock); return result; } void HashCheckQueue::perform() { pthread_mutex_lock(&m_lock); while (!empty()) { HashChunk* hash_chunk = base_type::front(); base_type::pop_front(); if (!hash_chunk->chunk()->is_loaded()) throw internal_error("HashCheckQueue::perform(): !entry.node->is_loaded()."); int64_t size = hash_chunk->chunk()->chunk()->chunk_size(); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_COUNT, -1); instrumentation_update(INSTRUMENTATION_MEMORY_HASHING_CHUNK_USAGE, -size); pthread_mutex_unlock(&m_lock); if (!hash_chunk->perform(~uint32_t(), true)) throw internal_error("HashCheckQueue::perform(): !hash_chunk->perform(~uint32_t(), true)."); HashString hash; hash_chunk->hash_c(hash.data()); m_slot_chunk_done(hash_chunk, hash); pthread_mutex_lock(&m_lock); } pthread_mutex_unlock(&m_lock); } } libtorrent-0.13.6/src/data/hash_check_queue.h000066400000000000000000000053051257211073700211320ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_HASH_CHECK_QUEUE_H #define LIBTORRENT_DATA_HASH_CHECK_QUEUE_H #include #include #include #include "rak/allocators.h" namespace torrent { class HashString; class HashChunk; class lt_cacheline_aligned HashCheckQueue : private std::deque > { public: typedef std::deque > base_type; typedef std::tr1::function slot_chunk_handle; using base_type::iterator; using base_type::empty; using base_type::size; using base_type::begin; using base_type::end; using base_type::front; using base_type::back; HashCheckQueue(); ~HashCheckQueue(); // Guarded functions for adding new... void push_back(HashChunk* node); void perform(); bool remove(HashChunk* node); slot_chunk_handle& slot_chunk_done() { return m_slot_chunk_done; } private: slot_chunk_handle m_slot_chunk_done; pthread_mutex_t m_lock; }; } #endif libtorrent-0.13.6/src/data/hash_chunk.cc000066400000000000000000000060771257211073700201260ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "hash_chunk.h" #include "chunk.h" #include "chunk_list_node.h" namespace torrent { bool HashChunk::perform(uint32_t length, bool force) { length = std::min(length, remaining()); if (m_position + length > m_chunk.chunk()->chunk_size()) throw internal_error("HashChunk::perform(...) received length out of range"); uint32_t l = force ? length : m_chunk.chunk()->incore_length(m_position); bool complete = l == length; while (l) { Chunk::iterator node = m_chunk.chunk()->at_position(m_position); l -= perform_part(node, l); } return complete; } void HashChunk::advise_willneed(uint32_t length) { if (!m_chunk.is_valid()) throw internal_error("HashChunk::willneed(...) called on an invalid chunk"); if (m_position + length > m_chunk.chunk()->chunk_size()) throw internal_error("HashChunk::willneed(...) received length out of range"); uint32_t pos = m_position; while (length) { Chunk::iterator itr = m_chunk.chunk()->at_position(pos); uint32_t l = std::min(length, remaining_part(itr, pos)); itr->chunk().advise(pos - itr->position(), l, MemoryChunk::advice_willneed); pos += l; length -= l; ++itr; } } uint32_t HashChunk::perform_part(Chunk::iterator itr, uint32_t length) { length = std::min(length, remaining_part(itr, m_position)); m_hash.update(itr->chunk().begin() + m_position - itr->position(), length); m_position += length; return length; } } libtorrent-0.13.6/src/data/hash_chunk.h000066400000000000000000000064601257211073700177640ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_HASH_CHUNK_H #define LIBTORRENT_HASH_CHUNK_H #include "torrent/exceptions.h" #include "utils/sha1.h" #include "chunk.h" #include "chunk_handle.h" namespace torrent { // This class interface assumes we're always going to check the whole // chunk. All we need is control of the (non-)blocking nature, and other // stuff related to performance and responsiveness. class ChunkListNode; class lt_cacheline_aligned HashChunk { public: HashChunk() {} HashChunk(ChunkHandle h) { set_chunk(h); } void set_chunk(ChunkHandle h) { m_position = 0; m_chunk = h; m_hash.init(); } ChunkHandle* chunk() { return &m_chunk; } ChunkHandle& handle() { return m_chunk; } void hash_c(char* buffer) { m_hash.final_c(buffer); } // If force is true, then the return value is always true. bool perform(uint32_t length, bool force = true); void advise_willneed(uint32_t length); uint32_t remaining(); private: inline uint32_t remaining_part(Chunk::iterator itr, uint32_t pos); uint32_t perform_part(Chunk::iterator itr, uint32_t length); uint32_t m_position; ChunkHandle m_chunk; Sha1 m_hash; }; inline uint32_t HashChunk::remaining_part(Chunk::iterator itr, uint32_t pos) { return itr->size() - (pos - itr->position()); } inline uint32_t HashChunk::remaining() { if (!m_chunk.is_loaded()) throw internal_error("HashChunk::remaining(...) called on an invalid chunk"); return m_chunk.chunk()->chunk_size() - m_position; } } #endif libtorrent-0.13.6/src/data/hash_queue.cc000066400000000000000000000155271257211073700201420ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include "torrent/exceptions.h" #include "torrent/data/download_data.h" #include "torrent/utils/log.h" #include "torrent/utils/thread_base.h" #include "hash_queue.h" #include "hash_chunk.h" #include "chunk.h" #include "chunk_list_node.h" #include "globals.h" #include "thread_disk.h" #define LT_LOG_DATA(data, log_level, log_fmt, ...) \ lt_log_print_data(LOG_STORAGE_##log_level, data, "hash_queue", log_fmt, __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { struct HashQueueEqual { HashQueueEqual(HashQueueNode::id_type id, uint32_t index) : m_id(id), m_index(index) {} bool operator () (const HashQueueNode& q) const { return m_id == q.id() && m_index == q.get_index(); } HashQueueNode::id_type m_id; uint32_t m_index; }; struct HashQueueWillneed { HashQueueWillneed(int bytes) : m_bytes(bytes) {} bool operator () (HashQueueNode& q) { return (m_bytes -= q.call_willneed()) <= 0; } int m_bytes; }; // If madvise is not available it will always mark the pages as being // in memory, thus we don't need to modify m_maxTries to have full // disk usage. But this may cause too much blocking as it will think // everything is in memory, thus we need to throttle. HashQueue::HashQueue(thread_disk* thread) : m_thread_disk(thread) { pthread_mutex_init(&m_done_chunks_lock, NULL); m_thread_disk->hash_queue()->slot_chunk_done() = tr1::bind(&HashQueue::chunk_done, this, tr1::placeholders::_1, tr1::placeholders::_2); } // If we're done immediately, move the chunk to the front of the list so // the next work cycle gets stuff done. void HashQueue::push_back(ChunkHandle handle, HashQueueNode::id_type id, slot_done_type d) { LT_LOG_DATA(id, DEBUG, "Adding index:%" PRIu32 " to queue.", handle.index()); if (!handle.is_loaded()) throw internal_error("HashQueue::add(...) received an invalid chunk"); HashChunk* hash_chunk = new HashChunk(handle); base_type::push_back(HashQueueNode(id, hash_chunk, d)); m_thread_disk->hash_queue()->push_back(hash_chunk); m_thread_disk->interrupt(); } bool HashQueue::has(HashQueueNode::id_type id) { return std::find_if(begin(), end(), rak::equal(id, std::mem_fun_ref(&HashQueueNode::id))) != end(); } bool HashQueue::has(HashQueueNode::id_type id, uint32_t index) { return std::find_if(begin(), end(), HashQueueEqual(id, index)) != end(); } void HashQueue::remove(HashQueueNode::id_type id) { iterator itr = begin(); while ((itr = std::find_if(itr, end(), rak::equal(id, std::mem_fun_ref(&HashQueueNode::id)))) != end()) { HashChunk *hash_chunk = itr->get_chunk(); LT_LOG_DATA(id, DEBUG, "Removing index:%" PRIu32 " from queue.", hash_chunk->handle().index()); thread_base::release_global_lock(); bool result = m_thread_disk->hash_queue()->remove(hash_chunk); thread_base::acquire_global_lock(); // The hash chunk was not found, so we need to wait until the hash // check finishes. if (!result) { pthread_mutex_lock(&m_done_chunks_lock); done_chunks_type::iterator done_itr; while ((done_itr = m_done_chunks.find(hash_chunk)) == m_done_chunks.end()) { pthread_mutex_unlock(&m_done_chunks_lock); usleep(100); pthread_mutex_lock(&m_done_chunks_lock); } m_done_chunks.erase(done_itr); pthread_mutex_unlock(&m_done_chunks_lock); } itr->slot_done()(*hash_chunk->chunk(), NULL); itr->clear(); itr = erase(itr); } } void HashQueue::clear() { if (!empty()) throw internal_error("HashQueue::clear() called but valid nodes were found."); // Replace with a dtor check to ensure it is empty? // std::for_each(begin(), end(), std::mem_fun_ref(&HashQueueNode::clear)); // base_type::clear(); } void HashQueue::work() { pthread_mutex_lock(&m_done_chunks_lock); while (!m_done_chunks.empty()) { HashChunk* hash_chunk = m_done_chunks.begin()->first; HashString hash_value = m_done_chunks.begin()->second; m_done_chunks.erase(m_done_chunks.begin()); // TODO: This is not optimal as we jump around... Check for front // of HashQueue in done_chunks instead. iterator itr = std::find_if(begin(), end(), tr1::bind(std::equal_to(), hash_chunk, tr1::bind(&HashQueueNode::get_chunk, tr1::placeholders::_1))); // TODO: Fix this... if (itr == end()) throw internal_error("Could not find done chunk's node."); LT_LOG_DATA(itr->id(), DEBUG, "Passing index:%" PRIu32 " to owner: %s.", hash_chunk->handle().index(), hash_string_to_hex_str(hash_value).c_str()); HashQueueNode::slot_done_type slotDone = itr->slot_done(); base_type::erase(itr); slotDone(hash_chunk->handle(), hash_value.c_str()); delete hash_chunk; } pthread_mutex_unlock(&m_done_chunks_lock); } void HashQueue::chunk_done(HashChunk* hash_chunk, const HashString& hash_value) { pthread_mutex_lock(&m_done_chunks_lock); m_done_chunks[hash_chunk] = hash_value; m_slot_has_work(m_done_chunks.empty()); pthread_mutex_unlock(&m_done_chunks_lock); } } libtorrent-0.13.6/src/data/hash_queue.h000066400000000000000000000067571257211073700200110ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_HASH_QUEUE_H #define LIBTORRENT_DATA_HASH_QUEUE_H #include #include #include #include #include "torrent/hash_string.h" #include "hash_queue_node.h" #include "chunk_handle.h" namespace torrent { class HashChunk; class thread_disk; // Calculating hash of incore memory is blindingly fast, it's always // the loading from swap/disk that takes time. So with the exception // of large resumed downloads, try to check the hash immediately. This // helps us in getting as much done as possible while the pages are in // memory. class lt_cacheline_aligned HashQueue : private std::deque { public: typedef std::deque base_type; typedef std::map done_chunks_type; typedef HashQueueNode::slot_done_type slot_done_type; typedef std::tr1::function slot_bool; using base_type::iterator; using base_type::empty; using base_type::size; using base_type::begin; using base_type::end; using base_type::front; using base_type::back; HashQueue(thread_disk* thread); ~HashQueue() { clear(); pthread_mutex_destroy(&m_done_chunks_lock); } void push_back(ChunkHandle handle, HashQueueNode::id_type id, slot_done_type d); bool has(HashQueueNode::id_type id); bool has(HashQueueNode::id_type id, uint32_t index); void remove(HashQueueNode::id_type id); void clear(); void work(); slot_bool& slot_has_work() { return m_slot_has_work; } private: void chunk_done(HashChunk* hash_chunk, const HashString& hash_value); thread_disk* m_thread_disk; done_chunks_type m_done_chunks; slot_bool m_slot_has_work; pthread_mutex_t m_done_chunks_lock lt_cacheline_aligned; }; } #endif libtorrent-0.13.6/src/data/hash_queue_node.cc000066400000000000000000000040751257211073700211430ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "hash_chunk.h" #include "hash_queue_node.h" #include "chunk_list_node.h" namespace torrent { uint32_t HashQueueNode::get_index() const { return m_chunk->chunk()->index(); } void HashQueueNode::clear() { delete m_chunk; m_chunk = NULL; } uint32_t HashQueueNode::call_willneed() { if (!m_willneed) { m_willneed = true; m_chunk->advise_willneed(m_chunk->remaining()); } return m_chunk->remaining(); } } libtorrent-0.13.6/src/data/hash_queue_node.h000066400000000000000000000057331257211073700210070ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_HASH_QUEUE_NODE_H #define LIBTORRENT_DATA_HASH_QUEUE_NODE_H #include #include #include #include "chunk_handle.h" #include "hash_chunk.h" namespace torrent { class download_data; class HashQueueNode { public: typedef std::tr1::function slot_done_type; typedef download_data* id_type; HashQueueNode(id_type id, HashChunk* c, slot_done_type d) : m_id(id), m_chunk(c), m_willneed(false), m_slot_done(d) {} id_type id() const { return m_id; } ChunkHandle& handle() { return *m_chunk->chunk(); } uint32_t get_index() const; HashChunk* get_chunk() { return m_chunk; } bool get_willneed() const { return m_willneed; } bool perform_remaining(bool force) { return m_chunk->perform(m_chunk->remaining(), force); } void clear(); // Does not call multiple times on the same chunk. Returns the // number of bytes not checked in this chunk. uint32_t call_willneed(); slot_done_type& slot_done() { return m_slot_done; } private: id_type m_id; HashChunk* m_chunk; bool m_willneed; slot_done_type m_slot_done; }; } #endif libtorrent-0.13.6/src/data/hash_torrent.cc000066400000000000000000000176411257211073700205120ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include "data/chunk_list.h" #include "torrent/exceptions.h" #include "torrent/data/download_data.h" #include "torrent/utils/log.h" #include "hash_torrent.h" #include "hash_queue.h" #include "globals.h" #define LT_LOG_THIS(log_level, log_fmt, ...) \ lt_log_print_data(LOG_STORAGE_##log_level, m_chunk_list->data(), "hash_torrent", log_fmt, __VA_ARGS__); namespace torrent { HashTorrent::HashTorrent(ChunkList* c) : m_position(0), m_outstanding(-1), m_errno(0), m_chunk_list(c) { } bool HashTorrent::start(bool try_quick) { LT_LOG_THIS(INFO, "Start: position:%u size:%" PRIu64 " try_quick:%u.", m_position, m_chunk_list->size(), try_quick); if (m_position == m_chunk_list->size()) return true; if (m_position > 0 || m_chunk_list->empty()) throw internal_error("HashTorrent::start() call failed."); m_outstanding = 0; queue(try_quick); return m_position == m_chunk_list->size(); } void HashTorrent::clear() { LT_LOG_THIS(INFO, "Clear.", 0); m_outstanding = -1; m_position = 0; m_errno = 0; // Correct? rak::priority_queue_erase(&taskScheduler, &m_delayChecked); } bool HashTorrent::is_checked() { // When closed the chunk list is empty. Position can be equal to // chunk list for a short while as we have outstanding chunks, so // check the latter. return !m_chunk_list->empty() && m_position == m_chunk_list->size() && m_outstanding == -1; } // After all chunks are checked it won't show as is_checked until // after this function is called. This allows for the hash done signal // to be delayed. void HashTorrent::confirm_checked() { LT_LOG_THIS(INFO, "Confirm checked.", 0); if (m_outstanding != 0) throw internal_error("HashTorrent::confirm_checked() m_outstanding != 0."); m_outstanding = -1; } void HashTorrent::receive_chunkdone(uint32_t index) { LT_LOG_THIS(DEBUG, "Received chunk done: index:%" PRIu32 ".", index); if (m_outstanding <= 0) throw internal_error("HashTorrent::receive_chunkdone() m_outstanding <= 0."); // m_signalChunk will always point to // DownloadMain::receive_hash_done, so it will take care of cleanup. // // Make sure we call chunkdone before torrentDone has a chance to // trigger. m_outstanding--; queue(false); } // Mark unsuccessful checks so that if we have just stopped the // hash checker it will ensure those pieces get rechecked upon // restart. void HashTorrent::receive_chunk_cleared(uint32_t index) { LT_LOG_THIS(DEBUG, "Received chunk cleared: index:%" PRIu32 ".", index); if (m_outstanding <= 0) throw internal_error("HashTorrent::receive_chunk_cleared() m_outstanding < 0."); if (m_ranges.has(index)) throw internal_error("HashTorrent::receive_chunk_cleared() m_ranges.has(index)."); m_outstanding--; m_ranges.insert(index, index + 1); } void HashTorrent::queue(bool quick) { LT_LOG_THIS(DEBUG, "Queue: position:%u outstanding:%i try_quick:%u.", m_position, m_outstanding, quick); if (!is_checking()) throw internal_error("HashTorrent::queue() called but it's not running."); while (m_position < m_chunk_list->size()) { if (m_outstanding > 10 && m_outstanding * m_chunk_list->chunk_size() > (128 << 20)) return; // Not very efficient, but this is seldomly done. Ranges::iterator itr = m_ranges.find(m_position); if (itr == m_ranges.end()) { m_position = m_chunk_list->size(); break; } else if (m_position < itr->first) { m_position = itr->first; } // Need to do increment later if we're going to support resume // hashing a quick hashed torrent. ChunkHandle handle = m_chunk_list->get(m_position, ChunkList::get_dont_log); if (quick) { // We're not actually interested in doing any hashing, so just // skip what we know is not possible to hash. // // If the file does not exist then no valid error number is // returned. if (m_outstanding != 0) throw internal_error("HashTorrent::queue() quick hashing but m_outstanding != 0."); if (handle.is_valid()) { LT_LOG_THIS(DEBUG, "Return on handle.is_valid(): position:%u.", m_position); return m_chunk_list->release(&handle, ChunkList::get_dont_log); } if (handle.error_number().is_valid() && handle.error_number().value() != rak::error_number::e_noent) { LT_LOG_THIS(DEBUG, "Return on handle errno == E_NOENT: position:%u.", m_position); return; } m_position++; continue; } // If the error number is not valid, then we've just encountered a // file that hasn't be created/resized. Which means we ignore it // when doing initial hashing. if (handle.error_number().is_valid() && handle.error_number().value() != rak::error_number::e_noent) { if (handle.is_valid()) throw internal_error("HashTorrent::queue() error, but handle.is_valid()."); // We wait for all the outstanding chunks to be checked before // borking completely, else low-memory devices might not be able // to finish the hash check. if (m_outstanding != 0) return; // The rest of the outstanding chunks get ignored by // DownloadWrapper::receive_hash_done. Obsolete. clear(); m_errno = handle.error_number().value(); LT_LOG_THIS(INFO, "Completed (error): position:%u try_quick:%u errno:%i msg:'%s'.", m_position, quick, m_errno, handle.error_number().c_str()); rak::priority_queue_erase(&taskScheduler, &m_delayChecked); rak::priority_queue_insert(&taskScheduler, &m_delayChecked, cachedTime); return; } m_position++; if (!handle.is_valid() && !handle.error_number().is_valid()) throw internal_error("Hash torrent errno == 0."); // Missing file, skip the hash check. if (!handle.is_valid()) continue; if (m_slot_check_chunk) m_slot_check_chunk(handle); m_outstanding++; } if (m_outstanding == 0) { LT_LOG_THIS(INFO, "Completed (normal): position:%u try_quick:%u.", m_position, quick); // Erase the scheduled item just to make sure that if hashing is // started again during the delay it won't cause an exception. rak::priority_queue_erase(&taskScheduler, &m_delayChecked); rak::priority_queue_insert(&taskScheduler, &m_delayChecked, cachedTime); } } } libtorrent-0.13.6/src/data/hash_torrent.h000066400000000000000000000063731257211073700203540ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_HASH_TORRENT_H #define LIBTORRENT_DATA_HASH_TORRENT_H #include #include #include #include #include "data/chunk_handle.h" #include "torrent/utils/ranges.h" namespace torrent { class ChunkList; class HashTorrent { public: typedef ranges Ranges; typedef std::tr1::function slot_chunk_handle; HashTorrent(ChunkList* c); ~HashTorrent() { clear(); } bool start(bool try_quick); void clear(); bool is_checking() { return m_outstanding >= 0; } bool is_checked(); void confirm_checked(); Ranges& hashing_ranges() { return m_ranges; } uint32_t position() const { return m_position; } uint32_t outstanding() const { return m_outstanding; } int error_number() const { return m_errno; } slot_chunk_handle& slot_check_chunk() { return m_slot_check_chunk; } rak::priority_item& delay_checked() { return m_delayChecked; } void receive_chunkdone(uint32_t index); void receive_chunk_cleared(uint32_t index); private: void queue(bool quick); unsigned int m_position; int m_outstanding; Ranges m_ranges; int m_errno; ChunkList* m_chunk_list; slot_chunk_handle m_slot_check_chunk; rak::priority_item m_delayChecked; }; } #endif libtorrent-0.13.6/src/data/memory_chunk.cc000066400000000000000000000114171257211073700205050ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include "torrent/exceptions.h" #include "memory_chunk.h" #ifdef __sun__ extern "C" int madvise(void *, size_t, int); //#include //Should be the include line instead, but Solaris //has an annoying bug wherein it doesn't declare //madvise for C++. #endif namespace torrent { uint32_t MemoryChunk::m_pagesize = getpagesize(); inline void MemoryChunk::align_pair(uint32_t* offset, uint32_t* length) const { *offset += page_align(); *length += *offset % m_pagesize; *offset -= *offset % m_pagesize; } MemoryChunk::MemoryChunk(char* ptr, char* begin, char* end, int prot, int flags) : m_ptr(ptr), m_begin(begin), m_end(end), m_prot(prot), m_flags(flags) { if (ptr == NULL) throw internal_error("MemoryChunk::MemoryChunk(...) received ptr == NULL"); if (page_align() >= m_pagesize) throw internal_error("MemoryChunk::MemoryChunk(...) received an page alignment >= page size"); if ((std::ptrdiff_t)ptr % m_pagesize) throw internal_error("MemoryChunk::MemoryChunk(...) is not aligned to a page"); } void MemoryChunk::unmap() { if (!is_valid()) throw internal_error("MemoryChunk::unmap() called on an invalid object"); if (munmap(m_ptr, m_end - m_ptr) != 0) throw internal_error("MemoryChunk::unmap() system call failed: " + std::string(rak::error_number::current().c_str())); } void MemoryChunk::incore(char* buf, uint32_t offset, uint32_t length) { if (!is_valid()) throw internal_error("Called MemoryChunk::incore(...) on an invalid object"); if (!is_valid_range(offset, length)) throw internal_error("MemoryChunk::incore(...) received out-of-range input"); align_pair(&offset, &length); #if USE_MINCORE #if USE_MINCORE_UNSIGNED if (mincore(m_ptr + offset, length, (unsigned char*)buf)) #else if (mincore(m_ptr + offset, length, (char*)buf)) #endif throw storage_error("System call mincore failed: " + std::string(rak::error_number::current().c_str())); #else // !USE_MINCORE // Pretend all pages are in memory. memset(buf, 1, pages_touched(offset, length)); #endif } bool MemoryChunk::advise(uint32_t offset, uint32_t length, int advice) { if (!is_valid()) throw internal_error("Called MemoryChunk::advise() on an invalid object"); if (!is_valid_range(offset, length)) throw internal_error("MemoryChunk::advise(...) received out-of-range input"); #if USE_MADVISE align_pair(&offset, &length); if (madvise(m_ptr + offset, length, advice) == 0) return true; else if (errno == EINVAL || (errno == ENOMEM && advice != advice_willneed) || errno == EBADF) throw internal_error("MemoryChunk::advise(...) " + std::string(strerror(errno))); else return false; #else return true; #endif } bool MemoryChunk::sync(uint32_t offset, uint32_t length, int flags) { if (!is_valid()) throw internal_error("Called MemoryChunk::sync() on an invalid object"); if (!is_valid_range(offset, length)) throw internal_error("MemoryChunk::sync(...) received out-of-range input"); align_pair(&offset, &length); return msync(m_ptr + offset, length, flags) == 0; } } libtorrent-0.13.6/src/data/memory_chunk.h000066400000000000000000000144131257211073700203460ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_MEMORY_CHUNK_H #define LIBTORRENT_DATA_MEMORY_CHUNK_H #include #include #include #include namespace torrent { class MemoryChunk { public: // Consider information about whetever the memory maps to a file or // not, since mincore etc can only be called on files. static const int prot_exec = PROT_EXEC; static const int prot_read = PROT_READ; static const int prot_write = PROT_WRITE; static const int prot_none = PROT_NONE; static const int map_shared = MAP_SHARED; #ifdef USE_MADVISE static const int advice_normal = MADV_NORMAL; static const int advice_random = MADV_RANDOM; static const int advice_sequential = MADV_SEQUENTIAL; static const int advice_willneed = MADV_WILLNEED; static const int advice_dontneed = MADV_DONTNEED; #else static const int advice_normal = 0; static const int advice_random = 1; static const int advice_sequential = 2; static const int advice_willneed = 3; static const int advice_dontneed = 4; #endif static const int sync_sync = MS_SYNC; static const int sync_async = MS_ASYNC; static const int sync_invalidate = MS_INVALIDATE; MemoryChunk() { clear(); } ~MemoryChunk() { clear(); } // Doesn't allow ptr == NULL, use the default ctor instead. MemoryChunk(char* ptr, char* begin, char* end, int prot, int flags); bool is_valid() const { return m_ptr; } bool is_readable() const { return m_prot & PROT_READ; } bool is_writable() const { return m_prot & PROT_WRITE; } bool is_exec() const { return m_prot & PROT_EXEC; } inline bool is_valid_range(uint32_t offset, uint32_t length) const; bool has_permissions(int prot) const { return !(prot & ~m_prot); } char* ptr() const { return m_ptr; } char* begin() const { return m_begin; } char* end() const { return m_end; } int get_prot() const { return m_prot; } uint32_t size() const { return m_end - m_begin; } uint32_t size_aligned() const; inline void clear(); void unmap(); // Use errno and strerror if you want to know why these failed. void incore(char* buf, uint32_t offset, uint32_t length); bool advise(uint32_t offset, uint32_t length, int advice); bool sync(uint32_t offset, uint32_t length, int flags); bool is_incore(uint32_t offset, uint32_t length); // Helper functions for aligning offsets and ranges to page boundaries. uint32_t page_align() const { return m_begin - m_ptr; } uint32_t page_align(uint32_t o) const { return (o + page_align()) % m_pagesize; } // This won't return correct values if length == 0. inline uint32_t pages_touched(uint32_t offset, uint32_t length) const; static uint32_t page_size() { return m_pagesize; } private: inline void align_pair(uint32_t* offset, uint32_t* length) const; static uint32_t m_pagesize; char* m_ptr; char* m_begin; char* m_end; int m_prot; int m_flags; }; inline bool MemoryChunk::is_valid_range(uint32_t offset, uint32_t length) const { return length != 0 && (uint64_t)offset + length <= size(); } inline void MemoryChunk::clear() { m_ptr = m_begin = m_end = NULL; m_flags = PROT_NONE; } inline uint32_t MemoryChunk::pages_touched(uint32_t offset, uint32_t length) const { if (length == 0) return 0; return (length + page_align(offset) + m_pagesize - 1) / m_pagesize; } // The size of the mapped memory. inline uint32_t MemoryChunk::size_aligned() const { return std::distance(m_ptr, m_end) + page_size() - ((std::distance(m_ptr, m_end) - 1) % page_size() + 1); } inline bool MemoryChunk::is_incore(uint32_t offset, uint32_t length) { uint32_t size = pages_touched(offset, length); char buf[size]; incore(buf, offset, length); return std::find(buf, buf + size, 0) == buf + size; } } #endif libtorrent-0.13.6/src/data/socket_file.cc000066400000000000000000000116441257211073700202760ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "socket_file.h" #include "torrent/exceptions.h" #include #include #include #include #include #include #include #ifdef HAVE_FALLOCATE #include #endif namespace torrent { bool SocketFile::open(const std::string& path, int prot, int flags, mode_t mode) { close(); if (prot & MemoryChunk::prot_read && prot & MemoryChunk::prot_write) flags |= O_RDWR; else if (prot & MemoryChunk::prot_read) flags |= O_RDONLY; else if (prot & MemoryChunk::prot_write) flags |= O_WRONLY; else throw internal_error("torrent::SocketFile::open(...) Tried to open file with no protection flags"); #ifdef O_LARGEFILE fd_type fd = ::open(path.c_str(), flags | O_LARGEFILE, mode); #else fd_type fd = ::open(path.c_str(), flags, mode); #endif if (fd == invalid_fd) return false; m_fd = fd; return true; } void SocketFile::close() { if (!is_open()) return; ::close(m_fd); m_fd = invalid_fd; } uint64_t SocketFile::size() const { if (!is_open()) throw internal_error("SocketFile::size() called on a closed file"); rak::file_stat fs; return fs.update(m_fd) ? fs.size() : 0; } bool SocketFile::set_size(uint64_t size, int flags) const { if (!is_open()) throw internal_error("SocketFile::set_size() called on a closed file"); #ifdef HAVE_FALLOCATE if (flags & flag_fallocate && fallocate(m_fd, 0, 0, size) == 0) return true; #endif #ifdef USE_POSIX_FALLOCATE if (flags & flag_fallocate && flags & flag_fallocate_blocking && posix_fallocate(m_fd, 0, size) == 0) return true; #endif #ifdef SYS_DARWIN if (flags & flag_fallocate) { fstore_t fstore; fstore.fst_flags = F_ALLOCATECONTIG; fstore.fst_posmode = F_PEOFPOSMODE; fstore.fst_offset = 0; fstore.fst_length = size; fstore.fst_bytesalloc = 0; // Hmm... this shouldn't really be something we fail the set_size // on... // // Yet is somehow fails with ENOSPC... // if (fcntl(m_fd, F_PREALLOCATE, &fstore) == -1) // throw internal_error("hack: fcntl failed" + std::string(strerror(errno))); fcntl(m_fd, F_PREALLOCATE, &fstore); // Ignore result for now... } #endif if (ftruncate(m_fd, size) == 0) return true; // Use workaround to resize files on vfat. It causes the whole // client to block while it is resizing the files, this really // should be in a seperate thread. if (size != 0 && lseek(m_fd, size - 1, SEEK_SET) == (off_t)(size - 1) && write(m_fd, &size, 1) == 1) return true; return false; } MemoryChunk SocketFile::create_chunk(uint64_t offset, uint32_t length, int prot, int flags) const { if (!is_open()) throw internal_error("SocketFile::get_chunk() called on a closed file"); // For some reason mapping beyond the extent of the file does not // cause mmap to complain, so we need to check manually here. if (length == 0 || offset > size() || offset + length > size()) return MemoryChunk(); uint64_t align = offset % MemoryChunk::page_size(); char* ptr = (char*)mmap(NULL, length + align, prot, flags, m_fd, offset - align); if (ptr == MAP_FAILED) return MemoryChunk(); return MemoryChunk(ptr, ptr + align, ptr + align + length, prot, flags); } } libtorrent-0.13.6/src/data/socket_file.h000066400000000000000000000057521257211073700201430ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_SOCKET_FILE_H #define LIBTORRENT_SOCKET_FILE_H #include #include #include #include #include "memory_chunk.h" namespace torrent { // Inherit from SocketBase? class SocketFile { public: typedef int fd_type; static const fd_type invalid_fd = -1; static const int o_create = O_CREAT; static const int o_truncate = O_TRUNC; static const int o_nonblock = O_NONBLOCK; static const int flag_fallocate = (1 << 0); static const int flag_fallocate_blocking = (1 << 1); SocketFile() : m_fd(invalid_fd) {} SocketFile(fd_type fd) : m_fd(fd) {} bool is_open() const { return m_fd != invalid_fd; } bool open(const std::string& path, int prot, int flags, mode_t mode = 0666); void close(); uint64_t size() const; bool set_size(uint64_t s, int flags = 0) const; MemoryChunk create_chunk(uint64_t offset, uint32_t length, int prot, int flags) const; fd_type fd() const { return m_fd; } private: // Use custom flags if stuff like file locking etc is implemented. SocketFile(const SocketFile&); void operator = (const SocketFile&); fd_type m_fd; }; } #endif libtorrent-0.13.6/src/dht/000077500000000000000000000000001257211073700153405ustar00rootroot00000000000000libtorrent-0.13.6/src/dht/Makefile.am000066400000000000000000000005261257211073700173770ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_dht.la libsub_dht_la_SOURCES = \ dht_bucket.cc \ dht_bucket.h \ dht_hash_map.h \ dht_node.cc \ dht_node.h \ dht_router.cc \ dht_router.h \ dht_server.cc \ dht_server.h \ dht_tracker.cc \ dht_tracker.h \ dht_transaction.cc \ dht_transaction.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/dht/dht_bucket.cc000066400000000000000000000135561257211073700177750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "dht_bucket.h" #include "dht_node.h" namespace torrent { DhtBucket::DhtBucket(const HashString& begin, const HashString& end) : m_parent(NULL), m_child(NULL), m_lastChanged(cachedTime.seconds()), m_good(0), m_bad(0), m_fullCacheLength(0), m_begin(begin), m_end(end) { reserve(num_nodes); } void DhtBucket::add_node(DhtNode* n) { push_back(n); touch(); if (n->is_good()) m_good++; else if (n->is_bad()) m_bad++; m_fullCacheLength = 0; } void DhtBucket::remove_node(DhtNode* n) { iterator itr = std::find_if(begin(), end(), std::bind2nd(std::equal_to(), n)); if (itr == end()) throw internal_error("DhtBucket::remove_node called for node not in bucket."); erase(itr); if (n->is_good()) m_good--; else if (n->is_bad()) m_bad--; m_fullCacheLength = 0; } void DhtBucket::count() { m_good = std::count_if(begin(), end(), std::mem_fun(&DhtNode::is_good)); m_bad = std::count_if(begin(), end(), std::mem_fun(&DhtNode::is_bad)); } // Called every 15 minutes for housekeeping. void DhtBucket::update() { count(); // In case adjacent buckets whose nodes we borrowed have changed, // we force an update of the cache. m_fullCacheLength = 0; } DhtBucket::iterator DhtBucket::find_replacement_candidate(bool onlyOldest) { iterator oldest = end(); unsigned int oldestTime = std::numeric_limits::max(); for (iterator itr = begin(); itr != end(); ++itr) { if ((*itr)->is_bad() && !onlyOldest) return itr; if ((*itr)->last_seen() < oldestTime) { oldestTime = (*itr)->last_seen(); oldest = itr; } } return oldest; } void DhtBucket::get_mid_point(HashString* middle) const { *middle = m_end; for (unsigned int i=0; i0; i--) { unsigned int sum = (uint8_t)mid_range[i-1] + carry; m_begin[i-1] = (uint8_t)sum; carry = sum >> 8; } // Move nodes over to other bucket if they fall in its range, then // delete them from this one. iterator split = std::partition(begin(), end(), std::bind2nd(std::mem_fun(&DhtNode::is_in_range), this)); other->insert(other->end(), split, end()); std::for_each(other->begin(), other->end(), std::bind2nd(std::mem_fun(&DhtNode::set_bucket), other)); erase(split, end()); other->set_time(m_lastChanged); other->count(); count(); // Maintain child (adjacent narrower bucket) and parent (adjacent wider bucket) // so that given router ID is in child. if (other->is_in_range(id)) { // Make other become our new child. m_child = other; other->m_parent = this; } else { // We become other's child, other becomes our parent's child. if (parent() != NULL) { parent()->m_child = other; other->m_parent = parent(); } m_parent = other; other->m_child = this; } return other; } void DhtBucket::build_full_cache() { DhtBucketChain chain(this); char* pos = m_fullCache; do { for (const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end() && pos < m_fullCache + sizeof(m_fullCache); ++itr) { if (!(*itr)->is_bad()) { pos = (*itr)->store_compact(pos); if (pos > m_fullCache + sizeof(m_fullCache)) throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end."); } } } while (pos < m_fullCache + sizeof(m_fullCache) && chain.next() != NULL); m_fullCacheLength = pos - m_fullCache; } } libtorrent-0.13.6/src/dht/dht_bucket.h000066400000000000000000000155431257211073700176350ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_BUCKET_H #define LIBTORRENT_DHT_BUCKET_H #include #include "globals.h" #include "torrent/hash_string.h" #include "torrent/object_raw_bencode.h" namespace torrent { class DhtNode; // A container holding a small number of nodes that fall in a given binary // partition of the 160-bit ID space (i.e. the range ID1..ID2 where ID2-ID1+1 is // a power of 2.) class DhtBucket : private std::vector { public: static const unsigned int num_nodes = 8; typedef std::vector base_type; using base_type::const_iterator; using base_type::iterator; using base_type::begin; using base_type::end; using base_type::size; using base_type::empty; DhtBucket(const HashString& begin, const HashString& end); // Add new node. Does NOT set node's bucket automatically (to allow adding a // node to multiple buckets, with only one "main" bucket.) void add_node(DhtNode* n); void remove_node(DhtNode* n); // Bucket's ID range functions. const HashString& id_range_begin() const { return m_begin; } HashString& id_range_begin() { return m_begin; } const HashString& id_range_end() const { return m_end; } bool is_in_range(const HashString& id) const { return m_begin <= id && id <= m_end; } // Find middle or random ID in bucket. void get_mid_point(HashString* middle) const; void get_random_id(HashString* rand_id) const; // Node counts and bucket stats. bool is_full() const { return size() >= num_nodes; } bool has_space() const { return !is_full() || num_bad() > 0; } unsigned int num_good() const { return m_good; } unsigned int num_bad() const { return m_bad; } unsigned int age() const { return cachedTime.seconds() - m_lastChanged; } void touch() { m_lastChanged = cachedTime.seconds(); } void set_time(int32_t time) { m_lastChanged = time; } // Called every 15 minutes after updating nodes. void update(); // Return candidate for replacement (a bad node or the oldest node); may // return end() unless has_space() is true. iterator find_replacement_candidate(bool onlyOldest = false); // Split the bucket in two and redistribute nodes. Returned bucket is the // lower half, "this" bucket keeps the upper half. Sets parent/child so // that the bucket the given ID falls in is the child. DhtBucket* split(const HashString& id); // Parent and child buckets. Parent is the adjacent bucket with double the // ID width, child the adjacent bucket with half the width (except the very // last child which has the same width.) DhtBucket* parent() const { return m_parent; } DhtBucket* child() const { return m_child; } // Return a full bucket's worth of compact node data. If this bucket is not // full, it uses nodes from the child/parent buckets until we have enough. raw_string full_bucket(); // Called by the DhtNode on its bucket to update good/bad node counts. void node_now_good(bool was_bad); void node_now_bad(bool was_good); private: void count(); void build_full_cache(); DhtBucket* m_parent; DhtBucket* m_child; int32_t m_lastChanged; unsigned int m_good; unsigned int m_bad; size_t m_fullCacheLength; // These are 40 bytes together, so might as well put them last. // m_end is const because it is used as key for the DhtRouter routing table // map, which would be inconsistent if m_end were changed carelessly. HashString m_begin; const HashString m_end; char m_fullCache[num_nodes * 26]; }; // Helper class to recursively follow a chain of buckets. It first recurses // into the bucket's children since they are by definition closer to the bucket, // then continues with the bucket's parents. class DhtBucketChain { public: DhtBucketChain(const DhtBucket* b) : m_restart(b), m_cur(b) { } const DhtBucket* bucket() { return m_cur; } const DhtBucket* next(); private: const DhtBucket* m_restart; const DhtBucket* m_cur; }; inline void DhtBucket::node_now_good(bool was_bad) { m_bad -= was_bad; m_good++; } inline void DhtBucket::node_now_bad(bool was_good) { m_good -= was_good; m_bad++; } inline raw_string DhtBucket::full_bucket() { if (!m_fullCacheLength) build_full_cache(); return raw_string(m_fullCache, m_fullCacheLength); } inline const DhtBucket* DhtBucketChain::next() { // m_restart is clear when we're done recursing into the children and // follow the parents instead. if (m_restart == NULL) { m_cur = m_cur->parent(); } else { m_cur = m_cur->child(); if (m_cur == NULL) { m_cur = m_restart->parent(); m_restart = NULL; } } return m_cur; } } #endif libtorrent-0.13.6/src/dht/dht_hash_map.h000066400000000000000000000151311257211073700201310ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_HASH_MAP_H #define LIBTORRENT_DHT_HASH_MAP_H #include "config.h" #if HAVE_TR1 #include #else #include #endif #include "torrent/hash_string.h" #include "dht_node.h" #include "dht_tracker.h" namespace torrent { #if HAVE_TR1 // Hash functions for HashString keys, and dereferencing HashString pointers. // Since the first few bits are very similar if not identical (since the IDs // will be close to our own node ID), we use an offset of 64 bits in the hash // string. These bits will be uniformly distributed until the number of DHT // nodes on the planet approaches 2^64 which is... unlikely. // An offset of 64 bits provides 96 significant bits which is fine as long as // the size of size_t does not exceed 12 bytes, while still having correctly // aligned 64-bit access. static const unsigned int hashstring_hash_ofs = 8; struct hashstring_ptr_hash : public std::unary_function { size_t operator () (const HashString* n) const { #if USE_ALIGNED size_t result = 0; const char *first = n->data() + hashstring_hash_ofs; const char *last = first + sizeof(size_t); while (first != last) result = (result << 8) + *first++; return result; #else return *(size_t*)(n->data() + hashstring_hash_ofs); #endif } }; struct hashstring_hash : public std::unary_function { size_t operator () (const HashString& n) const { #if USE_ALIGNED size_t result = 0; const char *first = n.data() + hashstring_hash_ofs; const char *last = first + sizeof(size_t); while (first != last) result = (result << 8) + *first++; return result; #else return *(size_t*)(n.data() + hashstring_hash_ofs); #endif } }; // Compare HashString pointers by dereferencing them. struct hashstring_ptr_equal : public std::binary_function { size_t operator () (const HashString* one, const HashString* two) const { return *one == *two; } }; class DhtNodeList : public std::tr1::unordered_map { public: typedef std::tr1::unordered_map base_type; // Define accessor iterator with more convenient access to the key and // element values. Allows changing the map definition more easily if needed. template struct accessor_wrapper : public T { accessor_wrapper(const T& itr) : T(itr) { } const HashString& id() const { return *(**this).first; } DhtNode* node() const { return (**this).second; } }; typedef accessor_wrapper const_accessor; typedef accessor_wrapper accessor; DhtNode* add_node(DhtNode* n); }; class DhtTrackerList : public std::tr1::unordered_map { public: typedef std::tr1::unordered_map base_type; template struct accessor_wrapper : public T { accessor_wrapper(const T& itr) : T(itr) { } const HashString& id() const { return (**this).first; } DhtTracker* tracker() const { return (**this).second; } }; typedef accessor_wrapper const_accessor; typedef accessor_wrapper accessor; }; #else // Compare HashString pointers by dereferencing them. struct hashstring_ptr_less : public std::binary_function { size_t operator () (const HashString* one, const HashString* two) const { return *one < *two; } }; class DhtNodeList : public std::map { public: typedef std::map base_type; // Define accessor iterator with more convenient access to the key and // element values. Allows changing the map definition more easily if needed. template struct accessor_wrapper : public T { accessor_wrapper(const T& itr) : T(itr) { } const HashString& id() const { return *(**this).first; } DhtNode* node() const { return (**this).second; } }; typedef accessor_wrapper const_accessor; typedef accessor_wrapper accessor; DhtNode* add_node(DhtNode* n); }; class DhtTrackerList : public std::map { public: typedef std::map base_type; template struct accessor_wrapper : public T { accessor_wrapper(const T& itr) : T(itr) { } const HashString& id() const { return (**this).first; } DhtTracker* tracker() const { return (**this).second; } }; typedef accessor_wrapper const_accessor; typedef accessor_wrapper accessor; }; #endif // HAVE_TR1 inline DhtNode* DhtNodeList::add_node(DhtNode* n) { insert(std::make_pair((const HashString*)n, (DhtNode*)n)); return n; } } #endif libtorrent-0.13.6/src/dht/dht_node.cc000066400000000000000000000057341257211073700174440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "globals.h" #include "torrent/exceptions.h" #include "torrent/object.h" #include "net/address_list.h" // For SA. #include "dht_node.h" namespace torrent { DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) : HashString(id), m_socketAddress(*sa), m_lastSeen(0), m_recentlyActive(false), m_recentlyInactive(0), m_bucket(NULL) { if (sa->family() != rak::socket_address::af_inet) throw resource_error("Address not af_inet"); } DhtNode::DhtNode(const std::string& id, const Object& cache) : HashString(*HashString::cast_from(id.c_str())), m_recentlyActive(false), m_recentlyInactive(0), m_bucket(NULL) { rak::socket_address_inet* sa = m_socketAddress.sa_inet(); sa->set_family(); sa->set_address_h(cache.get_key_value("i")); sa->set_port(cache.get_key_value("p")); m_lastSeen = cache.get_key_value("t"); update(); } char* DhtNode::store_compact(char* buffer) const { HashString::cast_from(buffer)->assign(data()); SocketAddressCompact sa(address()->sa_inet()); std::memcpy(buffer + 20, sa.c_str(), 6); return buffer + 26; } Object* DhtNode::store_cache(Object* container) const { container->insert_key("i", m_socketAddress.sa_inet()->address_h()); container->insert_key("p", m_socketAddress.sa_inet()->port()); container->insert_key("t", m_lastSeen); return container; } } libtorrent-0.13.6/src/dht/dht_node.h000066400000000000000000000117321257211073700173010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_NODE_H #define LIBTORRENT_DHT_NODE_H #include "globals.h" #include #include "torrent/hash_string.h" #include "torrent/object_raw_bencode.h" #include "dht_bucket.h" namespace torrent { class DhtBucket; class DhtNode : public HashString { friend class DhtSearch; public: // A node is considered bad if it failed to reply to this many queries. static const unsigned int max_failed_replies = 5; DhtNode(const HashString& id, const rak::socket_address* sa); DhtNode(const std::string& id, const Object& cache); const HashString& id() const { return *this; } raw_string id_raw_string() const { return raw_string(data(), size_data); } const rak::socket_address* address() const { return &m_socketAddress; } void set_address(const rak::socket_address* sa) { m_socketAddress = *sa; } // For determining node quality. unsigned int last_seen() const { return m_lastSeen; } unsigned int age() const { return cachedTime.seconds() - m_lastSeen; } bool is_good() const { return m_recentlyActive; } bool is_questionable() const { return !m_recentlyActive; } bool is_bad() const { return m_recentlyInactive >= max_failed_replies; }; bool is_active() const { return m_lastSeen; } // Update is called once every 15 minutes. void update() { m_recentlyActive = age() < 15 * 60; } // Called when node replies to us, queries us, or fails to reply. void replied() { set_good(); } void queried() { if (m_lastSeen) set_good(); } void inactive(); DhtBucket* bucket() const { return m_bucket; } DhtBucket* set_bucket(DhtBucket* b) { m_bucket = b; return b; } bool is_in_range(const DhtBucket* b) { return b->is_in_range(*this); } // Store compact node information (26 bytes address, port and ID) in the given // buffer and return pointer to end of stored information. char* store_compact(char* buffer) const; // Store node cache in the given container object and return it. Object* store_cache(Object* container) const; private: DhtNode(); void set_good(); void set_bad(); rak::socket_address m_socketAddress; unsigned int m_lastSeen; bool m_recentlyActive; unsigned int m_recentlyInactive; DhtBucket* m_bucket; }; inline void DhtNode::set_good() { if (m_bucket != NULL && !is_good()) m_bucket->node_now_good(is_bad()); m_lastSeen = cachedTime.seconds(); m_recentlyInactive = 0; m_recentlyActive = true; } inline void DhtNode::set_bad() { if (m_bucket != NULL && !is_bad()) m_bucket->node_now_bad(is_good()); m_recentlyInactive = max_failed_replies; m_recentlyActive = false; } inline void DhtNode::inactive() { if (m_recentlyInactive + 1 == max_failed_replies) set_bad(); else m_recentlyInactive++; } } #endif libtorrent-0.13.6/src/dht/dht_router.cc000066400000000000000000000507401257211073700200340ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "globals.h" #include #include #include "torrent/dht_manager.h" #include "torrent/download_info.h" #include "torrent/exceptions.h" #include "utils/sha1.h" #include "manager.h" #include "dht_bucket.h" #include "dht_router.h" #include "dht_tracker.h" #include "dht_transaction.h" namespace torrent { HashString DhtRouter::zero_id; DhtRouter::DhtRouter(const Object& cache, const rak::socket_address* sa) : DhtNode(zero_id, sa), // actual ID is set later m_server(this), m_contacts(NULL), m_numRefresh(0), m_curToken(random()), m_prevToken(random()) { HashString ones_id; zero_id.clear(); ones_id.clear(0xFF); if (cache.has_key("self_id")) { const std::string& id = cache.get_key_string("self_id"); if (id.length() != HashString::size_data) throw bencode_error("Loading cache: Invalid ID."); assign(id.c_str()); } else { long buffer[size_data]; for (long* itr = buffer; itr != buffer + size_data; ++itr) *itr = random(); Sha1 sha; sha.init(); sha.update(buffer, sizeof(buffer)); sha.final_c(data()); } set_bucket(new DhtBucket(zero_id, ones_id)); m_routingTable.insert(std::make_pair(bucket()->id_range_end(), bucket())); if (cache.has_key("nodes")) { const Object::map_type& nodes = cache.get_key_map("nodes"); for (Object::map_type::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr) { if (itr->first.length() != HashString::size_data) throw bencode_error("Loading cache: Invalid node hash."); add_node_to_bucket(m_nodes.add_node(new DhtNode(itr->first, itr->second))); } } if (m_nodes.size() < num_bootstrap_complete) { m_contacts = new std::deque; if (cache.has_key("contacts")) { const Object::list_type& contacts = cache.get_key_list("contacts"); for (Object::list_type::const_iterator itr = contacts.begin(); itr != contacts.end(); ++itr) { Object::list_type::const_iterator litr = itr->as_list().begin(); const std::string& host = litr->as_string(); int port = (++litr)->as_value(); m_contacts->push_back(std::make_pair(host, port)); } } } } DhtRouter::~DhtRouter() { stop(); delete m_contacts; for (DhtBucketList::iterator itr = m_routingTable.begin(), last = m_routingTable.end(); itr != last; itr++) delete itr->second; for (DhtTrackerList::iterator itr = m_trackers.begin(), last = m_trackers.end(); itr != last; itr++) delete itr->second; for (DhtNodeList::iterator itr = m_nodes.begin(), last = m_nodes.end(); itr != last; itr++) delete itr->second; } void DhtRouter::start(int port) { m_server.start(port); // Set timeout slot and schedule it to be called immediately for initial bootstrapping if necessary. m_taskTimeout.slot() = std::tr1::bind(&DhtRouter::receive_timeout_bootstrap, this); priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); } void DhtRouter::stop() { priority_queue_erase(&taskScheduler, &m_taskTimeout); m_server.stop(); } // Start a DHT get_peers and announce_peer request. void DhtRouter::announce(DownloadInfo* info, TrackerDht* tracker) { m_server.announce(*find_bucket(info->hash())->second, info->hash(), tracker); } // Cancel any running requests from the given tracker. // If info or tracker is not NULL, only cancel matching requests. void DhtRouter::cancel_announce(DownloadInfo* info, const TrackerDht* tracker) { m_server.cancel_announce(info, tracker); } DhtTracker* DhtRouter::get_tracker(const HashString& hash, bool create) { DhtTrackerList::accessor itr = m_trackers.find(hash); if (itr != m_trackers.end()) return itr.tracker(); if (!create) return NULL; std::pair res = m_trackers.insert(std::make_pair(hash, new DhtTracker())); if (!res.second) throw internal_error("DhtRouter::get_tracker did not actually insert tracker."); return res.first.tracker(); } bool DhtRouter::want_node(const HashString& id) { // We don't want to add ourself. Also, too many broken implementations // advertise an ID of 0, which causes collisions, so reject that. if (id == this->id() || id == zero_id) return false; // We are always interested in more nodes for our own bucket (causing it // to be split if full); in other buckets only if there's space. DhtBucket* b = find_bucket(id)->second; return b == bucket() || b->has_space(); } DhtNode* DhtRouter::get_node(const HashString& id) { DhtNodeList::accessor itr = m_nodes.find(&id); if (itr == m_nodes.end()) { if (id == this->id()) return this; else return NULL; } return itr.node(); } DhtRouter::DhtBucketList::iterator DhtRouter::find_bucket(const HashString& id) { DhtBucketList::iterator itr = m_routingTable.lower_bound(id); #ifdef USE_EXTRA_DEBUG if (itr == m_routingTable.end()) throw internal_error("DHT Buckets not covering entire ID space."); if (!itr->second->is_in_range(id)) throw internal_error("DhtRouter::find_bucket, m_routingTable.lower_bound did not find correct bucket."); #endif return itr; } void DhtRouter::add_contact(const std::string& host, int port) { // Externally obtained nodes are added to the contact list, but only if // we're still bootstrapping. We don't contact external nodes after that. if (m_contacts != NULL) { if (m_contacts->size() >= num_bootstrap_contacts) m_contacts->pop_front(); m_contacts->push_back(std::make_pair(host, port)); } } void DhtRouter::contact(const rak::socket_address* sa, int port) { if (is_active()) { rak::socket_address sa_port = *sa; sa_port.set_port(port); m_server.ping(zero_id, &sa_port); } } // Received a query from the given node. If it has previously replied // to one of our queries, consider it alive and update the bucket mtime, // otherwise if we could use it in a bucket, try contacting it. DhtNode* DhtRouter::node_queried(const HashString& id, const rak::socket_address* sa) { DhtNode* node = get_node(id); if (node == NULL) { if (want_node(id)) m_server.ping(id, sa); return NULL; } // If we know the ID but the address is different, don't set the original node // active, but neither use this new address to prevent rogue nodes from polluting // our routing table with fake source addresses. if (node->address()->sa_inet()->address_n() != sa->sa_inet()->address_n()) return NULL; node->queried(); if (node->is_good()) node->bucket()->touch(); return node; } // Received a reply from a node we queried. // Check that it matches the information we have, set that it has replied // and update the bucket mtime. DhtNode* DhtRouter::node_replied(const HashString& id, const rak::socket_address* sa) { DhtNode* node = get_node(id); if (node == NULL) { if (!want_node(id)) return NULL; // New node, create it. It's a good node (it replied!) so add it to a bucket. node = m_nodes.add_node(new DhtNode(id, sa)); if (!add_node_to_bucket(node)) // deletes the node if it fails return NULL; } if (node->address()->sa_inet()->address_n() != sa->sa_inet()->address_n()) return NULL; node->replied(); node->bucket()->touch(); return node; } // A node has not replied to one of our queries. DhtNode* DhtRouter::node_inactive(const HashString& id, const rak::socket_address* sa) { DhtNodeList::accessor itr = m_nodes.find(&id); // If not found add it to some blacklist so we won't try contacting it again immediately? if (itr == m_nodes.end()) return NULL; // Check source address. Normally node_inactive is called if we DON'T receive a reply, // however it can also be called if a node replied with an malformed response packet, // so check that the address matches so that a rogue node cannot cause other nodes // to be considered bad by sending malformed packets. if (itr.node()->address()->sa_inet()->address_n() != sa->sa_inet()->address_n()) return NULL; itr.node()->inactive(); // Old node age normally implies no replies for many consecutive queries, however // after loading the node cache after a day or more we want to give each node a few // chances to reply again instead of removing all nodes instantly. if (itr.node()->is_bad() && itr.node()->age() >= timeout_remove_node) { delete_node(itr); return NULL; } return itr.node(); } // We sent a query to the given node ID, but received a reply from a different // node ID, that means the address of the original ID is invalid now. void DhtRouter::node_invalid(const HashString& id) { DhtNode* node = get_node(id); if (node == NULL || node == this) return; delete_node(m_nodes.find(&node->id())); } Object* DhtRouter::store_cache(Object* container) const { container->insert_key("self_id", str()); // Insert all nodes. Object& nodes = container->insert_key("nodes", Object::create_map()); for (DhtNodeList::const_accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) { if (!itr.node()->is_bad()) itr.node()->store_cache(&nodes.insert_key(itr.id().str(), Object::create_map())); } // Insert contacts, if we have any. if (m_contacts != NULL) { Object& contacts = container->insert_key("contacts", Object::create_list()); for (std::deque::const_iterator itr = m_contacts->begin(); itr != m_contacts->end(); ++itr) { Object::list_type& list = contacts.insert_back(Object::create_list()).as_list(); list.push_back(itr->first); list.push_back(itr->second); } } return container; } DhtManager::statistics_type DhtRouter::get_statistics() const { DhtManager::statistics_type stats(*m_server.upload_throttle_node()->rate(), *m_server.download_throttle_node()->rate()); if (!m_server.is_active()) stats.cycle = 0; else if (m_numRefresh < 2) // still bootstrapping stats.cycle = 1; else stats.cycle = m_numRefresh; stats.queries_received = m_server.queries_received(); stats.queries_sent = m_server.queries_sent(); stats.replies_received = m_server.replies_received(); stats.errors_received = m_server.errors_received(); stats.errors_caught = m_server.errors_caught(); stats.num_nodes = m_nodes.size(); stats.num_buckets = m_routingTable.size(); stats.num_peers = 0; stats.max_peers = 0; stats.num_trackers = m_trackers.size(); for (DhtTrackerList::const_accessor itr = m_trackers.begin(); itr != m_trackers.end(); ++itr) { unsigned int peers = itr.tracker()->size(); stats.num_peers += peers; stats.max_peers = std::max(peers, stats.max_peers); } return stats; } void DhtRouter::receive_timeout_bootstrap() { // If we're still bootstrapping, restart the process every 60 seconds until // we have enough nodes in our routing table. After we have 32 nodes, we switch // to a less aggressive non-bootstrap mode of collecting nodes that contact us // and through doing normal torrent announces. if (m_nodes.size() < num_bootstrap_complete) { if (m_contacts == NULL) throw internal_error("DhtRouter::receive_timeout_bootstrap called without contact list."); if (!m_nodes.empty() || !m_contacts->empty()) bootstrap(); // Retry in 60 seconds. priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_bootstrap_retry)).round_seconds()); m_numRefresh = 1; // still bootstrapping } else { // We won't be needing external contacts after this. delete m_contacts; m_contacts = NULL; m_taskTimeout.slot() = std::tr1::bind(&DhtRouter::receive_timeout, this); if (!m_numRefresh) { // If we're still in the startup, do the usual refreshing too. receive_timeout(); } else { // Otherwise just set the 15 minute timer. priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_update)).round_seconds()); } m_numRefresh = 2; } } void DhtRouter::receive_timeout() { priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_update)).round_seconds()); m_prevToken = m_curToken; m_curToken = random(); // Do some periodic accounting, refreshing buckets and marking // bad nodes. // Update nodes. for (DhtNodeList::accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) { if (!itr.node()->bucket()) throw internal_error("DhtRouter::receive_timeout has node without bucket."); itr.node()->update(); // Try contacting nodes we haven't received anything from for a while. // Don't contact repeatedly unresponsive nodes; we keep them in case they // do send a query, until we find a better node. However, give it a last // chance just before deleting it. if (itr.node()->is_questionable() && (!itr.node()->is_bad() || itr.node()->age() >= timeout_remove_node)) m_server.ping(itr.node()->id(), itr.node()->address()); } // If bucket isn't full yet or hasn't received replies/queries from // its nodes for a while, try to find new nodes now. for (DhtBucketList::const_iterator itr = m_routingTable.begin(); itr != m_routingTable.end(); ++itr) { itr->second->update(); if (!itr->second->is_full() || itr->second == bucket() || itr->second->age() > timeout_bucket_bootstrap) bootstrap_bucket(itr->second); } // Remove old peers and empty torrents from the tracker. for (DhtTrackerList::accessor itr = m_trackers.begin(); itr != m_trackers.end(); ) { itr.tracker()->prune(timeout_peer_announce); if (itr.tracker()->empty()) { delete itr.tracker(); m_trackers.erase(itr++); } else { ++itr; } } m_server.update(); m_numRefresh++; } char* DhtRouter::generate_token(const rak::socket_address* sa, int token, char buffer[20]) { Sha1 sha; uint32_t key = sa->sa_inet()->address_n(); sha.init(); sha.update(&token, sizeof(token)); sha.update(&key, 4); sha.final_c(buffer); return buffer; } bool DhtRouter::token_valid(raw_string token, const rak::socket_address* sa) { if (token.size() != size_token) return false; // Compare given token to the reference token. char reference[20]; // First try current token. // // Else if token recently changed, some clients may be using the older one. // That way a token is valid for 15-30 minutes, instead of 0-15. return token == raw_string(generate_token(sa, m_curToken, reference), size_token) || token == raw_string(generate_token(sa, m_prevToken, reference), size_token); } DhtNode* DhtRouter::find_node(const rak::socket_address* sa) { for (DhtNodeList::accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) if (itr.node()->address()->sa_inet()->address_n() == sa->sa_inet()->address_n()) return itr.node(); return NULL; } DhtRouter::DhtBucketList::iterator DhtRouter::split_bucket(const DhtBucketList::iterator& itr, DhtNode* node) { // Split bucket. Current bucket keeps the upper half thus keeping the // map key valid, new bucket is the lower half of the original bucket. DhtBucket* newBucket = itr->second->split(id()); // If our bucket has a child now (the new bucket), move ourself into it. if (bucket()->child() != NULL) set_bucket(bucket()->child()); if (!bucket()->is_in_range(id())) throw internal_error("DhtRouter::split_bucket router ID ended up in wrong bucket."); // Insert new bucket with iterator hint = just before current bucket. DhtBucketList::iterator other = m_routingTable.insert(itr, std::make_pair(newBucket->id_range_end(), newBucket)); // Check that the bucket we're not adding the node to isn't empty. if (other->second->is_in_range(node->id())) { if (itr->second->empty()) bootstrap_bucket(itr->second); } else { if (other->second->empty()) bootstrap_bucket(other->second); other = itr; } return other; } bool DhtRouter::add_node_to_bucket(DhtNode* node) { DhtBucketList::iterator itr = find_bucket(node->id()); while (itr->second->is_full()) { // Bucket is full. If there are any bad nodes, remove the oldest. DhtBucket::iterator nodeItr = itr->second->find_replacement_candidate(); if (nodeItr == itr->second->end()) throw internal_error("DhtBucket::find_candidate returned no node."); if ((*nodeItr)->is_bad()) { delete_node(m_nodes.find(&(*nodeItr)->id())); } else { // Bucket is full of good nodes; if our own ID falls in // range then split the bucket else discard new node. if (itr->second != bucket()) { delete_node(m_nodes.find(&node->id())); return false; } itr = split_bucket(itr, node); } } itr->second->add_node(node); node->set_bucket(itr->second); return true; } void DhtRouter::delete_node(const DhtNodeList::accessor& itr) { if (itr == m_nodes.end()) throw internal_error("DhtRouter::delete_node called with invalid iterator."); if (itr.node()->bucket() != NULL) itr.node()->bucket()->remove_node(itr.node()); delete itr.node(); m_nodes.erase(itr); } struct contact_node_t { contact_node_t(DhtRouter* router, int port) : m_router(router), m_port(port) { } void operator() (const sockaddr* sa, int err) { if (sa != NULL) m_router->contact(rak::socket_address::cast_from(sa), m_port); } DhtRouter* m_router; int m_port; }; void DhtRouter::bootstrap() { // Contact up to 8 nodes from the contact list (newest first). for (int count = 0; count < 8 && !m_contacts->empty(); count++) { manager->connection_manager()->resolver()(m_contacts->back().first.c_str(), (int)rak::socket_address::pf_inet, SOCK_DGRAM, contact_node_t(this, m_contacts->back().second)); m_contacts->pop_back(); } // Abort unless we already found some nodes for a search. if (m_nodes.empty()) return; bootstrap_bucket(bucket()); // Aggressively ping all questionable nodes in our own bucket to weed // out bad nodes as early as possible and make room for fresh nodes. for (DhtBucket::iterator itr = bucket()->begin(); itr != bucket()->end(); ++itr) if (!(*itr)->is_good()) m_server.ping((*itr)->id(), (*itr)->address()); // Also bootstrap a random bucket, if there are others. if (m_routingTable.size() < 2) return; DhtBucketList::iterator itr = m_routingTable.begin(); std::advance(itr, random() % m_routingTable.size()); if (itr->second != bucket() && itr != m_routingTable.end()) bootstrap_bucket(itr->second); } void DhtRouter::bootstrap_bucket(const DhtBucket* bucket) { if (!m_server.is_active()) return; // Do a search for a random ID, or the ID adjacent to our // own when bootstrapping our own bucket. We don't search for // our own exact ID to avoid receiving only our own node info // instead of closest nodes, from nodes that know us already. HashString contactId; if (bucket == this->bucket()) { contactId = id(); contactId[contactId.size() - 1] ^= 1; } else { bucket->get_random_id(&contactId); } m_server.find_node(*bucket, contactId); } } libtorrent-0.13.6/src/dht/dht_router.h000066400000000000000000000172161257211073700176770ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_ROUTER_H #define LIBTORRENT_DHT_ROUTER_H #include #include #include "torrent/dht_manager.h" #include "torrent/hash_string.h" #include "torrent/object.h" #include "dht_node.h" #include "dht_hash_map.h" #include "dht_server.h" namespace torrent { class DhtBucket; class DhtTracker; class TrackerDht; // Main DHT class, maintains the routing table of known nodes and talks to the // DhtServer object that handles the actual communication. class DhtRouter : public DhtNode { public: // How many bytes to return and verify from the 20-byte SHA token. static const unsigned int size_token = 8; static const unsigned int timeout_bootstrap_retry = 60; // Retry initial bootstrapping every minute. static const unsigned int timeout_update = 15 * 60; // Regular housekeeping updates every 15 minutes. static const unsigned int timeout_bucket_bootstrap = 15 * 60; // Bootstrap idle buckets after 15 minutes. static const unsigned int timeout_remove_node = 4 * 60 * 60; // Remove unresponsive nodes after 4 hours. static const unsigned int timeout_peer_announce = 30 * 60; // Remove peers which haven't reannounced for 30 minutes. // A node ID of all zero. static HashString zero_id; DhtRouter(const Object& cache, const rak::socket_address* sa); ~DhtRouter(); // Start and stop the router. This starts/stops the UDP server as well. void start(int port); void stop(); bool is_active() { return m_server.is_active(); } // Find peers for given download and announce ourselves. void announce(DownloadInfo* info, TrackerDht* tracker); // Cancel any pending transactions related to the given download (or all if NULL). void cancel_announce(DownloadInfo* info, const TrackerDht* tracker); // Retrieve tracked torrent for the hash. // Returns NULL if not tracking the torrent unless create is true. DhtTracker* get_tracker(const HashString& hash, bool create); // Check if we are interested in inserting a new node of the given ID // into our table (i.e. if we have space or bad nodes in the corresponding bucket). bool want_node(const HashString& id); // Add the given host to the list of potential contacts if we haven't // completed the bootstrap process, or contact the given address directly. void add_contact(const std::string& host, int port); void contact(const rak::socket_address* sa, int port); // Retrieve node of given ID in constant time. Return NULL if not found, unless // it's our own ID in which case it returns the DhtRouter object. DhtNode* get_node(const HashString& id); // Search for node with given address in O(n), disregarding the port. DhtNode* find_node(const rak::socket_address* sa); // Whenever a node queries us, replies, or is confirmed inactive (no reply) or // invalid (reply with wrong ID), we need to update its status. DhtNode* node_queried(const HashString& id, const rak::socket_address* sa); DhtNode* node_replied(const HashString& id, const rak::socket_address* sa); DhtNode* node_inactive(const HashString& id, const rak::socket_address* sa); void node_invalid(const HashString& id); // Store compact node information (26 bytes) for nodes closest to the // given ID in the given buffer, return new buffer end. raw_string get_closest_nodes(const HashString& id) { return find_bucket(id)->second->full_bucket(); } // Store DHT cache in the given container. Object* store_cache(Object* container) const; // Create and verify a token. Tokens are valid between 15-30 minutes from creation. raw_string make_token(const rak::socket_address* sa, char* buffer); bool token_valid(raw_string token, const rak::socket_address* sa); DhtManager::statistics_type get_statistics() const; void reset_statistics() { m_server.reset_statistics(); } void set_upload_throttle(ThrottleList* t) { m_server.set_upload_throttle(t); } void set_download_throttle(ThrottleList* t) { m_server.set_download_throttle(t); } private: // Hostname and port of potential bootstrap nodes. typedef std::pair contact_t; // Number of nodes we need to consider the bootstrap process complete. static const unsigned int num_bootstrap_complete = 32; // Maximum number of potential contacts to keep until bootstrap complete. static const unsigned int num_bootstrap_contacts = 64; typedef std::map DhtBucketList; DhtBucketList::iterator find_bucket(const HashString& id); bool add_node_to_bucket(DhtNode* node); void delete_node(const DhtNodeList::accessor& itr); void store_closest_nodes(const HashString& id, DhtBucket* bucket); DhtBucketList::iterator split_bucket(const DhtBucketList::iterator& itr, DhtNode* node); void bootstrap(); void bootstrap_bucket(const DhtBucket* bucket); void receive_timeout(); void receive_timeout_bootstrap(); // buffer needs to hold an SHA1 hash (20 bytes), not just the token (8 bytes) char* generate_token(const rak::socket_address* sa, int token, char buffer[20]); rak::priority_item m_taskTimeout; DhtServer m_server; DhtNodeList m_nodes; DhtBucketList m_routingTable; DhtTrackerList m_trackers; std::deque* m_contacts; int m_numRefresh; bool m_networkUp; // Secret keys used for generating announce tokens. int m_curToken; int m_prevToken; }; inline raw_string DhtRouter::make_token(const rak::socket_address* sa, char* buffer) { return raw_string(generate_token(sa, m_curToken, buffer), size_token); } } #endif libtorrent-0.13.6/src/dht/dht_server.cc000066400000000000000000000706741257211073700200320ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "globals.h" #include #include #include #include "torrent/exceptions.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/object.h" #include "torrent/object_stream.h" #include "torrent/poll.h" #include "torrent/object_static_map.h" #include "torrent/throttle.h" #include "tracker/tracker_dht.h" #include "dht_bucket.h" #include "dht_router.h" #include "dht_transaction.h" #include "manager.h" namespace torrent { const char* DhtServer::queries[] = { "ping", "find_node", "get_peers", "announce_peer", }; // List of all possible keys we need/support in a DHT message. // Unsupported keys we receive are dropped (ignored) while decoding. // See torrent/object_static_map.h for how this works. template <> const DhtMessage::key_list_type DhtMessage::base_type::keys = { { key_a_id, "a::id*S" }, { key_a_infoHash, "a::info_hash*S" }, { key_a_port, "a::port", }, { key_a_target, "a::target*S" }, { key_a_token, "a::token*S" }, { key_e_0, "e[]*" }, { key_e_1, "e[]*" }, { key_q, "q*S" }, { key_r_id, "r::id*S" }, { key_r_nodes, "r::nodes*S" }, { key_r_token, "r::token*S" }, { key_r_values, "r::values*L" }, { key_t, "t*S" }, { key_v, "v*" }, { key_y, "y*S" }, }; // Error in DHT protocol, avoids std::string ctor from communication_error class dht_error : public network_error { public: dht_error(int code, const char* message) : m_message(message), m_code(code) {} virtual int code() const throw() { return m_code; } virtual const char* what() const throw() { return m_message; } private: const char* m_message; int m_code; }; DhtServer::DhtServer(DhtRouter* router) : m_router(router), m_uploadNode(60), m_downloadNode(60), m_uploadThrottle(manager->upload_throttle()->throttle_list()), m_downloadThrottle(manager->download_throttle()->throttle_list()), m_networkUp(false) { get_fd().clear(); reset_statistics(); // Reserve a socket for the DHT server, even though we don't // actually open it until the server is started, which may not // happen until the first non-private torrent is started. manager->connection_manager()->inc_socket_count(); } DhtServer::~DhtServer() { stop(); std::for_each(m_highQueue.begin(), m_highQueue.end(), rak::call_delete()); std::for_each(m_lowQueue.begin(), m_lowQueue.end(), rak::call_delete()); manager->connection_manager()->dec_socket_count(); } void DhtServer::start(int port) { try { if (!get_fd().open_datagram() || !get_fd().set_nonblock()) throw resource_error("Could not allocate datagram socket."); if (!get_fd().set_reuse_address(true)) throw resource_error("Could not set listening port to reuse address."); rak::socket_address sa = *m_router->address(); sa.set_port(port); if (!get_fd().bind(sa)) throw resource_error("Could not bind datagram socket."); } catch (torrent::base_error& e) { get_fd().close(); get_fd().clear(); throw; } m_taskTimeout.slot() = std::tr1::bind(&DhtServer::receive_timeout, this); m_uploadNode.set_list_iterator(m_uploadThrottle->end()); m_uploadNode.slot_activate() = std::tr1::bind(&SocketBase::receive_throttle_up_activate, static_cast(this)); m_downloadNode.set_list_iterator(m_downloadThrottle->end()); m_downloadThrottle->insert(&m_downloadNode); manager->poll()->open(this); manager->poll()->insert_read(this); manager->poll()->insert_error(this); } void DhtServer::stop() { if (!is_active()) return; clear_transactions(); priority_queue_erase(&taskScheduler, &m_taskTimeout); m_uploadThrottle->erase(&m_uploadNode); m_downloadThrottle->erase(&m_downloadNode); manager->poll()->remove_read(this); manager->poll()->remove_write(this); manager->poll()->remove_error(this); manager->poll()->close(this); get_fd().close(); get_fd().clear(); m_networkUp = false; } void DhtServer::reset_statistics() { m_queriesReceived = 0; m_queriesSent = 0; m_repliesReceived = 0; m_errorsReceived = 0; m_errorsCaught = 0; m_uploadNode.rate()->set_total(0); m_downloadNode.rate()->set_total(0); } // Ping a node whose ID we know. void DhtServer::ping(const HashString& id, const rak::socket_address* sa) { // No point pinging a node that we're already contacting otherwise. transaction_itr itr = m_transactions.lower_bound(DhtTransaction::key(sa, 0)); if (itr == m_transactions.end() || !DhtTransaction::key_match(itr->first, sa)) add_transaction(new DhtTransactionPing(id, sa), packet_prio_low); } // Contact nodes in given bucket and ask for their nodes closest to target. void DhtServer::find_node(const DhtBucket& contacts, const HashString& target) { DhtSearch* search = new DhtSearch(target, contacts); DhtSearch::const_accessor n; while ((n = search->get_contact()) != search->end()) add_transaction(new DhtTransactionFindNode(n), packet_prio_low); // This shouldn't happen, it means we had no contactable nodes at all. if (!search->start()) delete search; } void DhtServer::announce(const DhtBucket& contacts, const HashString& infoHash, TrackerDht* tracker) { DhtAnnounce* announce = new DhtAnnounce(infoHash, tracker, contacts); DhtSearch::const_accessor n; while ((n = announce->get_contact()) != announce->end()) add_transaction(new DhtTransactionFindNode(n), packet_prio_high); // This can only happen if all nodes we know are bad. if (!announce->start()) delete announce; else announce->update_status(); } void DhtServer::cancel_announce(DownloadInfo* info, const TrackerDht* tracker) { transaction_itr itr = m_transactions.begin(); while (itr != m_transactions.end()) { if (itr->second->is_search() && itr->second->as_search()->search()->is_announce()) { DhtAnnounce* announce = static_cast(itr->second->as_search()->search()); if ((info == NULL || announce->target() == info->hash()) && (tracker == NULL || announce->tracker() == tracker)) { delete itr->second; m_transactions.erase(itr++); continue; } } ++itr; } } void DhtServer::update() { // Reset this every 15 minutes. It'll get set back to true if we receive // any valid packets. This allows detecting when the entire network goes // down, and prevents all nodes from getting removed as unresponsive. m_networkUp = false; } void DhtServer::process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& msg) { m_queriesReceived++; m_networkUp = true; raw_string query = msg[key_q].as_raw_string(); // Construct reply. DhtMessage reply; if (query == raw_string::from_c_str("find_node")) create_find_node_response(msg, reply); else if (query == raw_string::from_c_str("get_peers")) create_get_peers_response(msg, sa, reply); else if (query == raw_string::from_c_str("announce_peer")) create_announce_peer_response(msg, sa, reply); else if (query != raw_string::from_c_str("ping")) throw dht_error(dht_error_bad_method, "Unknown query type."); m_router->node_queried(id, sa); create_response(msg, sa, reply); } void DhtServer::create_find_node_response(const DhtMessage& req, DhtMessage& reply) { raw_string target = req[key_a_target].as_raw_string(); if (target.size() < HashString::size_data) throw dht_error(dht_error_protocol, "target string too short"); reply[key_r_nodes] = m_router->get_closest_nodes(*HashString::cast_from(target.data())); if (reply[key_r_nodes].as_raw_string().empty()) throw dht_error(dht_error_generic, "No nodes"); } void DhtServer::create_get_peers_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { reply[key_r_token] = m_router->make_token(sa, reply.data_end); reply.data_end += reply[key_r_token].as_raw_string().size(); raw_string info_hash_str = req[key_a_infoHash].as_raw_string(); if (info_hash_str.size() < HashString::size_data) throw dht_error(dht_error_protocol, "info hash too short"); const HashString* info_hash = HashString::cast_from(info_hash_str.data()); DhtTracker* tracker = m_router->get_tracker(*info_hash, false); // If we're not tracking or have no peers, send closest nodes. if (!tracker || tracker->empty()) { raw_string nodes = m_router->get_closest_nodes(*info_hash); if (nodes.empty()) throw dht_error(dht_error_generic, "No peers nor nodes"); reply[key_r_nodes] = nodes; } else { reply[key_r_values] = tracker->get_peers(); } } void DhtServer::create_announce_peer_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { raw_string info_hash = req[key_a_infoHash].as_raw_string(); if (info_hash.size() < HashString::size_data) throw dht_error(dht_error_protocol, "info hash too short"); if (!m_router->token_valid(req[key_a_token].as_raw_string(), sa)) throw dht_error(dht_error_protocol, "Token invalid."); DhtTracker* tracker = m_router->get_tracker(*HashString::cast_from(info_hash.data()), true); tracker->add_peer(sa->sa_inet()->address_n(), req[key_a_port].as_value()); } void DhtServer::process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& response) { int transactionId = (unsigned char)response[key_t].as_raw_string().data()[0]; transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId)); // Response to a transaction we don't have in our table. At this point it's // impossible to tell whether it used to be a valid transaction but timed out // the node did not return the ID we sent it, or it returned it with a // different address than we sent it o. Best we can do is ignore the reply, // since the protocol doesn't call for returning errors in responses. if (itr == m_transactions.end()) return; m_repliesReceived++; m_networkUp = true; // Make sure transaction is erased even if an exception is thrown. try { DhtTransaction* transaction = itr->second; #ifdef USE_EXTRA_DEBUG if (DhtTransaction::key(sa, transactionId) != transaction->key(transactionId)) throw internal_error("DhtServer::process_response key mismatch."); #endif // If we contact a node but its ID is not the one we expect, ignore the reply // to prevent interference from rogue nodes. if ((id != transaction->id() && transaction->id() != m_router->zero_id)) return; switch (transaction->type()) { case DhtTransaction::DHT_FIND_NODE: parse_find_node_reply(transaction->as_find_node(), response[key_r_nodes].as_raw_string()); break; case DhtTransaction::DHT_GET_PEERS: parse_get_peers_reply(transaction->as_get_peers(), response); break; // Nothing to do for DHT_PING and DHT_ANNOUNCE_PEER default: break; } // Mark node responsive only if all processing was successful, without errors. m_router->node_replied(id, sa); } catch (std::exception& e) { delete itr->second; m_transactions.erase(itr); m_errorsCaught++; throw; } delete itr->second; m_transactions.erase(itr); } void DhtServer::process_error(const rak::socket_address* sa, const DhtMessage& error) { int transactionId = (unsigned char)error[key_t].as_raw_string().data()[0]; transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId)); if (itr == m_transactions.end()) return; m_repliesReceived++; m_errorsReceived++; m_networkUp = true; // Don't mark node as good (because it replied) or bad (because it returned an error). // If it consistently returns errors for valid queries it's probably broken. But a // few error messages are acceptable. So we do nothing and pretend the query never happened. delete itr->second; m_transactions.erase(itr); } void DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, raw_string nodes) { transaction->complete(true); if (sizeof(const compact_node_info) != 26) throw internal_error("DhtServer::parse_find_node_reply(...) bad struct size."); node_info_list list; std::copy(reinterpret_cast(nodes.data()), reinterpret_cast(nodes.data() + nodes.size() - nodes.size() % sizeof(compact_node_info)), std::back_inserter(list)); for (node_info_list::iterator itr = list.begin(); itr != list.end(); ++itr) { if (itr->id() != m_router->id()) { rak::socket_address sa = itr->address(); transaction->search()->add_contact(itr->id(), &sa); } } find_node_next(transaction); } void DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const DhtMessage& response) { DhtAnnounce* announce = static_cast(transaction->as_search()->search()); transaction->complete(true); if (response[key_r_values].is_raw_list()) announce->receive_peers(response[key_r_values].as_raw_list()); if (response[key_r_token].is_raw_string()) add_transaction(new DhtTransactionAnnouncePeer(transaction->id(), transaction->address(), announce->target(), response[key_r_token].as_raw_string()), packet_prio_low); announce->update_status(); } void DhtServer::find_node_next(DhtTransactionSearch* transaction) { int priority = packet_prio_low; if (transaction->search()->is_announce()) priority = packet_prio_high; DhtSearch::const_accessor node; while ((node = transaction->search()->get_contact()) != transaction->search()->end()) add_transaction(new DhtTransactionFindNode(node), priority); if (!transaction->search()->is_announce()) return; DhtAnnounce* announce = static_cast(transaction->search()); if (announce->complete()) { // We have found the 8 closest nodes to the info hash. Retrieve peers // from them and announce to them. for (node = announce->start_announce(); node != announce->end(); ++node) add_transaction(new DhtTransactionGetPeers(node), packet_prio_high); } announce->update_status(); } void DhtServer::add_packet(DhtTransactionPacket* packet, int priority) { switch (priority) { // High priority packets are for important queries, and quite small. // They're added to front of high priority queue and thus will be the // next packets sent. case packet_prio_high: m_highQueue.push_front(packet); break; // Low priority query packets are added to the back of the high priority // queue and will be sent when all high priority packets have been transmitted. case packet_prio_low: m_highQueue.push_back(packet); break; // Reply packets will be processed after all of our own packets have been send. case packet_prio_reply: m_lowQueue.push_back(packet); break; default: throw internal_error("DhtServer::add_packet called with invalid priority."); } } void DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority) { if (itr->second->id() == m_router->id()) throw internal_error("DhtServer::create_query trying to send to itself."); DhtMessage query; // Transaction ID is a bencode string. query[key_t] = raw_bencode(query.data_end, 3); *query.data_end++ = '1'; *query.data_end++ = ':'; *query.data_end++ = tID; DhtTransaction* transaction = itr->second; query[key_q] = raw_string::from_c_str(queries[transaction->type()]); query[key_y] = raw_bencode::from_c_str("1:q"); query[key_v] = raw_bencode("4:" PEER_VERSION, 6); query[key_a_id] = m_router->id_raw_string(); switch (transaction->type()) { case DhtTransaction::DHT_PING: // nothing to do break; case DhtTransaction::DHT_FIND_NODE: query[key_a_target] = transaction->as_find_node()->search()->target_raw_string(); break; case DhtTransaction::DHT_GET_PEERS: query[key_a_infoHash] = transaction->as_get_peers()->search()->target_raw_string(); break; case DhtTransaction::DHT_ANNOUNCE_PEER: query[key_a_infoHash] = transaction->as_announce_peer()->info_hash_raw_string(); query[key_a_token] = transaction->as_announce_peer()->token(); query[key_a_port] = manager->connection_manager()->listen_port(); break; } DhtTransactionPacket* packet = new DhtTransactionPacket(transaction->address(), query, tID, transaction); transaction->set_packet(packet); add_packet(packet, priority); m_queriesSent++; } void DhtServer::create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply) { reply[key_r_id] = m_router->id_raw_string(); reply[key_t] = req[key_t]; reply[key_y] = raw_bencode::from_c_str("1:r"); reply[key_v] = raw_bencode("4:" PEER_VERSION, 6); add_packet(new DhtTransactionPacket(sa, reply), packet_prio_reply); } void DhtServer::create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg) { DhtMessage error; if (req[key_t].is_raw_bencode() || req[key_t].is_raw_string()) error[key_t] = req[key_t]; error[key_y] = raw_bencode::from_c_str("1:e"); error[key_v] = raw_bencode("4:" PEER_VERSION, 6); error[key_e_0] = num; error[key_e_1] = raw_string::from_c_str(msg); add_packet(new DhtTransactionPacket(sa, error), packet_prio_reply); } int DhtServer::add_transaction(DhtTransaction* transaction, int priority) { // Try random transaction ID. This is to make it less likely that we reuse // a transaction ID from an earlier transaction which timed out and we forgot // about it, so that if the node replies after the timeout it's less likely // that we match the reply to the wrong transaction. // // If there's an existing transaction with the random ID we search for the next // unused one. Since normally only one or two transactions will be active per // node, a collision is extremely unlikely, and a linear search for the first // open one is the most efficient. unsigned int rnd = (uint8_t)random(); unsigned int id = rnd; transaction_itr insertItr = m_transactions.lower_bound(transaction->key(rnd)); // If key matches, keep trying successive IDs. while (insertItr != m_transactions.end() && insertItr->first == transaction->key(id)) { ++insertItr; id = (uint8_t)(id + 1); // Give up after trying all possible IDs. This should never happen. if (id == rnd) { delete transaction; return -1; } // Transaction ID wrapped around, reset iterator. if (id == 0) insertItr = m_transactions.lower_bound(transaction->key(id)); } // We know where to insert it, so pass that as hint. insertItr = m_transactions.insert(insertItr, std::make_pair(transaction->key(id), transaction)); create_query(insertItr, id, transaction->address(), priority); start_write(); return id; } // Transaction received no reply and timed out. Mark node as bad and remove // transaction (except if it was only the quick timeout). DhtServer::transaction_itr DhtServer::failed_transaction(transaction_itr itr, bool quick) { DhtTransaction* transaction = itr->second; // If it was a known node, remember that it didn't reply, unless the transaction // is only stalled (had quick timeout, but not full timeout). Also if the // transaction still has an associated packet, the packet never got sent due to // throttling, so don't blame the remote node for not replying. // Finally, if we haven't received anything whatsoever so far, assume the entire // network is down and so we can't blame the node either. if (!quick && m_networkUp && transaction->packet() == NULL && transaction->id() != m_router->zero_id) m_router->node_inactive(transaction->id(), transaction->address()); if (transaction->type() == DhtTransaction::DHT_FIND_NODE) { if (quick) transaction->as_find_node()->set_stalled(); else transaction->as_find_node()->complete(false); try { find_node_next(transaction->as_find_node()); } catch (std::exception& e) { if (!quick) { delete itr->second; m_transactions.erase(itr); } throw; } } if (quick) { return ++itr; // don't actually delete the transaction until the final timeout } else { delete itr->second; m_transactions.erase(itr++); return itr; } } void DhtServer::clear_transactions() { for (transaction_map::iterator itr = m_transactions.begin(), last = m_transactions.end(); itr != last; itr++) delete itr->second; m_transactions.clear(); } void DhtServer::event_read() { uint32_t total = 0; while (true) { Object request; rak::socket_address sa; int type = '?'; DhtMessage message; const HashString* nodeId = NULL; try { char buffer[2048]; int32_t read = read_datagram(buffer, sizeof(buffer), &sa); if (read < 0) break; total += read; // If it's not a valid bencode dictionary at all, it's probably not a DHT // packet at all, so we don't throw an error to prevent bounce loops. try { static_map_read_bencode(buffer, buffer + read, message); } catch (bencode_error& e) { continue; } if (!message[key_t].is_raw_string()) throw dht_error(dht_error_protocol, "No transaction ID"); if (!message[key_y].is_raw_string()) throw dht_error(dht_error_protocol, "No message type"); if (message[key_y].as_raw_string().size() != 1) throw dht_error(dht_error_bad_method, "Unsupported message type"); type = message[key_y].as_raw_string().data()[0]; // Queries and replies have node ID in different dictionaries. if (type == 'r' || type == 'q') { if (!message[type == 'q' ? key_a_id : key_r_id].is_raw_string()) throw dht_error(dht_error_protocol, "Invalid `id' value"); raw_string nodeIdStr = message[type == 'q' ? key_a_id : key_r_id].as_raw_string(); if (nodeIdStr.size() < HashString::size_data) throw dht_error(dht_error_protocol, "`id' value too short"); nodeId = HashString::cast_from(nodeIdStr.data()); } // Sanity check the returned transaction ID. if ((type == 'r' || type == 'e') && (!message[key_t].is_raw_string() || message[key_t].as_raw_string().size() != 1)) throw dht_error(dht_error_protocol, "Invalid transaction ID type/length."); // Stupid broken implementations. if (nodeId != NULL && *nodeId == m_router->id()) throw dht_error(dht_error_protocol, "Send your own ID, not mine"); switch (type) { case 'q': process_query(*nodeId, &sa, message); break; case 'r': process_response(*nodeId, &sa, message); break; case 'e': process_error(&sa, message); break; default: throw dht_error(dht_error_bad_method, "Unknown message type."); } // If node was querying us, reply with error packet, otherwise mark the node as "query failed", // so that if it repeatedly sends malformed replies we will drop it instead of propagating it // to other nodes. } catch (bencode_error& e) { if ((type == 'r' || type == 'e') && nodeId != NULL) { m_router->node_inactive(*nodeId, &sa); } else { snprintf(message.data_end, message.data + message.data_size - message.data_end - 1, "Malformed packet: %s", e.what()); message.data[message.data_size - 1] = '\0'; create_error(message, &sa, dht_error_protocol, message.data_end); } } catch (dht_error& e) { if ((type == 'r' || type == 'e') && nodeId != NULL) m_router->node_inactive(*nodeId, &sa); else create_error(message, &sa, e.code(), e.what()); } catch (network_error& e) { } } m_downloadThrottle->node_used_unthrottled(total); m_downloadNode.rate()->insert(total); start_write(); } bool DhtServer::process_queue(packet_queue& queue, uint32_t* quota) { uint32_t used = 0; while (!queue.empty()) { DhtTransactionPacket* packet = queue.front(); // Make sure its transaction hasn't timed out yet, if it has/had one // and don't bother sending non-transaction packets (replies) after // more than 15 seconds in the queue. if (packet->has_failed() || packet->age() > 15) { delete packet; queue.pop_front(); continue; } if (packet->length() > *quota) { m_uploadThrottle->node_used(&m_uploadNode, used); return false; } queue.pop_front(); try { int written = write_datagram(packet->c_str(), packet->length(), packet->address()); if (written == -1) throw network_error(); used += written; *quota -= written; if ((unsigned int)written != packet->length()) throw network_error(); } catch (network_error& e) { // Couldn't write packet, maybe something wrong with node address or routing, so mark node as bad. if (packet->has_transaction()) { transaction_itr itr = m_transactions.find(packet->transaction()->key(packet->id())); if (itr == m_transactions.end()) throw internal_error("DhtServer::process_queue could not find transaction."); failed_transaction(itr, false); } } if (packet->has_transaction()) packet->transaction()->set_packet(NULL); delete packet; } m_uploadThrottle->node_used(&m_uploadNode, used); return true; } void DhtServer::event_write() { if (m_highQueue.empty() && m_lowQueue.empty()) throw internal_error("DhtServer::event_write called but both write queues are empty."); if (!m_uploadThrottle->is_throttled(&m_uploadNode)) throw internal_error("DhtServer::event_write called while not in throttle list."); uint32_t quota = m_uploadThrottle->node_quota(&m_uploadNode); if (quota == 0 || !process_queue(m_highQueue, "a) || !process_queue(m_lowQueue, "a)) { manager->poll()->remove_write(this); m_uploadThrottle->node_deactivate(&m_uploadNode); } else if (m_highQueue.empty() && m_lowQueue.empty()) { manager->poll()->remove_write(this); m_uploadThrottle->erase(&m_uploadNode); } } void DhtServer::event_error() { } void DhtServer::start_write() { if ((!m_highQueue.empty() || !m_lowQueue.empty()) && !m_uploadThrottle->is_throttled(&m_uploadNode)) { m_uploadThrottle->insert(&m_uploadNode); manager->poll()->insert_write(this); } if (!m_taskTimeout.is_queued() && !m_transactions.empty()) priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(5)).round_seconds()); } void DhtServer::receive_timeout() { transaction_itr itr = m_transactions.begin(); while (itr != m_transactions.end()) { if (itr->second->has_quick_timeout() && itr->second->quick_timeout() < cachedTime.seconds()) { itr = failed_transaction(itr, true); } else if (itr->second->timeout() < cachedTime.seconds()) { itr = failed_transaction(itr, false); } else { ++itr; } } start_write(); } } libtorrent-0.13.6/src/dht/dht_server.h000066400000000000000000000172211257211073700176610ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_SERVER_H #define LIBTORRENT_DHT_SERVER_H #include #include #include #include #include "net/socket_datagram.h" #include "net/throttle_node.h" #include "torrent/hash_string.h" #include "torrent/object_raw_bencode.h" #include "dht_transaction.h" namespace torrent { class DhtBucket; class DhtNode; class DhtRouter; class DownloadInfo; class DhtMessage; class TrackerDht; // UDP server that handles the DHT node communications. class DhtServer : public SocketDatagram { public: DhtServer(DhtRouter* self); ~DhtServer(); const char* type_name() const { return "dht"; } void start(int port); void stop(); bool is_active() const { return get_fd().is_valid(); } unsigned int queries_received() const { return m_queriesReceived; } unsigned int queries_sent() const { return m_queriesSent; } unsigned int replies_received() const { return m_repliesReceived; } unsigned int errors_received() const { return m_errorsReceived; } unsigned int errors_caught() const { return m_errorsCaught; } void reset_statistics(); // Contact a node to see if it replies. Set id=0 if unknown. void ping(const HashString& id, const rak::socket_address* sa); // Do a find_node search with the given contacts as starting point for the // search. void find_node(const DhtBucket& contacts, const HashString& target); // Do DHT announce, starting with the given contacts. void announce(const DhtBucket& contacts, const HashString& infoHash, TrackerDht* tracker); // Cancel given announce for given tracker, or all matching announces if info/tracker NULL. void cancel_announce(DownloadInfo* info, const TrackerDht* tracker); // Called every 15 minutes. void update(); ThrottleNode* upload_throttle_node() { return &m_uploadNode; } const ThrottleNode* upload_throttle_node() const { return &m_uploadNode; } ThrottleNode* download_throttle_node() { return &m_downloadNode; } const ThrottleNode* download_throttle_node() const { return &m_downloadNode; } void set_upload_throttle(ThrottleList* t) { m_uploadThrottle = t; } void set_download_throttle(ThrottleList* t) { m_downloadThrottle = t; } virtual void event_read(); virtual void event_write(); virtual void event_error(); private: // DHT error codes. static const int dht_error_generic = 201; static const int dht_error_server = 202; static const int dht_error_protocol = 203; static const int dht_error_bad_method = 204; struct compact_node_info { char _id[20]; SocketAddressCompact _addr; HashString& id() { return *HashString::cast_from(_id); } rak::socket_address address() { return rak::socket_address(_addr); } } __attribute__ ((packed)); typedef std::deque packet_queue; typedef std::list node_info_list; // Pending transactions. typedef std::map transaction_map; typedef transaction_map::iterator transaction_itr; // DHT transaction names for given transaction type. static const char* queries[]; // Priorities for the outgoing packets. static const int packet_prio_high = 2; // For important queries we send (announces). static const int packet_prio_low = 1; // For (relatively) unimportant queries we send. static const int packet_prio_reply = 0; // For replies to peer queries. void start_write(); void process_query(const HashString& id, const rak::socket_address* sa, const DhtMessage& req); void process_response(const HashString& id, const rak::socket_address* sa, const DhtMessage& req); void process_error(const rak::socket_address* sa, const DhtMessage& error); void parse_find_node_reply(DhtTransactionSearch* t, raw_string nodes); void parse_get_peers_reply(DhtTransactionGetPeers* t, const DhtMessage& res); void find_node_next(DhtTransactionSearch* t); void add_packet(DhtTransactionPacket* packet, int priority); void create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority); void create_response(const DhtMessage& req, const rak::socket_address* sa, DhtMessage& reply); void create_error(const DhtMessage& req, const rak::socket_address* sa, int num, const char* msg); void create_find_node_response(const DhtMessage& arg, DhtMessage& reply); void create_get_peers_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply); void create_announce_peer_response(const DhtMessage& arg, const rak::socket_address* sa, DhtMessage& reply); int add_transaction(DhtTransaction* t, int priority); // This returns the iterator after the given one or end() transaction_itr failed_transaction(transaction_itr itr, bool quick); void clear_transactions(); bool process_queue(packet_queue& queue, uint32_t* quota); void receive_timeout(); DhtRouter* m_router; packet_queue m_highQueue; packet_queue m_lowQueue; transaction_map m_transactions; rak::priority_item m_taskTimeout; ThrottleNode m_uploadNode; ThrottleNode m_downloadNode; ThrottleList* m_uploadThrottle; ThrottleList* m_downloadThrottle; unsigned int m_queriesReceived; unsigned int m_queriesSent; unsigned int m_repliesReceived; unsigned int m_errorsReceived; unsigned int m_errorsCaught; bool m_networkUp; }; } #endif libtorrent-0.13.6/src/dht/dht_tracker.cc000066400000000000000000000100521257211073700201370ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/object.h" #include "dht_tracker.h" namespace torrent { void DhtTracker::add_peer(uint32_t addr, uint16_t port) { if (port == 0) return; SocketAddressCompact compact(addr, port); unsigned int oldest = 0; uint32_t minSeen = ~uint32_t(); // Check if peer exists. If not, find oldest peer. for (unsigned int i = 0; i < size(); i++) { if (m_peers[i].peer.addr == compact.addr) { m_peers[i].peer.port = compact.port; m_lastSeen[i] = cachedTime.seconds(); return; } else if (m_lastSeen[i] < minSeen) { minSeen = m_lastSeen[i]; oldest = i; } } // If peer doesn't exist, append to list if the table is not full. if (size() < max_size) { m_peers.push_back(compact); m_lastSeen.push_back(cachedTime.seconds()); // Peer doesn't exist and table is full: replace oldest peer. } else { m_peers[oldest] = compact; m_lastSeen[oldest] = cachedTime.seconds(); } } // Return compact info as bencoded string (8 bytes per peer) for up to 30 peers, // returning different peers for each call if there are more. raw_list DhtTracker::get_peers(unsigned int maxPeers) { if (sizeof(BencodeAddress) != 8) throw internal_error("DhtTracker::BencodeAddress is packed incorrectly."); PeerList::iterator first = m_peers.begin(); PeerList::iterator last = m_peers.end(); // If we have more than max_peers, randomly return block of peers. // The peers in overlapping blocks get picked twice as often, but // that's better than returning fewer peers. if (m_peers.size() > maxPeers) { unsigned int blocks = (m_peers.size() + maxPeers - 1) / maxPeers; first += (random() % blocks) * (m_peers.size() - maxPeers) / (blocks - 1); last = first + maxPeers; } return raw_list(first->bencode(), last->bencode() - first->bencode()); } // Remove old announces. void DhtTracker::prune(uint32_t maxAge) { uint32_t minSeen = cachedTime.seconds() - maxAge; for (unsigned int i = 0; i < m_lastSeen.size(); i++) if (m_lastSeen[i] < minSeen) m_peers[i].peer.port = 0; m_peers.erase(std::remove_if(m_peers.begin(), m_peers.end(), std::mem_fun_ref(&BencodeAddress::empty)), m_peers.end()); m_lastSeen.erase(std::remove_if(m_lastSeen.begin(), m_lastSeen.end(), std::bind2nd(std::less(), minSeen)), m_lastSeen.end()); if (m_peers.size() != m_lastSeen.size()) throw internal_error("DhtTracker::prune did inconsistent peer pruning."); } } libtorrent-0.13.6/src/dht/dht_tracker.h000066400000000000000000000067251257211073700200150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_TRACKER_H #define LIBTORRENT_DHT_TRACKER_H #include "globals.h" #include #include #include "net/address_list.h" // For SA. #include "torrent/object_raw_bencode.h" namespace torrent { // Container for peers tracked in a torrent. class DhtTracker { public: // Maximum number of peers we return for a GET_PEERS query (default value only). // Needs to be small enough so that a packet with a payload of num_peers*6 bytes // does not need fragmentation. Value chosen so that the size is approximately // equal to a FIND_NODE reply (8*26 bytes). static const unsigned int max_peers = 32; // Maximum number of peers we keep track of. For torrents with more peers, // we replace the oldest peer with each new announce to avoid excessively // large peer tables for very active torrents. static const unsigned int max_size = 128; bool empty() const { return m_peers.empty(); } size_t size() const { return m_peers.size(); } void add_peer(uint32_t addr, uint16_t port); raw_list get_peers(unsigned int maxPeers = max_peers); // Remove old announces from the tracker that have not reannounced for // more than the given number of seconds. void prune(uint32_t maxAge); private: // We need to store the address as a bencoded string. struct BencodeAddress { char header[2]; SocketAddressCompact peer; BencodeAddress(const SocketAddressCompact& p) : peer(p) { header[0] = '6'; header[1] = ':'; } const char* bencode() const { return header; } bool empty() const { return !peer.port; } } __attribute__ ((packed)); typedef std::vector PeerList; PeerList m_peers; std::vector m_lastSeen; }; } #endif libtorrent-0.13.6/src/dht/dht_transaction.cc000066400000000000000000000216061257211073700210400ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "torrent/object_stream.h" #include "tracker/tracker_dht.h" #include "dht_bucket.h" #include "dht_transaction.h" namespace torrent { DhtSearch::DhtSearch(const HashString& target, const DhtBucket& contacts) : base_type(dht_compare_closer(m_target = target)), m_pending(0), m_contacted(0), m_replied(0), m_concurrency(3), m_restart(false), m_started(false), m_next(end()) { add_contacts(contacts); } DhtSearch::~DhtSearch() { // Make sure transactions were destructed first. Since it is the destruction // of a transaction that triggers this destructor, that should always be the // case. if (m_pending) throw internal_error("DhtSearch::~DhtSearch called with pending transactions."); if (m_concurrency != 3) throw internal_error("DhtSearch::~DhtSearch with invalid concurrency limit."); for (accessor itr = begin(); itr != end(); ++itr) delete itr.node(); } bool DhtSearch::add_contact(const HashString& id, const rak::socket_address* sa) { DhtNode* n = new DhtNode(id, sa); bool added = insert(std::make_pair(n, this)).second; if (!added) delete n; else m_restart = true; return added; } void DhtSearch::add_contacts(const DhtBucket& contacts) { DhtBucketChain chain(&contacts); // Add max_contacts=18 closest nodes, and fill up so we also have at least 8 good nodes. int needClosest = max_contacts - size(); int needGood = DhtBucket::num_nodes; for (DhtBucket::const_iterator itr = chain.bucket()->begin(); needClosest > 0 || needGood > 0; ++itr) { while (itr == chain.bucket()->end()) { if (!chain.next()) return; itr = chain.bucket()->begin(); } if ((!(*itr)->is_bad() || needClosest > 0) && add_contact((*itr)->id(), (*itr)->address())) { needGood -= !(*itr)->is_bad(); needClosest--; } } } // Check if a node has been contacted yet. This is the case if it is not currently // being contacted, nor has it been found to be good or bad. bool DhtSearch::node_uncontacted(const DhtNode* node) const { return !node->is_active() && !node->is_good() && !node->is_bad(); } // After more contacts have been added, discard least closest nodes // except if node has a transaction pending. void DhtSearch::trim(bool final) { // We keep: // - the max_contacts=18 closest good or unknown nodes and all nodes closer // than them (to see if further searches find closer ones) // - for announces, also the 3 closest good nodes (i.e. nodes that have // replied) to have at least that many for the actual announce // - any node that currently has transactions pending // // However, after exhausting all search nodes, we only keep good nodes. // // For our purposes, the node status is as follows: // node is bad (contacted but hasn't replied) if is_bad() // node is good (contacted and replied) if is_good() // node is currently being contacted if is_active() // node is new and unknown otherwise int needClosest = final ? 0 : max_contacts; int needGood = is_announce() ? max_announce : 0; // We're done if we can't find any more nodes to contact. m_next = end(); for (accessor itr = base_type::begin(); itr != end(); ) { // If we have all we need, delete current node unless it is // currently being contacted. if (!itr.node()->is_active() && needClosest <= 0 && (!itr.node()->is_good() || needGood <= 0)) { delete itr.node(); erase(itr++); continue; } // Otherwise adjust needed counts appropriately. needClosest--; needGood -= itr.node()->is_good(); // Remember the first uncontacted node as the closest one to contact next. if (m_next == end() && node_uncontacted(itr.node())) m_next = const_accessor(itr); ++itr; } m_restart = false; } DhtSearch::const_accessor DhtSearch::get_contact() { if (m_pending >= m_concurrency) return end(); if (m_restart) trim(false); const_accessor ret = m_next; if (ret == end()) return ret; set_node_active(ret, true); m_pending++; m_contacted++; // Find next node to contact: any node we haven't contacted yet. while (++m_next != end()) { if (node_uncontacted(m_next.node())) break; } return ret; } void DhtSearch::node_status(const_accessor& n, bool success) { if (n == end() || !n.node()->is_active()) throw internal_error("DhtSearch::node_status called for invalid/inactive node."); if (success) { n.node()->set_good(); m_replied++; } else { n.node()->set_bad(); } m_pending--; set_node_active(n, false); } DhtAnnounce::~DhtAnnounce() { if (!complete()) throw internal_error("DhtAnnounce::~DhtAnnounce called while announce not complete."); const char* failure = NULL; if (m_tracker->get_state() != TrackerDht::state_announcing) { if (!m_contacted) failure = "No DHT nodes available for peer search."; else failure = "DHT search unsuccessful."; } else { if (!m_contacted) failure = "DHT search unsuccessful."; else if (m_replied == 0 && !m_tracker->has_peers()) failure = "Announce failed"; } if (failure != NULL) m_tracker->receive_failed(failure); else m_tracker->receive_success(); } DhtSearch::const_accessor DhtAnnounce::start_announce() { trim(true); if (empty()) return end(); if (!complete() || m_next != end() || size() > DhtBucket::num_nodes) throw internal_error("DhtSearch::start_announce called in inconsistent state."); m_contacted = m_pending = size(); m_replied = 0; m_tracker->set_state(TrackerDht::state_announcing); for (const_accessor itr(begin()); itr != end(); ++itr) set_node_active(itr, true); return const_accessor(begin()); } void DhtTransactionPacket::build_buffer(const DhtMessage& msg) { char buffer[1500]; // If the message would exceed an Ethernet frame, something went very wrong. object_buffer_t result = static_map_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), msg); m_length = result.second - buffer; m_data = new char[m_length]; memcpy(m_data, buffer, m_length); } DhtTransaction::DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa) : m_id(id), m_hasQuickTimeout(quick_timeout > 0), m_sa(*sa), m_timeout(cachedTime.seconds() + timeout), m_quickTimeout(cachedTime.seconds() + quick_timeout), m_packet(NULL) { } DhtTransaction::~DhtTransaction() { if (m_packet != NULL) m_packet->set_failed(); } void DhtTransactionSearch::set_stalled() { if (!m_hasQuickTimeout) throw internal_error("DhtTransactionSearch::set_stalled called on already stalled transaction."); m_hasQuickTimeout = false; m_search->m_concurrency++; } void DhtTransactionSearch::complete(bool success) { if (m_node == m_search->end()) throw internal_error("DhtTransactionSearch::complete called multiple times."); if (m_node.search() != m_search) throw internal_error("DhtTransactionSearch::complete called for node from wrong search."); if (!m_hasQuickTimeout) m_search->m_concurrency--; m_search->node_status(m_node, success); m_node = m_search->end(); } DhtTransactionSearch::~DhtTransactionSearch() { if (m_node != m_search->end()) complete(false); if (m_search->complete()) delete m_search; } } libtorrent-0.13.6/src/dht/dht_transaction.h000066400000000000000000000373201257211073700207020ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DHT_TRANSACTION_H #define LIBTORRENT_DHT_TRANSACTION_H #include #include #include "dht/dht_node.h" #include "torrent/hash_string.h" #include "torrent/object_static_map.h" #include "tracker/tracker_dht.h" namespace torrent { class TrackerDht; class DhtBucket; class DhtSearch; class DhtAnnounce; class DhtTransactionSearch; class DhtTransaction; class DhtTransactionPing; class DhtTransactionFindNode; class DhtTransactionFindNodeAnnounce; class DhtTransactionGetPeers; class DhtTransactionAnnouncePeer; // DhtSearch implements the DHT search algorithm and holds search data // that needs to be persistent across multiple find_node transactions. // // DhtAnnounce is a derived class used for searches that will eventually // lead to an announce to the closest nodes. // Compare predicate for ID closeness. struct dht_compare_closer : public std::binary_function { dht_compare_closer(const HashString& target) : m_target(target) { } bool operator () (const DhtNode* one, const DhtNode* two) const; const HashString& target() const { return m_target; } raw_string target_raw_string() const { return raw_string(m_target.data(), HashString::size_data); } private: const HashString& m_target; }; // DhtSearch contains a list of nodes sorted by closeness to the given target, // and returns what nodes to contact with up to three concurrent transactions pending. // The map element is the DhtSearch object itself to allow the returned accessors // to know which search a given node belongs to. class DhtSearch : protected std::map { friend class DhtTransactionSearch; public: typedef std::map base_type; // Number of closest potential contact nodes to keep. static const unsigned int max_contacts = 18; // Number of closest nodes we actually announce to. static const unsigned int max_announce = 3; DhtSearch(const HashString& target, const DhtBucket& contacts); virtual ~DhtSearch(); // Wrapper for iterators, allowing more convenient access to the key // and element values, which also makes it easier to change the container // without having to modify much code using iterators. template struct accessor_wrapper : public T { accessor_wrapper() { } accessor_wrapper(const T& itr) : T(itr) { } DhtNode* node() const { return (**this).first; } DhtSearch* search() const { return (**this).second; } }; typedef accessor_wrapper const_accessor; typedef accessor_wrapper accessor; // Add a potential node to contact for the search. bool add_contact(const HashString& id, const rak::socket_address* sa); void add_contacts(const DhtBucket& contacts); // Return next node to contact. Up to concurrent_searches nodes are returned, // and end() after that. Don't advance the accessor to get further contacts! const_accessor get_contact(); // Search statistics. int num_contacted() { return m_contacted; } int num_replied() { return m_replied; } bool start() { m_started = true; return m_pending; } bool complete() const { return m_started && !m_pending; } const HashString& target() const { return m_target; } raw_string target_raw_string() const { return raw_string(m_target.data(), HashString::size_data); } virtual bool is_announce() const { return false; } // Expose the otherwise private end() function but return an accessor, // to allow code checking whether get_contact returned a valid accessor. const_accessor end() const { return base_type::end(); } // Used by the sorting/comparison predicate to see which node is closer. static bool is_closer(const HashString& one, const HashString& two, const HashString& target); protected: void trim(bool final); void node_status(const_accessor& n, bool success); void set_node_active(const_accessor& n, bool active); // Statistics about contacted nodes. unsigned int m_pending; unsigned int m_contacted; unsigned int m_replied; unsigned int m_concurrency; bool m_restart; // If true, trim nodes and reset m_next on the following get_contact call. bool m_started; // Next node to return in get_contact, is end() if we have no more contactable nodes. const_accessor m_next; private: DhtSearch(const DhtSearch& s); bool node_uncontacted(const DhtNode* node) const; HashString m_target; }; class DhtAnnounce : public DhtSearch { public: DhtAnnounce(const HashString& infoHash, TrackerDht* tracker, const DhtBucket& contacts) : DhtSearch(infoHash, contacts), m_tracker(tracker) { } ~DhtAnnounce(); virtual bool is_announce() const { return true; } const TrackerDht* tracker() const { return m_tracker; } // Start announce and return final set of nodes in get_contact() calls. // This resets DhtSearch's completed() function, which now // counts announces instead. const_accessor start_announce(); void receive_peers(raw_list peers) { m_tracker->receive_peers(peers); } void update_status() { m_tracker->receive_progress(m_replied, m_contacted); } private: TrackerDht* m_tracker; }; // Possible bencode keys in a DHT message. enum dht_keys { key_a_id, key_a_infoHash, key_a_port, key_a_target, key_a_token, key_e_0, key_e_1, key_q, key_r_id, key_r_nodes, key_r_token, key_r_values, key_t, key_v, key_y, key_LAST, }; class DhtMessage : public static_map_type { public: typedef static_map_type base_type; DhtMessage() : data_end(data) {}; // Must be big enough to hold one of the possible variable-sized reply data. // Currently either: // - error message (size doesn't really matter, it'll be truncated at worst) // - announce token (8 bytes, needs 20 bytes buffer to build) // Never more than one of the above. // And additionally for queries we send: // - transaction ID (3 bytes) static const size_t data_size = 64; char data[data_size]; char* data_end; }; // Class holding transaction data to be transmitted. class DhtTransactionPacket { public: // transaction packet DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d, unsigned int id, DhtTransaction* t) : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); }; // non-transaction packet DhtTransactionPacket(const rak::socket_address* s, const DhtMessage& d) : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); }; ~DhtTransactionPacket() { delete[] m_data; } bool has_transaction() const { return m_id >= -1; } bool has_failed() const { return m_id == -1; } void set_failed() { m_id = -1; } const rak::socket_address* address() const { return &m_sa; } rak::socket_address* address() { return &m_sa; } const char* c_str() const { return m_data; } size_t length() const { return m_length; } int id() const { return m_id; } int age() const { return has_transaction() ? 0 : cachedTime.seconds() + m_id; } const DhtTransaction* transaction() const { return m_transaction; } DhtTransaction* transaction() { return m_transaction; } private: void build_buffer(const DhtMessage& data); rak::socket_address m_sa; char* m_data; size_t m_length; int m_id; DhtTransaction* m_transaction; }; // DHT Transaction classes. DhtTransaction and DhtTransactionSearch // are not directly usable with no public constructor, since type() // is a pure virtual function. class DhtTransaction { public: virtual ~DhtTransaction(); typedef enum { DHT_PING, DHT_FIND_NODE, DHT_GET_PEERS, DHT_ANNOUNCE_PEER, } transaction_type; virtual transaction_type type() = 0; virtual bool is_search() { return false; } // Key to uniquely identify a transaction with given per-node transaction id. typedef uint64_t key_type; key_type key(int id) const { return key(&m_sa, id); } static key_type key(const rak::socket_address* sa, int id); static bool key_match(key_type key, const rak::socket_address* sa); // Node ID and address. const HashString& id() { return m_id; } const rak::socket_address* address() { return &m_sa; } int timeout() { return m_timeout; } int quick_timeout() { return m_quickTimeout; } bool has_quick_timeout() { return m_hasQuickTimeout; } DhtTransactionPacket* packet() { return m_packet; } void set_packet(DhtTransactionPacket* p) { m_packet = p; } DhtTransactionSearch* as_search(); DhtTransactionPing* as_ping(); DhtTransactionFindNode* as_find_node(); DhtTransactionGetPeers* as_get_peers(); DhtTransactionAnnouncePeer* as_announce_peer(); protected: DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa); // m_id must be the first element to ensure it is aligned properly, // because we later read a size_t value from it. const HashString m_id; bool m_hasQuickTimeout; private: DhtTransaction(const DhtTransaction& t); rak::socket_address m_sa; int m_timeout; int m_quickTimeout; DhtTransactionPacket* m_packet; }; class DhtTransactionSearch : public DhtTransaction { public: virtual ~DhtTransactionSearch(); virtual bool is_search() { return true; } DhtSearch::const_accessor node() { return m_node; } DhtSearch* search() { return m_search; } void set_stalled(); void complete(bool success); protected: DhtTransactionSearch(int quick_timeout, int timeout, DhtSearch::const_accessor& node) : DhtTransaction(quick_timeout, timeout, node.node()->id(), node.node()->address()), m_node(node), m_search(node.search()) { if (!m_hasQuickTimeout) m_search->m_concurrency++; } private: DhtSearch::const_accessor m_node; DhtSearch* m_search; }; // Actual transaction classes. class DhtTransactionPing : public DhtTransaction { public: DhtTransactionPing(const HashString& id, const rak::socket_address* sa) : DhtTransaction(-1, 30, id, sa) { } virtual transaction_type type() { return DHT_PING; } }; class DhtTransactionFindNode : public DhtTransactionSearch { public: DhtTransactionFindNode(DhtSearch::const_accessor& node) : DhtTransactionSearch(4, 30, node) { } virtual transaction_type type() { return DHT_FIND_NODE; } }; class DhtTransactionGetPeers : public DhtTransactionSearch { public: DhtTransactionGetPeers(DhtSearch::const_accessor& node) : DhtTransactionSearch(-1, 30, node) { } virtual transaction_type type() { return DHT_GET_PEERS; } }; class DhtTransactionAnnouncePeer : public DhtTransaction { public: DhtTransactionAnnouncePeer(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, raw_string token) : DhtTransaction(-1, 30, id, sa), m_infoHash(infoHash), m_token(token) { } virtual transaction_type type() { return DHT_ANNOUNCE_PEER; } const HashString& info_hash() { return m_infoHash; } raw_string info_hash_raw_string() const { return raw_string(m_infoHash.data(), HashString::size_data); } raw_string token() { return m_token; } private: HashString m_infoHash; raw_string m_token; }; inline bool DhtSearch::is_closer(const HashString& one, const HashString& two, const HashString& target) { for (unsigned int i=0; im_lastSeen = active; } inline bool dht_compare_closer::operator () (const DhtNode* one, const DhtNode* two) const { return DhtSearch::is_closer(*one, *two, m_target); } inline DhtTransaction::key_type DhtTransaction::key(const rak::socket_address* sa, int id) { return ((uint64_t)sa->sa_inet()->address_n() << 32) + id; } inline bool DhtTransaction::key_match(key_type key, const rak::socket_address* sa) { return (key >> 32) == sa->sa_inet()->address_n(); } // These could (should?) check that the type matches, or use dynamic_cast if we have RTTI. inline DhtTransactionSearch* DhtTransaction::as_search() { return static_cast(this); } inline DhtTransactionPing* DhtTransaction::as_ping() { return static_cast(this); } inline DhtTransactionFindNode* DhtTransaction::as_find_node() { return static_cast(this); } inline DhtTransactionGetPeers* DhtTransaction::as_get_peers() { return static_cast(this); } inline DhtTransactionAnnouncePeer* DhtTransaction::as_announce_peer() { return static_cast(this); } } #endif libtorrent-0.13.6/src/download/000077500000000000000000000000001257211073700163705ustar00rootroot00000000000000libtorrent-0.13.6/src/download/Makefile.am000066400000000000000000000006471257211073700204330ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_download.la libsub_download_la_SOURCES = \ available_list.cc \ available_list.h \ chunk_selector.cc \ chunk_selector.h \ chunk_statistics.cc \ chunk_statistics.h \ delegator.cc \ delegator.h \ download_constructor.cc \ download_constructor.h \ download_main.cc \ download_main.h \ download_wrapper.cc \ download_wrapper.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/download/available_list.cc000066400000000000000000000056021257211073700216550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "torrent/exceptions.h" #include "available_list.h" namespace torrent { AvailableList::value_type AvailableList::pop_random() { if (empty()) throw internal_error("AvailableList::pop_random() called on an empty container"); size_type idx = random() % size(); value_type tmp = *(begin() + idx); *(begin() + idx) = back(); pop_back(); return tmp; } void AvailableList::push_back(const rak::socket_address* sa) { if (std::find(begin(), end(), *sa) != end()) return; base_type::push_back(*sa); } void AvailableList::insert(AddressList* l) { if (!want_more()) return; std::sort(begin(), end()); // Can i use use the std::remove* semantics for this, and just copy // to 'l'?. // // 'l' is guaranteed to be sorted, so we can just do // std::set_difference. AddressList difference; std::set_difference(l->begin(), l->end(), begin(), end(), std::back_inserter(difference)); std::copy(difference.begin(), difference.end(), std::back_inserter(*static_cast(this))); } void AvailableList::erase(const rak::socket_address& sa) { iterator itr = std::find(begin(), end(), sa); if (itr != end()) { *itr = back(); pop_back(); } } } libtorrent-0.13.6/src/download/available_list.h000066400000000000000000000067141257211073700215240ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_AVAILABLE_LIST_H #define LIBTORRENT_DOWNLOAD_AVAILABLE_LIST_H #include #include #include #include "net/address_list.h" namespace torrent { class AvailableList : private std::vector { public: typedef std::vector base_type; typedef uint32_t size_type; using base_type::value_type; using base_type::reference; using base_type::const_reference; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::size; using base_type::capacity; using base_type::reserve; using base_type::empty; using base_type::clear; using base_type::back; using base_type::pop_back; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; AvailableList() : m_maxSize(1000) {} value_type pop_random(); // Fuzzy size limit. size_type max_size() const { return m_maxSize; } void set_max_size(size_type s) { m_maxSize = s; } bool want_more() const { return size() <= m_maxSize; } // This push is somewhat inefficient as it iterates through the // whole container to see if the address already exists. void push_back(const rak::socket_address* sa); void insert(AddressList* l); void erase(const rak::socket_address& sa); void erase(iterator itr) { *itr = back(); pop_back(); } // A place to temporarily put addresses before re-adding them to the // AvailableList. AddressList* buffer() { return &m_buffer; } private: size_type m_maxSize; AddressList m_buffer; }; } #endif libtorrent-0.13.6/src/download/chunk_selector.cc000066400000000000000000000221431257211073700217110ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "protocol/peer_chunks.h" #include "torrent/exceptions.h" #include "chunk_selector.h" #include "chunk_statistics.h" namespace torrent { // Consider making statistics a part of selector. void ChunkSelector::initialize(ChunkStatistics* cs) { m_position = invalid_chunk; m_statistics = cs; Bitfield* completed = m_data->mutable_completed_bitfield(); Bitfield* untouched = m_data->mutable_untouched_bitfield(); untouched->set_size_bits(completed->size_bits()); untouched->allocate(); std::transform(completed->begin(), completed->end(), untouched->begin(), rak::invert()); untouched->update(); m_sharedQueue.enable(32); m_sharedQueue.clear(); } void ChunkSelector::cleanup() { m_data->mutable_untouched_bitfield()->clear(); m_statistics = NULL; } // Consider if ChunksSelector::not_using_index(...) needs to be // modified. void ChunkSelector::update_priorities() { if (empty()) return; m_sharedQueue.clear(); if (m_position == invalid_chunk) m_position = random() % size(); advance_position(); } uint32_t ChunkSelector::find(PeerChunks* pc, __UNUSED bool highPriority) { // This needs to be re-enabled. if (m_position == invalid_chunk) return invalid_chunk; // When we're a seeder, 'm_sharedQueue' is used. Since the peer's // bitfield is guaranteed to be filled we can use the same code as // for non-seeders. This generalization does incur a slight // performance hit as it compares against a bitfield we know has all // set. rak::partial_queue* queue = pc->is_seeder() ? &m_sharedQueue : pc->download_cache(); // Randomize position on average every 16 chunks to prevent // inefficient distribution with a slow seed and fast peers // all arriving at the same position. if ((random() & 63) == 0) { m_position = random() % size(); queue->clear(); } if (queue->is_enabled()) { // First check the cached queue. while (queue->prepare_pop()) { uint32_t pos = queue->pop(); if (!m_data->untouched_bitfield()->get(pos)) continue; return pos; } } else { // Only non-seeders can reach this branch as m_sharedQueue is // always enabled. queue->enable(8); } queue->clear(); (search_linear(pc->bitfield(), queue, m_data->high_priority(), m_position, size()) && search_linear(pc->bitfield(), queue, m_data->high_priority(), 0, m_position)); if (queue->prepare_pop()) { // Set that the peer has high priority pieces cached. } else { // Set that the peer has normal priority pieces cached. // Urgh... queue->clear(); (search_linear(pc->bitfield(), queue, m_data->normal_priority(), m_position, size()) && search_linear(pc->bitfield(), queue, m_data->normal_priority(), 0, m_position)); if (!queue->prepare_pop()) return invalid_chunk; } uint32_t pos = queue->pop(); if (!m_data->untouched_bitfield()->get(pos)) throw internal_error("ChunkSelector::find(...) bad index."); return pos; } bool ChunkSelector::is_wanted(uint32_t index) const { return m_data->untouched_bitfield()->get(index) && (m_data->normal_priority()->has(index) || m_data->high_priority()->has(index)); } void ChunkSelector::using_index(uint32_t index) { if (index >= size()) throw internal_error("ChunkSelector::select_index(...) index out of range."); if (!m_data->untouched_bitfield()->get(index)) throw internal_error("ChunkSelector::select_index(...) index already set."); m_data->mutable_untouched_bitfield()->unset(index); // We always know 'm_position' points to a wanted chunk. If it // changes, we need to move m_position to the next one. if (index == m_position) advance_position(); } void ChunkSelector::not_using_index(uint32_t index) { if (index >= size()) throw internal_error("ChunkSelector::deselect_index(...) index out of range."); if (m_data->untouched_bitfield()->get(index)) throw internal_error("ChunkSelector::deselect_index(...) index already unset."); m_data->mutable_untouched_bitfield()->set(index); // This will make sure that if we enable new chunks, it will start // downloading them event when 'index == invalid_chunk'. if (m_position == invalid_chunk) m_position = index; } // This could propably be split into two functions, one for checking // if it shoul insert into the request_list(), and the other // whetever we are interested in the new piece. // // Since this gets called whenever a new piece arrives, we can skip // the rarity-first/linear etc searches through bitfields and just // start downloading. bool ChunkSelector::received_have_chunk(PeerChunks* pc, uint32_t index) { if (!m_data->untouched_bitfield()->get(index)) return false; // Also check if the peer only has high-priority chunks. if (!m_data->high_priority()->has(index) && !m_data->normal_priority()->has(index)) return false; if (pc->download_cache()->is_enabled()) pc->download_cache()->insert(m_statistics->rarity(index), index); return true; } bool ChunkSelector::search_linear(const Bitfield* bf, rak::partial_queue* pq, const download_data::priority_ranges* ranges, uint32_t first, uint32_t last) { download_data::priority_ranges::const_iterator itr = ranges->find(first); while (itr != ranges->end() && itr->first < last) { if (!search_linear_range(bf, pq, std::max(first, itr->first), std::min(last, itr->second))) return false; ++itr; } return true; } // Could propably add another argument for max seen or something, this // would be used to find better chunks to request. inline bool ChunkSelector::search_linear_range(const Bitfield* bf, rak::partial_queue* pq, uint32_t first, uint32_t last) { if (first >= last || last > size()) throw internal_error("ChunkSelector::search_linear_range(...) received an invalid range."); Bitfield::const_iterator local = m_data->untouched_bitfield()->begin() + first / 8; Bitfield::const_iterator source = bf->begin() + first / 8; // Unset any bits before 'first'. Bitfield::value_type wanted = (*source & *local) & Bitfield::mask_from(first % 8); while (m_data->untouched_bitfield()->position(local + 1) < last) { if (wanted && !search_linear_byte(pq, m_data->untouched_bitfield()->position(local), wanted)) return false; wanted = (*++source & *++local); } // Unset any bits from 'last'. wanted &= Bitfield::mask_before(last - m_data->untouched_bitfield()->position(local)); if (wanted) return search_linear_byte(pq, m_data->untouched_bitfield()->position(local), wanted); else return true; } // Take pointer to partial_queue inline bool ChunkSelector::search_linear_byte(rak::partial_queue* pq, uint32_t index, Bitfield::value_type wanted) { for (int i = 0; i < 8; ++i) { if (!(wanted & Bitfield::mask_at(i))) continue; if (!pq->insert(m_statistics->rarity(index + i), index + i) && pq->is_full()) return false; } return true; } void ChunkSelector::advance_position() { // Need to replace with a special-purpose function for finding the // next position. // int position = m_position; // ((m_position = search_linear(&m_bitfield, &m_highPriority, position, size())) == invalid_chunk && // (m_position = search_linear(&m_bitfield, &m_highPriority, 0, position)) == invalid_chunk && // (m_position = search_linear(&m_bitfield, &m_normalPriority, position, size())) == invalid_chunk && // (m_position = search_linear(&m_bitfield, &m_normalPriority, 0, position)) == invalid_chunk); } } libtorrent-0.13.6/src/download/chunk_selector.h000066400000000000000000000114471257211073700215600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_CHUNK_SELECTOR_H #define LIBTORRENT_DOWNLOAD_CHUNK_SELECTOR_H #include #include #include "torrent/bitfield.h" #include "torrent/data/download_data.h" #include "torrent/utils/ranges.h" namespace torrent { // This class is responsible for deciding on which chunk index to // download next based on the peer's bitfield. It keeps its own // bitfield which starts out as a copy of Content::bitfield but sets // chunks that are being downloaded. // // When updating Content::bitfield, make sure you update this bitfield // and unmark any chunks in Delegator. class ChunkStatistics; class PeerChunks; class ChunkSelector { public: static const uint32_t invalid_chunk = ~(uint32_t)0; ChunkSelector(download_data* data) : m_data(data) {} bool empty() const { return size() == 0; } uint32_t size() const { return m_data->untouched_bitfield()->size_bits(); } // const Bitfield* bitfield() { return m_data->untouched_bitfield(); } // priority_ranges* high_priority() { return &m_highPriority; } // priority_ranges* normal_priority() { return &m_normalPriority; } // Initialize doesn't update the priority cache, so it is as if it // has empty priority ranges. void initialize(ChunkStatistics* cs); void cleanup(); // Call this once you've modified the bitfield or priorities to // update cached information. This must be called once before using // find. void update_priorities(); uint32_t find(PeerChunks* pc, bool highPriority); bool is_wanted(uint32_t index) const; // Call this to set the index as being downloaded, finished etc, // thus ignored. Propably should find a better name for this. void using_index(uint32_t index); void not_using_index(uint32_t index); // The caller must ensure that the chunk index is valid and has not // been set already. // // The user only needs to call this when it needs to know whetever // it should become interested, or if it is in the process of // downloading. // // Returns whetever we're interested in that piece. bool received_have_chunk(PeerChunks* pc, uint32_t index); private: bool search_linear(const Bitfield* bf, rak::partial_queue* pq, const download_data::priority_ranges* ranges, uint32_t first, uint32_t last); inline bool search_linear_range(const Bitfield* bf, rak::partial_queue* pq, uint32_t first, uint32_t last); inline bool search_linear_byte(rak::partial_queue* pq, uint32_t index, Bitfield::value_type wanted); // inline uint32_t search_rarest(const Bitfield* bf, priority_ranges* ranges, uint32_t first, uint32_t last); // inline uint32_t search_rarest_range(const Bitfield* bf, uint32_t first, uint32_t last); // inline uint32_t search_rarest_byte(uint8_t wanted); void advance_position(); download_data* m_data; ChunkStatistics* m_statistics; rak::partial_queue m_sharedQueue; uint32_t m_position; }; } #endif libtorrent-0.13.6/src/download/chunk_statistics.cc000066400000000000000000000115201257211073700222600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "protocol/peer_chunks.h" #include "chunk_statistics.h" namespace torrent { inline bool ChunkStatistics::should_add(PeerChunks* pc) { return m_accounted < max_accounted; } void ChunkStatistics::initialize(size_type s) { if (!empty()) throw internal_error("ChunkStatistics::initialize(...) called on an initialized object."); base_type::resize(s); } void ChunkStatistics::clear() { if (m_complete != 0) throw internal_error("ChunkStatistics::clear() m_complete != 0."); base_type::clear(); } void ChunkStatistics::received_connect(PeerChunks* pc) { if (pc->using_counter()) throw internal_error("ChunkStatistics::received_connect(...) pc->using_counter() == true."); if (pc->bitfield()->is_all_set()) { pc->set_using_counter(true); m_complete++; } else if (!pc->bitfield()->is_all_unset() && should_add(pc)) { // There should be additional checks, so that we don't do this // when there's no need. pc->set_using_counter(true); m_accounted++; iterator itr = base_type::begin(); // Use a bitfield iterator instead. for (Bitfield::size_type index = 0; index < pc->bitfield()->size_bits(); ++index, ++itr) *itr += pc->bitfield()->get(index); } } void ChunkStatistics::received_disconnect(PeerChunks* pc) { // The 'using_counter' of complete peers is used, but not added to // 'm_accounted', so that we can safely disconnect peers right after // receiving the bitfield without calling 'received_connect'. if (!pc->using_counter()) return; pc->set_using_counter(false); if (pc->bitfield()->is_all_set()) { m_complete--; } else { if (m_accounted == 0) throw internal_error("ChunkStatistics::received_disconnect(...) m_accounted == 0."); m_accounted--; iterator itr = base_type::begin(); // Use a bitfield iterator instead. for (Bitfield::size_type index = 0; index < pc->bitfield()->size_bits(); ++index, ++itr) *itr -= pc->bitfield()->get(index); } } void ChunkStatistics::received_have_chunk(PeerChunks* pc, uint32_t index, uint32_t length) { // When the bitfield is empty, it is very cheap to add the peer to // the statistics. It needs to be done here else we need to check if // a connection has sent any messages, else it might send a bitfield. if (pc->bitfield()->is_all_unset() && should_add(pc)) { if (pc->using_counter()) throw internal_error("ChunkStatistics::received_have_chunk(...) pc->using_counter() == true."); pc->set_using_counter(true); m_accounted++; } pc->bitfield()->set(index); pc->peer_rate()->insert(length); if (pc->using_counter()) { base_type::operator[](index)++; // The below code should not cause useless work to be done in case // of immediate disconnect. if (pc->bitfield()->is_all_set()) { if (m_accounted == 0) throw internal_error("ChunkStatistics::received_disconnect(...) m_accounted == 0."); m_complete++; m_accounted--; for (iterator itr = base_type::begin(), last = base_type::end(); itr != last; ++itr) *itr -= 1; } } else { if (pc->bitfield()->is_all_set()) { pc->set_using_counter(true); m_complete++; } } } } libtorrent-0.13.6/src/download/chunk_statistics.h000066400000000000000000000103111257211073700221170ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_CHUNK_STATISTICS_H #define LIBTORRENT_DOWNLOAD_CHUNK_STATISTICS_H #include #include namespace torrent { class PeerChunks; class ChunkStatistics : public std::vector { public: typedef std::vector base_type; typedef uint32_t size_type; typedef base_type::value_type value_type; typedef base_type::reference reference; typedef base_type::const_reference const_reference; typedef base_type::iterator iterator; typedef base_type::const_iterator const_iterator; typedef base_type::reverse_iterator reverse_iterator; using base_type::empty; using base_type::size; static const size_type max_accounted = 255; ChunkStatistics() : m_complete(0), m_accounted(0) {} ~ChunkStatistics() {} size_type complete() const { return m_complete; } //size_type incomplete() const; // Number of non-complete peers whom's bitfield is added to the // statistics. size_type accounted() const { return m_accounted; } void initialize(size_type s); void clear(); // When a peer connects and sends a non-empty bitfield and is not a // seeder, we can be fairly sure it won't just disconnect // immediately. Thus it should be resonable to possibly spend the // effort adding it to the statistics if nessesary. // Where do we decide on policy? On whetever we count the chunks, // the type of connection shouldn't matter? As f.ex PCSeed will only // make sense when seeding, it won't be counted. // Might want to prefer to add peers we are interested in, but which // arn't in us. void received_connect(PeerChunks* pc); void received_disconnect(PeerChunks* pc); // The caller must ensure that the chunk index is valid and has not // been set already. void received_have_chunk(PeerChunks* pc, uint32_t index, uint32_t length); const_iterator begin() const { return base_type::begin(); } const_iterator end() const { return base_type::end(); } const_reference rarity(size_type n) const { return base_type::operator[](n); } const_reference operator [] (size_type n) const { return base_type::operator[](n); } private: inline bool should_add(PeerChunks* pc); ChunkStatistics(const ChunkStatistics&); void operator = (const ChunkStatistics&); size_type m_complete; size_type m_accounted; }; } #endif libtorrent-0.13.6/src/download/delegator.cc000066400000000000000000000176431257211073700206600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Fucked up ugly piece of hack, this code. #include "config.h" #include #include #include "torrent/exceptions.h" #include "torrent/bitfield.h" #include "torrent/data/block.h" #include "torrent/data/block_list.h" #include "torrent/data/block_transfer.h" #include "protocol/peer_chunks.h" #include "delegator.h" namespace torrent { struct DelegatorCheckAffinity { DelegatorCheckAffinity(Delegator* delegator, Block** target, unsigned int index, const PeerInfo* peerInfo) : m_delegator(delegator), m_target(target), m_index(index), m_peerInfo(peerInfo) {} bool operator () (BlockList* d) { return m_index == d->index() && (*m_target = m_delegator->delegate_piece(d, m_peerInfo)) != NULL; } Delegator* m_delegator; Block** m_target; unsigned int m_index; const PeerInfo* m_peerInfo; }; struct DelegatorCheckSeeder { DelegatorCheckSeeder(Delegator* delegator, Block** target, const PeerInfo* peerInfo) : m_delegator(delegator), m_target(target), m_peerInfo(peerInfo) {} bool operator () (BlockList* d) { return d->by_seeder() && (*m_target = m_delegator->delegate_piece(d, m_peerInfo)) != NULL; } Delegator* m_delegator; Block** m_target; const PeerInfo* m_peerInfo; }; struct DelegatorCheckPriority { DelegatorCheckPriority(Delegator* delegator, Block** target, priority_t p, const PeerChunks* peerChunks) : m_delegator(delegator), m_target(target), m_priority(p), m_peerChunks(peerChunks) {} bool operator () (BlockList* d) { return m_priority == d->priority() && m_peerChunks->bitfield()->get(d->index()) && (*m_target = m_delegator->delegate_piece(d, m_peerChunks->peer_info())) != NULL; } Delegator* m_delegator; Block** m_target; priority_t m_priority; const PeerChunks* m_peerChunks; }; // TODO: Should this ensure we don't download pieces that are priority off? struct DelegatorCheckAggressive { DelegatorCheckAggressive(Delegator* delegator, Block** target, uint16_t* o, const PeerChunks* peerChunks) : m_delegator(delegator), m_target(target), m_overlapp(o), m_peerChunks(peerChunks) {} bool operator () (BlockList* d) { Block* tmp; if (!m_peerChunks->bitfield()->get(d->index()) || d->priority() == PRIORITY_OFF || (tmp = m_delegator->delegate_aggressive(d, m_overlapp, m_peerChunks->peer_info())) == NULL) return false; *m_target = tmp; return m_overlapp == 0; } Delegator* m_delegator; Block** m_target; uint16_t* m_overlapp; const PeerChunks* m_peerChunks; }; BlockTransfer* Delegator::delegate(PeerChunks* peerChunks, int affinity) { // TODO: Make sure we don't queue the same piece several time on the same peer when // it timeout cancels them. Block* target = NULL; // Find piece with same index as affinity. This affinity should ensure that we // never start another piece while the chunk this peer used to download is still // in progress. // // TODO: What if the hash failed? Don't want data from that peer again. if (affinity >= 0 && std::find_if(m_transfers.begin(), m_transfers.end(), DelegatorCheckAffinity(this, &target, affinity, peerChunks->peer_info())) != m_transfers.end()) return target->insert(peerChunks->peer_info()); if (peerChunks->is_seeder() && (target = delegate_seeder(peerChunks)) != NULL) return target->insert(peerChunks->peer_info()); // High priority pieces. if (std::find_if(m_transfers.begin(), m_transfers.end(), DelegatorCheckPriority(this, &target, PRIORITY_HIGH, peerChunks)) != m_transfers.end()) return target->insert(peerChunks->peer_info()); // Find normal priority pieces. if ((target = new_chunk(peerChunks, true))) return target->insert(peerChunks->peer_info()); // Normal priority pieces. if (std::find_if(m_transfers.begin(), m_transfers.end(), DelegatorCheckPriority(this, &target, PRIORITY_NORMAL, peerChunks)) != m_transfers.end()) return target->insert(peerChunks->peer_info()); if ((target = new_chunk(peerChunks, false))) return target->insert(peerChunks->peer_info()); if (!m_aggressive) return NULL; // Aggressive mode, look for possible downloads that already have // one or more queued. // No more than 4 per piece. uint16_t overlapped = 5; std::find_if(m_transfers.begin(), m_transfers.end(), DelegatorCheckAggressive(this, &target, &overlapped, peerChunks)); return target ? target->insert(peerChunks->peer_info()) : NULL; } Block* Delegator::delegate_seeder(PeerChunks* peerChunks) { Block* target = NULL; if (std::find_if(m_transfers.begin(), m_transfers.end(), DelegatorCheckSeeder(this, &target, peerChunks->peer_info())) != m_transfers.end()) return target; if ((target = new_chunk(peerChunks, true))) return target; if ((target = new_chunk(peerChunks, false))) return target; return NULL; } Block* Delegator::new_chunk(PeerChunks* pc, bool highPriority) { uint32_t index = m_slot_chunk_find(pc, highPriority); if (index == ~(uint32_t)0) return NULL; TransferList::iterator itr = m_transfers.insert(Piece(index, 0, m_slot_chunk_size(index)), block_size); (*itr)->set_by_seeder(pc->is_seeder()); if (highPriority) (*itr)->set_priority(PRIORITY_HIGH); else (*itr)->set_priority(PRIORITY_NORMAL); return &*(*itr)->begin(); } Block* Delegator::delegate_piece(BlockList* c, const PeerInfo* peerInfo) { Block* p = NULL; for (BlockList::iterator i = c->begin(); i != c->end(); ++i) { if (i->is_finished() || !i->is_stalled()) continue; if (i->size_all() == 0) { // No one is downloading this, assign. return &*i; } else if (p == NULL && i->find(peerInfo) == NULL) { // Stalled but we really want to finish this piece. Check 'p' so // that we don't end up queuing the pieces in reverse. p = &*i; } } return p; } Block* Delegator::delegate_aggressive(BlockList* c, uint16_t* overlapped, const PeerInfo* peerInfo) { Block* p = NULL; for (BlockList::iterator i = c->begin(); i != c->end() && *overlapped != 0; ++i) if (!i->is_finished() && i->size_not_stalled() < *overlapped && i->find(peerInfo) == NULL) { p = &*i; *overlapped = i->size_not_stalled(); } return p; } } // namespace torrent libtorrent-0.13.6/src/download/delegator.h000066400000000000000000000067151257211073700205200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DELEGATOR_H #define LIBTORRENT_DELEGATOR_H #include #include #include #include "torrent/data/transfer_list.h" namespace torrent { class Block; class BlockList; class BlockTransfer; class Piece; class PeerChunks; class PeerInfo; class Delegator { public: typedef std::tr1::function slot_peer_chunk; typedef std::tr1::function slot_size; static const unsigned int block_size = 1 << 14; Delegator() : m_aggressive(false) { } TransferList* transfer_list() { return &m_transfers; } const TransferList* transfer_list() const { return &m_transfers; } BlockTransfer* delegate(PeerChunks* peerChunks, int affinity); bool get_aggressive() { return m_aggressive; } void set_aggressive(bool a) { m_aggressive = a; } slot_peer_chunk& slot_chunk_find() { return m_slot_chunk_find; } slot_size& slot_chunk_size() { return m_slot_chunk_size; } // Don't call this from the outside. Block* delegate_piece(BlockList* c, const PeerInfo* peerInfo); Block* delegate_aggressive(BlockList* c, uint16_t* overlapped, const PeerInfo* peerInfo); private: // Start on a new chunk, returns .end() if none possible. bf is // remote peer's bitfield. Block* new_chunk(PeerChunks* pc, bool highPriority); Block* delegate_seeder(PeerChunks* peerChunks); TransferList m_transfers; bool m_aggressive; // Propably should add a m_slotChunkStart thing, which will take // care of enabling etc, and will be possible to listen to. slot_peer_chunk m_slot_chunk_find; slot_size m_slot_chunk_size; }; } #endif libtorrent-0.13.6/src/download/download_constructor.cc000066400000000000000000000402211257211073700231520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include "download/download_wrapper.h" #include "torrent/dht_manager.h" #include "torrent/exceptions.h" #include "torrent/object.h" #include "torrent/tracker_controller.h" #include "torrent/tracker_list.h" #include "torrent/data/file.h" #include "torrent/data/file_list.h" #include "download_constructor.h" #include "manager.h" namespace torrent { struct download_constructor_is_single_path { bool operator () (Object::map_type::const_reference v) const { return std::strncmp(v.first.c_str(), "name.", sizeof("name.") - 1) == 0 && v.second.is_string(); } }; struct download_constructor_is_multi_path { bool operator () (Object::map_type::const_reference v) const { return std::strncmp(v.first.c_str(), "path.", sizeof("path.") - 1) == 0 && v.second.is_list(); } }; struct download_constructor_encoding_match : public std::binary_function { bool operator () (const Path& p, const char* enc) { return strcasecmp(p.encoding().c_str(), enc) == 0; } }; void DownloadConstructor::initialize(Object& b) { if (!b.has_key_map("info") && b.has_key_string("magnet-uri")) parse_magnet_uri(b, b.get_key_string("magnet-uri")); if (b.has_key_string("encoding")) m_defaultEncoding = b.get_key_string("encoding"); if (b.has_key_value("creation date")) m_download->info()->set_creation_date(b.get_key_value("creation date")); if (b.get_key("info").has_key_value("private") && b.get_key("info").get_key_value("private") == 1) m_download->info()->set_private(); parse_name(b.get_key("info")); parse_info(b.get_key("info")); } // Currently using a hack of the path thingie to extract the correct // torrent name. void DownloadConstructor::parse_name(const Object& b) { if (is_invalid_path_element(b.get_key("name"))) throw input_error("Bad torrent file, \"name\" is an invalid path name."); std::list pathList; pathList.push_back(Path()); pathList.back().set_encoding(m_defaultEncoding); pathList.back().push_back(b.get_key_string("name")); for (Object::map_const_iterator itr = b.as_map().begin(); (itr = std::find_if(itr, b.as_map().end(), download_constructor_is_single_path())) != b.as_map().end(); ++itr) { pathList.push_back(Path()); pathList.back().set_encoding(itr->first.substr(sizeof("name.") - 1)); pathList.back().push_back(itr->second.as_string()); } if (pathList.empty()) throw input_error("Bad torrent file, an entry has no valid name."); Path name = choose_path(&pathList); if (name.empty()) throw internal_error("DownloadConstructor::parse_name(...) Ended up with an empty Path."); m_download->info()->set_name(name.front()); } void DownloadConstructor::parse_info(const Object& b) { FileList* fileList = m_download->main()->file_list(); if (!fileList->empty()) throw internal_error("parse_info received an already initialized Content object."); if (b.flags() & Object::flag_unordered) throw input_error("Download has unordered info dictionary."); uint32_t chunkSize; if (b.has_key_value("meta_download") && b.get_key_value("meta_download")) m_download->info()->set_flags(DownloadInfo::flag_meta_download); if (m_download->info()->is_meta_download()) { if (b.get_key_string("pieces").length() != HashString::size_data) throw input_error("Meta-download has invalid piece data."); chunkSize = 1; parse_single_file(b, chunkSize); } else { chunkSize = b.get_key_value("piece length"); if (chunkSize <= (1 << 10) || chunkSize > (128 << 20)) throw input_error("Torrent has an invalid \"piece length\"."); } if (b.has_key("length")) { parse_single_file(b, chunkSize); } else if (b.has_key("files")) { parse_multi_files(b.get_key("files"), chunkSize); fileList->set_root_dir("./" + m_download->info()->name()); } else if (!m_download->info()->is_meta_download()) { throw input_error("Torrent must have either length or files entry."); } if (fileList->size_bytes() == 0 && !m_download->info()->is_meta_download()) throw input_error("Torrent has zero length."); // Set chunksize before adding files to make sure the index range is // correct. m_download->set_complete_hash(b.get_key_string("pieces")); if (m_download->complete_hash().size() / 20 < fileList->size_chunks()) throw bencode_error("Torrent size and 'info:pieces' length does not match."); } void DownloadConstructor::parse_tracker(const Object& b) { const Object::list_type* announce_list = NULL; if (b.has_key_list("announce-list") && // Some torrent makers create empty/invalid 'announce-list' // entries while still having valid 'announce'. !(announce_list = &b.get_key_list("announce-list"))->empty() && std::find_if(announce_list->begin(), announce_list->end(), std::mem_fun_ref(&Object::is_list)) != announce_list->end()) std::for_each(announce_list->begin(), announce_list->end(), rak::make_mem_fun(this, &DownloadConstructor::add_tracker_group)); else if (b.has_key("announce")) add_tracker_single(b.get_key("announce"), 0); else if (!manager->dht_manager()->is_valid() || m_download->info()->is_private()) throw bencode_error("Could not find any trackers"); if (manager->dht_manager()->is_valid() && !m_download->info()->is_private()) m_download->main()->tracker_list()->insert_url(m_download->main()->tracker_list()->size_group(), "dht://"); if (manager->dht_manager()->is_valid() && b.has_key_list("nodes")) std::for_each(b.get_key_list("nodes").begin(), b.get_key_list("nodes").end(), rak::make_mem_fun(this, &DownloadConstructor::add_dht_node)); m_download->main()->tracker_list()->randomize_group_entries(); } void DownloadConstructor::add_tracker_group(const Object& b) { if (!b.is_list()) throw bencode_error("Tracker group list not a list"); std::for_each(b.as_list().begin(), b.as_list().end(), rak::bind2nd(rak::make_mem_fun(this, &DownloadConstructor::add_tracker_single), m_download->main()->tracker_list()->size_group())); } void DownloadConstructor::add_tracker_single(const Object& b, int group) { if (!b.is_string()) throw bencode_error("Tracker entry not a string"); m_download->main()->tracker_list()->insert_url(group, rak::trim_classic(b.as_string())); } void DownloadConstructor::add_dht_node(const Object& b) { if (!b.is_list() || b.as_list().size() < 2) return; Object::list_type::const_iterator el = b.as_list().begin(); if (!el->is_string()) return; const std::string& host = el->as_string(); if (!(++el)->is_value()) return; manager->dht_manager()->add_node(host, el->as_value()); } bool DownloadConstructor::is_valid_path_element(const Object& b) { return b.is_string() && b.as_string() != "." && b.as_string() != ".." && std::find(b.as_string().begin(), b.as_string().end(), '/') == b.as_string().end() && std::find(b.as_string().begin(), b.as_string().end(), '\0') == b.as_string().end(); } void DownloadConstructor::parse_single_file(const Object& b, uint32_t chunkSize) { if (is_invalid_path_element(b.get_key("name"))) throw input_error("Bad torrent file, \"name\" is an invalid path name."); FileList* fileList = m_download->main()->file_list(); fileList->initialize(chunkSize == 1 ? 1 : b.get_key_value("length"), chunkSize); fileList->set_multi_file(false); std::list pathList; pathList.push_back(Path()); pathList.back().set_encoding(m_defaultEncoding); pathList.back().push_back(b.get_key_string("name")); for (Object::map_const_iterator itr = b.as_map().begin(); (itr = std::find_if(itr, b.as_map().end(), download_constructor_is_single_path())) != b.as_map().end(); ++itr) { pathList.push_back(Path()); pathList.back().set_encoding(itr->first.substr(sizeof("name.") - 1)); pathList.back().push_back(itr->second.as_string()); } if (pathList.empty()) throw input_error("Bad torrent file, an entry has no valid filename."); *fileList->front()->mutable_path() = choose_path(&pathList); fileList->update_paths(fileList->begin(), fileList->end()); } void DownloadConstructor::parse_multi_files(const Object& b, uint32_t chunkSize) { const Object::list_type& objectList = b.as_list(); // Multi file torrent if (objectList.empty()) throw input_error("Bad torrent file, entry has no files."); int64_t torrentSize = 0; std::vector splitList(objectList.size()); std::vector::iterator splitItr = splitList.begin(); for (Object::list_const_iterator listItr = objectList.begin(), listLast = objectList.end(); listItr != listLast; ++listItr, ++splitItr) { std::list pathList; if (listItr->has_key_list("path")) pathList.push_back(create_path(listItr->get_key_list("path"), m_defaultEncoding)); Object::map_const_iterator itr = listItr->as_map().begin(); Object::map_const_iterator last = listItr->as_map().end(); while ((itr = std::find_if(itr, last, download_constructor_is_multi_path())) != last) { pathList.push_back(create_path(itr->second.as_list(), itr->first.substr(sizeof("path.") - 1))); ++itr; } if (pathList.empty()) throw input_error("Bad torrent file, an entry has no valid filename."); int64_t length = listItr->get_key_value("length"); if (length < 0 || torrentSize + length < 0) throw input_error("Bad torrent file, invalid length for file."); torrentSize += length; *splitItr = FileList::split_type(length, choose_path(&pathList)); } FileList* fileList = m_download->main()->file_list(); fileList->set_multi_file(true); fileList->initialize(torrentSize, chunkSize); fileList->split(fileList->begin(), &*splitList.begin(), &*splitList.end()); fileList->update_paths(fileList->begin(), fileList->end()); } inline Path DownloadConstructor::create_path(const Object::list_type& plist, const std::string enc) { // Make sure we are given a proper file path. if (plist.empty()) throw input_error("Bad torrent file, \"path\" has zero entries."); if (std::find_if(plist.begin(), plist.end(), std::ptr_fun(&DownloadConstructor::is_invalid_path_element)) != plist.end()) throw input_error("Bad torrent file, \"path\" has zero entries or a zero length entry."); Path p; p.set_encoding(enc); std::transform(plist.begin(), plist.end(), std::back_inserter(p), std::mem_fun_ref(&Object::as_string)); return p; } inline Path DownloadConstructor::choose_path(std::list* pathList) { std::list::iterator pathFirst = pathList->begin(); std::list::iterator pathLast = pathList->end(); EncodingList::const_iterator encodingFirst = m_encodingList->begin(); EncodingList::const_iterator encodingLast = m_encodingList->end(); for ( ; encodingFirst != encodingLast; ++encodingFirst) { std::list::iterator itr = std::find_if(pathFirst, pathLast, rak::bind2nd(download_constructor_encoding_match(), encodingFirst->c_str())); if (itr != pathLast) pathList->splice(pathFirst, *pathList, itr); } return pathList->front(); } static const char* parse_base32_sha1(const char* pos, HashString& hash) { HashString::iterator hashItr = hash.begin(); static const int base_shift = 8+8-5; int shift = base_shift; uint16_t decoded = 0; while (*pos) { char c = *pos++; uint16_t value; if (c >= 'A' && c <= 'Z') value = c - 'A'; else if (c >= 'a' && c <= 'z') value = c - 'a'; else if (c >= '2' && c <= '7') value = 26 + c - '2'; else if (c == '&') break; else return NULL; decoded |= (value << shift); if (shift <= 8) { // Too many characters for a base32 SHA1. if (hashItr == hash.end()) return NULL; *hashItr++ = (decoded >> 8); decoded <<= 8; shift += 3; } else { shift -= 5; } } return hashItr != hash.end() || shift != base_shift ? NULL : pos; } void DownloadConstructor::parse_magnet_uri(Object& b, const std::string& uri) { if (std::strncmp(uri.c_str(), "magnet:?", 8)) throw input_error("Invalid magnet URI."); const char* pos = uri.c_str() + 8; Object trackers(Object::create_list()); HashString hash; bool hashValid = false; while (*pos) { const char* tagStart = pos; while (*pos != '=') if (!*pos++) break; raw_string tag(tagStart, pos - tagStart); pos++; // hash may be base32 encoded (optional in BEP 0009 and common practice) if (raw_bencode_equal_c_str(tag, "xt")) { if (strncmp(pos, "urn:btih:", 9)) throw input_error("Invalid magnet URI."); pos += 9; const char* nextPos = parse_base32_sha1(pos, hash); if (nextPos != NULL) { pos = nextPos; hashValid = true; continue; } } // everything else, including sometimes the hash, is url encoded. std::string decoded; while (*pos) { char c = *pos++; if (c == '%') { if (sscanf(pos, "%02hhx", &c) != 1) throw input_error("Invalid magnet URI."); pos += 2; } else if (c == '&') { break; } decoded.push_back(c); } if (raw_bencode_equal_c_str(tag, "xt")) { // url-encoded hash as per magnet URN specs if (decoded.length() == hash.size_data) { hash = *HashString::cast_from(decoded); hashValid = true; // hex-encoded hash as per BEP 0009 } else if (decoded.length() == hash.size_data * 2) { std::string::iterator hexItr = decoded.begin(); for (HashString::iterator itr = hash.begin(), last = hash.end(); itr != last; itr++, hexItr += 2) *itr = (rak::hexchar_to_value(*hexItr) << 4) + rak::hexchar_to_value(*(hexItr + 1)); hashValid = true; } else { throw input_error("Invalid magnet URI."); } } else if (raw_bencode_equal_c_str(tag, "tr")) { trackers.insert_back(Object::create_list()).insert_back(decoded); } // could also handle "dn" = display name (torrent name), but we can't really use that } if (!hashValid) throw input_error("Invalid magnet URI."); Object& info = b.insert_key("info", Object::create_map()); info.insert_key("pieces", hash.str()); info.insert_key("name", rak::transform_hex(hash.str()) + ".meta"); info.insert_key("meta_download", (int64_t)1); if (!trackers.as_list().empty()) { b.insert_preserve_copy("announce", trackers.as_list().begin()->as_list().begin()->as_string()); b.insert_preserve_type("announce-list", trackers); } } } libtorrent-0.13.6/src/download/download_constructor.h000066400000000000000000000063311257211073700230200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PARSE_DOWNLOAD_CONSTRUCTOR_H #define LIBTORRENT_PARSE_DOWNLOAD_CONSTRUCTOR_H #include #include #include namespace torrent { class Object; class Content; class DownloadWrapper; class Path; typedef std::list EncodingList; class DownloadConstructor { public: DownloadConstructor() : m_download(NULL), m_encodingList(NULL) {} void initialize(Object& b); void parse_tracker(const Object& b); void set_download(DownloadWrapper* d) { m_download = d; } void set_encoding_list(const EncodingList* e) { m_encodingList = e; } private: void parse_name(const Object& b); void parse_info(const Object& b); void parse_magnet_uri(Object& b, const std::string& uri); void add_tracker_group(const Object& b); void add_tracker_single(const Object& b, int group); void add_dht_node(const Object& b); static bool is_valid_path_element(const Object& b); static bool is_invalid_path_element(const Object& b) { return !is_valid_path_element(b); } void parse_single_file(const Object& b, uint32_t chunkSize); void parse_multi_files(const Object& b, uint32_t chunkSize); inline Path create_path(const Object::list_type& plist, const std::string enc); inline Path choose_path(std::list* pathList); DownloadWrapper* m_download; const EncodingList* m_encodingList; std::string m_defaultEncoding; }; } #endif libtorrent-0.13.6/src/download/download_main.cc000066400000000000000000000436651257211073700215300ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "data/chunk_list.h" #include "protocol/extensions.h" #include "protocol/handshake_manager.h" #include "protocol/initial_seed.h" #include "protocol/peer_connection_base.h" #include "protocol/peer_factory.h" #include "torrent/download.h" #include "torrent/exceptions.h" #include "torrent/throttle.h" #include "torrent/data/file_list.h" #include "torrent/download/download_manager.h" #include "torrent/download/choke_queue.h" #include "torrent/download/choke_group.h" #include "torrent/peer/connection_list.h" #include "torrent/peer/peer.h" #include "torrent/peer/peer_info.h" #include "torrent/tracker_controller.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "available_list.h" #include "chunk_selector.h" #include "chunk_statistics.h" #include "download_main.h" #include "download_wrapper.h" #define LT_LOG_THIS(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TORRENT_##log_level, m_ptr->info(), "download", log_fmt, __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { DownloadInfo::DownloadInfo() : m_flags(flag_compact | flag_accepting_new_peers | flag_accepting_seeders | flag_pex_enabled | flag_pex_active), m_upRate(60), m_downRate(60), m_skipRate(60), m_uploadedBaseline(0), m_completedBaseline(0), m_sizePex(0), m_maxSizePex(8), m_metadataSize(0), m_creationDate(0), m_loadDate(rak::timer::current_seconds()), m_upload_unchoked(0), m_download_unchoked(0) { } DownloadMain::DownloadMain() : m_info(new DownloadInfo), m_choke_group(NULL), m_chunkList(new ChunkList), m_chunkSelector(new ChunkSelector(file_list()->mutable_data())), m_chunkStatistics(new ChunkStatistics), m_initialSeeding(NULL), m_uploadThrottle(NULL), m_downloadThrottle(NULL) { m_tracker_list = new TrackerList(); m_tracker_controller = new TrackerController(m_tracker_list); m_tracker_list->slot_success() = tr1::bind(&TrackerController::receive_success, m_tracker_controller, tr1::placeholders::_1, tr1::placeholders::_2); m_tracker_list->slot_failure() = tr1::bind(&TrackerController::receive_failure, m_tracker_controller, tr1::placeholders::_1, tr1::placeholders::_2); m_tracker_list->slot_scrape_success() = tr1::bind(&TrackerController::receive_scrape, m_tracker_controller, tr1::placeholders::_1); m_tracker_list->slot_tracker_enabled() = tr1::bind(&TrackerController::receive_tracker_enabled, m_tracker_controller, tr1::placeholders::_1); m_tracker_list->slot_tracker_disabled() = tr1::bind(&TrackerController::receive_tracker_disabled, m_tracker_controller, tr1::placeholders::_1); m_connectionList = new ConnectionList(this); m_delegator.slot_chunk_find() = std::tr1::bind(&ChunkSelector::find, m_chunkSelector, tr1::placeholders::_1, tr1::placeholders::_2); m_delegator.slot_chunk_size() = std::tr1::bind(&FileList::chunk_index_size, file_list(), tr1::placeholders::_1); m_delegator.transfer_list()->slot_canceled() = std::tr1::bind(&ChunkSelector::not_using_index, m_chunkSelector, tr1::placeholders::_1); m_delegator.transfer_list()->slot_queued() = std::tr1::bind(&ChunkSelector::using_index, m_chunkSelector, tr1::placeholders::_1); m_delegator.transfer_list()->slot_completed() = std::tr1::bind(&DownloadMain::receive_chunk_done, this, tr1::placeholders::_1); m_delegator.transfer_list()->slot_corrupt() = std::tr1::bind(&DownloadMain::receive_corrupt_chunk, this, tr1::placeholders::_1); m_delayDisconnectPeers.slot() = std::tr1::bind(&ConnectionList::disconnect_queued, m_connectionList); m_taskTrackerRequest.slot() = std::tr1::bind(&DownloadMain::receive_tracker_request, this); m_chunkList->set_data(file_list()->mutable_data()); m_chunkList->slot_create_chunk() = tr1::bind(&FileList::create_chunk_index, file_list(), tr1::placeholders::_1, tr1::placeholders::_2); m_chunkList->slot_free_diskspace() = tr1::bind(&FileList::free_diskspace, file_list()); } DownloadMain::~DownloadMain() { if (m_taskTrackerRequest.is_queued()) throw internal_error("DownloadMain::~DownloadMain(): m_taskTrackerRequest is queued."); // Check if needed. m_connectionList->clear(); m_tracker_list->clear(); if (m_info->size_pex() != 0) throw internal_error("DownloadMain::~DownloadMain(): m_info->size_pex() != 0."); delete m_tracker_controller; delete m_tracker_list; delete m_connectionList; delete m_chunkStatistics; delete m_chunkList; delete m_chunkSelector; delete m_info; m_ut_pex_delta.clear(); m_ut_pex_initial.clear(); } std::pair DownloadMain::throttles(const sockaddr* sa) { ThrottlePair pair = ThrottlePair(NULL, NULL); if (manager->connection_manager()->address_throttle()) pair = manager->connection_manager()->address_throttle()(sa); return std::make_pair(pair.first == NULL ? upload_throttle() : pair.first->throttle_list(), pair.second == NULL ? download_throttle() : pair.second->throttle_list()); } void DownloadMain::open(int flags) { if (info()->is_open()) throw internal_error("Tried to open a download that is already open"); file_list()->open(flags & FileList::open_no_create); m_chunkList->resize(file_list()->size_chunks()); m_chunkStatistics->initialize(file_list()->size_chunks()); info()->set_flags(DownloadInfo::flag_open); } void DownloadMain::close() { if (info()->is_active()) throw internal_error("Tried to close an active download"); if (!info()->is_open()) return; info()->unset_flags(DownloadInfo::flag_open); // Don't close the tracker manager here else it will cause STOPPED // requests to be lost. TODO: Check that this is valid. // m_trackerManager->close(); m_delegator.transfer_list()->clear(); file_list()->close(); // Clear the chunklist last as it requires all referenced chunks to // be released. m_chunkStatistics->clear(); m_chunkList->clear(); m_chunkSelector->cleanup(); } void DownloadMain::start() { if (!info()->is_open()) throw internal_error("Tried to start a closed download"); if (info()->is_active()) throw internal_error("Tried to start an active download"); info()->set_flags(DownloadInfo::flag_active); chunk_list()->set_flags(ChunkList::flag_active); m_delegator.set_aggressive(false); update_endgame(); receive_connect_peers(); } void DownloadMain::stop() { if (!info()->is_active()) return; // Set this early so functions like receive_connect_peers() knows // not to eat available peers. info()->unset_flags(DownloadInfo::flag_active); chunk_list()->unset_flags(ChunkList::flag_active); m_slotStopHandshakes(this); connection_list()->erase_remaining(connection_list()->begin(), ConnectionList::disconnect_available); delete m_initialSeeding; m_initialSeeding = NULL; priority_queue_erase(&taskScheduler, &m_delayDisconnectPeers); priority_queue_erase(&taskScheduler, &m_taskTrackerRequest); if (info()->upload_unchoked() != 0 || info()->download_unchoked() != 0) throw internal_error("DownloadMain::stop(): info()->upload_unchoked() != 0 || info()->download_unchoked() != 0."); } bool DownloadMain::start_initial_seeding() { if (!file_list()->is_done()) return false; m_initialSeeding = new InitialSeeding(this); return true; } void DownloadMain::initial_seeding_done(PeerConnectionBase* pcb) { if (m_initialSeeding == NULL) throw internal_error("DownloadMain::initial_seeding_done called when not initial seeding."); // Close all connections but the currently active one (pcb), that // one will be closed by throw close_connection() later. // // When calling initial_seed()->new_peer(...) the 'pcb' won't be in // the connection list, so don't treat it as an error. Make sure to // catch close_connection() at the caller of new_peer(...) and just // close the filedesc before proceeding as normal. ConnectionList::iterator pcb_itr = std::find(m_connectionList->begin(), m_connectionList->end(), pcb); if (pcb_itr != m_connectionList->end()) { std::iter_swap(m_connectionList->begin(), pcb_itr); m_connectionList->erase_remaining(m_connectionList->begin() + 1, ConnectionList::disconnect_available); } else { m_connectionList->erase_remaining(m_connectionList->begin(), ConnectionList::disconnect_available); } // Switch to normal seeding. DownloadManager::iterator itr = manager->download_manager()->find(m_info); (*itr)->set_connection_type(Download::CONNECTION_SEED); m_connectionList->slot_new_connection(&createPeerConnectionSeed); delete m_initialSeeding; m_initialSeeding = NULL; // And close the current connection. throw close_connection(); } void DownloadMain::update_endgame() { if (!m_delegator.get_aggressive() && file_list()->completed_chunks() + m_delegator.transfer_list()->size() + 5 >= file_list()->size_chunks()) m_delegator.set_aggressive(true); } void DownloadMain::receive_chunk_done(unsigned int index) { ChunkHandle handle = m_chunkList->get(index); if (!handle.is_valid()) throw storage_error("DownloadState::chunk_done(...) called with an index we couldn't retrieve from storage"); m_slotHashCheckAdd(handle); } void DownloadMain::receive_corrupt_chunk(PeerInfo* peerInfo) { peerInfo->set_failed_counter(peerInfo->failed_counter() + 1); // Just use some very primitive heuristics here to decide if we're // going to disconnect the peer. Also, consider adding a flag so we // don't recalculate these things whenever the peer reconnects. // That is... non at all ;) if (peerInfo->failed_counter() > HandshakeManager::max_failed) connection_list()->erase(peerInfo, ConnectionList::disconnect_unwanted); } void DownloadMain::add_peer(const rak::socket_address& sa) { m_slotStartHandshake(sa, this); } void DownloadMain::receive_connect_peers() { if (!info()->is_active()) return; // TODO: Is this actually going to be used? AddressList* alist = peer_list()->available_list()->buffer(); if (!alist->empty()) { alist->sort(); peer_list()->insert_available(alist); alist->clear(); } while (!peer_list()->available_list()->empty() && manager->connection_manager()->can_connect() && connection_list()->size() < connection_list()->min_size() && connection_list()->size() + m_slotCountHandshakes(this) < connection_list()->max_size()) { rak::socket_address sa = peer_list()->available_list()->pop_random(); if (connection_list()->find(sa.c_sockaddr()) == connection_list()->end()) m_slotStartHandshake(sa, this); } } void DownloadMain::receive_tracker_success() { if (!info()->is_active()) return; priority_queue_erase(&taskScheduler, &m_taskTrackerRequest); priority_queue_insert(&taskScheduler, &m_taskTrackerRequest, (cachedTime + rak::timer::from_seconds(10)).round_seconds()); } void DownloadMain::receive_tracker_request() { bool should_stop = false; bool should_start = false; if (info()->is_pex_enabled() && info()->size_pex() > 0) should_stop = true; if (connection_list()->size() + peer_list()->available_list()->size() / 2 < connection_list()->min_size()) should_start = true; if (should_stop) m_tracker_controller->stop_requesting(); else if (should_start) m_tracker_controller->start_requesting(); } struct SocketAddressCompact_less { bool operator () (const SocketAddressCompact& a, const SocketAddressCompact& b) const { return (a.addr < b.addr) || ((a.addr == b.addr) && (a.port < b.port)); } }; void DownloadMain::do_peer_exchange() { if (!info()->is_active()) throw internal_error("DownloadMain::do_peer_exchange called on inactive download."); // Check whether we should tell the peers to stop/start sending PEX // messages. int togglePex = 0; if (!m_info->is_pex_active() && m_connectionList->size() < m_connectionList->min_size() / 2 && m_peerList.available_list()->size() < m_peerList.available_list()->max_size() / 4) { m_info->set_flags(DownloadInfo::flag_pex_active); // Only set PEX_ENABLE if we don't have max_size_pex set to zero. if (m_info->size_pex() < m_info->max_size_pex()) togglePex = PeerConnectionBase::PEX_ENABLE; } else if (m_info->is_pex_active() && m_connectionList->size() >= m_connectionList->min_size()) { // m_peerList.available_list()->size() >= m_peerList.available_list()->max_size() / 2) { togglePex = PeerConnectionBase::PEX_DISABLE; m_info->unset_flags(DownloadInfo::flag_pex_active); } // Return if we don't really want to do anything? ProtocolExtension::PEXList current; for (ConnectionList::iterator itr = m_connectionList->begin(); itr != m_connectionList->end(); ++itr) { PeerConnectionBase* pcb = (*itr)->m_ptr(); const rak::socket_address* sa = rak::socket_address::cast_from(pcb->peer_info()->socket_address()); if (pcb->peer_info()->listen_port() != 0 && sa->family() == rak::socket_address::af_inet) current.push_back(SocketAddressCompact(sa->sa_inet()->address_n(), pcb->peer_info()->listen_port())); if (!pcb->extensions()->is_remote_supported(ProtocolExtension::UT_PEX)) continue; if (togglePex == PeerConnectionBase::PEX_ENABLE) { pcb->set_peer_exchange(true); if (m_info->size_pex() >= m_info->max_size_pex()) togglePex = 0; } else if (!pcb->extensions()->is_local_enabled(ProtocolExtension::UT_PEX)) { continue; } else if (togglePex == PeerConnectionBase::PEX_DISABLE) { pcb->set_peer_exchange(false); continue; } // Still using the old buffer? Make a copy in this rare case. DataBuffer* message = pcb->extension_message(); if (!message->empty() && (message->data() == m_ut_pex_initial.data() || message->data() == m_ut_pex_delta.data())) { char* buffer = new char[message->length()]; memcpy(buffer, message->data(), message->length()); message->set(buffer, buffer + message->length(), true); } pcb->do_peer_exchange(); } std::sort(current.begin(), current.end(), SocketAddressCompact_less()); ProtocolExtension::PEXList added; added.reserve(current.size()); std::set_difference(current.begin(), current.end(), m_ut_pex_list.begin(), m_ut_pex_list.end(), std::back_inserter(added), SocketAddressCompact_less()); ProtocolExtension::PEXList removed; removed.reserve(m_ut_pex_list.size()); std::set_difference(m_ut_pex_list.begin(), m_ut_pex_list.end(), current.begin(), current.end(), std::back_inserter(removed), SocketAddressCompact_less()); if (current.size() > m_info->max_size_pex_list()) { // This test is only correct as long as we have a constant max // size. if (added.size() < current.size() - m_info->max_size_pex_list()) throw internal_error("DownloadMain::do_peer_exchange() added.size() < current.size() - m_info->max_size_pex_list()."); // Randomize this: added.erase(added.end() - (current.size() - m_info->max_size_pex_list()), added.end()); // Create the new m_ut_pex_list by removing any 'removed' // addresses from the original list and then adding the new // addresses. m_ut_pex_list.erase(std::set_difference(m_ut_pex_list.begin(), m_ut_pex_list.end(), removed.begin(), removed.end(), m_ut_pex_list.begin(), SocketAddressCompact_less()), m_ut_pex_list.end()); m_ut_pex_list.insert(m_ut_pex_list.end(), added.begin(), added.end()); std::sort(m_ut_pex_list.begin(), m_ut_pex_list.end(), SocketAddressCompact_less()); } else { m_ut_pex_list.swap(current); } current.clear(); m_ut_pex_delta.clear(); // If no peers were added or removed, the initial message is still correct and // the delta message stays emptied. Otherwise generate the appropriate messages. if (!added.empty() || !m_ut_pex_list.empty()) { m_ut_pex_delta = ProtocolExtension::generate_ut_pex_message(added, removed); m_ut_pex_initial.clear(); m_ut_pex_initial = ProtocolExtension::generate_ut_pex_message(m_ut_pex_list, current); } } void DownloadMain::set_metadata_size(size_t size) { if (m_info->is_meta_download()) { if (m_fileList.size_bytes() < 2) file_list()->reset_filesize(size); else if (size != m_fileList.size_bytes()) throw communication_error("Peer-supplied metadata size mismatch."); } else if (m_info->metadata_size() && m_info->metadata_size() != size) { throw communication_error("Peer-supplied metadata size mismatch."); } m_info->set_metadata_size(size); } } libtorrent-0.13.6/src/download/download_main.h000066400000000000000000000203371257211073700213610ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_MAIN_H #define LIBTORRENT_DOWNLOAD_MAIN_H #include #include #include "globals.h" #include "delegator.h" #include "data/chunk_handle.h" #include "download/available_list.h" #include "net/data_buffer.h" #include "torrent/download_info.h" #include "torrent/download/group_entry.h" #include "torrent/data/file_list.h" #include "torrent/peer/peer_list.h" namespace torrent { class ChunkList; class ChunkSelector; class ChunkStatistics; class choke_group; class ConnectionList; class DownloadWrapper; class HandshakeManager; class TrackerController; class TrackerList; class DownloadInfo; class ThrottleList; class InitialSeeding; class DownloadMain { public: typedef std::deque > have_queue_type; typedef std::vector pex_list; DownloadMain(); ~DownloadMain(); void open(int flags); void close(); void start(); void stop(); class choke_group* choke_group() { return m_choke_group; } const class choke_group* c_choke_group() const { return m_choke_group; } void set_choke_group(class choke_group* grp) { m_choke_group = grp; } TrackerController* tracker_controller() { return m_tracker_controller; } TrackerList* tracker_list() { return m_tracker_list; } DownloadInfo* info() { return m_info; } // Only retrieve writable chunks when the download is active. ChunkList* chunk_list() { return m_chunkList; } ChunkSelector* chunk_selector() { return m_chunkSelector; } ChunkStatistics* chunk_statistics() { return m_chunkStatistics; } Delegator* delegator() { return &m_delegator; } have_queue_type* have_queue() { return &m_haveQueue; } InitialSeeding* initial_seeding() { return m_initialSeeding; } bool start_initial_seeding(); void initial_seeding_done(PeerConnectionBase* pcb); ConnectionList* connection_list() { return m_connectionList; } FileList* file_list() { return &m_fileList; } PeerList* peer_list() { return &m_peerList; } std::pair throttles(const sockaddr* sa); ThrottleList* upload_throttle() { return m_uploadThrottle; } void set_upload_throttle(ThrottleList* t) { m_uploadThrottle = t; } ThrottleList* download_throttle() { return m_downloadThrottle; } void set_download_throttle(ThrottleList* t) { m_downloadThrottle = t; } group_entry* up_group_entry() { return &m_up_group_entry; } group_entry* down_group_entry() { return &m_down_group_entry; } DataBuffer get_ut_pex(bool initial) { return (initial ? m_ut_pex_initial : m_ut_pex_delta).clone(); } bool want_pex_msg() { return m_info->is_pex_active() && m_peerList.available_list()->want_more(); }; void set_metadata_size(size_t s); // Carefull with these. void setup_delegator(); void setup_tracker(); typedef rak::const_mem_fun1 SlotCountHandshakes; typedef rak::mem_fun1 SlotHashCheckAdd; typedef rak::mem_fun2 slot_start_handshake_type; typedef rak::mem_fun1 slot_stop_handshakes_type; void slot_start_handshake(slot_start_handshake_type s) { m_slotStartHandshake = s; } void slot_stop_handshakes(slot_stop_handshakes_type s) { m_slotStopHandshakes = s; } void slot_count_handshakes(SlotCountHandshakes s) { m_slotCountHandshakes = s; } void slot_hash_check_add(SlotHashCheckAdd s) { m_slotHashCheckAdd = s; } void add_peer(const rak::socket_address& sa); void receive_connect_peers(); void receive_chunk_done(unsigned int index); void receive_corrupt_chunk(PeerInfo* peerInfo); void receive_tracker_success(); void receive_tracker_request(); void receive_do_peer_exchange(); void do_peer_exchange(); void update_endgame(); rak::priority_item& delay_download_done() { return m_delay_download_done; } rak::priority_item& delay_partially_done() { return m_delay_partially_done; } rak::priority_item& delay_partially_restarted() { return m_delay_partially_restarted; } rak::priority_item& delay_disconnect_peers() { return m_delayDisconnectPeers; } private: // Disable copy ctor and assignment. DownloadMain(const DownloadMain&); void operator = (const DownloadMain&); void setup_start(); void setup_stop(); DownloadInfo* m_info; TrackerController* m_tracker_controller; TrackerList* m_tracker_list; class choke_group* m_choke_group; group_entry m_up_group_entry; group_entry m_down_group_entry; ChunkList* m_chunkList; ChunkSelector* m_chunkSelector; ChunkStatistics* m_chunkStatistics; Delegator m_delegator; have_queue_type m_haveQueue; InitialSeeding* m_initialSeeding; ConnectionList* m_connectionList; FileList m_fileList; PeerList m_peerList; DataBuffer m_ut_pex_delta; DataBuffer m_ut_pex_initial; pex_list m_ut_pex_list; ThrottleList* m_uploadThrottle; ThrottleList* m_downloadThrottle; slot_start_handshake_type m_slotStartHandshake; slot_stop_handshakes_type m_slotStopHandshakes; SlotCountHandshakes m_slotCountHandshakes; SlotHashCheckAdd m_slotHashCheckAdd; rak::priority_item m_delay_download_done; rak::priority_item m_delay_partially_done; rak::priority_item m_delay_partially_restarted; rak::priority_item m_delayDisconnectPeers; rak::priority_item m_taskTrackerRequest; }; } #endif libtorrent-0.13.6/src/download/download_wrapper.cc000066400000000000000000000321371257211073700222540ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "data/chunk_list.h" #include "data/hash_queue.h" #include "data/hash_torrent.h" #include "protocol/handshake_manager.h" #include "protocol/peer_connection_base.h" #include "torrent/exceptions.h" #include "torrent/object.h" #include "torrent/tracker_list.h" #include "torrent/data/file.h" #include "torrent/data/file_list.h" #include "torrent/data/file_manager.h" #include "torrent/peer/peer.h" #include "torrent/peer/connection_list.h" #include "torrent/tracker_controller.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "available_list.h" #include "chunk_selector.h" #include "download_wrapper.h" #define LT_LOG_STORAGE_ERRORS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_STORAGE_ERRORS, this->info(), "storage_errors", log_fmt, __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { DownloadWrapper::DownloadWrapper() : m_main(new DownloadMain), m_bencode(NULL), m_hashChecker(NULL), m_connectionType(0) { m_main->delay_download_done().slot() = std::tr1::bind(&download_data::call_download_done, data()); m_main->peer_list()->set_info(info()); m_main->tracker_list()->set_info(info()); m_main->tracker_controller()->slot_success() = tr1::bind(&DownloadWrapper::receive_tracker_success, this, tr1::placeholders::_1); m_main->tracker_controller()->slot_failure() = tr1::bind(&DownloadWrapper::receive_tracker_failed, this, tr1::placeholders::_1); m_main->chunk_list()->slot_storage_error() = tr1::bind(&DownloadWrapper::receive_storage_error, this, tr1::placeholders::_1); } DownloadWrapper::~DownloadWrapper() { if (info()->is_active()) m_main->stop(); if (info()->is_open()) close(); // If the client wants to do a quick cleanup after calling close, it // will need to manually cancel the tracker requests. m_main->tracker_controller()->close(); delete m_hashChecker; delete m_bencode; delete m_main; } void DownloadWrapper::initialize(const std::string& hash, const std::string& id) { char hashObfuscated[20]; sha1_salt("req2", 4, hash.c_str(), hash.length(), hashObfuscated); info()->mutable_hash().assign(hash.c_str()); info()->mutable_hash_obfuscated().assign(hashObfuscated); info()->mutable_local_id().assign(id.c_str()); info()->slot_left() = tr1::bind(&FileList::left_bytes, m_main->file_list()); info()->slot_completed() = tr1::bind(&FileList::completed_bytes, m_main->file_list()); file_list()->mutable_data()->mutable_hash().assign(hash.c_str()); m_main->slot_hash_check_add(rak::make_mem_fun(this, &DownloadWrapper::check_chunk_hash)); // Info hash must be calculate from here on. m_hashChecker = new HashTorrent(m_main->chunk_list()); // Connect various signals and slots. m_hashChecker->slot_check_chunk() = std::tr1::bind(&DownloadWrapper::check_chunk_hash, this, std::tr1::placeholders::_1); m_hashChecker->delay_checked().slot() = std::tr1::bind(&DownloadWrapper::receive_initial_hash, this); } void DownloadWrapper::close() { // Stop the hashing first as we need to make sure all chunks are // released when DownloadMain::close() is called. m_hashChecker->clear(); // Clear after m_hashChecker to ensure that the empty hash done signal does // not get passed to HashTorrent. hash_queue()->remove(data()); // This could/should be async as we do not care that much if it // succeeds or not, any chunks not included in that last // hash_resume_save get ignored anyway. m_main->chunk_list()->sync_chunks(ChunkList::sync_all | ChunkList::sync_force | ChunkList::sync_sloppy | ChunkList::sync_ignore_error); m_main->close(); // Should this perhaps be in stop? priority_queue_erase(&taskScheduler, &m_main->delay_download_done()); } bool DownloadWrapper::is_stopped() const { return !m_main->tracker_controller()->is_active() && !m_main->tracker_list()->has_active(); } void DownloadWrapper::receive_initial_hash() { if (info()->is_active()) throw internal_error("DownloadWrapper::receive_initial_hash() but we're in a bad state."); if (!m_hashChecker->is_checking()) { receive_storage_error("Hash checker was unable to map chunk: " + std::string(rak::error_number(m_hashChecker->error_number()).c_str())); } else { m_hashChecker->confirm_checked(); if (hash_queue()->has(data())) throw internal_error("DownloadWrapper::receive_initial_hash() found a chunk in the HashQueue."); // Initialize the ChunkSelector here so that no chunks will be // marked by HashTorrent that are not accounted for. m_main->chunk_selector()->initialize(m_main->chunk_statistics()); receive_update_priorities(); } if (data()->slot_initial_hash()) data()->slot_initial_hash()(); } void DownloadWrapper::receive_hash_done(ChunkHandle handle, const char* hash) { if (!handle.is_valid()) throw internal_error("DownloadWrapper::receive_hash_done(...) called on an invalid chunk."); if (!info()->is_open()) throw internal_error("DownloadWrapper::receive_hash_done(...) called but the download is not open."); if (m_hashChecker->is_checking()) { if (hash == NULL) { m_hashChecker->receive_chunk_cleared(handle.index()); } else { if (std::memcmp(hash, chunk_hash(handle.index()), 20) == 0) m_main->file_list()->mark_completed(handle.index()); m_hashChecker->receive_chunkdone(handle.index()); } m_main->chunk_list()->release(&handle, ChunkList::get_dont_log); return; } // If hash == NULL we're clearing the queue, so do nothing. if (hash != NULL) { if (!m_hashChecker->is_checked()) throw internal_error("DownloadWrapper::receive_hash_done(...) Was not expecting non-NULL hash."); // Receiving chunk hashes after stopping the torrent should be // safe. if (data()->untouched_bitfield()->get(handle.index())) throw internal_error("DownloadWrapper::receive_hash_done(...) received a chunk that isn't set in ChunkSelector."); if (std::memcmp(hash, chunk_hash(handle.index()), 20) == 0) { m_main->file_list()->mark_completed(handle.index()); m_main->delegator()->transfer_list()->hash_succeeded(handle.index(), handle.chunk()); m_main->update_endgame(); if (m_main->file_list()->is_done()) finished_download(); if (!m_main->have_queue()->empty() && m_main->have_queue()->front().first >= cachedTime) m_main->have_queue()->push_front(DownloadMain::have_queue_type::value_type(m_main->have_queue()->front().first + 1, handle.index())); else m_main->have_queue()->push_front(DownloadMain::have_queue_type::value_type(cachedTime, handle.index())); } else { // This needs to ensure the chunk is still valid. m_main->delegator()->transfer_list()->hash_failed(handle.index(), handle.chunk()); } } m_main->chunk_list()->release(&handle); } void DownloadWrapper::check_chunk_hash(ChunkHandle handle) { // TODO: Hack... ChunkHandle new_handle = m_main->chunk_list()->get(handle.index(), ChunkList::get_blocking); m_main->chunk_list()->release(&handle); hash_queue()->push_back(new_handle, data(), tr1::bind(&DownloadWrapper::receive_hash_done, this, tr1::placeholders::_1, tr1::placeholders::_2)); } void DownloadWrapper::receive_storage_error(const std::string& str) { m_main->stop(); close(); m_main->tracker_controller()->disable(); m_main->tracker_controller()->close(); LT_LOG_STORAGE_ERRORS("%s", str.c_str()); } uint32_t DownloadWrapper::receive_tracker_success(AddressList* l) { uint32_t inserted = m_main->peer_list()->insert_available(l); m_main->receive_connect_peers(); m_main->receive_tracker_success(); rak::slot_list_call(info()->signal_tracker_success()); return inserted; } void DownloadWrapper::receive_tracker_failed(const std::string& msg) { rak::slot_list_call(info()->signal_tracker_failed(), msg); } void DownloadWrapper::receive_tick(uint32_t ticks) { // Trigger culling of PeerInfo's every hour. This should be called // before the is_open check to ensure that stopped torrents reduce // their memory usage. if (ticks % 120 == 0) // if (ticks % 1 == 0) m_main->peer_list()->cull_peers(PeerList::cull_old | PeerList::cull_keep_interesting); if (!info()->is_open()) return; // Every 2 minutes. if (ticks % 4 == 0) { if (info()->is_active()) { if (info()->is_pex_enabled()) { m_main->do_peer_exchange(); // If PEX was disabled since the last peer exchange, deactivate it now. } else if (info()->is_pex_active()) { info()->unset_flags(DownloadInfo::flag_pex_active); for (ConnectionList::iterator itr = m_main->connection_list()->begin(); itr != m_main->connection_list()->end(); ++itr) (*itr)->m_ptr()->set_peer_exchange(false); } } for (ConnectionList::iterator itr = m_main->connection_list()->begin(); itr != m_main->connection_list()->end(); ) if (!(*itr)->m_ptr()->receive_keepalive()) itr = m_main->connection_list()->erase(itr, ConnectionList::disconnect_available); else itr++; } DownloadMain::have_queue_type* haveQueue = m_main->have_queue(); haveQueue->erase(std::find_if(haveQueue->rbegin(), haveQueue->rend(), rak::less(cachedTime - rak::timer::from_seconds(600), rak::mem_ref(&DownloadMain::have_queue_type::value_type::first))).base(), haveQueue->end()); m_main->receive_connect_peers(); } void DownloadWrapper::receive_update_priorities() { if (m_main->chunk_selector()->empty()) return; data()->mutable_high_priority()->clear(); data()->mutable_normal_priority()->clear(); for (FileList::iterator itr = m_main->file_list()->begin(); itr != m_main->file_list()->end(); ++itr) { switch ((*itr)->priority()) { case PRIORITY_NORMAL: { File::range_type range = (*itr)->range(); if ((*itr)->has_flags(File::flag_prioritize_first) && range.first != range.second) { data()->mutable_high_priority()->insert(range.first, range.first + 1); range.first++; } if ((*itr)->has_flags(File::flag_prioritize_last) && range.first != range.second) { data()->mutable_high_priority()->insert(range.second - 1, range.second); range.second--; } data()->mutable_normal_priority()->insert(range); break; } case PRIORITY_HIGH: data()->mutable_high_priority()->insert((*itr)->range().first, (*itr)->range().second); break; default: break; } } data()->update_wanted_chunks(); m_main->chunk_selector()->update_priorities(); std::for_each(m_main->connection_list()->begin(), m_main->connection_list()->end(), rak::on(std::mem_fun(&Peer::m_ptr), std::mem_fun(&PeerConnectionBase::update_interested))); // TODO: Trigger event if this results in the torrent being // partially finished, or going from partially finished to needing // to download more. } void DownloadWrapper::finished_download() { // We delay emitting the signal to allow the delegator to // clean up. If we do a straight call it would cause problems // for clients that wish to close and reopen the torrent, as // HashQueue, Delegator etc shouldn't be cleaned up at this // point. // // This needs to be seperated into a new function. if (!m_main->delay_download_done().is_queued()) priority_queue_insert(&taskScheduler, &m_main->delay_download_done(), cachedTime); m_main->connection_list()->erase_seeders(); info()->mutable_down_rate()->reset_rate(); } } libtorrent-0.13.6/src/download/download_wrapper.h000066400000000000000000000110231257211073700221050ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_WRAPPER_H #define LIBTORRENT_DOWNLOAD_WRAPPER_H #include "data/chunk_handle.h" #include "download_main.h" namespace torrent { // Remember to clean up the pointers, DownloadWrapper won't do it. class AddressList; class FileManager; class HashQueue; class HashTorrent; class HandshakeManager; class DownloadInfo; class Object; class Peer; class DownloadWrapper { public: DownloadWrapper(); ~DownloadWrapper(); DownloadInfo* info() { return m_main->info(); } download_data* data() { return m_main->file_list()->mutable_data(); } FileList* file_list() { return m_main->file_list(); } ChunkList* chunk_list() { return m_main->chunk_list(); } // Initialize hash checker and various download stuff. void initialize(const std::string& hash, const std::string& id); void close(); bool is_stopped() const; DownloadMain* main() { return m_main; } const DownloadMain* main() const { return m_main; } HashTorrent* hash_checker() { return m_hashChecker; } Object* bencode() { return m_bencode; } void set_bencode(Object* o) { m_bencode = o; } HashQueue* hash_queue() { return m_hashQueue; } void set_hash_queue(HashQueue* q) { m_hashQueue = q; } const std::string& complete_hash() { return m_hash; } const char* chunk_hash(unsigned int index) { return m_hash.c_str() + 20 * index; } void set_complete_hash(const std::string& hash) { m_hash = hash; } int connection_type() const { return m_connectionType; } void set_connection_type(int t) { m_connectionType = t; } // // Internal: // void receive_initial_hash(); void receive_hash_done(ChunkHandle handle, const char* hash); void check_chunk_hash(ChunkHandle handle); void receive_storage_error(const std::string& str); uint32_t receive_tracker_success(AddressList* l); void receive_tracker_failed(const std::string& msg); void receive_tick(uint32_t ticks); void receive_update_priorities(); private: DownloadWrapper(const DownloadWrapper&); void operator = (const DownloadWrapper&); void finished_download(); DownloadMain* m_main; Object* m_bencode; HashTorrent* m_hashChecker; HashQueue* m_hashQueue; std::string m_hash; int m_connectionType; }; } #endif libtorrent-0.13.6/src/globals.cc000066400000000000000000000035131257211073700165150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "globals.h" #include "torrent/common.h" namespace torrent { LIBTORRENT_EXPORT rak::priority_queue_default taskScheduler; LIBTORRENT_EXPORT rak::timer cachedTime; } libtorrent-0.13.6/src/globals.h000066400000000000000000000035611257211073700163620ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_GLOBALS_H #define LIBTORRENT_GLOBALS_H #include #include namespace torrent { extern rak::priority_queue_default taskScheduler; extern rak::timer cachedTime; } #endif libtorrent-0.13.6/src/manager.cc000066400000000000000000000147541257211073700165150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "download/download_wrapper.h" #include "download/download_main.h" #include "data/hash_torrent.h" #include "data/chunk_list.h" #include "protocol/handshake_manager.h" #include "data/hash_queue.h" #include "net/listen.h" #include "utils/instrumentation.h" #include "torrent/chunk_manager.h" #include "torrent/connection_manager.h" #include "torrent/dht_manager.h" #include "torrent/data/file_manager.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "torrent/download/download_manager.h" #include "torrent/download/resource_manager.h" #include "torrent/peer/client_list.h" #include "torrent/throttle.h" #include "manager.h" namespace tr1 { using namespace std::tr1; } namespace torrent { Manager* manager = NULL; Manager::Manager() : m_downloadManager(new DownloadManager), m_fileManager(new FileManager), m_handshakeManager(new HandshakeManager), m_resourceManager(new ResourceManager), m_chunkManager(new ChunkManager), m_clientList(new ClientList), m_connectionManager(new ConnectionManager), m_dhtManager(new DhtManager), m_uploadThrottle(Throttle::create_throttle()), m_downloadThrottle(Throttle::create_throttle()), m_ticks(0) { m_hashQueue = new HashQueue(&m_main_thread_disk); m_hashQueue->slot_has_work() = tr1::bind(&thread_base::send_event_signal, &m_main_thread_main, m_main_thread_main.signal_bitfield()->add_signal(tr1::bind(&HashQueue::work, m_hashQueue)), tr1::placeholders::_1); m_taskTick.slot() = std::tr1::bind(&Manager::receive_tick, this); priority_queue_insert(&taskScheduler, &m_taskTick, cachedTime.round_seconds()); m_handshakeManager->slot_download_id() = std::tr1::bind(&DownloadManager::find_main, m_downloadManager, std::tr1::placeholders::_1); m_handshakeManager->slot_download_obfuscated() = std::tr1::bind(&DownloadManager::find_main_obfuscated, m_downloadManager, std::tr1::placeholders::_1); m_connectionManager->listen()->slot_accepted() = std::tr1::bind(&HandshakeManager::add_incoming, m_handshakeManager, std::tr1::placeholders::_1, std::tr1::placeholders::_2); // m_resourceManager->push_group("default"); // m_resourceManager->group_back()->up_queue()->set_heuristics(choke_queue::HEURISTICS_UPLOAD_LEECH); // m_resourceManager->group_back()->down_queue()->set_heuristics(choke_queue::HEURISTICS_DOWNLOAD_LEECH); } Manager::~Manager() { priority_queue_erase(&taskScheduler, &m_taskTick); m_handshakeManager->clear(); m_downloadManager->clear(); delete m_downloadManager; delete m_fileManager; delete m_handshakeManager; delete m_hashQueue; delete m_resourceManager; delete m_dhtManager; delete m_connectionManager; delete m_chunkManager; delete m_clientList; Throttle::destroy_throttle(m_uploadThrottle); Throttle::destroy_throttle(m_downloadThrottle); instrumentation_tick(); } void Manager::initialize_download(DownloadWrapper* d) { d->main()->slot_count_handshakes(rak::make_mem_fun(m_handshakeManager, &HandshakeManager::size_info)); d->main()->slot_start_handshake(rak::make_mem_fun(m_handshakeManager, &HandshakeManager::add_outgoing)); d->main()->slot_stop_handshakes(rak::make_mem_fun(m_handshakeManager, &HandshakeManager::erase_download)); // TODO: The resource manager doesn't need to know about this // download until we start/stop the torrent. m_downloadManager->insert(d); m_resourceManager->insert(d->main(), 1); m_chunkManager->insert(d->main()->chunk_list()); d->main()->chunk_list()->set_chunk_size(d->main()->file_list()->chunk_size()); d->main()->set_upload_throttle(m_uploadThrottle->throttle_list()); d->main()->set_download_throttle(m_downloadThrottle->throttle_list()); } void Manager::cleanup_download(DownloadWrapper* d) { d->main()->stop(); d->close(); m_resourceManager->erase(d->main()); m_chunkManager->erase(d->main()->chunk_list()); m_downloadManager->erase(d); } void Manager::receive_tick() { m_ticks++; if (m_ticks % 2 == 0) instrumentation_tick(); m_resourceManager->receive_tick(); m_chunkManager->periodic_sync(); // To ensure the downloads get equal chance over time at using // various limited resources, like sockets for handshakes, cycle the // group in reverse order. if (!m_downloadManager->empty()) { DownloadManager::iterator split = m_downloadManager->end() - m_ticks % m_downloadManager->size() - 1; std::for_each(split, m_downloadManager->end(), std::bind2nd(std::mem_fun(&DownloadWrapper::receive_tick), m_ticks)); std::for_each(m_downloadManager->begin(), split, std::bind2nd(std::mem_fun(&DownloadWrapper::receive_tick), m_ticks)); } // If you change the interval, make sure the keepalives gets // triggered every 120 seconds. priority_queue_insert(&taskScheduler, &m_taskTick, (cachedTime + rak::timer::from_seconds(30)).round_seconds()); } } libtorrent-0.13.6/src/manager.h000066400000000000000000000106051257211073700163460ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_MANAGER_H #define LIBTORRENT_MANAGER_H #include #include #include #include "thread_disk.h" #include "thread_main.h" #include "net/socket_fd.h" namespace torrent { class Poll; class HashQueue; class HandshakeManager; class DownloadManager; class DownloadWrapper; class DownloadMain; class FileManager; class ResourceManager; class PeerInfo; class ChunkManager; class ConnectionManager; class Throttle; class DhtManager; typedef std::list EncodingList; class Manager { public: Manager(); ~Manager(); DownloadManager* download_manager() { return m_downloadManager; } FileManager* file_manager() { return m_fileManager; } HandshakeManager* handshake_manager() { return m_handshakeManager; } HashQueue* hash_queue() { return m_hashQueue; } ResourceManager* resource_manager() { return m_resourceManager; } ChunkManager* chunk_manager() { return m_chunkManager; } ClientList* client_list() { return m_clientList; } ConnectionManager* connection_manager() { return m_connectionManager; } DhtManager* dht_manager() { return m_dhtManager; } Poll* poll() { return m_main_thread_main.poll(); } thread_main* main_thread_main() { return &m_main_thread_main; } thread_disk* main_thread_disk() { return &m_main_thread_disk; } EncodingList* encoding_list() { return &m_encodingList; } Throttle* upload_throttle() { return m_uploadThrottle; } Throttle* download_throttle() { return m_downloadThrottle; } void initialize_download(DownloadWrapper* d); void cleanup_download(DownloadWrapper* d); void receive_tick(); private: DownloadManager* m_downloadManager; FileManager* m_fileManager; HandshakeManager* m_handshakeManager; HashQueue* m_hashQueue; ResourceManager* m_resourceManager; ChunkManager* m_chunkManager; ClientList* m_clientList; ConnectionManager* m_connectionManager; DhtManager* m_dhtManager; thread_main m_main_thread_main; thread_disk m_main_thread_disk; EncodingList m_encodingList; Throttle* m_uploadThrottle; Throttle* m_downloadThrottle; unsigned int m_ticks; rak::priority_item m_taskTick; }; extern Manager* manager; } #endif libtorrent-0.13.6/src/net/000077500000000000000000000000001257211073700153475ustar00rootroot00000000000000libtorrent-0.13.6/src/net/Makefile.am000066400000000000000000000007771257211073700174160ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_net.la libsub_net_la_SOURCES = \ address_list.cc \ address_list.h \ data_buffer.h \ listen.cc \ listen.h \ protocol_buffer.h \ socket_base.cc \ socket_base.h \ socket_datagram.cc \ socket_datagram.h \ socket_fd.cc \ socket_fd.h \ socket_set.cc \ socket_set.h \ socket_stream.cc \ socket_stream.h \ throttle_internal.cc \ throttle_internal.h \ throttle_list.cc \ throttle_list.h \ throttle_node.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/net/address_list.cc000066400000000000000000000063341257211073700203440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "address_list.h" namespace torrent { inline rak::socket_address AddressList::parse_address(const Object& b) { rak::socket_address sa; sa.clear(); if (!b.is_map()) return sa; if (!b.has_key_string("ip") || !sa.set_address_str(b.get_key_string("ip"))) return sa; if (!b.has_key_value("port") || b.get_key_value("port") <= 0 || b.get_key_value("port") >= (1 << 16)) return sa; sa.set_port(b.get_key_value("port")); return sa; } void AddressList::parse_address_normal(const Object::list_type& b) { std::for_each(b.begin(), b.end(), rak::on(std::ptr_fun(&AddressList::parse_address), AddressList::add_address(this))); } void AddressList::parse_address_compact(raw_string s) { if (sizeof(const SocketAddressCompact) != 6) throw internal_error("ConnectionList::AddressList::parse_address_compact(...) bad struct size."); std::copy(reinterpret_cast(s.data()), reinterpret_cast(s.data() + s.size() - s.size() % sizeof(SocketAddressCompact)), std::back_inserter(*this)); } void AddressList::parse_address_bencode(raw_list s) { if (sizeof(const SocketAddressCompact) != 6) throw internal_error("AddressList::parse_address_bencode(...) bad struct size."); for (raw_list::const_iterator itr = s.begin(); itr + 2 + sizeof(SocketAddressCompact) <= s.end(); itr += sizeof(SocketAddressCompact)) { if (*itr++ != '6' || *itr++ != ':') break; insert(end(), *reinterpret_cast(s.data())); } } } libtorrent-0.13.6/src/net/address_list.h000066400000000000000000000065661257211073700202150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_ADDRESS_LIST_H #define LIBTORRENT_DOWNLOAD_ADDRESS_LIST_H #include #include #include #include #include namespace torrent { class AddressList : public std::list { public: // Parse normal or compact list of addresses and add to AddressList void parse_address_normal(const Object::list_type& b); void parse_address_bencode(raw_list s); void parse_address_compact(raw_string s); void parse_address_compact(const std::string& s); private: static rak::socket_address parse_address(const Object& b); struct add_address : public std::unary_function { add_address(AddressList* l) : m_list(l) {} void operator () (const rak::socket_address& sa) const { if (!sa.is_valid()) return; m_list->push_back(sa); } AddressList* m_list; }; }; inline void AddressList::parse_address_compact(const std::string& s) { return parse_address_compact(raw_string(s.data(), s.size())); } // Move somewhere else. struct SocketAddressCompact { SocketAddressCompact() {} SocketAddressCompact(uint32_t a, uint16_t p) : addr(a), port(p) {} SocketAddressCompact(const rak::socket_address_inet* sa) : addr(sa->address_n()), port(sa->port_n()) {} operator rak::socket_address () const { rak::socket_address sa; sa.sa_inet()->clear(); sa.sa_inet()->set_port_n(port); sa.sa_inet()->set_address_n(addr); return sa; } uint32_t addr; uint16_t port; const char* c_str() const { return reinterpret_cast(this); } } __attribute__ ((packed)); } #endif libtorrent-0.13.6/src/net/data_buffer.h000066400000000000000000000061411257211073700177640ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_DATA_BUFFER_H #define LIBTORRENT_NET_DATA_BUFFER_H #include #include namespace torrent { // Recipient must call clear() when done with the buffer. struct DataBuffer { DataBuffer() : m_data(NULL), m_end(NULL), m_owned(true) {} DataBuffer(char* data, char* end) : m_data(data), m_end(end), m_owned(true) {} DataBuffer clone() const { DataBuffer d = *this; d.m_owned = false; return d; } DataBuffer release() { DataBuffer d = *this; set(NULL, NULL, false); return d; } char* data() const { return m_data; } char* end() const { return m_end; } bool owned() const { return m_owned; } bool empty() const { return m_data == NULL; } size_t length() const { return m_end - m_data; } void clear(); void set(char* data, char* end, bool owned); private: char* m_data; char* m_end; // Used to indicate if buffer held by PCB is its own and needs to be // deleted after transmission (false if shared with other connections). bool m_owned; }; inline void DataBuffer::clear() { if (!empty() && m_owned) delete[] m_data; m_data = m_end = NULL; m_owned = false; } inline void DataBuffer::set(char* data, char* end, bool owned) { m_data = data; m_end = end; m_owned = owned; } } #endif libtorrent-0.13.6/src/net/listen.cc000066400000000000000000000076631257211073700171700ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include #include "torrent/exceptions.h" #include "torrent/connection_manager.h" #include "torrent/poll.h" #include "torrent/utils/log.h" #include "listen.h" #include "manager.h" namespace torrent { bool Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_address* bindAddress) { close(); if (first == 0 || first > last) throw input_error("Tried to open listening port with an invalid range."); if (bindAddress->family() != rak::socket_address::af_inet && bindAddress->family() != rak::socket_address::af_inet6) throw input_error("Listening socket must be bound to an inet or inet6 address."); if (!get_fd().open_stream() || !get_fd().set_nonblock() || !get_fd().set_reuse_address(true)) throw resource_error("Could not allocate socket for listening."); rak::socket_address sa; sa.copy(*bindAddress, bindAddress->length()); do { sa.set_port(first); if (get_fd().bind(sa) && get_fd().listen(backlog)) { m_port = first; manager->connection_manager()->inc_socket_count(); manager->poll()->open(this); manager->poll()->insert_read(this); manager->poll()->insert_error(this); lt_log_print(LOG_CONNECTION_INFO, "listen port %" PRIu16 " opened with backlog set to %i", m_port, backlog); return true; } } while (first++ < last); // This needs to be done if local_error is thrown too... get_fd().close(); get_fd().clear(); lt_log_print(LOG_CONNECTION_INFO, "failed to open listen port"); return false; } void Listen::close() { if (!get_fd().is_valid()) return; manager->poll()->remove_read(this); manager->poll()->remove_error(this); manager->poll()->close(this); manager->connection_manager()->dec_socket_count(); get_fd().close(); get_fd().clear(); m_port = 0; } void Listen::event_read() { rak::socket_address sa; SocketFd fd; while ((fd = get_fd().accept(&sa)).is_valid()) m_slot_accepted(fd, sa); } void Listen::event_write() { throw internal_error("Listener does not support write()."); } void Listen::event_error() { throw internal_error("Listener port received an error event."); } } libtorrent-0.13.6/src/net/listen.h000066400000000000000000000051121257211073700170150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_LISTEN_H #define LIBTORRENT_LISTEN_H #include #include #include #include "socket_base.h" #include "socket_fd.h" namespace torrent { class Listen : public SocketBase { public: typedef std::tr1::function slot_connection; Listen() : m_port(0) {} ~Listen() { close(); } bool open(uint16_t first, uint16_t last, int backlog, const rak::socket_address* bindAddress); void close(); bool is_open() const { return get_fd().is_valid(); } uint16_t port() const { return m_port; } slot_connection& slot_accepted() { return m_slot_accepted; } virtual void event_read(); virtual void event_write(); virtual void event_error(); private: uint64_t m_port; slot_connection m_slot_accepted; }; } // namespace torrent #endif // LIBTORRENT_TORRENT_H libtorrent-0.13.6/src/net/protocol_buffer.h000066400000000000000000000212131257211073700207110ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_PROTOCOL_BUFFER_H #define LIBTORRENT_NET_PROTOCOL_BUFFER_H #include #include #include #include "torrent/exceptions.h" namespace torrent { template class ProtocolBuffer { public: typedef uint8_t value_type; typedef value_type* iterator; typedef uint16_t size_type; typedef int16_t difference_type; void reset() { m_position = m_end = begin(); } void reset_position() { m_position = m_buffer; } bool consume(difference_type v); void set_position_itr(iterator itr) { m_position = itr; } void set_end(size_type v) { m_end = m_buffer + v; } void set_end_itr(iterator itr) { m_end = itr; } difference_type move_end(difference_type v) { m_end += v; return v; } uint8_t read_8() { return *m_position++; } uint8_t peek_8() { return *m_position; } uint16_t read_16(); uint16_t peek_16(); uint32_t read_32(); uint32_t peek_32(); uint64_t read_64() { return read_int(); } uint64_t peek_64() { return peek_int(); } uint8_t peek_8_at(size_type pos) { return *(m_position + pos); } template void read_range(Out first, Out last); template void read_len(Out start, unsigned int len); template inline T read_int(); template inline T peek_int(); void write_8(uint8_t v) { *m_end++ = v; validate_end(); } void write_16(uint16_t v); void write_32(uint32_t v); void write_32_n(uint32_t v); void write_64(uint64_t v) { write_int(v); } template inline void write_int(T t); template void write_range(In first, In last); template void write_len(In start, unsigned int len); iterator begin() { return m_buffer; } iterator position() { return m_position; } iterator end() { return m_end; } size_type size_position() const { return m_position - m_buffer; } size_type size_end() const { return m_end - m_buffer; } size_type remaining() const { return m_end - m_position; } size_type reserved() const { return tmpl_size; } size_type reserved_left() const { return reserved() - size_end(); } void move_unused(); void validate_position() const { #ifdef USE_EXTRA_DEBUG if (m_position > m_buffer + tmpl_size) throw internal_error("ProtocolBuffer tried to read beyond scope of the buffer."); if (m_position > m_end) throw internal_error("ProtocolBuffer tried to read beyond end of the buffer."); #endif } void validate_end() const { #ifdef USE_EXTRA_DEBUG if (m_end > m_buffer + tmpl_size) throw internal_error("ProtocolBuffer tried to write beyond scope of the buffer."); #endif } private: iterator m_position; iterator m_end; value_type m_buffer[tmpl_size]; }; template inline bool ProtocolBuffer::consume(difference_type v) { m_position += v; if (remaining()) return false; return true; } template inline uint16_t ProtocolBuffer::read_16() { #ifndef USE_ALIGNED uint16_t t = ntohs(*reinterpret_cast(m_position)); m_position += sizeof(uint16_t); return t; #else return read_int(); #endif } template inline uint16_t ProtocolBuffer::peek_16() { #ifndef USE_ALIGNED return ntohs(*reinterpret_cast(m_position)); #else return peek_int(); #endif } template inline uint32_t ProtocolBuffer::read_32() { #ifndef USE_ALIGNED uint32_t t = ntohl(*reinterpret_cast(m_position)); m_position += sizeof(uint32_t); return t; #else return read_int(); #endif } template inline uint32_t ProtocolBuffer::peek_32() { #ifndef USE_ALIGNED return ntohl(*reinterpret_cast(m_position)); #else return peek_int(); #endif } template template inline T ProtocolBuffer::read_int() { T t = T(); for (iterator last = m_position + sizeof(T); m_position != last; ++m_position) t = (t << 8) + *m_position; return t; } template template inline T ProtocolBuffer::peek_int() { T t = T(); for (iterator itr = m_position, last = m_position + sizeof(T); itr != last; ++itr) t = (t << 8) + *itr; return t; } template inline void ProtocolBuffer::write_16(uint16_t v) { #ifndef USE_ALIGNED *reinterpret_cast(m_end) = htons(v); m_end += sizeof(uint16_t); validate_end(); #else write_int(v); #endif } template inline void ProtocolBuffer::write_32(uint32_t v) { #ifndef USE_ALIGNED *reinterpret_cast(m_end) = htonl(v); m_end += sizeof(uint32_t); validate_end(); #else write_int(v); #endif } template inline void ProtocolBuffer::write_32_n(uint32_t v) { *reinterpret_cast(m_end) = v; m_end += sizeof(uint32_t); validate_end(); } template template void ProtocolBuffer::read_range(Out first, Out last) { for ( ; first != last; ++m_position, ++first) *first = *m_position; validate_position(); } template template void ProtocolBuffer::read_len(Out start, unsigned int len) { for ( ; len > 0; ++m_position, ++start, --len) *start = *m_position; validate_position(); } template template inline void ProtocolBuffer::write_int(T v) { for (iterator itr = m_end + sizeof(T); itr != m_end; v >>= 8) *(--itr) = v; m_end += sizeof(T); validate_end(); } template template void ProtocolBuffer::write_range(In first, In last) { for ( ; first != last; ++m_end, ++first) *m_end = *first; validate_end(); } template template void ProtocolBuffer::write_len(In start, unsigned int len) { for ( ; len > 0; ++m_end, ++start, --len) *m_end = *start; validate_end(); } template void ProtocolBuffer::move_unused() { std::memmove(begin(), position(), remaining()); set_end(remaining()); reset_position(); } } #endif libtorrent-0.13.6/src/net/socket_base.cc000066400000000000000000000050601257211073700201410ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "torrent/exceptions.h" #include "torrent/poll.h" #include "manager.h" #include "socket_base.h" namespace torrent { char* SocketBase::m_nullBuffer = new char[SocketBase::null_buffer_size]; SocketBase::~SocketBase() { if (get_fd().is_valid()) throw internal_error("SocketBase::~SocketBase() called but m_fd is still valid"); } bool SocketBase::read_oob(void* buffer) { int r = ::recv(m_fileDesc, buffer, 1, MSG_OOB); // if (r < 0) // m_errno = errno; return r == 1; } bool SocketBase::write_oob(const void* buffer) { int r = ::send(m_fileDesc, buffer, 1, MSG_OOB); // if (r < 0) // m_errno = errno; return r == 1; } void SocketBase::receive_throttle_down_activate() { manager->poll()->insert_read(this); } void SocketBase::receive_throttle_up_activate() { manager->poll()->insert_write(this); } } // namespace torrent libtorrent-0.13.6/src/net/socket_base.h000066400000000000000000000053751257211073700200140ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_SOCKET_BASE_H #define LIBTORRENT_NET_SOCKET_BASE_H #include #include #include "torrent/event.h" #include "socket_fd.h" namespace torrent { class SocketBase : public Event { public: SocketBase() { set_fd(SocketFd()); } virtual ~SocketBase(); // Ugly hack... But the alternative is to include SocketFd as part // of the library API or make SocketFd::m_fd into an non-modifiable // value. SocketFd& get_fd() { return *reinterpret_cast(&m_fileDesc); } const SocketFd& get_fd() const { return *reinterpret_cast(&m_fileDesc); } void set_fd(SocketFd fd) { m_fileDesc = fd.get_fd(); } bool read_oob(void* buffer); bool write_oob(const void* buffer); void receive_throttle_down_activate(); void receive_throttle_up_activate(); protected: // Disable copying SocketBase(const SocketBase&); void operator = (const SocketBase&); static const size_t null_buffer_size = 1 << 17; static char* m_nullBuffer; }; } #endif // LIBTORRENT_SOCKET_BASE_H libtorrent-0.13.6/src/net/socket_datagram.cc000066400000000000000000000052011257211073700210040ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "torrent/exceptions.h" #include #include "socket_datagram.h" namespace torrent { int SocketDatagram::read_datagram(void* buffer, unsigned int length, rak::socket_address* sa) { if (length == 0) throw internal_error("Tried to receive buffer length 0"); int r; socklen_t fromlen; if (sa != NULL) { fromlen = sizeof(rak::socket_address); r = ::recvfrom(m_fileDesc, buffer, length, 0, sa->c_sockaddr(), &fromlen); } else { r = ::recv(m_fileDesc, buffer, length, 0); } return r; } int SocketDatagram::write_datagram(const void* buffer, unsigned int length, rak::socket_address* sa) { if (length == 0) throw internal_error("Tried to send buffer length 0"); int r; if (sa != NULL) { r = ::sendto(m_fileDesc, buffer, length, 0, sa->sa_inet()->c_sockaddr(), sizeof(rak::socket_address_inet)); } else { r = ::send(m_fileDesc, buffer, length, 0); } return r; } } libtorrent-0.13.6/src/net/socket_datagram.h000066400000000000000000000041301257211073700206460ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_SOCKET_DGRAM_H #define LIBTORRENT_NET_SOCKET_DGRAM_H #include "socket_base.h" namespace torrent { class SocketDatagram : public SocketBase { public: // TODO: Make two seperate functions depending on whetever sa is // used. int read_datagram(void* buffer, unsigned int length, rak::socket_address* sa = NULL); int write_datagram(const void* buffer, unsigned int length, rak::socket_address* sa = NULL); }; } #endif libtorrent-0.13.6/src/net/socket_fd.cc000066400000000000000000000117061257211073700176240ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "torrent/exceptions.h" #include "socket_fd.h" namespace torrent { inline void SocketFd::check_valid() const { if (!is_valid()) throw internal_error("SocketFd function called on an invalid fd."); } bool SocketFd::set_nonblock() { check_valid(); return fcntl(m_fd, F_SETFL, O_NONBLOCK) == 0; } bool SocketFd::set_priority(priority_type p) { check_valid(); int opt = p; return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0; } bool SocketFd::set_reuse_address(bool state) { check_valid(); int opt = state; return setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0; } bool SocketFd::set_send_buffer_size(uint32_t s) { check_valid(); int opt = s; return setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) == 0; } bool SocketFd::set_receive_buffer_size(uint32_t s) { check_valid(); int opt = s; return setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == 0; } int SocketFd::get_error() const { check_valid(); int err; socklen_t length = sizeof(err); if (getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &err, &length) == -1) throw internal_error("SocketFd::get_error() could not get error"); return err; } bool SocketFd::open_stream() { return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; } bool SocketFd::open_datagram() { return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; } bool SocketFd::open_local() { return (m_fd = socket(rak::socket_address::pf_local, SOCK_STREAM, 0)) != -1; } bool SocketFd::open_socket_pair(int& fd1, int& fd2) { int result[2]; if (socketpair(rak::socket_address::pf_local, SOCK_STREAM, 0, result) == -1) return false; fd1 = result[0]; fd2 = result[1]; return true; } void SocketFd::close() { if (::close(m_fd) && errno == EBADF) throw internal_error("SocketFd::close() called on an invalid file descriptor"); } bool SocketFd::bind(const rak::socket_address& sa) { check_valid(); return !::bind(m_fd, sa.c_sockaddr(), sa.length()); } bool SocketFd::bind(const rak::socket_address& sa, unsigned int length) { check_valid(); return !::bind(m_fd, sa.c_sockaddr(), length); } bool SocketFd::connect(const rak::socket_address& sa) { check_valid(); return !::connect(m_fd, sa.c_sockaddr(), sa.length()) || errno == EINPROGRESS; } bool SocketFd::listen(int size) { check_valid(); return !::listen(m_fd, size); } SocketFd SocketFd::accept(rak::socket_address* sa) { check_valid(); socklen_t len = sizeof(rak::socket_address); return SocketFd(::accept(m_fd, sa != NULL ? sa->c_sockaddr() : NULL, &len)); } // unsigned int // SocketFd::get_read_queue_size() const { // unsigned int v; // if (!is_valid() || ioctl(m_fd, SIOCINQ, &v) < 0) // throw internal_error("SocketFd::get_read_queue_size() could not be performed"); // return v; // } // unsigned int // SocketFd::get_write_queue_size() const { // unsigned int v; // if (!is_valid() || ioctl(m_fd, SIOCOUTQ, &v) < 0) // throw internal_error("SocketFd::get_write_queue_size() could not be performed"); // return v; // } } libtorrent-0.13.6/src/net/socket_fd.h000066400000000000000000000062501257211073700174640ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_SOCKET_FD_H #define LIBTORRENT_NET_SOCKET_FD_H #include namespace rak { class socket_address; } namespace torrent { class SocketFd { public: typedef uint8_t priority_type; SocketFd() : m_fd(-1) {} explicit SocketFd(int fd) : m_fd(fd) {} bool is_valid() const { return m_fd >= 0; } int get_fd() const { return m_fd; } void set_fd(int fd) { m_fd = fd; } bool set_nonblock(); bool set_reuse_address(bool state); bool set_priority(priority_type p); bool set_send_buffer_size(uint32_t s); bool set_receive_buffer_size(uint32_t s); int get_error() const; bool open_stream(); bool open_datagram(); bool open_local(); static bool open_socket_pair(int& fd1, int& fd2); void close(); void clear() { m_fd = -1; } bool bind(const rak::socket_address& sa); bool bind(const rak::socket_address& sa, unsigned int length); bool connect(const rak::socket_address& sa); bool listen(int size); SocketFd accept(rak::socket_address* sa); // unsigned int get_read_queue_size() const; // unsigned int get_write_queue_size() const; private: inline void check_valid() const; int m_fd; }; } #endif libtorrent-0.13.6/src/net/socket_set.cc000066400000000000000000000050451257211073700200250ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "socket_set.h" namespace torrent { const SocketSet::size_type SocketSet::npos; inline void SocketSet::_replace_with_last(size_type idx) { while (!base_type::empty() && base_type::back() == NULL) base_type::pop_back(); if (idx >= m_table.size()) throw internal_error("SocketSet::_replace_with_last(...) input out-of-bounds"); // This should handle both npos and those that have already been // removed with the above while loop. if (idx >= size()) return; *(begin() + idx) = base_type::back(); _index(base_type::back()) = idx; base_type::pop_back(); } void SocketSet::prepare() { std::for_each(m_erased.begin(), m_erased.end(), std::bind1st(std::mem_fun(&SocketSet::_replace_with_last), this)); m_erased.clear(); } void SocketSet::reserve(size_t openMax) { m_table.resize(openMax, npos); base_type::reserve(openMax); } } libtorrent-0.13.6/src/net/socket_set.h000066400000000000000000000104661257211073700176720ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_SOCKET_SET_H #define LIBTORRENT_NET_SOCKET_SET_H #include #include #include #include #include "torrent/exceptions.h" #include "torrent/event.h" namespace torrent { // SocketSet's Base is a vector of active SocketBase // instances. 'm_table' is a vector with the size 'openMax', each // element of which points to an active instance in the Base vector. // Propably should rename to EventSet... class SocketSet : private std::vector > { public: typedef uint32_t size_type; typedef std::vector > base_type; typedef std::vector > Table; static const size_type npos = static_cast(-1); using base_type::value_type; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::empty; using base_type::size; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; bool has(Event* s) const { return _index(s) != npos; } iterator find(Event* s); void insert(Event* s); void erase(Event* s); // Remove all erased elements from the container. void prepare(); // Allocate storage for fd's with up to 'openMax' value. TODO: Remove reserve void reserve(size_t openMax); size_t max_size() const { return m_table.size(); } private: size_type& _index(Event* s) { return m_table[s->file_descriptor()]; } const size_type& _index(Event* s) const { return m_table[s->file_descriptor()]; } inline void _replace_with_last(size_type idx); // TODO: Table of indexes or iterators? Table m_table; Table m_erased; }; inline SocketSet::iterator SocketSet::find(Event* s) { if (_index(s) == npos) return end(); return begin() + _index(s); } inline void SocketSet::insert(Event* s) { if (static_cast(s->file_descriptor()) >= m_table.size()) throw internal_error("Tried to insert an out-of-bounds file descriptor to SocketSet"); if (_index(s) != npos) return; _index(s) = size(); base_type::push_back(s); } inline void SocketSet::erase(Event* s) { if (static_cast(s->file_descriptor()) >= m_table.size()) throw internal_error("Tried to erase an out-of-bounds file descriptor from SocketSet"); size_type idx = _index(s); if (idx == npos) return; _index(s) = npos; *(begin() + idx) = NULL; m_erased.push_back(idx); } } #endif libtorrent-0.13.6/src/net/socket_stream.cc000066400000000000000000000056431257211073700205310ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "socket_stream.h" #include #include #include #include namespace torrent { std::string int_to_string(int v) { char buf[20]; std::sprintf(buf, "%i", v); return buf; } uint32_t SocketStream::read_stream_throws(void* buf, uint32_t length) { int r = read_stream(buf, length); if (r == 0) throw close_connection(); if (r < 0) { if (rak::error_number::current().is_blocked_momentary()) return 0; else if (rak::error_number::current().is_closed()) throw close_connection(); else if (rak::error_number::current().is_blocked_prolonged()) throw blocked_connection(); else throw connection_error(rak::error_number::current().value()); } return r; } uint32_t SocketStream::write_stream_throws(const void* buf, uint32_t length) { int r = write_stream(buf, length); if (r == 0) throw close_connection(); if (r < 0) { if (rak::error_number::current().is_blocked_momentary()) return 0; else if (rak::error_number::current().is_closed()) throw close_connection(); else if (rak::error_number::current().is_blocked_prolonged()) throw blocked_connection(); else throw connection_error(rak::error_number::current().value()); } return r; } } libtorrent-0.13.6/src/net/socket_stream.h000066400000000000000000000066111257211073700203670ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_SOCKET_STREAM_H #define LIBTORRENT_NET_SOCKET_STREAM_H #include #include #include "torrent/exceptions.h" #include "socket_base.h" namespace torrent { class SocketStream : public SocketBase { public: int read_stream(void* buf, uint32_t length); int write_stream(const void* buf, uint32_t length); // Returns the number of bytes read, or zero if the socket is // blocking. On errors or closed sockets it will throw an // appropriate exception. uint32_t read_stream_throws(void* buf, uint32_t length); uint32_t write_stream_throws(const void* buf, uint32_t length); // Handles all the error catching etc. Returns true if the buffer is // finished reading/writing. bool read_buffer(void* buf, uint32_t length, uint32_t& pos); bool write_buffer(const void* buf, uint32_t length, uint32_t& pos); uint32_t ignore_stream_throws(uint32_t length) { return read_stream_throws(m_nullBuffer, length); } }; inline bool SocketStream::read_buffer(void* buf, uint32_t length, uint32_t& pos) { pos += read_stream_throws(buf, length - pos); return pos == length; } inline bool SocketStream::write_buffer(const void* buf, uint32_t length, uint32_t& pos) { pos += write_stream_throws(buf, length - pos); return pos == length; } inline int SocketStream::read_stream(void* buf, uint32_t length) { if (length == 0) throw internal_error("Tried to read to buffer length 0."); return ::recv(m_fileDesc, buf, length, 0); } inline int SocketStream::write_stream(const void* buf, uint32_t length) { if (length == 0) throw internal_error("Tried to write to buffer length 0."); return ::send(m_fileDesc, buf, length, 0); } } #endif libtorrent-0.13.6/src/net/throttle_handle.h000066400000000000000000000033771257211073700207120ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2007, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_THROTTLE_HANDLE_H #define LIBTORRENT_NET_THROTTLE_HANDLE_H namespace torrent { class ThrottleHandle { public: libtorrent-0.13.6/src/net/throttle_internal.cc000066400000000000000000000122171257211073700214220ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "net/throttle_internal.h" #include "net/throttle_list.h" #include "torrent/exceptions.h" #include "torrent/throttle.h" #include "globals.h" namespace torrent { // Plans: // // Make ThrottleList do a callback when it needs more? This would // allow us to remove us from the task scheduler when we're full. Also // this would let us be abit more flexible with the interval. ThrottleInternal::ThrottleInternal(int flags) : m_flags(flags), m_nextSlave(m_slaveList.end()), m_unusedQuota(0), m_timeLastTick(cachedTime) { if (is_root()) m_taskTick.slot() = std::tr1::bind(&ThrottleInternal::receive_tick, this); } ThrottleInternal::~ThrottleInternal() { if (is_root()) priority_queue_erase(&taskScheduler, &m_taskTick); std::for_each(m_slaveList.begin(), m_slaveList.end(), rak::call_delete()); } void ThrottleInternal::enable() { m_throttleList->enable(); std::for_each(m_slaveList.begin(), m_slaveList.end(), std::mem_fun(&ThrottleInternal::enable)); if (is_root()) { // We need to start the ticks, and make sure we set timeLastTick // to a value that gives an reasonable initial quota. m_timeLastTick = cachedTime - rak::timer::from_seconds(1); receive_tick(); } } void ThrottleInternal::disable() { m_throttleList->disable(); std::for_each(m_slaveList.begin(), m_slaveList.end(), std::mem_fun(&ThrottleInternal::disable)); if (is_root()) priority_queue_erase(&taskScheduler, &m_taskTick); } ThrottleInternal* ThrottleInternal::create_slave() { ThrottleInternal* slave = new ThrottleInternal(flag_none); slave->m_maxRate = m_maxRate; slave->m_throttleList = new ThrottleList(); if (m_throttleList->is_enabled()) slave->enable(); m_slaveList.push_back(slave); m_nextSlave = m_slaveList.end(); return slave; } void ThrottleInternal::receive_tick() { if (cachedTime <= m_timeLastTick + rak::timer::from_milliseconds(90)) throw internal_error("ThrottleInternal::receive_tick() called at a to short interval."); uint32_t quota = ((uint64_t)(cachedTime.usec() - m_ptr()->m_timeLastTick.usec())) * m_maxRate / 1000000; uint32_t fraction = ((uint64_t)(cachedTime.usec() - m_ptr()->m_timeLastTick.usec())) * fraction_base / 1000000; receive_quota(quota, fraction); priority_queue_insert(&taskScheduler, &m_taskTick, cachedTime + calculate_interval()); m_timeLastTick = cachedTime; } int32_t ThrottleInternal::receive_quota(uint32_t quota, uint32_t fraction) { uint32_t need; m_unusedQuota += quota; while (m_nextSlave != m_slaveList.end()) { need = std::min(quota, (uint64_t)fraction * (*m_nextSlave)->max_rate() >> fraction_bits); if (m_unusedQuota < need) break; m_unusedQuota -= (*m_nextSlave)->receive_quota(need, fraction); m_throttleList->add_rate((*m_nextSlave)->throttle_list()->rate_added()); ++m_nextSlave; } need = std::min(quota, (uint64_t)fraction * m_maxRate >> fraction_bits); if (m_nextSlave == m_slaveList.end() && m_unusedQuota >= need) { m_unusedQuota -= m_throttleList->update_quota(need); m_nextSlave = m_slaveList.begin(); } // Return how much quota we used, but keep as much as one // allocation's worth until the next tick to avoid rounding errors. int32_t used = quota; if (m_unusedQuota > quota) { used -= m_unusedQuota - quota; m_unusedQuota = quota; } // Amount used may be negative if rate changed between last tick and now. return used; } } libtorrent-0.13.6/src/net/throttle_internal.h000066400000000000000000000056161257211073700212710ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_THROTTLE_INTERNAL_H #define LIBTORRENT_NET_THROTTLE_INTERNAL_H #include #include #include "torrent/common.h" #include "torrent/throttle.h" namespace torrent { class ThrottleInternal : public Throttle { public: static const int flag_none = 0; static const int flag_root = 1; ThrottleInternal(int flags); ~ThrottleInternal(); ThrottleInternal* create_slave(); bool is_root() { return m_flags & flag_root; } void enable(); void disable(); private: // Fraction is a fixed-precision value with the given number of bits after the decimal point. static const uint32_t fraction_bits = 16; static const uint32_t fraction_base = (1 << fraction_bits); typedef std::vector SlaveList; void receive_tick(); // Distribute quota, return amount of quota used. May be negative // if it had more unused quota than is now allowed. int32_t receive_quota(uint32_t quota, uint32_t fraction); int m_flags; SlaveList m_slaveList; SlaveList::iterator m_nextSlave; uint32_t m_unusedQuota; rak::timer m_timeLastTick; rak::priority_item m_taskTick; }; } #endif libtorrent-0.13.6/src/net/throttle_list.cc000066400000000000000000000174141257211073700205650ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "throttle_list.h" #include "throttle_node.h" namespace torrent { ThrottleList::ThrottleList() : m_enabled(false), m_size(0), m_outstandingQuota(0), m_unallocatedQuota(0), m_unusedUnthrottledQuota(0), m_rateAdded(0), m_minChunkSize(2 << 10), m_maxChunkSize(16 << 10), m_rateSlow(60), m_splitActive(end()) { } bool ThrottleList::is_active(const ThrottleNode* node) const { return std::find(begin(), (const_iterator)m_splitActive, node) != m_splitActive; } bool ThrottleList::is_inactive(const ThrottleNode* node) const { return std::find((const_iterator)m_splitActive, end(), node) != end(); } bool ThrottleList::is_throttled(const ThrottleNode* node) const { return node->list_iterator() != end(); } // The quota already present in the node is preserved and unallocated // quota is transferred to the node. The node's quota will be less // than or equal to 'm_minChunkSize'. inline void ThrottleList::allocate_quota(ThrottleNode* node) { if (node->quota() >= m_minChunkSize) return; int quota = std::min(m_maxChunkSize - node->quota(), m_unallocatedQuota); node->set_quota(node->quota() + quota); m_outstandingQuota += quota; m_unallocatedQuota -= quota; } void ThrottleList::enable() { if (m_enabled) return; m_enabled = true; if (!empty() && m_splitActive == begin()) throw internal_error("ThrottleList::enable() m_splitActive is invalid."); } void ThrottleList::disable() { if (!m_enabled) return; m_enabled = false; m_outstandingQuota = 0; m_unallocatedQuota = 0; m_unusedUnthrottledQuota = 0; std::for_each(begin(), end(), std::mem_fun(&ThrottleNode::clear_quota)); std::for_each(m_splitActive, end(), std::mem_fun(&ThrottleNode::activate)); m_splitActive = end(); } int32_t ThrottleList::update_quota(uint32_t quota) { if (!m_enabled) throw internal_error("ThrottleList::update_quota(...) called but the object is not enabled."); // Distribute new quota to unthrottled quota first, and use // left-over unthrottled quota from last turn to be allocated // to throttled nodes this turn. // When distributing, we include the unallocated quota from the // previous turn. This will ensure that quota that was reclaimed // will have a chance of being used, even by those nodes that were // deactivated. m_unallocatedQuota += m_unusedUnthrottledQuota; m_unusedUnthrottledQuota = quota; // Add remaining to the next, even when less than activate border. while (m_splitActive != end()) { allocate_quota(*m_splitActive); if ((*m_splitActive)->quota() < m_minChunkSize) break; (*m_splitActive)->activate(); m_splitActive++; } // Use 'quota' as an upper bound to avoid accumulating unused quota // over time. Return actually used amount of quota. int32_t used = quota; if (m_unallocatedQuota > quota) { used -= m_unallocatedQuota - quota; m_unallocatedQuota = quota; } return used; } uint32_t ThrottleList::node_quota(ThrottleNode* node) { if (!m_enabled) { // Returns max for signed integer to ensure we don't overflow // claculations. return std::numeric_limits::max(); } else if (!is_active(node)) { throw internal_error(is_inactive(node) ? "ThrottleList::node_quota(...) called on an inactive node." : "ThrottleList::node_quota(...) could not find node."); } else if (node->quota() + m_unallocatedQuota >= m_minChunkSize) { return node->quota() + m_unallocatedQuota; } else { return 0; } } void ThrottleList::add_rate(uint32_t used) { m_rateSlow.insert(used); m_rateAdded += used; } uint32_t ThrottleList::node_used(ThrottleNode* node, uint32_t used) { add_rate(used); node->rate()->insert(used); if (used == 0 || !m_enabled || node->list_iterator() == end()) return used; uint32_t quota = std::min(used, node->quota()); if (quota > m_outstandingQuota) throw internal_error("ThrottleList::node_used(...) used too much quota."); node->set_quota(node->quota() - quota); m_outstandingQuota -= quota; m_unallocatedQuota -= std::min(used - quota, m_unallocatedQuota); return used; } uint32_t ThrottleList::node_used_unthrottled(uint32_t used) { add_rate(used); // Use what we can from the unthrottled quota, // if node used too much borrow from throttled quota. uint32_t avail = std::min(used, m_unusedUnthrottledQuota); m_unusedUnthrottledQuota -= avail; m_unallocatedQuota -= std::min(used - avail, m_unallocatedQuota); return used; } void ThrottleList::node_deactivate(ThrottleNode* node) { if (!is_active(node)) throw internal_error(is_inactive(node) ? "ThrottleList::node_deactivate(...) called on an inactive node." : "ThrottleList::node_deactivate(...) could not find node."); base_type::splice(end(), *this, node->list_iterator()); if (m_splitActive == end()) m_splitActive = node->list_iterator(); } void ThrottleList::insert(ThrottleNode* node) { if (node->list_iterator() != end()) return; if (!m_enabled) { // Add to waiting queue. node->set_list_iterator(base_type::insert(end(), node)); node->clear_quota(); } else { // Add before the active split, so if we only need to decrement // m_splitActive to change the queue it is in. node->set_list_iterator(base_type::insert(m_splitActive, node)); allocate_quota(node); } m_size++; } void ThrottleList::erase(ThrottleNode* node) { if (node->list_iterator() == end()) return; if (m_size == 0) throw internal_error("ThrottleList::erase(...) called on an empty list."); // Do we need an if-statement here? if (node->quota() != 0) { if (node->quota() > m_outstandingQuota) throw internal_error("ThrottleList::erase(...) node->quota() > m_outstandingQuota."); m_outstandingQuota -= node->quota(); m_unallocatedQuota += node->quota(); } if (node->list_iterator() == m_splitActive) m_splitActive = base_type::erase(node->list_iterator()); else base_type::erase(node->list_iterator()); node->clear_quota(); node->set_list_iterator(end()); m_size--; } } libtorrent-0.13.6/src/net/throttle_list.h000066400000000000000000000116701257211073700204250ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_THROTTLE_LIST_H #define LIBTORRENT_NET_THROTTLE_LIST_H #include #include "torrent/rate.h" // To allow conditional compilation depending on whether this patch is applied or not. #define LIBTORRENT_CUSTOM_THROTTLES 1 namespace torrent { class ThrottleNode; class ThrottleList : private std::list { public: typedef std::list base_type; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::clear; using base_type::size; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; ThrottleList(); bool is_enabled() const { return m_enabled; } bool is_active(const ThrottleNode* node) const; bool is_inactive(const ThrottleNode* node) const; bool is_throttled(const ThrottleNode* node) const; // When disabled all nodes are active at all times. void enable(); void disable(); // Returns the amount of quota used. May be negative if it had unused // quota left over from the last call that was more than is now allowed. int32_t update_quota(uint32_t quota); uint32_t size() const { return m_size; } uint32_t outstanding_quota() const { return m_outstandingQuota; } uint32_t unallocated_quota() const { return m_unallocatedQuota; } uint32_t min_chunk_size() const { return m_minChunkSize; } void set_min_chunk_size(uint32_t v) { m_minChunkSize = v; } uint32_t max_chunk_size() const { return m_maxChunkSize; } void set_max_chunk_size(uint32_t v) { m_maxChunkSize = v; } uint32_t node_quota(ThrottleNode* node); uint32_t node_used(ThrottleNode* node, uint32_t used); // both node_used functions uint32_t node_used_unthrottled(uint32_t used); // return the "used" argument void node_deactivate(ThrottleNode* node); const Rate* rate_slow() const { return &m_rateSlow; } void add_rate(uint32_t used); uint32_t rate_added() { uint32_t v = m_rateAdded; m_rateAdded = 0; return v; } // It is asumed that inserted nodes are currently active. It won't // matter if they do not get any initial quota as a later activation // of an active node should be safe. void insert(ThrottleNode* node); void erase(ThrottleNode* node); private: inline void allocate_quota(ThrottleNode* node); bool m_enabled; uint32_t m_size; uint32_t m_outstandingQuota; uint32_t m_unallocatedQuota; uint32_t m_unusedUnthrottledQuota; uint32_t m_rateAdded; uint32_t m_minChunkSize; uint32_t m_maxChunkSize; Rate m_rateSlow; // [m_splitActive,end> contains nodes that are inactive and need // more quote, sorted from the most urgent // node. [begin,m_splitActive> holds nodes with a large enough quota // to transmit, but are blocking. These are sorted from the longest // blocking node. iterator m_splitActive; }; } #endif libtorrent-0.13.6/src/net/throttle_node.h000066400000000000000000000061361257211073700204000ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_THROTTLE_NODE_H #define LIBTORRENT_NET_THROTTLE_NODE_H #include #include "torrent/rate.h" #include "throttle_list.h" namespace torrent { class SocketBase; class ThrottleNode { public: typedef ThrottleList::iterator iterator; typedef ThrottleList::const_iterator const_iterator; typedef std::tr1::function slot_void; ThrottleNode(uint32_t rateSpan) : m_rate(rateSpan) { clear_quota(); } Rate* rate() { return &m_rate; } const Rate* rate() const { return &m_rate; } uint32_t quota() const { return m_quota; } void clear_quota() { m_quota = 0; } void set_quota(uint32_t q) { m_quota = q; } iterator list_iterator() { return m_listIterator; } const_iterator list_iterator() const { return m_listIterator; } void set_list_iterator(iterator itr) { m_listIterator = itr; } void activate() { if (m_slot_activate) m_slot_activate(); } slot_void& slot_activate() { return m_slot_activate; } private: ThrottleNode(const ThrottleNode&); void operator = (const ThrottleNode&); uint32_t m_quota; iterator m_listIterator; Rate m_rate; slot_void m_slot_activate; }; } #endif libtorrent-0.13.6/src/protocol/000077500000000000000000000000001257211073700164225ustar00rootroot00000000000000libtorrent-0.13.6/src/protocol/Makefile.am000066400000000000000000000012461257211073700204610ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_protocol.la libsub_protocol_la_SOURCES = \ encryption_info.h \ extensions.cc \ extensions.h \ handshake.cc \ handshake.h \ handshake_encryption.cc \ handshake_encryption.h \ handshake_manager.cc \ handshake_manager.h \ initial_seed.cc \ initial_seed.h \ peer_chunks.h \ peer_connection_base.cc \ peer_connection_base.h \ peer_connection_leech.cc \ peer_connection_leech.h \ peer_connection_metadata.cc \ peer_connection_metadata.h \ peer_factory.cc \ peer_factory.h \ protocol_base.h \ request_list.cc \ request_list.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/protocol/encryption_info.h000066400000000000000000000061701257211073700220040ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_ENCRYPTION_H #define LIBTORRENT_PROTOCOL_ENCRYPTION_H #include "utils/rc4.h" namespace torrent { class EncryptionInfo { public: EncryptionInfo() : m_encrypted(false), m_obfuscated(false), m_decryptValid(false) {}; void encrypt(const void *indata, void *outdata, unsigned int length) { m_encrypt.crypt(indata, outdata, length); } void encrypt(void *data, unsigned int length) { m_encrypt.crypt(data, length); } void decrypt(const void *indata, void *outdata, unsigned int length) { m_decrypt.crypt(indata, outdata, length); } void decrypt(void *data, unsigned int length) { m_decrypt.crypt(data, length); } bool is_encrypted() const { return m_encrypted; } bool is_obfuscated() const { return m_obfuscated; } bool decrypt_valid() const { return m_decryptValid; } void set_obfuscated() { m_obfuscated = true; m_encrypted = m_decryptValid = false; } void set_encrypt(const RC4& encrypt) { m_encrypt = encrypt; m_encrypted = m_obfuscated = true; } void set_decrypt(const RC4& decrypt) { m_decrypt = decrypt; m_decryptValid = true; } private: bool m_encrypted; bool m_obfuscated; bool m_decryptValid; RC4 m_encrypt; RC4 m_decrypt; }; } #endif libtorrent-0.13.6/src/protocol/extensions.cc000066400000000000000000000310521257211073700211310ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "download/available_list.h" #include "download/download_main.h" #include "download/download_wrapper.h" #include "protocol/peer_connection_base.h" #include "torrent/connection_manager.h" #include "torrent/object_stream.h" #include "torrent/download/download_manager.h" #include "torrent/peer/connection_list.h" #include "torrent/peer/peer_info.h" #include "manager.h" #include "extensions.h" namespace torrent { template <> const ExtHandshakeMessage::key_list_type ExtHandshakeMessage::keys = { { key_e, "e" }, { key_m_utMetadata, "m::ut_metadata" }, { key_m_utPex, "m::ut_pex" }, { key_metadataSize, "metadata_size" }, { key_p, "p" }, { key_reqq, "reqq" }, { key_v, "v" }, }; template <> const ExtPEXMessage::key_list_type ExtPEXMessage::keys = { { key_pex_added, "added*S" }, }; // DEBUG: Add type info. template <> const ExtMetadataMessage::key_list_type ExtMetadataMessage::keys = { { key_msgType, "msg_type" }, { key_piece, "piece" }, { key_totalSize, "total_size" }, }; struct message_type { const char* key; ext_handshake_keys index; }; const message_type message_keys[] = { { "HANDSHAKE", key_handshake_LAST }, { "ut_pex", key_m_utPex }, { "metadata_size", key_m_utMetadata } }; void ProtocolExtension::cleanup() { // if (is_default()) // return; for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) if (is_local_enabled(t)) unset_local_enabled(t); } void ProtocolExtension::set_local_enabled(int t) { if (is_local_enabled(t)) return; m_flags |= flag_local_enabled_base << t; switch (t) { case UT_PEX: m_download->info()->set_size_pex(m_download->info()->size_pex() + 1); break; default: break; } } void ProtocolExtension::unset_local_enabled(int t) { if (!is_local_enabled(t)) return; m_flags &= ~(flag_local_enabled_base << t); switch (t) { case UT_PEX: m_download->info()->set_size_pex(m_download->info()->size_pex() - 1); break; default: break; } } DataBuffer ProtocolExtension::generate_handshake_message() { ExtHandshakeMessage message; // Add "e" key if encryption is enabled, set it to 1 if we require // encryption for incoming connections, or 0 otherwise. if ((manager->connection_manager()->encryption_options() & ConnectionManager::encryption_allow_incoming) != 0) message[key_e] = (manager->connection_manager()->encryption_options() & ConnectionManager::encryption_require) != 0; message[key_p] = manager->connection_manager()->listen_port(); message[key_v] = raw_string::from_c_str("libTorrent " VERSION); message[key_reqq] = 2048; // maximum request queue size if (!m_download->info()->is_meta_download()) message[key_metadataSize] = m_download->info()->metadata_size(); message[key_m_utPex] = is_local_enabled(UT_PEX) ? UT_PEX : 0; message[key_m_utMetadata] = UT_METADATA; char buffer[1024]; object_buffer_t result = static_map_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), message); int length = result.second - buffer; char* copy = new char[length]; memcpy(copy, buffer, length); return DataBuffer(copy, copy + length); } inline DataBuffer ProtocolExtension::build_bencode(size_t maxLength, const char* format, ...) { char* b = new char[maxLength]; va_list args; va_start(args, format); unsigned int length = vsnprintf(b, maxLength, format, args); va_end(args); if (length > maxLength) throw internal_error("ProtocolExtension::build_bencode wrote past buffer."); return DataBuffer(b, b + length); } DataBuffer ProtocolExtension::generate_toggle_message(MessageType t, bool on) { // TODO: Check if we're accepting this message type? // Manually create bencoded map { "m" => { message_keys[t] => on ? t : 0 } } return build_bencode(32, "d1:md%zu:%si%deee", strlen(message_keys[t].key), message_keys[t].key, on ? t : 0); } DataBuffer ProtocolExtension::generate_ut_pex_message(const PEXList& added, const PEXList& removed) { if (added.empty() && removed.empty()) return DataBuffer(); int added_len = added.size() * 6; int removed_len = removed.size() * 6; // Manually create bencoded map { "added" => added, "dropped" => dropped } char* buffer = new char[32 + added_len + removed_len]; char* end = buffer; end += sprintf(end, "d5:added%d:", added_len); memcpy(end, added.begin()->c_str(), added_len); end += added_len; end += sprintf(end, "7:dropped%d:", removed_len); memcpy(end, removed.begin()->c_str(), removed_len); end += removed_len; *end++ = 'e'; if (end - buffer > 32 + added_len + removed_len) throw internal_error("ProtocolExtension::ut_pex_message wrote beyond buffer."); return DataBuffer(buffer, end); } void ProtocolExtension::read_start(int type, uint32_t length, bool skip) { if (is_default() || (type >= FIRST_INVALID) || length > (1 << 15)) throw communication_error("Received invalid extension message."); if (m_read != NULL || (int32_t)length < 0) throw internal_error("ProtocolExtension::read_start called in inconsistent state."); m_readLeft = length; if (skip || !is_local_enabled(type)) { m_readType = SKIP_EXTENSION; } else { m_readType = type; } // Allocate the buffer even for SKIP_EXTENSION, just to make things // simpler. m_readPos = m_read = new char[length]; } bool ProtocolExtension::read_done() { bool result = true; try { switch(m_readType) { case SKIP_EXTENSION: break; case HANDSHAKE: result = parse_handshake(); break; case UT_PEX: result = parse_ut_pex(); break; case UT_METADATA: result = parse_ut_metadata(); break; default: throw internal_error("ProtocolExtension::read_done called with invalid extension type."); } } catch (bencode_error& e) { // Ignore malformed messages. // DEBUG: // throw internal_error("ProtocolExtension::read_done '" + std::string(m_read, std::distance(m_read, m_readPos)) + "'"); } delete [] m_read; m_read = NULL; m_readType = FIRST_INVALID; m_flags |= flag_received_ext; return result; } // Called whenever peer enables or disables an extension. void ProtocolExtension::peer_toggle_remote(int type, bool active) { if (type == UT_PEX) { // When ut_pex is enabled, the first peer exchange afterwards needs // to be a full message, not delta. if (active) m_flags |= flag_initial_pex; } } bool ProtocolExtension::parse_handshake() { ExtHandshakeMessage message; static_map_read_bencode(m_read, m_readPos, message); for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) { if (!message[message_keys[t].index].is_value()) continue; uint8_t id = message[message_keys[t].index].as_value(); set_remote_supported(t); if (id != m_idMap[t - 1]) { peer_toggle_remote(t, id != 0); m_idMap[t - 1] = id; } } // If this is the first handshake, then disable any local extensions // not supported by remote. if (is_initial_handshake()) { for (int t = HANDSHAKE + 1; t < FIRST_INVALID; t++) if (!is_remote_supported(t)) unset_local_enabled(t); } if (message[key_p].is_value()) { uint16_t port = message[key_p].as_value(); if (port > 0) m_peerInfo->set_listen_port(port); } if (message[key_reqq].is_value()) m_maxQueueLength = message[key_reqq].as_value(); if (message[key_metadataSize].is_value()) m_download->set_metadata_size(message[key_metadataSize].as_value()); m_flags &= ~flag_initial_handshake; return true; } bool ProtocolExtension::parse_ut_pex() { // Ignore message if we're still in the handshake (no connection // yet), or no peers are present. ExtPEXMessage message; static_map_read_bencode(m_read, m_readPos, message); // TODO: Check if pex is enabled? if (!message[key_pex_added].is_raw_string()) return true; raw_string peers = message[key_pex_added].as_raw_string(); if (peers.empty()) return true; // TODO: Sort the list before adding it. AddressList l; l.parse_address_compact(peers); l.sort(); l.erase(std::unique(l.begin(), l.end()), l.end()); m_download->peer_list()->insert_available(&l); return true; } bool ProtocolExtension::parse_ut_metadata() { ExtMetadataMessage message; // Piece data comes after bencoded extension message. const char* dataStart = static_map_read_bencode(m_read, m_readPos, message); switch(message[key_msgType].as_value()) { case 0: // Can't process new request while still having data to send. if (has_pending_message()) return false; send_metadata_piece(message[key_piece].as_value()); break; case 1: if (m_connection == NULL) break; m_connection->receive_metadata_piece(message[key_piece].as_value(), dataStart, m_readPos - dataStart); break; case 2: if (m_connection == NULL) break; m_connection->receive_metadata_piece(message[key_piece].as_value(), NULL, 0); break; }; return true; } void ProtocolExtension::send_metadata_piece(size_t piece) { // Reject out-of-range piece, or if we don't have the complete metadata yet. size_t metadataSize = m_download->info()->metadata_size(); size_t pieceEnd = (metadataSize + metadata_piece_size - 1) >> metadata_piece_shift; if (m_download->info()->is_meta_download() || piece >= pieceEnd) { // reject: { "msg_type" => 2, "piece" => ... } m_pendingType = UT_METADATA; m_pending = build_bencode(40, "d8:msg_typei2e5:piecei%zuee", piece); return; } // These messages will be rare, so we'll just build the // metadata here instead of caching it uselessly. char* buffer = new char[metadataSize]; object_write_bencode_c(object_write_to_buffer, NULL, object_buffer_t(buffer, buffer + metadataSize), &(*manager->download_manager()->find(m_download->info()))->bencode()->get_key("info")); // data: { "msg_type" => 1, "piece" => ..., "total_size" => ... } followed by piece data (outside of dictionary) size_t length = piece == pieceEnd - 1 ? m_download->info()->metadata_size() % metadata_piece_size : metadata_piece_size; m_pendingType = UT_METADATA; m_pending = build_bencode(length + 128, "d8:msg_typei1e5:piecei%zue10:total_sizei%zuee", piece, metadataSize); memcpy(m_pending.end(), buffer + (piece << metadata_piece_shift), length); m_pending.set(m_pending.data(), m_pending.end() + length, m_pending.owned()); delete [] buffer; } bool ProtocolExtension::request_metadata_piece(const Piece* p) { if (p->offset() % metadata_piece_size) throw internal_error("ProtocolExtension::request_metadata_piece got misaligned piece offset."); if (has_pending_message()) return false; m_pendingType = UT_METADATA; m_pending = build_bencode(40, "d8:msg_typei0e5:piecei%uee", (unsigned)(p->offset() >> metadata_piece_shift)); return true; } } libtorrent-0.13.6/src/protocol/extensions.h000066400000000000000000000207071257211073700210000ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_EXTENSIONS_H #define LIBTORRENT_PROTOCOL_EXTENSIONS_H #include #include #include "torrent/exceptions.h" #include "torrent/object.h" #include "torrent/object_static_map.h" #include "torrent/download_info.h" #include "net/address_list.h" #include "net/data_buffer.h" // Not really important, so no need to make this a configure check. #ifdef __GNUC__ #define ATTRIBUTE_PRINTF(num) __attribute__ ((format (printf, num, num+1))) #else #define ATTRIBUTE_PRINTF(num) #endif namespace torrent { class ProtocolExtension { public: typedef enum { HANDSHAKE = 0, UT_PEX, UT_METADATA, FIRST_INVALID, // first invalid message ID SKIP_EXTENSION, } MessageType; typedef std::vector PEXList; static const int flag_default = 1<<0; static const int flag_initial_handshake = 1<<1; static const int flag_initial_pex = 1<<2; static const int flag_received_ext = 1<<3; // The base bit to shift by MessageType to check if the extension is // enabled locally or supported by the peer. static const int flag_local_enabled_base = 1<<8; static const int flag_remote_supported_base = 1<<16; // Number of extensions we support, not counting handshake. static const int extension_count = FIRST_INVALID - HANDSHAKE - 1; // Fixed size of a metadata piece (16 KB). static const size_t metadata_piece_shift = 14; static const size_t metadata_piece_size = 1 << metadata_piece_shift; ProtocolExtension(); ~ProtocolExtension() { delete [] m_read; } void cleanup(); // Create default extension object, with all extensions disabled. // Useful for eliminating checks whether peer supports extensions at all. static ProtocolExtension make_default(); void set_info(PeerInfo* peerInfo, DownloadMain* download) { m_peerInfo = peerInfo; m_download = download; } void set_connection(PeerConnectionBase* c) { m_connection = c; } DataBuffer generate_handshake_message(); static DataBuffer generate_toggle_message(MessageType t, bool on); static DataBuffer generate_ut_pex_message(const PEXList& added, const PEXList& removed); // Return peer's extension ID for the given extension type, or 0 if // disabled by peer. uint8_t id(int t) const; bool is_local_enabled(int t) const { return m_flags & flag_local_enabled_base << t; } bool is_remote_supported(int t) const { return m_flags & flag_remote_supported_base << t; } void set_local_enabled(int t); void unset_local_enabled(int t); void set_remote_supported(int t) { m_flags |= flag_remote_supported_base << t; } // General information about peer from extension handshake. uint32_t max_queue_length() const { return m_maxQueueLength; } // Handle reading extension data from peer. void read_start(int type, uint32_t length, bool skip); bool read_done(); char* read_position() { return m_readPos; } bool read_move(uint32_t v) { m_readPos += v; return (m_readLeft -= v) == 0; } uint32_t read_need() const { return m_readLeft; } bool is_complete() const { return m_readLeft == 0; } bool is_invalid() const { return m_readType == FIRST_INVALID; } bool is_default() const { return m_flags & flag_default; } // Initial PEX message after peer enables PEX needs to send full list // of peers instead of the delta list, so keep track of that. bool is_initial_handshake() const { return m_flags & flag_initial_handshake; } bool is_initial_pex() const { return m_flags & flag_initial_pex; } bool is_received_ext() const { return m_flags & flag_received_ext; } void clear_initial_pex() { m_flags &= ~flag_initial_pex; } void reset() { std::memset(&m_idMap, 0, sizeof(m_idMap)); } bool request_metadata_piece(const Piece* p); // To handle cases where the extension protocol needs to send a reply. bool has_pending_message() const { return m_pendingType != HANDSHAKE; } MessageType pending_message_type() const { return m_pendingType; } DataBuffer pending_message_data() { return m_pending.release(); } void clear_pending_message() { if (m_pending.empty()) m_pendingType = HANDSHAKE; } private: bool parse_handshake(); bool parse_ut_pex(); bool parse_ut_metadata(); static DataBuffer build_bencode(size_t maxLength, const char* format, ...) ATTRIBUTE_PRINTF(2); void peer_toggle_remote(int type, bool active); void send_metadata_piece(size_t piece); // Map of IDs peer uses for each extension message type, excluding // HANDSHAKE. uint8_t m_idMap[extension_count]; uint32_t m_maxQueueLength; int m_flags; PeerInfo* m_peerInfo; DownloadMain* m_download; PeerConnectionBase* m_connection; uint8_t m_readType; uint32_t m_readLeft; char* m_read; char* m_readPos; MessageType m_pendingType; DataBuffer m_pending; }; // // // enum ext_handshake_keys { key_e, key_m_utMetadata, key_m_utPex, key_metadataSize, key_p, key_reqq, key_v, key_handshake_LAST }; enum ext_pex_keys { key_pex_added, key_pex_LAST }; enum ext_metadata_keys { key_msgType, key_piece, key_totalSize, key_metadata_LAST }; typedef static_map_type ExtHandshakeMessage; typedef static_map_type ExtPEXMessage; typedef static_map_type ExtMetadataMessage; // // // inline ProtocolExtension::ProtocolExtension() : // Set HANDSHAKE as enabled and supported. Those bits should not be // touched. m_flags(flag_local_enabled_base | flag_remote_supported_base | flag_initial_handshake), m_peerInfo(NULL), m_download(NULL), m_connection(NULL), m_readType(FIRST_INVALID), m_read(NULL), m_pendingType(HANDSHAKE) { reset(); set_local_enabled(UT_METADATA); } inline ProtocolExtension ProtocolExtension::make_default() { ProtocolExtension extension; extension.m_flags |= flag_default; return extension; } inline uint8_t ProtocolExtension::id(int t) const { if (t == HANDSHAKE) return 0; if (t - 1 >= extension_count) throw internal_error("ProtocolExtension::id message type out of range."); return m_idMap[t - 1]; } } #endif libtorrent-0.13.6/src/protocol/handshake.cc000066400000000000000000001161651257211073700206710ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "download/download_main.h" #include "net/throttle_list.h" #include "torrent/dht_manager.h" #include "torrent/download_info.h" #include "torrent/exceptions.h" #include "torrent/error.h" #include "torrent/poll.h" #include "torrent/throttle.h" #include "utils/diffie_hellman.h" #include "globals.h" #include "manager.h" #include "extensions.h" #include "handshake.h" #include "handshake_manager.h" namespace torrent { const char* Handshake::m_protocol = "BitTorrent protocol"; class handshake_error : public network_error { public: handshake_error(int type, int error) : m_type(type), m_error(error) {} virtual const char* what() const throw() { return "Handshake error"; } virtual int type() const throw() { return m_type; } virtual int error() const throw() { return m_error; } private: int m_type; int m_error; }; class handshake_succeeded : public network_error { public: handshake_succeeded() {} }; Handshake::Handshake(SocketFd fd, HandshakeManager* m, int encryptionOptions) : m_state(INACTIVE), m_manager(m), m_peerInfo(NULL), m_download(NULL), // Use global throttles until we know which download it is. m_uploadThrottle(manager->upload_throttle()->throttle_list()), m_downloadThrottle(manager->download_throttle()->throttle_list()), m_readDone(false), m_writeDone(false), m_encryption(encryptionOptions), m_extensions(m->default_extensions()) { set_fd(fd); m_readBuffer.reset(); m_writeBuffer.reset(); m_taskTimeout.clear_time(); m_taskTimeout.slot() = std::tr1::bind(&HandshakeManager::receive_timeout, m, this); } Handshake::~Handshake() { if (m_taskTimeout.is_queued()) throw internal_error("Handshake m_taskTimeout bork bork bork."); if (get_fd().is_valid()) throw internal_error("Handshake dtor called but m_fd is still open."); m_encryption.cleanup(); } void Handshake::initialize_incoming(const rak::socket_address& sa) { m_incoming = true; m_address = sa; if (m_encryption.options() & (ConnectionManager::encryption_allow_incoming | ConnectionManager::encryption_require)) m_state = READ_ENC_KEY; else m_state = READ_INFO; manager->poll()->open(this); manager->poll()->insert_read(this); manager->poll()->insert_error(this); // Use lower timeout here. priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(60)).round_seconds()); } void Handshake::initialize_outgoing(const rak::socket_address& sa, DownloadMain* d, PeerInfo* peerInfo) { m_download = d; m_peerInfo = peerInfo; m_peerInfo->set_flags(PeerInfo::flag_handshake); m_incoming = false; m_address = sa; std::make_pair(m_uploadThrottle, m_downloadThrottle) = m_download->throttles(m_address.c_sockaddr()); m_state = CONNECTING; manager->poll()->open(this); manager->poll()->insert_write(this); manager->poll()->insert_error(this); priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(60)).round_seconds()); } void Handshake::deactivate_connection() { if (!get_fd().is_valid()) throw internal_error("Handshake::deactivate_connection called but m_fd is not open."); m_state = INACTIVE; priority_queue_erase(&taskScheduler, &m_taskTimeout); manager->poll()->remove_read(this); manager->poll()->remove_write(this); manager->poll()->remove_error(this); manager->poll()->close(this); } void Handshake::release_connection() { if (!get_fd().is_valid()) throw internal_error("Handshake::release_connection called but m_fd is not open."); m_peerInfo->unset_flags(PeerInfo::flag_handshake); m_peerInfo = NULL; get_fd().clear(); } void Handshake::destroy_connection() { if (!get_fd().is_valid()) throw internal_error("Handshake::destroy_connection called but m_fd is not open."); manager->connection_manager()->dec_socket_count(); get_fd().close(); get_fd().clear(); if (m_peerInfo == NULL) return; m_download->peer_list()->disconnected(m_peerInfo, 0); m_peerInfo->unset_flags(PeerInfo::flag_handshake); m_peerInfo = NULL; if (!m_extensions->is_default()) { m_extensions->cleanup(); delete m_extensions; } } int Handshake::retry_options() { uint32_t options = m_encryption.options() & ~ConnectionManager::encryption_enable_retry; if (m_encryption.retry() == HandshakeEncryption::RETRY_PLAIN) options &= ~ConnectionManager::encryption_try_outgoing; else if (m_encryption.retry() == HandshakeEncryption::RETRY_ENCRYPTED) options |= ConnectionManager::encryption_try_outgoing; else throw internal_error("Invalid retry type."); return options; } inline uint32_t Handshake::read_unthrottled(void* buf, uint32_t length) { return m_downloadThrottle->node_used_unthrottled(read_stream_throws(buf, length)); } inline uint32_t Handshake::write_unthrottled(const void* buf, uint32_t length) { return m_uploadThrottle->node_used_unthrottled(write_stream_throws(buf, length)); } // Handshake::read_proxy_connect() // Entry: 0, 0 // * 0, [0, 508> bool Handshake::read_proxy_connect() { // Being greedy for now. m_readBuffer.move_end(read_unthrottled(m_readBuffer.end(), 512)); const char* pattern = "\r\n\r\n"; const unsigned int patternLength = 4; if (m_readBuffer.remaining() < patternLength) return false; Buffer::iterator itr = std::search(m_readBuffer.begin(), m_readBuffer.end(), (uint8_t*)pattern, (uint8_t*)pattern + patternLength); m_readBuffer.set_position_itr(itr != m_readBuffer.end() ? (itr + patternLength) : (itr - patternLength)); m_readBuffer.move_unused(); return itr != m_readBuffer.end(); } // Handshake::read_encryption_key() // Entry: * 0, [0, 508> // IU 20, [20, enc_pad_read_size> // *E 96, [96, enc_pad_read_size> bool Handshake::read_encryption_key() { if (m_incoming) { if (m_readBuffer.remaining() < 20) m_readBuffer.move_end(read_unthrottled(m_readBuffer.end(), 20 - m_readBuffer.remaining())); if (m_readBuffer.remaining() < 20) return false; if (m_readBuffer.peek_8() == 19 && std::memcmp(m_readBuffer.position() + 1, m_protocol, 19) == 0) { // got unencrypted BT handshake if (m_encryption.options() & ConnectionManager::encryption_require) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unencrypted_rejected); m_state = READ_INFO; return true; } } // Read as much of key, pad and sync string as we can; this is safe // because peer can't send anything beyond the initial BT handshake // because it doesn't know our encryption choice yet. if (m_readBuffer.remaining() < enc_pad_read_size) m_readBuffer.move_end(read_unthrottled(m_readBuffer.end(), enc_pad_read_size - m_readBuffer.remaining())); // but we need at least the key at this point if (m_readBuffer.size_end() < 96) return false; // If the handshake fails after this, it wasn't because the peer // doesn't like encrypted connections, so don't retry unencrypted. m_encryption.set_retry(HandshakeEncryption::RETRY_NONE); if (m_incoming) prepare_key_plus_pad(); if(!m_encryption.key()->compute_secret(m_readBuffer.position(), 96)) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_encryption); m_readBuffer.consume(96); // Determine the synchronisation string. if (m_incoming) m_encryption.hash_req1_to_sync(); else m_encryption.encrypt_vc_to_sync(m_download->info()->hash().c_str()); // also put as much as we can write so far in the buffer if (!m_incoming) prepare_enc_negotiation(); m_state = READ_ENC_SYNC; return true; } // Handshake::read_encryption_sync() // *E 96, [96, enc_pad_read_size> bool Handshake::read_encryption_sync() { // Check if we've read the sync string already in the previous // state. This is very likely and avoids an unneeded read. Buffer::iterator itr = std::search(m_readBuffer.position(), m_readBuffer.end(), (uint8_t*)m_encryption.sync(), (uint8_t*)m_encryption.sync() + m_encryption.sync_length()); if (itr == m_readBuffer.end()) { // Otherwise read as many bytes as possible until we find the sync // string. int toRead = enc_pad_size + m_encryption.sync_length() - m_readBuffer.remaining(); if (toRead <= 0) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_encryption_sync_failed); m_readBuffer.move_end(read_unthrottled(m_readBuffer.end(), toRead)); itr = std::search(m_readBuffer.position(), m_readBuffer.end(), (uint8_t*)m_encryption.sync(), (uint8_t*)m_encryption.sync() + m_encryption.sync_length()); if (itr == m_readBuffer.end()) return false; } if (m_incoming) { // We've found HASH('req1' + S), skip that and go on reading the // SKEY hash. m_readBuffer.consume(std::distance(m_readBuffer.position(), itr) + 20); m_state = READ_ENC_SKEY; } else { m_readBuffer.consume(std::distance(m_readBuffer.position(), itr)); m_state = READ_ENC_NEGOT; } return true; } bool Handshake::read_encryption_skey() { if (!fill_read_buffer(20)) return false; m_encryption.deobfuscate_hash((char*)m_readBuffer.position()); m_download = m_manager->download_info_obfuscated((char*)m_readBuffer.position()); m_readBuffer.consume(20); validate_download(); std::make_pair(m_uploadThrottle, m_downloadThrottle) = m_download->throttles(m_address.c_sockaddr()); m_encryption.initialize_encrypt(m_download->info()->hash().c_str(), m_incoming); m_encryption.initialize_decrypt(m_download->info()->hash().c_str(), m_incoming); m_encryption.info()->decrypt(m_readBuffer.position(), m_readBuffer.remaining()); HandshakeEncryption::copy_vc(m_writeBuffer.end()); m_encryption.info()->encrypt(m_writeBuffer.end(), HandshakeEncryption::vc_length); m_writeBuffer.move_end(HandshakeEncryption::vc_length); m_state = READ_ENC_NEGOT; return true; } bool Handshake::read_encryption_negotiation() { if (!fill_read_buffer(enc_negotiation_size)) return false; if (!m_incoming) { // Start decrypting, but don't decrypt beyond the initial // encrypted handshake and later the pad because we may have read // too much data which may be unencrypted if the peer chose that // option. m_encryption.initialize_decrypt(m_download->info()->hash().c_str(), m_incoming); m_encryption.info()->decrypt(m_readBuffer.position(), enc_negotiation_size); } if (!HandshakeEncryption::compare_vc(m_readBuffer.position())) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); m_readBuffer.consume(HandshakeEncryption::vc_length); m_encryption.set_crypto(m_readBuffer.read_32()); m_readPos = m_readBuffer.read_16(); // length of padC/padD if (m_readPos > enc_pad_size) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); // choose one of the offered encryptions, or check the chosen one is valid if (m_incoming) { if ((m_encryption.options() & ConnectionManager::encryption_prefer_plaintext) && m_encryption.has_crypto_plain()) { m_encryption.set_crypto(HandshakeEncryption::crypto_plain); } else if ((m_encryption.options() & ConnectionManager::encryption_require_RC4) && !m_encryption.has_crypto_rc4()) { throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unencrypted_rejected); } else if (m_encryption.has_crypto_rc4()) { m_encryption.set_crypto(HandshakeEncryption::crypto_rc4); } else if (m_encryption.has_crypto_plain()) { m_encryption.set_crypto(HandshakeEncryption::crypto_plain); } else { throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_encryption); } // at this point we can also write the rest of our negotiation reply m_writeBuffer.write_32(m_encryption.crypto()); m_writeBuffer.write_16(0); m_encryption.info()->encrypt(m_writeBuffer.end() - 4 - 2, 4 + 2); } else { if (m_encryption.crypto() != HandshakeEncryption::crypto_rc4 && m_encryption.crypto() != HandshakeEncryption::crypto_plain) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_encryption); if ((m_encryption.options() & ConnectionManager::encryption_require_RC4) && (m_encryption.crypto() != HandshakeEncryption::crypto_rc4)) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_encryption); } if (!m_incoming) { // decrypt appropriate part of buffer: only pad or all if (m_encryption.crypto() == HandshakeEncryption::crypto_plain) m_encryption.info()->decrypt(m_readBuffer.position(), std::min(m_readPos, m_readBuffer.remaining())); else m_encryption.info()->decrypt(m_readBuffer.position(), m_readBuffer.remaining()); } // next, skip padC/padD m_state = READ_ENC_PAD; return true; } bool Handshake::read_negotiation_reply() { if (!m_incoming) { if (m_encryption.crypto() != HandshakeEncryption::crypto_rc4) m_encryption.info()->set_obfuscated(); m_state = READ_INFO; return true; } if (!fill_read_buffer(2)) return false; // The peer may send initial payload that is RC4 encrypted even if // we have selected plaintext encryption, so read it ahead of BT // handshake. m_encryption.set_length_ia(m_readBuffer.read_16()); if (m_encryption.length_ia() > handshake_size) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); m_state = READ_ENC_IA; return true; } bool Handshake::read_info() { fill_read_buffer(handshake_size); // Check the first byte as early as possible so we can // disconnect non-BT connections if they send less than 20 bytes. if ((m_readBuffer.remaining() >= 1 && m_readBuffer.peek_8() != 19) || (m_readBuffer.remaining() >= 20 && (std::memcmp(m_readBuffer.position() + 1, m_protocol, 19) != 0))) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_not_bittorrent); if (m_readBuffer.remaining() < part1_size) return false; // If the handshake fails after this, it isn't being rejected because // it is unencrypted, so don't retry. m_encryption.set_retry(HandshakeEncryption::RETRY_NONE); m_readBuffer.consume(20); // Should do some option field stuff here, for now just copy. m_readBuffer.read_range(m_options, m_options + 8); // Check the info hash. if (m_incoming) { if (m_download != NULL) { // Have the download from the encrypted handshake, make sure it // matches the BT handshake. if (m_download->info()->hash().not_equal_to((char*)m_readBuffer.position())) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); } else { m_download = m_manager->download_info((char*)m_readBuffer.position()); } validate_download(); std::make_pair(m_uploadThrottle, m_downloadThrottle) = m_download->throttles(m_address.c_sockaddr()); prepare_handshake(); } else { if (m_download->info()->hash().not_equal_to((char*)m_readBuffer.position())) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); } m_readBuffer.consume(20); m_state = READ_PEER; return true; } bool Handshake::read_peer() { if (!fill_read_buffer(20)) return false; prepare_peer_info(); // Send EXTENSION_PROTOCOL handshake message if peer supports it. if (m_peerInfo->supports_extensions()) write_extension_handshake(); // Replay HAVE messages we receive after starting to send the bitfield. // This avoids replaying HAVEs for pieces received between starting the // handshake and now (e.g. when connecting takes longer). Ideally we // should make a snapshot of the bitfield here in case it changes while // we're sending it (if it can't be sent in one write() call). m_initializedTime = cachedTime; // The download is just starting so we're not sending any // bitfield. Pretend we wrote it already. if (m_download->file_list()->bitfield()->is_all_unset() || m_download->initial_seeding() != NULL) { m_writePos = m_download->file_list()->bitfield()->size_bytes(); m_writeBuffer.write_32(0); if (m_encryption.info()->is_encrypted()) m_encryption.info()->encrypt(m_writeBuffer.end() - 4, 4); } else { prepare_bitfield(); } m_state = READ_MESSAGE; manager->poll()->insert_write(this); // Give some extra time for reading/writing the bitfield. priority_queue_erase(&taskScheduler, &m_taskTimeout); priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(120)).round_seconds()); return true; } bool Handshake::read_bitfield() { if (m_readPos < m_bitfield.size_bytes()) { uint32_t length = read_unthrottled(m_bitfield.begin() + m_readPos, m_bitfield.size_bytes() - m_readPos); if (m_encryption.info()->decrypt_valid()) m_encryption.info()->decrypt(m_bitfield.begin() + m_readPos, length); m_readPos += length; } return m_readPos == m_bitfield.size_bytes(); } bool Handshake::read_extension() { if (m_readBuffer.peek_32() > m_readBuffer.reserved()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); int32_t need = m_readBuffer.peek_32() + 4 - m_readBuffer.remaining(); // We currently can't handle an extension handshake that doesn't // completely fit in the buffer. However these messages are usually // ~100 bytes large and the buffer holds over 1000 bytes so it // should be ok. Else maybe figure out how to disable extensions for // when peer connects next time. // // In addition, make sure there's at least 5 bytes available after // the PEX message has been read, so that we can fit the preamble of // the BITFIELD message. if (need + 5 > m_readBuffer.reserved_left()) { m_readBuffer.move_unused(); if (need + 5 > m_readBuffer.reserved_left()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); } if (!fill_read_buffer(m_readBuffer.peek_32() + 4)) return false; uint32_t length = m_readBuffer.read_32() - 2; m_readBuffer.read_8(); m_extensions->read_start(m_readBuffer.read_8(), length, false); std::memcpy(m_extensions->read_position(), m_readBuffer.position(), length); m_extensions->read_move(length); // Does this check need to check if it is a handshake we read? if (!m_extensions->is_complete()) throw internal_error("Could not read extension handshake even though it should be in the read buffer."); m_extensions->read_done(); m_readBuffer.consume(length); return true; } bool Handshake::read_port() { if (m_readBuffer.peek_32() > m_readBuffer.reserved()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); int32_t need = m_readBuffer.peek_32() + 4 - m_readBuffer.remaining(); if (need + 5 > m_readBuffer.reserved_left()) { m_readBuffer.move_unused(); if (need + 5 > m_readBuffer.reserved_left()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); } if (!fill_read_buffer(m_readBuffer.peek_32() + 4)) return false; uint32_t length = m_readBuffer.read_32() - 1; m_readBuffer.read_8(); if (length == 2) manager->dht_manager()->add_node(m_address.c_sockaddr(), m_readBuffer.peek_16()); m_readBuffer.consume(length); return true; } void Handshake::read_done() { if (m_readDone != false) throw internal_error("Handshake::read_done() m_readDone != false."); // if (m_peerInfo->supports_extensions() && m_extensions->is_initial_handshake()) // throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_order); m_readDone = true; manager->poll()->remove_read(this); if (m_bitfield.empty()) { m_bitfield.set_size_bits(m_download->file_list()->bitfield()->size_bits()); m_bitfield.allocate(); m_bitfield.unset_all(); } else { m_bitfield.update(); } // Should've started to write post handshake data already, but we were // still reading the bitfield/extension and postponed it. If we had no // bitfield to send, we need to send a keep-alive now. if (m_writePos == m_download->file_list()->bitfield()->size_bytes()) prepare_post_handshake(m_download->file_list()->bitfield()->is_all_unset() || m_download->initial_seeding() != NULL); if (m_writeDone) throw handshake_succeeded(); } void Handshake::event_read() { try { restart: switch (m_state) { case PROXY_CONNECT: if (!read_proxy_connect()) break; m_state = PROXY_DONE; manager->poll()->insert_write(this); return event_write(); case READ_ENC_KEY: if (!read_encryption_key()) break; if (m_state != READ_ENC_SYNC) goto restart; case READ_ENC_SYNC: if (!read_encryption_sync()) break; if (m_state != READ_ENC_SKEY) goto restart; case READ_ENC_SKEY: if (!read_encryption_skey()) break; case READ_ENC_NEGOT: if (!read_encryption_negotiation()) break; if (m_state != READ_ENC_PAD) goto restart; case READ_ENC_PAD: if (m_readPos) { // Read padC + lenIA or padD; pad length in m_readPos. if (!fill_read_buffer(m_readPos + (m_incoming ? 2 : 0))) // This can be improved (consume as much as was read) break; m_readBuffer.consume(m_readPos); m_readPos = 0; } if (!read_negotiation_reply()) break; if (m_state != READ_ENC_IA) goto restart; case READ_ENC_IA: // Just read (and automatically decrypt) the initial payload // and leave it in the buffer for READ_INFO later. if (m_encryption.length_ia() > 0 && !fill_read_buffer(m_encryption.length_ia())) break; if (m_readBuffer.remaining() > m_encryption.length_ia()) throw internal_error("Read past initial payload after incoming encrypted handshake."); if (m_encryption.crypto() != HandshakeEncryption::crypto_rc4) m_encryption.info()->set_obfuscated(); m_state = READ_INFO; case READ_INFO: if (!read_info()) break; if (m_state != READ_PEER) goto restart; case READ_PEER: if (!read_peer()) break; // Is this correct? if (m_state != READ_MESSAGE) goto restart; case READ_MESSAGE: case POST_HANDSHAKE: // For meta-downloads, we aren't interested in the bitfield or // extension messages here, PCMetadata handles all that. The // bitfield only refers to the single-chunk meta-data, so fake that. if (m_download->info()->is_meta_download()) { m_bitfield.set_size_bits(1); m_bitfield.allocate(); m_bitfield.set(0); read_done(); break; } fill_read_buffer(5); // Received a keep-alive message which means we won't be // getting any bitfield. if (m_readBuffer.remaining() >= 4 && m_readBuffer.peek_32() == 0) { m_readBuffer.read_32(); read_done(); break; } if (m_readBuffer.remaining() < 5) break; m_readPos = 0; // Extension handshake was sent after BT handshake but before // bitfield, so handle that. If we've already received a message // of this type then we will assume the peer won't be sending a // bitfield, as the second extension message will be part of the // normal traffic, not the handshake. if (m_readBuffer.peek_8_at(4) == protocol_bitfield) { const Bitfield* bitfield = m_download->file_list()->bitfield(); if (!m_bitfield.empty() || m_readBuffer.read_32() != bitfield->size_bytes() + 1) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); m_readBuffer.read_8(); m_bitfield.set_size_bits(bitfield->size_bits()); m_bitfield.allocate(); m_readPos = std::min(m_bitfield.size_bytes(), m_readBuffer.remaining()); std::memcpy(m_bitfield.begin(), m_readBuffer.position(), m_readPos); m_readBuffer.consume(m_readPos); m_state = READ_BITFIELD; } else if (m_readBuffer.peek_8_at(4) == protocol_extension && m_extensions->is_initial_handshake()) { m_readPos = 0; m_state = READ_EXT; } else if (m_readBuffer.peek_8_at(4) == protocol_port) { // Some peers seem to send the port message before handshake, // so handle it here. m_readPos = 0; m_state = READ_PORT; } else { read_done(); break; } case READ_BITFIELD: case READ_EXT: case READ_PORT: // Gather the different command types into the same case group // so that we don't need 'goto restart' above. if ((m_state == READ_BITFIELD && !read_bitfield()) || (m_state == READ_EXT && !read_extension()) || (m_state == READ_PORT && !read_port())) break; m_state = READ_MESSAGE; if (!m_bitfield.empty() && (!m_peerInfo->supports_extensions() || !m_extensions->is_initial_handshake())) { read_done(); break; } goto restart; default: throw internal_error("Handshake::event_read() called in invalid state."); } // Call event_write if we have any data to write. Make sure // event_write() doesn't get called twice in this function. if (m_writeBuffer.remaining() && !manager->poll()->in_write(this)) { manager->poll()->insert_write(this); return event_write(); } } catch (handshake_succeeded& e) { m_manager->receive_succeeded(this); } catch (handshake_error& e) { m_manager->receive_failed(this, e.type(), e.error()); } catch (network_error& e) { m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error); } } bool Handshake::fill_read_buffer(int size) { if (m_readBuffer.remaining() < size) { if (size - m_readBuffer.remaining() > m_readBuffer.reserved_left()) throw internal_error("Handshake::fill_read_buffer(...) Buffer overflow."); int read = m_readBuffer.move_end(read_unthrottled(m_readBuffer.end(), size - m_readBuffer.remaining())); if (m_encryption.info()->decrypt_valid()) m_encryption.info()->decrypt(m_readBuffer.end() - read, read); } return m_readBuffer.remaining() >= size; } inline void Handshake::validate_download() { if (m_download == NULL) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unknown_download); if (!m_download->info()->is_active()) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_inactive_download); if (!m_download->info()->is_accepting_new_peers()) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_not_accepting_connections); } void Handshake::event_write() { try { switch (m_state) { case CONNECTING: if (get_fd().get_error()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_network_unreachable); manager->poll()->insert_read(this); if (m_encryption.options() & ConnectionManager::encryption_use_proxy) { prepare_proxy_connect(); m_state = PROXY_CONNECT; break; } case PROXY_DONE: // If there's any bytes remaining, it means we got a reply from // the other side before our proxy connect command was finished // written. This probably means the other side isn't a proxy. if (m_writeBuffer.remaining()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_not_bittorrent); m_writeBuffer.reset(); if (m_encryption.options() & (ConnectionManager::encryption_try_outgoing | ConnectionManager::encryption_require)) { prepare_key_plus_pad(); // if connection fails, peer probably closed it because it was encrypted, so retry encrypted if enabled if (!(m_encryption.options() & ConnectionManager::encryption_require)) m_encryption.set_retry(HandshakeEncryption::RETRY_PLAIN); m_state = READ_ENC_KEY; } else { // if connection is closed before we read the handshake, it might // be rejected because it is unencrypted, in that case retry encrypted m_encryption.set_retry(HandshakeEncryption::RETRY_ENCRYPTED); prepare_handshake(); if (m_incoming) m_state = READ_PEER; else m_state = READ_INFO; } break; case READ_MESSAGE: case READ_BITFIELD: case READ_EXT: write_bitfield(); return; default: break; } if (!m_writeBuffer.remaining()) throw internal_error("event_write called with empty write buffer."); if (m_writeBuffer.consume(write_unthrottled(m_writeBuffer.position(), m_writeBuffer.remaining()))) { if (m_state == POST_HANDSHAKE) write_done(); else manager->poll()->remove_write(this); } } catch (handshake_succeeded& e) { m_manager->receive_succeeded(this); } catch (handshake_error& e) { m_manager->receive_failed(this, e.type(), e.error()); } catch (network_error& e) { m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error); } } void Handshake::prepare_proxy_connect() { char buf[256]; m_address.address_c_str(buf, 256); int advance = snprintf((char*)m_writeBuffer.position(), m_writeBuffer.reserved_left(), "CONNECT %s:%hu HTTP/1.0\r\n\r\n", buf, m_address.port()); if (advance == -1 || advance > m_writeBuffer.reserved_left()) throw internal_error("Handshake::prepare_proxy_connect() snprintf failed."); m_writeBuffer.move_end(advance); } void Handshake::prepare_key_plus_pad() { if (!m_encryption.initialize()) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_invalid_value); m_encryption.key()->store_pub_key(m_writeBuffer.end(), 96); m_writeBuffer.move_end(96); int length = random() % enc_pad_size; char pad[length]; std::generate_n(pad, length, &::random); m_writeBuffer.write_len(pad, length); } void Handshake::prepare_enc_negotiation() { char hash[20]; // first piece, HASH('req1' + S) sha1_salt("req1", 4, m_encryption.key()->c_str(), m_encryption.key()->size(), m_writeBuffer.end()); m_writeBuffer.move_end(20); // second piece, HASH('req2' + SKEY) ^ HASH('req3' + S) m_writeBuffer.write_len(m_download->info()->hash_obfuscated().c_str(), 20); sha1_salt("req3", 4, m_encryption.key()->c_str(), m_encryption.key()->size(), hash); for (int i = 0; i < 20; i++) m_writeBuffer.end()[i - 20] ^= hash[i]; // last piece, ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)) m_encryption.initialize_encrypt(m_download->info()->hash().c_str(), m_incoming); Buffer::iterator old_end = m_writeBuffer.end(); HandshakeEncryption::copy_vc(m_writeBuffer.end()); m_writeBuffer.move_end(HandshakeEncryption::vc_length); if (m_encryption.options() & ConnectionManager::encryption_require_RC4) m_writeBuffer.write_32(HandshakeEncryption::crypto_rc4); else m_writeBuffer.write_32(HandshakeEncryption::crypto_plain | HandshakeEncryption::crypto_rc4); m_writeBuffer.write_16(0); m_writeBuffer.write_16(handshake_size); m_encryption.info()->encrypt(old_end, m_writeBuffer.end() - old_end); // write and encrypt BT handshake as initial payload IA prepare_handshake(); } void Handshake::prepare_handshake() { m_writeBuffer.write_8(19); m_writeBuffer.write_range(m_protocol, m_protocol + 19); std::memset(m_writeBuffer.end(), 0, 8); *(m_writeBuffer.end()+5) |= 0x10; // support extension protocol if (manager->dht_manager()->is_active()) *(m_writeBuffer.end()+7) |= 0x01; // DHT support, enable PORT message m_writeBuffer.move_end(8); m_writeBuffer.write_range(m_download->info()->hash().c_str(), m_download->info()->hash().c_str() + 20); m_writeBuffer.write_range(m_download->info()->local_id().c_str(), m_download->info()->local_id().c_str() + 20); if (m_encryption.info()->is_encrypted()) m_encryption.info()->encrypt(m_writeBuffer.end() - handshake_size, handshake_size); } void Handshake::prepare_peer_info() { if (std::memcmp(m_readBuffer.position(), m_download->info()->local_id().c_str(), 20) == 0) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_is_self); // PeerInfo handling for outgoing connections needs to be moved to // HandshakeManager. if (m_peerInfo == NULL) { if (!m_incoming) throw internal_error("Handshake::prepare_peer_info() !m_incoming."); m_peerInfo = m_download->peer_list()->connected(m_address.c_sockaddr(), PeerList::connect_incoming); if (m_peerInfo == NULL) throw handshake_error(ConnectionManager::handshake_failed, e_handshake_network_error); if (m_peerInfo->failed_counter() > m_manager->max_failed) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_toomanyfailed); m_peerInfo->set_flags(PeerInfo::flag_handshake); } std::memcpy(m_peerInfo->set_options(), m_options, 8); m_peerInfo->mutable_id().assign((const char*)m_readBuffer.position()); m_readBuffer.consume(20); hash_string_to_hex(m_peerInfo->id(), m_peerInfo->mutable_id_hex()); // For meta downloads, we require support of the extension protocol. if (m_download->info()->is_meta_download() && !m_peerInfo->supports_extensions()) throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unwanted_connection); } void Handshake::prepare_bitfield() { m_writeBuffer.write_32(m_download->file_list()->bitfield()->size_bytes() + 1); m_writeBuffer.write_8(protocol_bitfield); if (m_encryption.info()->is_encrypted()) m_encryption.info()->encrypt(m_writeBuffer.end() - 5, 5); m_writePos = 0; } void Handshake::prepare_post_handshake(bool must_write) { if (m_writePos != m_download->file_list()->bitfield()->size_bytes()) throw internal_error("Handshake::prepare_post_handshake called while bitfield not written completely."); m_state = POST_HANDSHAKE; Buffer::iterator old_end = m_writeBuffer.end(); // Send PORT message for DHT if enabled and peer supports it. if (m_peerInfo->supports_dht() && manager->dht_manager()->is_active() && manager->dht_manager()->can_receive_queries()) { m_writeBuffer.write_32(3); m_writeBuffer.write_8(protocol_port); m_writeBuffer.write_16(manager->dht_manager()->port()); manager->dht_manager()->port_sent(); } // Send a keep-alive if we still must send something. if (must_write && old_end == m_writeBuffer.end()) m_writeBuffer.write_32(0); if (m_encryption.info()->is_encrypted()) m_encryption.info()->encrypt(old_end, m_writeBuffer.end() - old_end); if (!m_writeBuffer.remaining()) write_done(); } void Handshake::write_done() { m_writeDone = true; manager->poll()->remove_write(this); // Ok to just check m_readDone as the call in event_read() won't // set it before the call. if (m_readDone) throw handshake_succeeded(); } void Handshake::write_extension_handshake() { DownloadInfo* info = m_download->info(); if (m_extensions->is_default()) { m_extensions = new ProtocolExtension; m_extensions->set_info(m_peerInfo, m_download); } // PEX may be disabled but still active if disabled since last download tick. if (info->is_pex_enabled() && info->is_pex_active() && info->size_pex() < info->max_size_pex()) m_extensions->set_local_enabled(ProtocolExtension::UT_PEX); DataBuffer message = m_extensions->generate_handshake_message(); m_writeBuffer.write_32(message.length() + 2); m_writeBuffer.write_8(protocol_extension); m_writeBuffer.write_8(ProtocolExtension::HANDSHAKE); m_writeBuffer.write_range(message.data(), message.end()); if (m_encryption.info()->is_encrypted()) m_encryption.info()->encrypt(m_writeBuffer.end() - message.length() - 2 - 4, message.length() + 2 + 4); message.clear(); } void Handshake::write_bitfield() { const Bitfield* bitfield = m_download->file_list()->bitfield(); if (m_writeDone != false) throw internal_error("Handshake::event_write() m_writeDone != false."); if (m_writeBuffer.remaining()) if (!m_writeBuffer.consume(write_unthrottled(m_writeBuffer.position(), m_writeBuffer.remaining()))) return; if (m_writePos != bitfield->size_bytes()) { if (m_encryption.info()->is_encrypted()) { if (m_writePos == 0) m_writeBuffer.reset(); // this should be unnecessary now uint32_t length = std::min(bitfield->size_bytes() - m_writePos, m_writeBuffer.reserved()) - m_writeBuffer.size_end(); if (length > 0) { std::memcpy(m_writeBuffer.end(), bitfield->begin() + m_writePos + m_writeBuffer.size_end(), length); m_encryption.info()->encrypt(m_writeBuffer.end(), length); m_writeBuffer.move_end(length); } length = write_unthrottled(m_writeBuffer.begin(), m_writeBuffer.size_end()); m_writePos += length; if (length != m_writeBuffer.size_end() && length > 0) std::memmove(m_writeBuffer.begin(), m_writeBuffer.begin() + length, m_writeBuffer.size_end() - length); m_writeBuffer.move_end(-length); } else { m_writePos += write_unthrottled(bitfield->begin() + m_writePos, bitfield->size_bytes() - m_writePos); } } // We can't call prepare_post_handshake until the read code is done reading // the bitfield, so if we get here before then, postpone the post handshake // data until reading is done. Since we're done writing, remove us from the // poll in that case. if (m_writePos == bitfield->size_bytes()) { if (!m_readDone) manager->poll()->remove_write(this); else prepare_post_handshake(false); } } void Handshake::event_error() { if (m_state == INACTIVE) throw internal_error("Handshake::event_error() called on an inactive handshake."); m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error); } } libtorrent-0.13.6/src/protocol/handshake.h000066400000000000000000000153001257211073700205200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_HANDSHAKE_H #define LIBTORRENT_HANDSHAKE_H #include #include "net/protocol_buffer.h" #include "net/socket_stream.h" #include "torrent/bitfield.h" #include "torrent/peer/peer_info.h" #include "utils/sha1.h" #include "handshake_encryption.h" namespace torrent { class HandshakeManager; class DownloadMain; class Handshake : public SocketStream { public: static const uint32_t part1_size = 20 + 28; static const uint32_t part2_size = 20; static const uint32_t handshake_size = part1_size + part2_size; static const uint32_t protocol_bitfield = 5; static const uint32_t protocol_port = 9; static const uint32_t protocol_extension = 20; static const uint32_t enc_negotiation_size = 8 + 4 + 2; static const uint32_t enc_pad_size = 512; static const uint32_t enc_pad_read_size = 96 + enc_pad_size + 20; static const uint32_t buffer_size = enc_pad_read_size + 20 + enc_negotiation_size + enc_pad_size + 2 + handshake_size + 5; typedef ProtocolBuffer Buffer; typedef enum { INACTIVE, CONNECTING, POST_HANDSHAKE, PROXY_CONNECT, PROXY_DONE, READ_ENC_KEY, READ_ENC_SYNC, READ_ENC_SKEY, READ_ENC_NEGOT, READ_ENC_PAD, READ_ENC_IA, READ_INFO, READ_PEER, READ_MESSAGE, READ_BITFIELD, READ_EXT, READ_PORT } State; Handshake(SocketFd fd, HandshakeManager* m, int encryption_options); ~Handshake(); const char* type_name() const { return "handshake"; } bool is_active() const { return m_state != INACTIVE; } State state() const { return m_state; } void initialize_incoming(const rak::socket_address& sa); void initialize_outgoing(const rak::socket_address& sa, DownloadMain* d, PeerInfo* peerInfo); PeerInfo* peer_info() { return m_peerInfo; } const PeerInfo* peer_info() const { return m_peerInfo; } void set_peer_info(PeerInfo* p) { m_peerInfo = p; } const rak::socket_address* socket_address() const { return &m_address; } DownloadMain* download() { return m_download; } Bitfield* bitfield() { return &m_bitfield; } void deactivate_connection(); void release_connection(); void destroy_connection(); const void* unread_data() { return m_readBuffer.position(); } uint32_t unread_size() const { return m_readBuffer.remaining(); } rak::timer initialized_time() const { return m_initializedTime; } virtual void event_read(); virtual void event_write(); virtual void event_error(); HandshakeEncryption* encryption() { return &m_encryption; } ProtocolExtension* extensions() { return m_extensions; } int retry_options(); protected: Handshake(const Handshake&); void operator = (const Handshake&); void read_done(); void write_done(); bool fill_read_buffer(int size); // Check what is unnessesary. bool read_proxy_connect(); bool read_encryption_key(); bool read_encryption_sync(); bool read_encryption_skey(); bool read_encryption_negotiation(); bool read_negotiation_reply(); bool read_info(); bool read_peer(); bool read_bitfield(); bool read_extension(); bool read_port(); void prepare_proxy_connect(); void prepare_key_plus_pad(); void prepare_enc_negotiation(); void prepare_handshake(); void prepare_peer_info(); void prepare_bitfield(); void prepare_post_handshake(bool must_write); void write_extension_handshake(); void write_bitfield(); inline void validate_download(); uint32_t read_unthrottled(void* buf, uint32_t length); uint32_t write_unthrottled(const void* buf, uint32_t length); static const char* m_protocol; State m_state; HandshakeManager* m_manager; PeerInfo* m_peerInfo; DownloadMain* m_download; Bitfield m_bitfield; ThrottleList* m_uploadThrottle; ThrottleList* m_downloadThrottle; rak::priority_item m_taskTimeout; rak::timer m_initializedTime; uint32_t m_readPos; uint32_t m_writePos; bool m_readDone; bool m_writeDone; bool m_incoming; rak::socket_address m_address; char m_options[8]; HandshakeEncryption m_encryption; ProtocolExtension* m_extensions; // Put these last to keep variables closer to *this. Buffer m_readBuffer; Buffer m_writeBuffer; }; } #endif libtorrent-0.13.6/src/protocol/handshake_encryption.cc000066400000000000000000000111771257211073700231400ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "torrent/connection_manager.h" #include "torrent/exceptions.h" #include "utils/diffie_hellman.h" #include "utils/sha1.h" #include "handshake_encryption.h" namespace torrent { const unsigned char HandshakeEncryption::dh_prime[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63, }; const unsigned char HandshakeEncryption::dh_generator[] = { 2 }; const unsigned char HandshakeEncryption::vc_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; bool HandshakeEncryption::should_retry() const { return (m_options & ConnectionManager::encryption_enable_retry) != 0 && m_retry != HandshakeEncryption::RETRY_NONE; } bool HandshakeEncryption::initialize() { m_key = new DiffieHellman(dh_prime, dh_prime_length, dh_generator, dh_generator_length); return m_key->is_valid(); } void HandshakeEncryption::cleanup() { delete m_key; m_key = NULL; } bool HandshakeEncryption::compare_vc(const void* buf) { return std::memcmp(buf, vc_data, vc_length) == 0; } void HandshakeEncryption::initialize_decrypt(const char* origHash, bool incoming) { char hash[20]; unsigned char discard[1024]; sha1_salt(incoming ? "keyA" : "keyB", 4, m_key->c_str(), 96, origHash, 20, hash); m_info.set_decrypt(RC4((const unsigned char*)hash, 20)); m_info.decrypt(discard, 1024); } void HandshakeEncryption::initialize_encrypt(const char* origHash, bool incoming) { char hash[20]; unsigned char discard[1024]; sha1_salt(incoming ? "keyB" : "keyA", 4, m_key->c_str(), 96, origHash, 20, hash); m_info.set_encrypt(RC4((const unsigned char*)hash, 20)); m_info.encrypt(discard, 1024); } // Obfuscated hash is HASH('req2', download_hash), extract that from // HASH('req2', download_hash) ^ HASH('req3', S). void HandshakeEncryption::deobfuscate_hash(char* src) const { char tmp[20]; sha1_salt("req3", 4, m_key->c_str(), m_key->size(), tmp); for (int i = 0; i < 20; i++) src[i] ^= tmp[i]; } void HandshakeEncryption::hash_req1_to_sync() { sha1_salt("req1", 4, m_key->c_str(), m_key->size(), modify_sync(20)); } void HandshakeEncryption::encrypt_vc_to_sync(const char* origHash) { m_syncLength = vc_length; std::memcpy(m_sync, vc_data, vc_length); char hash[20]; char discard[1024]; sha1_salt("keyB", 4, m_key->c_str(), 96, origHash, 20, hash); RC4 peerEncrypt((const unsigned char*)hash, 20); peerEncrypt.crypt(discard, 1024); peerEncrypt.crypt(m_sync, HandshakeEncryption::vc_length); } } libtorrent-0.13.6/src/protocol/handshake_encryption.h000066400000000000000000000113161257211073700227750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_HANDSHAKE_ENCRYPTION_H #define LIBTORRENT_PROTOCOL_HANDSHAKE_ENCRYPTION_H #include #include "encryption_info.h" namespace torrent { class DiffieHellman; class HandshakeEncryption { public: typedef enum { RETRY_NONE, RETRY_PLAIN, RETRY_ENCRYPTED, } Retry; static const int crypto_plain = 1; static const int crypto_rc4 = 2; static const unsigned char dh_prime[]; static const unsigned int dh_prime_length = 96; static const unsigned char dh_generator[]; static const unsigned int dh_generator_length = 1; static const unsigned char vc_data[]; static const unsigned int vc_length = 8; HandshakeEncryption(int options) : m_key(NULL), m_options(options), m_crypto(0), m_retry(RETRY_NONE), m_syncLength(0), m_lengthIA(0) {} bool has_crypto_plain() const { return m_crypto & crypto_plain; } bool has_crypto_rc4() const { return m_crypto & crypto_rc4; } DiffieHellman* key() { return m_key; } EncryptionInfo* info() { return &m_info; } int options() const { return m_options; } int crypto() const { return m_crypto; } void set_crypto(int val) { m_crypto = val; } Retry retry() const { return m_retry; } void set_retry(Retry val) { m_retry = val; } bool should_retry() const; const char* sync() const { return m_sync; } unsigned int sync_length() const { return m_syncLength; } void set_sync(const char* src, unsigned int len) { std::memcpy(m_sync, src, (m_syncLength = len)); } char* modify_sync(unsigned int len) { m_syncLength = len; return m_sync; } unsigned int length_ia() const { return m_lengthIA; } void set_length_ia(unsigned int len) { m_lengthIA = len; } bool initialize(); void cleanup(); void initialize_decrypt(const char* origHash, bool incoming); void initialize_encrypt(const char* origHash, bool incoming); void deobfuscate_hash(char* src) const; void hash_req1_to_sync(); void encrypt_vc_to_sync(const char* origHash); static void copy_vc(void* dest) { std::memset(dest, 0, vc_length); } static bool compare_vc(const void* buf); private: DiffieHellman* m_key; // A pointer instead? EncryptionInfo m_info; int m_options; int m_crypto; Retry m_retry; char m_sync[20]; unsigned int m_syncLength; unsigned int m_lengthIA; }; } #endif libtorrent-0.13.6/src/protocol/handshake_manager.cc000066400000000000000000000250621257211073700223560ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "torrent/exceptions.h" #include "torrent/error.h" #include "download/download_main.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/peer/peer_info.h" #include "torrent/peer/client_list.h" #include "torrent/peer/connection_list.h" #include "torrent/utils/log.h" #include "peer_connection_base.h" #include "handshake.h" #include "handshake_manager.h" #include "manager.h" #define LT_LOG_SA(log_level, sa, log_fmt, ...) \ lt_log_print(LOG_CONNECTION_##log_level, "handshake_manager->%s: " log_fmt, (sa)->address_str().c_str(), __VA_ARGS__); #define LT_LOG_SA_C(log_level, sa, log_fmt, ...) \ lt_log_print(LOG_CONNECTION_##log_level, "handshake_manager->%s: " log_fmt, \ reinterpret_cast(sa)->address_str().c_str(), __VA_ARGS__); namespace torrent { ProtocolExtension HandshakeManager::DefaultExtensions = ProtocolExtension::make_default(); inline void handshake_manager_delete_handshake(Handshake* h) { h->deactivate_connection(); h->destroy_connection(); delete h; } HandshakeManager::size_type HandshakeManager::size_info(DownloadMain* info) const { return std::count_if(base_type::begin(), base_type::end(), rak::equal(info, std::mem_fun(&Handshake::download))); } void HandshakeManager::clear() { std::for_each(base_type::begin(), base_type::end(), std::ptr_fun(&handshake_manager_delete_handshake)); base_type::clear(); } void HandshakeManager::erase(Handshake* handshake) { iterator itr = std::find(base_type::begin(), base_type::end(), handshake); if (itr == base_type::end()) throw internal_error("HandshakeManager::erase(...) could not find handshake."); base_type::erase(itr); } struct handshake_manager_equal : std::binary_function { bool operator () (const rak::socket_address* sa1, const Handshake* p2) const { return p2->peer_info() != NULL && *sa1 == *rak::socket_address::cast_from(p2->peer_info()->socket_address()); } }; bool HandshakeManager::find(const rak::socket_address& sa) { return std::find_if(base_type::begin(), base_type::end(), std::bind1st(handshake_manager_equal(), &sa)) != base_type::end(); } void HandshakeManager::erase_download(DownloadMain* info) { iterator split = std::partition(base_type::begin(), base_type::end(), rak::not_equal(info, std::mem_fun(&Handshake::download))); std::for_each(split, base_type::end(), std::ptr_fun(&handshake_manager_delete_handshake)); base_type::erase(split, base_type::end()); } void HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) { if (!manager->connection_manager()->can_connect() || !manager->connection_manager()->filter(sa.c_sockaddr()) || !setup_socket(fd)) { fd.close(); return; } LT_LOG_SA(INFO, &sa, "Adding incoming connection: fd:%i.", fd.get_fd()); manager->connection_manager()->inc_socket_count(); Handshake* h = new Handshake(fd, this, manager->connection_manager()->encryption_options()); h->initialize_incoming(sa); base_type::push_back(h); } void HandshakeManager::add_outgoing(const rak::socket_address& sa, DownloadMain* download) { if (!manager->connection_manager()->can_connect() || !manager->connection_manager()->filter(sa.c_sockaddr())) return; create_outgoing(sa, download, manager->connection_manager()->encryption_options()); } void HandshakeManager::create_outgoing(const rak::socket_address& sa, DownloadMain* download, int encryptionOptions) { int connection_options = PeerList::connect_keep_handshakes; if (!(encryptionOptions & ConnectionManager::encryption_retrying)) connection_options |= PeerList::connect_filter_recent; PeerInfo* peerInfo = download->peer_list()->connected(sa.c_sockaddr(), connection_options); if (peerInfo == NULL || peerInfo->failed_counter() > max_failed) return; SocketFd fd; const rak::socket_address* bindAddress = rak::socket_address::cast_from(manager->connection_manager()->bind_address()); const rak::socket_address* connectAddress = &sa; if (rak::socket_address::cast_from(manager->connection_manager()->proxy_address())->is_valid()) { connectAddress = rak::socket_address::cast_from(manager->connection_manager()->proxy_address()); encryptionOptions |= ConnectionManager::encryption_use_proxy; } if (!fd.open_stream() || !setup_socket(fd) || (bindAddress->is_bindable() && !fd.bind(*bindAddress)) || !fd.connect(*connectAddress)) { if (fd.is_valid()) fd.close(); download->peer_list()->disconnected(peerInfo, 0); return; } int message; if (encryptionOptions & ConnectionManager::encryption_use_proxy) message = ConnectionManager::handshake_outgoing_proxy; else if (encryptionOptions & (ConnectionManager::encryption_try_outgoing | ConnectionManager::encryption_require)) message = ConnectionManager::handshake_outgoing_encrypted; else message = ConnectionManager::handshake_outgoing; LT_LOG_SA(INFO, &sa, "Adding outcoming connection: encryption:%x message:%x.", encryptionOptions, message); manager->connection_manager()->inc_socket_count(); Handshake* handshake = new Handshake(fd, this, encryptionOptions); handshake->initialize_outgoing(sa, download, peerInfo); base_type::push_back(handshake); } void HandshakeManager::receive_succeeded(Handshake* handshake) { if (!handshake->is_active()) throw internal_error("HandshakeManager::receive_succeeded(...) called on an inactive handshake."); erase(handshake); handshake->deactivate_connection(); DownloadMain* download = handshake->download(); PeerConnectionBase* pcb; if (download->info()->is_active() && download->connection_list()->want_connection(handshake->peer_info(), handshake->bitfield()) && (pcb = download->connection_list()->insert(handshake->peer_info(), handshake->get_fd(), handshake->bitfield(), handshake->encryption()->info(), handshake->extensions())) != NULL) { manager->client_list()->retrieve_id(&handshake->peer_info()->mutable_client_info(), handshake->peer_info()->id()); LT_LOG_SA_C(INFO, handshake->peer_info()->socket_address(), "Handshake success.", 0); pcb->peer_chunks()->set_have_timer(handshake->initialized_time()); if (handshake->unread_size() != 0) { if (handshake->unread_size() > PeerConnectionBase::ProtocolRead::buffer_size) throw internal_error("HandshakeManager::receive_succeeded(...) Unread data won't fit PCB's read buffer."); pcb->push_unread(handshake->unread_data(), handshake->unread_size()); pcb->event_read(); } handshake->release_connection(); } else { uint32_t reason; if (!download->info()->is_active()) reason = e_handshake_inactive_download; else if (download->file_list()->is_done() && handshake->bitfield()->is_all_set()) reason = e_handshake_unwanted_connection; else reason = e_handshake_duplicate; LT_LOG_SA_C(INFO, handshake->peer_info()->socket_address(), "Handshake dropped: %s.", strerror(reason)); handshake->destroy_connection(); } delete handshake; } void HandshakeManager::receive_failed(Handshake* handshake, int message, int error) { if (!handshake->is_active()) throw internal_error("HandshakeManager::receive_failed(...) called on an inactive handshake."); const rak::socket_address* sa = handshake->socket_address(); erase(handshake); handshake->deactivate_connection(); handshake->destroy_connection(); LT_LOG_SA(INFO, sa, "Received error: message:%x %s.", message, strerror(error)); if (handshake->encryption()->should_retry()) { int retry_options = handshake->retry_options() | ConnectionManager::encryption_retrying; DownloadMain* download = handshake->download(); LT_LOG_SA(INFO, sa, "Retrying %s.", retry_options & ConnectionManager::encryption_try_outgoing ? "encrypted" : "plaintext"); create_outgoing(*sa, download, retry_options); } delete handshake; } void HandshakeManager::receive_timeout(Handshake* h) { receive_failed(h, ConnectionManager::handshake_failed, h->state() == Handshake::CONNECTING ? e_handshake_network_unreachable : e_handshake_network_timeout); } bool HandshakeManager::setup_socket(SocketFd fd) { if (!fd.set_nonblock()) return false; ConnectionManager* m = manager->connection_manager(); if (m->priority() != ConnectionManager::iptos_default && !fd.set_priority(m->priority())) return false; if (m->send_buffer_size() != 0 && !fd.set_send_buffer_size(m->send_buffer_size())) return false; if (m->receive_buffer_size() != 0 && !fd.set_receive_buffer_size(m->receive_buffer_size())) return false; return true; } } libtorrent-0.13.6/src/protocol/handshake_manager.h000066400000000000000000000100021257211073700222040ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_HANDSHAKE_MANAGER_H #define LIBTORRENT_NET_HANDSHAKE_MANAGER_H #include #include #include #include #include #include #include #include "net/socket_fd.h" namespace torrent { class Handshake; class DownloadManager; class DownloadMain; class PeerConnectionBase; class HandshakeManager : private rak::unordered_vector { public: typedef rak::unordered_vector base_type; typedef uint32_t size_type; typedef std::tr1::function slot_download; // Do not connect to peers with this many or more failed chunks. static const unsigned int max_failed = 3; using base_type::empty; HandshakeManager() { } ~HandshakeManager() { clear(); } size_type size() const { return base_type::size(); } size_type size_info(DownloadMain* info) const; void clear(); bool find(const rak::socket_address& sa); void erase_download(DownloadMain* info); // Cleanup. void add_incoming(SocketFd fd, const rak::socket_address& sa); void add_outgoing(const rak::socket_address& sa, DownloadMain* info); slot_download& slot_download_id() { return m_slot_download_id; } slot_download& slot_download_obfuscated() { return m_slot_download_obfuscated; } // This needs to be filterable slot. DownloadMain* download_info(const char* hash) { return m_slot_download_id(hash); } DownloadMain* download_info_obfuscated(const char* hash) { return m_slot_download_obfuscated(hash); } void receive_succeeded(Handshake* h); void receive_failed(Handshake* h, int message, int error); void receive_timeout(Handshake* h); ProtocolExtension* default_extensions() const { return &DefaultExtensions; } private: void create_outgoing(const rak::socket_address& sa, DownloadMain* info, int encryptionOptions); void erase(Handshake* handshake); bool setup_socket(SocketFd fd); static ProtocolExtension DefaultExtensions; slot_download m_slot_download_id; slot_download m_slot_download_obfuscated; }; } #endif libtorrent-0.13.6/src/protocol/initial_seed.cc000066400000000000000000000240711257211073700213660ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "download/chunk_statistics.h" #include "initial_seed.h" #include "peer_connection_leech.h" namespace torrent { PeerInfo* const InitialSeeding::chunk_unsent = (PeerInfo*) 0; PeerInfo* const InitialSeeding::chunk_unknown = (PeerInfo*) 1; PeerInfo* const InitialSeeding::chunk_done = (PeerInfo*) 2; InitialSeeding::InitialSeeding(DownloadMain* download) : m_nextChunk(0), m_chunksLeft(download->file_list()->size_chunks()), m_download(download), m_peerChunks(new PeerInfo*[m_chunksLeft]) { memset(m_peerChunks, 0, m_chunksLeft * sizeof(m_peerChunks[0])); } InitialSeeding::~InitialSeeding() { unblock_all(); delete[] m_peerChunks; } inline bool InitialSeeding::valid_peer(PeerInfo* peer) { return peer > chunk_done; } void InitialSeeding::clear_peer(PeerInfo* peer) { if (!valid_peer(peer)) return; peer->unset_flags(PeerInfo::flag_blocked); // If peer is still connected, offer new piece right away. if (peer->connection() != NULL) peer->connection()->write_insert_poll_safe(); } void InitialSeeding::chunk_seen(uint32_t index, PeerConnectionBase* pcb) { // When we have two other seeds, trust that the download will // be sufficiently seeded and switch to normal seeding. This is // mainly for when the user accidentally enables initial seeding. if (m_download->chunk_statistics()->complete() > 1) complete(pcb); PeerInfo* peer = pcb->mutable_peer_info(); PeerInfo* old = m_peerChunks[index]; // We didn't send this chunk. Is someone else initial seeding too? // Or maybe we restarted and the peer got this chunk from someone // we did send it to. Either way, we don't know who it belongs to. // Don't mark it done until we see it from someone else, though. if (old == chunk_unsent) { m_peerChunks[index] = chunk_unknown; return; } if (old == peer || old == chunk_done) return; // We've seen two peers on the swarm receive this chunk. m_peerChunks[index] = chunk_done; if (--m_chunksLeft == 0) complete(pcb); // The peer we sent it to originally may now receive another chunk. clear_peer(old); } void InitialSeeding::chunk_complete(uint32_t index, PeerConnectionBase* pcb) { clear_peer(m_peerChunks[index]); m_peerChunks[index] = chunk_unknown; chunk_seen(index, pcb); } void InitialSeeding::new_peer(PeerConnectionBase* pcb) { PeerInfo* peer = pcb->mutable_peer_info(); if (peer->is_blocked()) peer->set_flags(PeerInfo::flag_restart); // We don't go through the peer's entire bitfield here. This eliminates // cheating by sending a bogus bitfield if it figures out we are initial // seeding, to drop us out of it. We should see HAVE messages for pieces // it has that we were waiting for anyway. We will check individual chunks // as we are about to offer them, to avoid the overhead of checking each // peer's bitfield as well. If it really was cheating, the pieces it isn't // sharing will be sent during the second round of initial seeding. // If we're on the second round, don't check // it until we're about to offer a chunk. if (m_peerChunks[m_nextChunk] != chunk_unsent) return; // But during primary initial seeding (some chunks not sent at all), // check that nobody already has the next chunk we were going to send. while (m_peerChunks[m_nextChunk] == chunk_unsent && (*m_download->chunk_statistics())[m_nextChunk]) { // Could set to chunk_done if enough peers have it, but if that was the // last one it could cause initial seeding to end and all connections to // be closed, and now is a bad time for that (still being set up). Plus // this gives us the opportunity to wait for HAVE messages and resend // the chunk if it's not being shared. m_peerChunks[m_nextChunk] = chunk_unknown; find_next(false, pcb); } } uint32_t InitialSeeding::chunk_offer(PeerConnectionBase* pcb, uint32_t chunkDone) { PeerInfo* peer = pcb->mutable_peer_info(); // If this peer completely downloaded the chunk we offered and we have too // many unused upload slots, give it another chunk to download for free. if (peer->is_blocked() && chunkDone != no_offer && m_peerChunks[chunkDone] == peer && m_download->choke_group()->up_queue()->size_total() * 10 < 9 * m_download->choke_group()->up_queue()->max_unchoked()) { m_peerChunks[chunkDone] = chunk_unknown; peer->unset_flags(PeerInfo::flag_blocked); // Otherwise check if we can offer a chunk normally. } else if (peer->is_blocked()) { if (!peer->is_restart()) return no_offer; peer->unset_flags(PeerInfo::flag_restart); // Re-connection of a peer we already sent a chunk. // Offer the same chunk again. PeerInfo** peerChunksEnd = m_peerChunks + m_download->file_list()->size_chunks(); PeerInfo** itr = std::find_if(m_peerChunks, peerChunksEnd, std::bind2nd(std::equal_to(), peer)); if (itr != peerChunksEnd) return itr - m_peerChunks; // Couldn't find the chunk, we probably sent it to someone // else since the disconnection. So offer a new one. } uint32_t index = m_nextChunk; bool secondary = false; // If we already sent this chunk to someone else, we're on the second // (or more) round. We might have already found this chunk elsewhere on // the swarm since then and need to find a different one if so. if (m_peerChunks[index] != chunk_unsent) { secondary = true; // Accounting for peers whose bitfield we didn't check when connecting. // If the chunk stats say there are enough peers who have it, believe that. if (m_peerChunks[index] != chunk_done && (*m_download->chunk_statistics())[index] > 1) chunk_complete(index, pcb); if (m_peerChunks[index] == chunk_done) index = find_next(true, pcb); } // When we only have one chunk left and we already offered it // to someone who hasn't shared it yet, offer it to everyone // else. We do not override the peer we sent it to, so they // cannot be unblocked, but when initial seeding completes // everyone is unblocked anyway. if (m_chunksLeft == 1 && valid_peer(m_peerChunks[index])) { peer->set_flags(PeerInfo::flag_blocked); return index; } // Make sure we don't accidentally offer a chunk it has // already, or it would never even request it from us. // We'll just offer it to the next peer instead. if (pcb->bitfield()->get(index)) return no_offer; m_peerChunks[index] = peer; peer->set_flags(PeerInfo::flag_blocked); find_next(secondary, pcb); return index; } bool InitialSeeding::should_upload(uint32_t index) { return m_peerChunks[index] != chunk_done; } uint32_t InitialSeeding::find_next(bool secondary, PeerConnectionBase* pcb) { if (!secondary) { // Primary seeding: find next chunk not sent yet. while (++m_nextChunk < m_download->file_list()->size_chunks()) { if (m_peerChunks[m_nextChunk] == chunk_unsent) { if (!(*m_download->chunk_statistics())[m_nextChunk]) return m_nextChunk; // Someone has this one already. We don't know if we sent it or not. m_peerChunks[m_nextChunk] = chunk_unknown; } } // Went through all chunks. Continue with secondary seeding. m_nextChunk--; } // Secondary seeding: find next chunk that's not done yet. do { if (++m_nextChunk == m_download->file_list()->size_chunks()) m_nextChunk = 0; if (m_peerChunks[m_nextChunk] != chunk_done && (*m_download->chunk_statistics())[m_nextChunk] > 1) chunk_complete(m_nextChunk, pcb); } while (m_peerChunks[m_nextChunk] == chunk_done); return m_nextChunk; } void InitialSeeding::complete(PeerConnectionBase* pcb) { unblock_all(); m_chunksLeft = 0; m_nextChunk = no_offer; // We think all chunks should be well seeded now. Check to make sure. for (uint32_t i = 0; i < m_download->file_list()->size_chunks(); i++) { if (m_download->chunk_statistics()->complete() + (*m_download->chunk_statistics())[i] < 2) { // Chunk too rare, send it again before switching to normal seeding. m_chunksLeft++; m_peerChunks[i] = chunk_unsent; if (m_nextChunk == no_offer) m_nextChunk = i; } } if (m_chunksLeft) return; m_download->initial_seeding_done(pcb); } void InitialSeeding::unblock_all() { for (PeerList::const_iterator itr = m_download->peer_list()->begin(); itr != m_download->peer_list()->end(); ++itr) itr->second->unset_flags(PeerInfo::flag_blocked); } } libtorrent-0.13.6/src/protocol/initial_seed.h000066400000000000000000000062171257211073700212320ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_INITIAL_SEED_H #define LIBTORRENT_PROTOCOL_INITIAL_SEED_H #include "download/download_main.h" namespace torrent { class InitialSeeding { public: InitialSeeding(DownloadMain* download); ~InitialSeeding(); static const uint32_t no_offer = ~uint32_t(); void new_peer(PeerConnectionBase* pcb); // Chunk was seen distributed to a peer in the swarm. void chunk_seen(uint32_t index, PeerConnectionBase* pcb); // Returns chunk we may offer the peer or no_offer if none. uint32_t chunk_offer(PeerConnectionBase* pcb, uint32_t indexDone); // During the second stage (seeding rare chunks), return // false if given chunk is already well-seeded now. True otherwise. bool should_upload(uint32_t index); private: static PeerInfo* const chunk_unsent; // Chunk never sent to anyone. static PeerInfo* const chunk_unknown; // Peer has chunk, we don't know who we sent it to. static PeerInfo* const chunk_done; // Chunk properly distributed by peer. uint32_t find_next(bool secondary, PeerConnectionBase* pcb); bool valid_peer(PeerInfo* peer); void clear_peer(PeerInfo* peer); void chunk_complete(uint32_t index, PeerConnectionBase* pcb); void complete(PeerConnectionBase* pcb); void unblock_all(); uint32_t m_nextChunk; uint32_t m_chunksLeft; DownloadMain* m_download; PeerInfo** m_peerChunks; }; } #endif libtorrent-0.13.6/src/protocol/peer_chunks.h000066400000000000000000000102631257211073700211030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_PEER_CHUNKS_H #define LIBTORRENT_PROTOCOL_PEER_CHUNKS_H #include #include #include #include "net/throttle_node.h" #include "torrent/bitfield.h" #include "torrent/data/piece.h" #include "torrent/rate.h" namespace torrent { class PeerInfo; class PeerChunks { public: typedef std::list piece_list_type; PeerChunks(); bool is_seeder() const { return m_bitfield.is_all_set(); } PeerInfo* peer_info() { return m_peerInfo; } const PeerInfo* peer_info() const { return m_peerInfo; } void set_peer_info(PeerInfo* p) { m_peerInfo = p; } bool using_counter() const { return m_usingCounter; } void set_using_counter(bool state) { m_usingCounter = state; } Bitfield* bitfield() { return &m_bitfield; } const Bitfield* bitfield() const { return &m_bitfield; } rak::partial_queue* download_cache() { return &m_downloadCache; } piece_list_type* upload_queue() { return &m_uploadQueue; } const piece_list_type* upload_queue() const { return &m_uploadQueue; } piece_list_type* cancel_queue() { return &m_cancelQueue; } // Timer used to figure out what HAVE_PIECE messages have not been // sent. rak::timer have_timer() const { return m_haveTimer; } void set_have_timer(rak::timer t) { m_haveTimer = t; } Rate* peer_rate() { return &m_peerRate; } const Rate* peer_rate() const { return &m_peerRate; } ThrottleNode* download_throttle() { return &m_downloadThrottle; } const ThrottleNode* download_throttle() const { return &m_downloadThrottle; } ThrottleNode* upload_throttle() { return &m_uploadThrottle; } const ThrottleNode* upload_throttle() const { return &m_uploadThrottle; } private: PeerInfo* m_peerInfo; bool m_usingCounter; Bitfield m_bitfield; rak::partial_queue m_downloadCache; piece_list_type m_uploadQueue; piece_list_type m_cancelQueue; rak::timer m_haveTimer; Rate m_peerRate; ThrottleNode m_downloadThrottle; ThrottleNode m_uploadThrottle; }; inline PeerChunks::PeerChunks() : m_peerInfo(NULL), m_usingCounter(false), m_peerRate(600), m_downloadThrottle(30), m_uploadThrottle(30) { } } #endif libtorrent-0.13.6/src/protocol/peer_connection_base.cc000066400000000000000000001065161257211073700231060ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include #include "data/chunk_iterator.h" #include "data/chunk_list.h" #include "download/chunk_selector.h" #include "download/chunk_statistics.h" #include "download/download_main.h" #include "net/socket_base.h" #include "torrent/exceptions.h" #include "torrent/data/block.h" #include "torrent/chunk_manager.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/throttle.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "torrent/peer/peer_info.h" #include "torrent/peer/connection_list.h" #include "torrent/utils/log.h" #include "utils/instrumentation.h" #include "extensions.h" #include "peer_connection_base.h" #include "manager.h" #define LT_LOG_PIECE_EVENTS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_PIECE_EVENTS, this->download()->info(), "piece_events", "%40s " log_fmt, this->peer_info()->id_hex(), __VA_ARGS__); namespace torrent { inline void log_mincore_stats_func(bool is_incore, bool new_index, bool& continous) { if (!new_index && is_incore) { instrumentation_update(INSTRUMENTATION_MINCORE_INCORE_TOUCHED, 1); } if (new_index && is_incore) { instrumentation_update(INSTRUMENTATION_MINCORE_INCORE_NEW, 1); } if (!new_index && !is_incore) { instrumentation_update(INSTRUMENTATION_MINCORE_NOT_INCORE_TOUCHED, 1); } if (new_index && !is_incore) { instrumentation_update(INSTRUMENTATION_MINCORE_NOT_INCORE_NEW, 1); } if (continous && !is_incore) { instrumentation_update(INSTRUMENTATION_MINCORE_INCORE_BREAK, 1); } continous = is_incore; } PeerConnectionBase::PeerConnectionBase() : m_download(NULL), m_down(new ProtocolRead()), m_up(new ProtocolWrite()), m_downStall(0), m_downInterested(false), m_downUnchoked(false), m_sendChoked(false), m_sendInterested(false), m_tryRequest(true), m_sendPEXMask(0), m_encryptBuffer(NULL), m_extensions(NULL), m_incoreContinous(false) { m_peerInfo = NULL; } PeerConnectionBase::~PeerConnectionBase() { delete m_up; delete m_down; delete m_encryptBuffer; if (m_extensions != NULL && !m_extensions->is_default()) delete m_extensions; m_extensionMessage.clear(); } void PeerConnectionBase::initialize(DownloadMain* download, PeerInfo* peerInfo, SocketFd fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions) { if (get_fd().is_valid()) throw internal_error("Tried to re-set PeerConnection."); if (!fd.is_valid()) throw internal_error("PeerConnectionBase::set(...) received bad input."); if (encryptionInfo->is_encrypted() != encryptionInfo->decrypt_valid()) throw internal_error("Encryption and decryption inconsistent."); set_fd(fd); m_peerInfo = peerInfo; m_download = download; m_encryption = *encryptionInfo; m_extensions = extensions; m_extensions->set_connection(this); m_upChoke.set_entry(m_download->up_group_entry()); m_downChoke.set_entry(m_download->down_group_entry()); m_peerChunks.set_peer_info(m_peerInfo); m_peerChunks.bitfield()->swap(*bitfield); std::pair throttles = m_download->throttles(m_peerInfo->socket_address()); m_up->set_throttle(throttles.first); m_down->set_throttle(throttles.second); m_peerChunks.upload_throttle()->set_list_iterator(m_up->throttle()->end()); m_peerChunks.upload_throttle()->slot_activate() = std::tr1::bind(&SocketBase::receive_throttle_up_activate, static_cast(this)); m_peerChunks.download_throttle()->set_list_iterator(m_down->throttle()->end()); m_peerChunks.download_throttle()->slot_activate() = std::tr1::bind(&SocketBase::receive_throttle_down_activate, static_cast(this)); request_list()->set_delegator(m_download->delegator()); request_list()->set_peer_chunks(&m_peerChunks); try { initialize_custom(); } catch (close_connection& e) { // The handshake manager closes the socket for us. m_peerInfo = NULL; m_download = NULL; m_extensions = NULL; get_fd().clear(); return; } manager->poll()->open(this); manager->poll()->insert_read(this); manager->poll()->insert_write(this); manager->poll()->insert_error(this); m_timeLastRead = cachedTime; m_download->chunk_statistics()->received_connect(&m_peerChunks); // Hmm... cleanup? // update_interested(); m_peerChunks.download_cache()->clear(); if (!m_download->file_list()->is_done()) { m_sendInterested = true; m_downInterested = true; } } void PeerConnectionBase::cleanup() { if (!get_fd().is_valid()) return; if (m_download == NULL) throw internal_error("PeerConnection::~PeerConnection() m_fd is valid but m_state and/or m_net is NULL"); // TODO: Verify that transfer counter gets modified by this... m_request_list.clear(); up_chunk_release(); down_chunk_release(); m_download->info()->set_upload_unchoked(m_download->info()->upload_unchoked() - m_upChoke.unchoked()); m_download->info()->set_download_unchoked(m_download->info()->download_unchoked() - m_downChoke.unchoked()); m_download->choke_group()->up_queue()->disconnected(this, &m_upChoke); m_download->choke_group()->down_queue()->disconnected(this, &m_downChoke); m_download->chunk_statistics()->received_disconnect(&m_peerChunks); if (!m_extensions->is_default()) m_extensions->cleanup(); manager->poll()->remove_read(this); manager->poll()->remove_write(this); manager->poll()->remove_error(this); manager->poll()->close(this); manager->connection_manager()->dec_socket_count(); get_fd().close(); get_fd().clear(); m_up->throttle()->erase(m_peerChunks.upload_throttle()); m_down->throttle()->erase(m_peerChunks.download_throttle()); m_up->set_state(ProtocolWrite::INTERNAL_ERROR); m_down->set_state(ProtocolRead::INTERNAL_ERROR); m_download = NULL; } void PeerConnectionBase::set_upload_snubbed(bool v) { if (v) m_download->choke_group()->up_queue()->set_snubbed(this, &m_upChoke); else m_download->choke_group()->up_queue()->set_not_snubbed(this, &m_upChoke); } bool PeerConnectionBase::receive_upload_choke(bool choke) { if (choke == m_upChoke.choked()) throw internal_error("PeerConnectionBase::receive_upload_choke(...) already set to the same state."); write_insert_poll_safe(); m_sendChoked = true; m_upChoke.set_unchoked(!choke); m_upChoke.set_time_last_choke(cachedTime.usec()); if (choke) { m_download->info()->set_upload_unchoked(m_download->info()->upload_unchoked() - 1); m_upChoke.entry()->connection_choked(this); m_upChoke.entry()->connection_queued(this); m_download->choke_group()->up_queue()->modify_currently_unchoked(-1); m_download->choke_group()->up_queue()->modify_currently_queued(1); } else { m_download->info()->set_upload_unchoked(m_download->info()->upload_unchoked() + 1); m_upChoke.entry()->connection_unqueued(this); m_upChoke.entry()->connection_unchoked(this); m_download->choke_group()->up_queue()->modify_currently_unchoked(1); m_download->choke_group()->up_queue()->modify_currently_queued(-1); } return true; } bool PeerConnectionBase::receive_download_choke(bool choke) { if (choke == m_downChoke.choked()) throw internal_error("PeerConnectionBase::receive_download_choke(...) already set to the same state."); write_insert_poll_safe(); m_downChoke.set_unchoked(!choke); m_downChoke.set_time_last_choke(cachedTime.usec()); if (choke) { m_download->info()->set_download_unchoked(m_download->info()->download_unchoked() - 1); m_downChoke.entry()->connection_choked(this); m_downChoke.entry()->connection_queued(this); m_download->choke_group()->down_queue()->modify_currently_unchoked(-1); m_download->choke_group()->down_queue()->modify_currently_queued(1); } else { m_download->info()->set_download_unchoked(m_download->info()->download_unchoked() + 1); m_downChoke.entry()->connection_unqueued(this); m_downChoke.entry()->connection_unchoked(this); m_download->choke_group()->down_queue()->modify_currently_unchoked(1); m_download->choke_group()->down_queue()->modify_currently_queued(-1); } if (choke) { m_peerChunks.download_cache()->disable(); // If the queue isn't empty, then we might still receive some // pieces, so don't remove us from throttle or release the chunk. if (!request_list()->is_downloading() && request_list()->queued_empty()) { m_down->throttle()->erase(m_peerChunks.download_throttle()); down_chunk_release(); } // Send uninterested if unchoked, but only _after_ receiving our // chunks? if (m_downUnchoked) { // Tell the peer we're no longer interested to avoid // disconnects. We keep the connection in the queue so that // ChokeManager::cycle(...) can attempt to get us unchoked // again. m_sendInterested = m_downInterested; m_downInterested = false; } else { // Remove from queue so that an unchoke from the remote peer // will cause the connection to be unchoked immediately by the // choke manager. // // TODO: This doesn't seem safe... m_download->choke_group()->down_queue()->set_not_queued(this, &m_downChoke); return false; } } else { m_tryRequest = true; if (!m_downInterested) { // We were marked as not interested by the cycling choke and // kept in the queue, thus the peer should have some pieces of // interest. // // We have now been 'unchoked' by the choke manager, so tell the // peer that we're again interested. If the peer doesn't unchoke // us within a cycle or two we're likely to be choked and left // out of the queue. So if the peer unchokes us at a later time, // we skip the queue and unchoke immediately. m_sendInterested = !m_downInterested; m_downInterested = true; } } return true; } void PeerConnectionBase::load_up_chunk() { if (m_upChunk.is_valid() && m_upChunk.index() == m_upPiece.index()) { // Better checking needed. // m_upChunk.chunk()->preload(m_upPiece.offset(), m_upChunk.chunk()->size()); if (lt_log_is_valid(LOG_INSTRUMENTATION_MINCORE)) log_mincore_stats_func(m_upChunk.chunk()->is_incore(m_upPiece.offset(), m_upPiece.length()), false, m_incoreContinous); return; } up_chunk_release(); m_upChunk = m_download->chunk_list()->get(m_upPiece.index()); if (!m_upChunk.is_valid()) throw storage_error("File chunk read error: " + std::string(m_upChunk.error_number().c_str())); if (is_encrypted() && m_encryptBuffer == NULL) { m_encryptBuffer = new EncryptBuffer(); m_encryptBuffer->reset(); } m_incoreContinous = false; if (lt_log_is_valid(LOG_INSTRUMENTATION_MINCORE)) log_mincore_stats_func(m_upChunk.chunk()->is_incore(m_upPiece.offset(), m_upPiece.length()), true, m_incoreContinous); m_incoreContinous = true; // Also check if we've already preloaded in the recent past, even // past unmaps. ChunkManager* cm = manager->chunk_manager(); uint32_t preloadSize = m_upChunk.chunk()->chunk_size() - m_upPiece.offset(); if (cm->preload_type() == 0 || m_upChunk.object()->time_preloaded() >= cachedTime - rak::timer::from_seconds(60) || preloadSize < cm->preload_min_size() || m_peerChunks.upload_throttle()->rate()->rate() < cm->preload_required_rate() * ((preloadSize + (2 << 20) - 1) / (2 << 20))) { cm->inc_stats_not_preloaded(); return; } cm->inc_stats_preloaded(); m_upChunk.object()->set_time_preloaded(cachedTime); m_upChunk.chunk()->preload(m_upPiece.offset(), m_upChunk.chunk()->chunk_size(), cm->preload_type() == 1); } void PeerConnectionBase::cancel_transfer(BlockTransfer* transfer) { if (!get_fd().is_valid()) throw internal_error("PeerConnectionBase::cancel_transfer(...) !get_fd().is_valid()"); if (transfer->peer_info() != peer_info()) throw internal_error("PeerConnectionBase::cancel_transfer(...) peer info doesn't match"); // We don't send cancel messages if the transfer has already // started. if (transfer == m_request_list.transfer()) return; write_insert_poll_safe(); m_peerChunks.cancel_queue()->push_back(transfer->piece()); } void PeerConnectionBase::event_error() { m_download->connection_list()->erase(this, 0); } bool PeerConnectionBase::should_connection_unchoke(choke_queue* cq) const { if (cq == m_download->choke_group()->up_queue()) return m_download->info()->upload_unchoked() < m_download->up_group_entry()->max_slots(); if (cq == m_download->choke_group()->down_queue()) return m_download->info()->download_unchoked() < m_download->down_group_entry()->max_slots(); return true; } bool PeerConnectionBase::down_chunk_start(const Piece& piece) { if (!request_list()->downloading(piece)) { if (piece.length() == 0) { LT_LOG_PIECE_EVENTS("(down) skipping_empty %" PRIu32 " %" PRIu32 " %" PRIu32, piece.index(), piece.offset(), piece.length()); } else { LT_LOG_PIECE_EVENTS("(down) skipping_unneeded %" PRIu32 " %" PRIu32 " %" PRIu32, piece.index(), piece.offset(), piece.length()); } return false; } if (!m_download->file_list()->is_valid_piece(piece)) throw internal_error("Incoming pieces list contains a bad piece."); if (!m_downChunk.is_valid() || piece.index() != m_downChunk.index()) { down_chunk_release(); m_downChunk = m_download->chunk_list()->get(piece.index(), ChunkList::get_writable); if (!m_downChunk.is_valid()) throw storage_error("File chunk write error: " + std::string(m_downChunk.error_number().c_str()) + "."); } LT_LOG_PIECE_EVENTS("(down) %s %" PRIu32 " %" PRIu32 " %" PRIu32, request_list()->transfer()->is_leader() ? "started_on" : "skipping_partial", piece.index(), piece.offset(), piece.length()); return request_list()->transfer()->is_leader(); } void PeerConnectionBase::down_chunk_finished() { if (!request_list()->transfer()->is_finished()) throw internal_error("PeerConnectionBase::down_chunk_finished() Transfer not finished."); BlockTransfer* transfer = request_list()->transfer(); LT_LOG_PIECE_EVENTS("(down) %s %" PRIu32 " %" PRIu32 " %" PRIu32, transfer->is_leader() ? "completed " : "skipped ", transfer->piece().index(), transfer->piece().offset(), transfer->piece().length()); if (transfer->is_leader()) { if (!m_downChunk.is_valid()) throw internal_error("PeerConnectionBase::down_chunk_finished() Transfer is the leader, but no chunk allocated."); request_list()->finished(); m_downChunk.object()->set_time_modified(cachedTime); } else { request_list()->skipped(); } if (m_downStall > 0) m_downStall--; // We need to release chunks when we're not sure if they will be // used in the near future so as to avoid hitting the address space // limit in high-bandwidth situations. // // Some tweaking of the pipe size might be necessary if the queue // empties too often. if (m_downChunk.is_valid() && (request_list()->queued_empty() || m_downChunk.index() != request_list()->next_queued_piece().index())) down_chunk_release(); // If we were choked by choke_manager but still had queued pieces, // then we might still be in the throttle. if (m_downChoke.choked() && request_list()->queued_empty()) m_down->throttle()->erase(m_peerChunks.download_throttle()); write_insert_poll_safe(); } bool PeerConnectionBase::down_chunk() { if (!m_down->throttle()->is_throttled(m_peerChunks.download_throttle())) throw internal_error("PeerConnectionBase::down_chunk() tried to read a piece but is not in throttle list"); if (!m_downChunk.chunk()->is_writable()) throw internal_error("PeerConnectionBase::down_part() chunk not writable, permission denided"); uint32_t quota = m_down->throttle()->node_quota(m_peerChunks.download_throttle()); if (quota == 0) { manager->poll()->remove_read(this); m_down->throttle()->node_deactivate(m_peerChunks.download_throttle()); return false; } uint32_t bytesTransfered = 0; BlockTransfer* transfer = m_request_list.transfer(); Chunk::data_type data; ChunkIterator itr(m_downChunk.chunk(), transfer->piece().offset() + transfer->position(), transfer->piece().offset() + std::min(transfer->position() + quota, transfer->piece().length())); do { data = itr.data(); data.second = read_stream_throws(data.first, data.second); if (is_encrypted()) m_encryption.decrypt(data.first, data.second); bytesTransfered += data.second; } while (data.second != 0 && itr.forward(data.second)); transfer->adjust_position(bytesTransfered); m_down->throttle()->node_used(m_peerChunks.download_throttle(), bytesTransfered); m_download->info()->mutable_down_rate()->insert(bytesTransfered); return transfer->is_finished(); } bool PeerConnectionBase::down_chunk_from_buffer() { m_down->buffer()->consume(down_chunk_process(m_down->buffer()->position(), m_down->buffer()->remaining())); if (!m_request_list.transfer()->is_finished() && m_down->buffer()->remaining() != 0) throw internal_error("PeerConnectionBase::down_chunk_from_buffer() !transfer->is_finished() && m_down->buffer()->remaining() != 0."); return m_request_list.transfer()->is_finished(); } // When this transfer again becomes the leader, we just return false // and wait for the next polling. It is an exceptional case so we // don't really care that much about performance. bool PeerConnectionBase::down_chunk_skip() { ThrottleList* throttle = m_down->throttle(); if (!throttle->is_throttled(m_peerChunks.download_throttle())) throw internal_error("PeerConnectionBase::down_chunk_skip() tried to read a piece but is not in throttle list"); uint32_t quota = throttle->node_quota(m_peerChunks.download_throttle()); if (quota == 0) { manager->poll()->remove_read(this); throttle->node_deactivate(m_peerChunks.download_throttle()); return false; } uint32_t length = read_stream_throws(m_nullBuffer, std::min(quota, m_request_list.transfer()->piece().length() - m_request_list.transfer()->position())); throttle->node_used(m_peerChunks.download_throttle(), length); if (is_encrypted()) m_encryption.decrypt(m_nullBuffer, length); if (down_chunk_skip_process(m_nullBuffer, length) != length) throw internal_error("PeerConnectionBase::down_chunk_skip() down_chunk_skip_process(m_nullBuffer, length) != length."); return m_request_list.transfer()->is_finished(); } bool PeerConnectionBase::down_chunk_skip_from_buffer() { m_down->buffer()->consume(down_chunk_skip_process(m_down->buffer()->position(), m_down->buffer()->remaining())); return m_request_list.transfer()->is_finished(); } // Process data from a leading transfer. uint32_t PeerConnectionBase::down_chunk_process(const void* buffer, uint32_t length) { if (!m_downChunk.is_valid() || m_downChunk.index() != m_request_list.transfer()->index()) throw internal_error("PeerConnectionBase::down_chunk_process(...) !m_downChunk.is_valid() || m_downChunk.index() != m_request_list.transfer()->index()."); if (length == 0) return length; BlockTransfer* transfer = m_request_list.transfer(); length = std::min(transfer->piece().length() - transfer->position(), length); m_downChunk.chunk()->from_buffer(buffer, transfer->piece().offset() + transfer->position(), length); transfer->adjust_position(length); m_down->throttle()->node_used(m_peerChunks.download_throttle(), length); m_download->info()->mutable_down_rate()->insert(length); return length; } // Process data from non-leading transfer. If this transfer encounters // mismatching data with the leader then bork this transfer. If we get // ahead of the leader, we switch the leader. uint32_t PeerConnectionBase::down_chunk_skip_process(const void* buffer, uint32_t length) { BlockTransfer* transfer = m_request_list.transfer(); // Adjust 'length' to be less than or equal to what is remaining of // the block to simplify the rest of the function. length = std::min(length, transfer->piece().length() - transfer->position()); // Hmm, this might result in more bytes than nessesary being // counted. m_down->throttle()->node_used(m_peerChunks.download_throttle(), length); m_download->info()->mutable_down_rate()->insert(length); m_download->info()->mutable_skip_rate()->insert(length); if (!transfer->is_valid()) { transfer->adjust_position(length); return length; } if (!transfer->block()->is_transfering()) throw internal_error("PeerConnectionBase::down_chunk_skip_process(...) block is not transferring, yet we have non-leaders."); // Temporary test. if (transfer->position() > transfer->block()->leader()->position()) throw internal_error("PeerConnectionBase::down_chunk_skip_process(...) transfer is past the Block's position."); // If the transfer is valid, compare the downloaded data to the // leader. uint32_t compareLength = std::min(length, transfer->block()->leader()->position() - transfer->position()); // The data doesn't match with what has previously been downloaded, // bork this transfer. if (!m_downChunk.chunk()->compare_buffer(buffer, transfer->piece().offset() + transfer->position(), compareLength)) { LT_LOG_PIECE_EVENTS("(down) download_data_mismatch %" PRIu32 " %" PRIu32 " %" PRIu32, transfer->piece().index(), transfer->piece().offset(), transfer->piece().length()); m_request_list.transfer_dissimilar(); m_request_list.transfer()->adjust_position(length); return length; } transfer->adjust_position(compareLength); if (compareLength == length) return length; // Add another check here to see if we really want to be the new // leader. transfer->block()->change_leader(transfer); if (down_chunk_process(static_cast(buffer) + compareLength, length - compareLength) != length - compareLength) throw internal_error("PeerConnectionBase::down_chunk_skip_process(...) down_chunk_process(...) returned wrong value."); return length; } bool PeerConnectionBase::down_extension() { if (m_down->buffer()->remaining()) { uint32_t need = std::min(m_extensions->read_need(), (uint32_t)m_down->buffer()->remaining()); std::memcpy(m_extensions->read_position(), m_down->buffer()->position(), need); m_extensions->read_move(need); m_down->buffer()->consume(need); } if (!m_extensions->is_complete()) { uint32_t bytes = read_stream_throws(m_extensions->read_position(), m_extensions->read_need()); m_down->throttle()->node_used_unthrottled(bytes); if (is_encrypted()) m_encryption.decrypt(m_extensions->read_position(), bytes); m_extensions->read_move(bytes); } // If extension can't be processed yet (due to a pending write), // disable reads until the pending message is completely sent. if (m_extensions->is_complete() && !m_extensions->is_invalid() && !m_extensions->read_done()) { manager->poll()->remove_read(this); return false; } return m_extensions->is_complete(); } inline uint32_t PeerConnectionBase::up_chunk_encrypt(uint32_t quota) { if (m_encryptBuffer == NULL) throw internal_error("PeerConnectionBase::up_chunk: m_encryptBuffer is NULL."); if (quota <= m_encryptBuffer->remaining()) return quota; // Also, consider checking here if the number of bytes remaining in // the buffer is small enought that the cost of moving them would // outweigh the extra context switches, etc. if (m_encryptBuffer->remaining() == 0) { // This handles reset also for new chunk transfers. m_encryptBuffer->reset(); quota = std::min(quota, m_encryptBuffer->reserved()); } else { quota = std::min(quota - m_encryptBuffer->remaining(), m_encryptBuffer->reserved_left()); } m_upChunk.chunk()->to_buffer(m_encryptBuffer->end(), m_upPiece.offset() + m_encryptBuffer->remaining(), quota); m_encryption.encrypt(m_encryptBuffer->end(), quota); m_encryptBuffer->move_end(quota); return m_encryptBuffer->remaining(); } bool PeerConnectionBase::up_chunk() { if (!m_up->throttle()->is_throttled(m_peerChunks.upload_throttle())) throw internal_error("PeerConnectionBase::up_chunk() tried to write a piece but is not in throttle list"); if (!m_upChunk.chunk()->is_readable()) throw internal_error("ProtocolChunk::write_part() chunk not readable, permission denided"); uint32_t quota = m_up->throttle()->node_quota(m_peerChunks.upload_throttle()); if (quota == 0) { manager->poll()->remove_write(this); m_up->throttle()->node_deactivate(m_peerChunks.upload_throttle()); return false; } uint32_t bytesTransfered = 0; if (is_encrypted()) { // Prepare as many bytes as quota specifies, up to end of piece or // buffer. Only bytes beyond remaining() are new and will be // encrypted. quota = up_chunk_encrypt(std::min(quota, m_upPiece.length())); bytesTransfered = write_stream_throws(m_encryptBuffer->position(), quota); m_encryptBuffer->consume(bytesTransfered); } else { Chunk::data_type data; ChunkIterator itr(m_upChunk.chunk(), m_upPiece.offset(), m_upPiece.offset() + std::min(quota, m_upPiece.length())); do { data = itr.data(); data.second = write_stream_throws(data.first, data.second); bytesTransfered += data.second; } while (data.second != 0 && itr.forward(data.second)); } m_up->throttle()->node_used(m_peerChunks.upload_throttle(), bytesTransfered); m_download->info()->mutable_up_rate()->insert(bytesTransfered); // Just modifying the piece to cover the remaining data ends up // being much cleaner and we avoid an unnessesary position variable. m_upPiece.set_offset(m_upPiece.offset() + bytesTransfered); m_upPiece.set_length(m_upPiece.length() - bytesTransfered); return m_upPiece.length() == 0; } bool PeerConnectionBase::up_extension() { if (m_extensionOffset == extension_must_encrypt) { if (m_extensionMessage.owned()) { m_encryption.encrypt(m_extensionMessage.data(), m_extensionMessage.length()); } else { char* buffer = new char[m_extensionMessage.length()]; m_encryption.encrypt(m_extensionMessage.data(), buffer, m_extensionMessage.length()); m_extensionMessage.set(buffer, buffer + m_extensionMessage.length(), true); } m_extensionOffset = 0; } if (m_extensionOffset >= m_extensionMessage.length()) throw internal_error("PeerConnectionBase::up_extension bad offset."); uint32_t written = write_stream_throws(m_extensionMessage.data() + m_extensionOffset, m_extensionMessage.length() - m_extensionOffset); m_up->throttle()->node_used_unthrottled(written); m_extensionOffset += written; if (m_extensionOffset < m_extensionMessage.length()) return false; m_extensionMessage.clear(); // If we have an unprocessed message, process it now and enable reads again. if (m_extensions->is_complete() && !m_extensions->is_invalid()) { // DEBUG: What, this should fail when we block, no? if (!m_extensions->read_done()) throw internal_error("PeerConnectionBase::up_extension could not process complete extension message."); manager->poll()->insert_read(this); } return true; } void PeerConnectionBase::down_chunk_release() { if (m_downChunk.is_valid()) m_download->chunk_list()->release(&m_downChunk); } void PeerConnectionBase::up_chunk_release() { if (m_upChunk.is_valid()) m_download->chunk_list()->release(&m_upChunk); } void PeerConnectionBase::read_request_piece(const Piece& p) { PeerChunks::piece_list_type::iterator itr = std::find(m_peerChunks.upload_queue()->begin(), m_peerChunks.upload_queue()->end(), p); if (m_upChoke.choked() || itr != m_peerChunks.upload_queue()->end() || p.length() > (1 << 17)) { LT_LOG_PIECE_EVENTS("(up) request_ignored %" PRIu32 " %" PRIu32 " %" PRIu32, p.index(), p.offset(), p.length()); return; } m_peerChunks.upload_queue()->push_back(p); write_insert_poll_safe(); LT_LOG_PIECE_EVENTS("(up) request_added %" PRIu32 " %" PRIu32 " %" PRIu32, p.index(), p.offset(), p.length()); } void PeerConnectionBase::read_cancel_piece(const Piece& p) { PeerChunks::piece_list_type::iterator itr = std::find(m_peerChunks.upload_queue()->begin(), m_peerChunks.upload_queue()->end(), p); if (itr != m_peerChunks.upload_queue()->end()) { m_peerChunks.upload_queue()->erase(itr); LT_LOG_PIECE_EVENTS("(up) cancel_requested %" PRIu32 " %" PRIu32 " %" PRIu32, p.index(), p.offset(), p.length()); } else { LT_LOG_PIECE_EVENTS("(up) cancel_ignored %" PRIu32 " %" PRIu32 " %" PRIu32, p.index(), p.offset(), p.length()); } } void PeerConnectionBase::write_prepare_piece() { m_upPiece = m_peerChunks.upload_queue()->front(); m_peerChunks.upload_queue()->pop_front(); // Move these checks somewhere else? if (!m_download->file_list()->is_valid_piece(m_upPiece) || !m_download->file_list()->bitfield()->get(m_upPiece.index())) { char buffer[128]; snprintf(buffer, 128, "Peer requested an invalid piece: %u %u %u", m_upPiece.index(), m_upPiece.length(), m_upPiece.offset()); LT_LOG_PIECE_EVENTS("(up) invalid_piece_in_upload_queue %" PRIu32 " %" PRIu32 " %" PRIu32, m_upPiece.index(), m_upPiece.length(), m_upPiece.offset()); throw communication_error(buffer); } m_up->write_piece(m_upPiece); LT_LOG_PIECE_EVENTS("(up) prepared %" PRIu32 " %" PRIu32 " %" PRIu32, m_upPiece.index(), m_upPiece.length(), m_upPiece.offset()); } void PeerConnectionBase::write_prepare_extension(int type, const DataBuffer& message) { m_up->write_extension(m_extensions->id(type), message.length()); m_extensionOffset = 0; m_extensionMessage = message; // Need to encrypt the buffer, but not until the m_up // write buffer has been flushed, so flag it for now. if (is_encrypted()) m_extensionOffset = extension_must_encrypt; } // High stall count peers should request if we're *not* in endgame, or // if we're in endgame and the download is too slow. Prefere not to request // from high stall counts when we are doing decent speeds. bool PeerConnectionBase::should_request() { if (m_downChoke.choked() || !m_downInterested || !m_downUnchoked) // || m_down->get_state() == ProtocolRead::READ_SKIP_PIECE) return false; else if (!m_download->delegator()->get_aggressive()) return true; else // We check if the peer is stalled, if it is not then we should // request. If the peer is stalled then we only request if the // download rate is below a certain value. return m_downStall <= 1 || m_download->info()->down_rate()->rate() < (10 << 10); } bool PeerConnectionBase::try_request_pieces() { if (request_list()->queued_empty()) m_downStall = 0; uint32_t pipeSize = request_list()->calculate_pipe_size(m_peerChunks.download_throttle()->rate()->rate()); // Don't start requesting if we can't do it in large enough chunks. if (request_list()->pipe_size() >= (pipeSize + 10) / 2) return false; bool success = false; while (request_list()->queued_size() < pipeSize && m_up->can_write_request()) { // Delegator should return a vector of pieces, and it should be // passed the number of pieces it should delegate. Try to ensure // it receives large enough request to fill a whole chunk if the // peer is fast enough. const Piece* p = request_list()->delegate(); if (p == NULL) break; if (!m_download->file_list()->is_valid_piece(*p) || !m_peerChunks.bitfield()->get(p->index())) throw internal_error("PeerConnectionBase::try_request_pieces() tried to use an invalid piece."); m_up->write_request(*p); LT_LOG_PIECE_EVENTS("(down) requesting %" PRIu32 " %" PRIu32 " %" PRIu32, p->index(), p->offset(), p->length()); success = true; } return success; } // Send one peer exchange message according to bits set in m_sendPEXMask. // We can only send one message at a time, because the derived class // needs to flush the buffer and call up_extension before the next one. bool PeerConnectionBase::send_pex_message() { if (!m_extensions->is_remote_supported(ProtocolExtension::UT_PEX)) { m_sendPEXMask = 0; return false; } // Message to tell peer to stop/start doing PEX is small so send it first. if (m_sendPEXMask & (PEX_ENABLE | PEX_DISABLE)) { if (!m_extensions->is_remote_supported(ProtocolExtension::UT_PEX)) throw internal_error("PeerConnectionBase::send_pex_message() Not supported by peer."); write_prepare_extension(ProtocolExtension::HANDSHAKE, ProtocolExtension::generate_toggle_message(ProtocolExtension::UT_PEX, (m_sendPEXMask & PEX_ENABLE) != 0)); m_sendPEXMask &= ~(PEX_ENABLE | PEX_DISABLE); } else if (m_sendPEXMask & PEX_DO && m_extensions->id(ProtocolExtension::UT_PEX)) { const DataBuffer& pexMessage = m_download->get_ut_pex(m_extensions->is_initial_pex()); m_extensions->clear_initial_pex(); m_sendPEXMask &= ~PEX_DO; if (pexMessage.empty()) return false; write_prepare_extension(ProtocolExtension::UT_PEX, pexMessage); } else { m_sendPEXMask = 0; } return true; } // Extension protocol needs to send a reply. bool PeerConnectionBase::send_ext_message() { write_prepare_extension(m_extensions->pending_message_type(), m_extensions->pending_message_data()); m_extensions->clear_pending_message(); return true; } void PeerConnectionBase::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) { } } libtorrent-0.13.6/src/protocol/peer_connection_base.h000066400000000000000000000231211257211073700227360ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_PEER_CONNECTION_BASE_H #define LIBTORRENT_PROTOCOL_PEER_CONNECTION_BASE_H #include "data/chunk_handle.h" #include "net/socket_stream.h" #include "torrent/poll.h" #include "torrent/peer/peer.h" #include "torrent/peer/choke_status.h" #include "encryption_info.h" #include "extensions.h" #include "peer_chunks.h" #include "protocol_base.h" #include "request_list.h" #include "globals.h" #include "manager.h" namespace torrent { // Base class for peer connection classes. Rename to PeerConnection // when the migration is complete? // // This should really be modularized abit, there's too much stuff in // PeerConnectionBase and its children. Do we use additional layers of // inheritance or member instances? class choke_queue; class DownloadMain; class PeerConnectionBase : public Peer, public SocketStream { public: typedef ProtocolBase ProtocolRead; typedef ProtocolBase ProtocolWrite; #if USE_EXTRA_DEBUG == 666 // For testing, use a really small buffer. typedef ProtocolBuffer<256> EncryptBuffer; #else typedef ProtocolBuffer<16384> EncryptBuffer; #endif // Find an optimal number for this. static const uint32_t read_size = 64; // Bitmasks for peer exchange messages to send. static const int PEX_DO = (1 << 0); static const int PEX_ENABLE = (1 << 1); static const int PEX_DISABLE = (1 << 2); PeerConnectionBase(); virtual ~PeerConnectionBase(); const char* type_name() const { return "pcb"; } void initialize(DownloadMain* download, PeerInfo* p, SocketFd fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions); void cleanup(); bool is_up_choked() const { return m_upChoke.choked(); } bool is_up_interested() const { return m_upChoke.queued(); } bool is_up_snubbed() const { return m_upChoke.snubbed(); } bool is_down_queued() const { return m_downChoke.queued(); } bool is_down_local_unchoked() const { return m_downChoke.unchoked(); } bool is_down_remote_unchoked() const { return m_downUnchoked; } bool is_down_interested() const { return m_downInterested; } void set_upload_snubbed(bool v); bool is_seeder() const { return m_peerChunks.is_seeder(); } bool is_not_seeder() const { return !m_peerChunks.is_seeder(); } bool is_encrypted() const { return m_encryption.is_encrypted(); } bool is_obfuscated() const { return m_encryption.is_obfuscated(); } PeerInfo* mutable_peer_info() { return m_peerInfo; } PeerChunks* peer_chunks() { return &m_peerChunks; } const PeerChunks* c_peer_chunks() const { return &m_peerChunks; } choke_status* up_choke() { return &m_upChoke; } choke_status* down_choke() { return &m_downChoke; } DownloadMain* download() { return m_download; } RequestList* request_list() { return &m_request_list; } const RequestList* request_list() const { return &m_request_list; } ProtocolExtension* extensions() { return m_extensions; } DataBuffer* extension_message() { return &m_extensionMessage; } void do_peer_exchange() { m_sendPEXMask |= PEX_DO; } inline void set_peer_exchange(bool state); // These must be implemented by the child class. virtual void initialize_custom() = 0; virtual void update_interested() = 0; virtual bool receive_keepalive() = 0; bool receive_upload_choke(bool choke); bool receive_download_choke(bool choke); virtual void event_error(); void push_unread(const void* data, uint32_t size); void cancel_transfer(BlockTransfer* transfer); // Insert into the poll unless we're blocking for throttling etc. void read_insert_poll_safe(); void write_insert_poll_safe(); // Communication with the protocol extensions virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length); bool should_connection_unchoke(choke_queue* cq) const; protected: static const uint32_t extension_must_encrypt = ~uint32_t(); inline bool read_remaining(); inline bool write_remaining(); void load_up_chunk(); void read_request_piece(const Piece& p); void read_cancel_piece(const Piece& p); void write_prepare_piece(); void write_prepare_extension(int type, const DataBuffer& message); bool down_chunk_start(const Piece& p); void down_chunk_finished(); bool down_chunk(); bool down_chunk_from_buffer(); bool down_chunk_skip(); bool down_chunk_skip_from_buffer(); uint32_t down_chunk_process(const void* buffer, uint32_t length); uint32_t down_chunk_skip_process(const void* buffer, uint32_t length); bool down_extension(); bool up_chunk(); inline uint32_t up_chunk_encrypt(uint32_t quota); bool up_extension(); void down_chunk_release(); void up_chunk_release(); bool should_request(); bool try_request_pieces(); bool send_pex_message(); bool send_ext_message(); DownloadMain* m_download; ProtocolRead* m_down; ProtocolWrite* m_up; PeerChunks m_peerChunks; RequestList m_request_list; ChunkHandle m_downChunk; uint32_t m_downStall; Piece m_upPiece; ChunkHandle m_upChunk; // The interested state no longer follows the spec's wording as it // has been swapped. // // Thus the same ProtocolBase object now groups the same choke and // interested states togheter, thus for m_up 'interested' means the // remote peer wants upload and 'choke' means we've choked upload to // that peer. // // In the downlod object, 'queued' now means the same as the spec's // 'unchoked', while 'unchoked' means we start requesting pieces. choke_status m_upChoke; choke_status m_downChoke; bool m_downInterested; bool m_downUnchoked; bool m_sendChoked; bool m_sendInterested; bool m_tryRequest; int m_sendPEXMask; rak::timer m_timeLastRead; DataBuffer m_extensionMessage; uint32_t m_extensionOffset; EncryptBuffer* m_encryptBuffer; EncryptionInfo m_encryption; ProtocolExtension* m_extensions; bool m_incoreContinous; }; inline void PeerConnectionBase::set_peer_exchange(bool state) { if (m_extensions->is_default() || !m_extensions->is_remote_supported(ProtocolExtension::UT_PEX)) return; if (state) { m_sendPEXMask = PEX_ENABLE | (m_sendPEXMask & ~PEX_DISABLE); m_extensions->set_local_enabled(ProtocolExtension::UT_PEX); } else { m_sendPEXMask = PEX_DISABLE | (m_sendPEXMask & ~PEX_ENABLE); m_extensions->unset_local_enabled(ProtocolExtension::UT_PEX); } } inline void PeerConnectionBase::push_unread(const void* data, uint32_t size) { std::memcpy(m_down->buffer()->end(), data, size); m_down->buffer()->move_end(size); } inline void PeerConnectionBase::read_insert_poll_safe() { if (m_down->get_state() != ProtocolRead::IDLE) return; manager->poll()->insert_read(this); } inline void PeerConnectionBase::write_insert_poll_safe() { if (m_up->get_state() != ProtocolWrite::IDLE) return; manager->poll()->insert_write(this); } } #endif libtorrent-0.13.6/src/protocol/peer_connection_leech.cc000066400000000000000000000606241257211073700232530ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include "data/chunk_list_node.h" #include "download/chunk_selector.h" #include "download/chunk_statistics.h" #include "download/download_main.h" #include "torrent/dht_manager.h" #include "torrent/download_info.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "torrent/peer/connection_list.h" #include "torrent/peer/peer_info.h" #include "torrent/utils/log.h" #include "extensions.h" #include "initial_seed.h" #include "peer_connection_leech.h" #define LT_LOG_NETWORK_ERRORS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_NETWORK_ERRORS, this->download()->info(), "network_errors", "%40s " log_fmt, this->peer_info()->id_hex(), __VA_ARGS__); #define LT_LOG_STORAGE_ERRORS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_STORAGE_ERRORS, this->download()->info(), "storage_errors", "%40s " log_fmt, this->peer_info()->id_hex(), __VA_ARGS__); namespace torrent { template PeerConnection::~PeerConnection() { // if (m_download != NULL && m_down->get_state() != ProtocolRead::READ_BITFIELD) // m_download->bitfield_counter().dec(m_peerChunks.bitfield()->bitfield()); // priority_queue_erase(&taskScheduler, &m_taskSendChoke); } template void PeerConnection::initialize_custom() { if (type == Download::CONNECTION_INITIAL_SEED) { if (m_download->initial_seeding() == NULL) throw close_connection(); m_download->initial_seeding()->new_peer(this); } // if (m_download->content()->chunks_completed() != 0) { // m_up->write_bitfield(m_download->file_list()->bitfield()->size_bytes()); // m_up->buffer()->prepare_end(); // m_up->set_position(0); // m_up->set_state(ProtocolWrite::WRITE_BITFIELD_HEADER); // } } template void PeerConnection::update_interested() { if (type != Download::CONNECTION_LEECH) return; m_peerChunks.download_cache()->clear(); if (m_downInterested) return; // Consider just setting to interested without checking the // bitfield. The status might change by the time we get unchoked // anyway. m_sendInterested = !m_downInterested; m_downInterested = true; // Hmm... does this belong here, or should we insert ourselves into // the queue when we receive the unchoke? // m_download->choke_group()->down_queue()->set_queued(this, &m_downChoke); } template bool PeerConnection::receive_keepalive() { if (cachedTime - m_timeLastRead > rak::timer::from_seconds(240)) return false; // There's no point in adding ourselves to the write poll if the // buffer is full, as that will already have been taken care of. if (m_up->get_state() == ProtocolWrite::IDLE && m_up->can_write_keepalive()) { write_insert_poll_safe(); ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); m_up->write_keepalive(); if (is_encrypted()) m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); } if (type != Download::CONNECTION_LEECH) return true; m_tryRequest = true; // Stall pieces when more than one receive_keepalive() has been // called while a single piece is downloading. // // m_downStall is decremented for every successfull download, so it // should stay at zero or one when downloading at an acceptable // speed. Thus only when m_downStall >= 2 is the download actually // stalling. // // If more than 6 ticks have gone by, assume the peer forgot about // our requests or tried to cheat with empty piece messages, and try // again. // TODO: Do we need to remove from download throttle here? // // Do we also need to remove from download throttle? Check how it // worked again. // if (request_list()->empty()) // return true; if (m_downStall >= 2) request_list()->stall_prolonged(); else if (m_downStall++ != 0) request_list()->stall_initial(); return true; } // We keep the message in the buffer if it is incomplete instead of // keeping the state and remembering the read information. This // shouldn't happen very often compared to full reads. template inline bool PeerConnection::read_message() { ProtocolBuffer<512>* buf = m_down->buffer(); if (buf->remaining() < 4) return false; // Remember the start of the message so we may reset it if we don't // have the whole message. ProtocolBuffer<512>::iterator beginning = buf->position(); uint32_t length = buf->read_32(); if (length == 0) { // Keepalive message. m_down->set_last_command(ProtocolBase::KEEP_ALIVE); return true; } else if (buf->remaining() < 1) { buf->set_position_itr(beginning); return false; } else if (length > (1 << 20)) { throw communication_error("PeerConnection::read_message() got an invalid message length."); } // We do not verify the message length of those with static // length. A bug in the remote client causing the message start to // be unsyncronized would in practically all cases be caught with // the above test. // // Those that do in some weird way manage to produce a valid // command, will not be able to do any more damage than a malicious // peer. Those cases should be caught elsewhere in the code. // Temporary. m_down->set_last_command((ProtocolBase::Protocol)buf->peek_8()); switch (buf->read_8()) { case ProtocolBase::CHOKE: if (type != Download::CONNECTION_LEECH) return true; // Cancel before dequeueing so receive_download_choke knows if it // should remove us from throttle. // // Hmm... that won't work, as we arn't necessarily unchoked when // in throttle. // Which needs to be done before, and which after calling choke // manager? m_downUnchoked = false; down_chunk_release(); request_list()->choked(); m_download->choke_group()->down_queue()->set_not_queued(this, &m_downChoke); m_down->throttle()->erase(m_peerChunks.download_throttle()); return true; case ProtocolBase::UNCHOKE: if (type != Download::CONNECTION_LEECH) return true; m_downUnchoked = true; // Some peers unchoke us even though we're not interested, so we // need to ensure it doesn't get added to the queue. if (!m_downInterested) return true; request_list()->unchoked(); m_download->choke_group()->down_queue()->set_queued(this, &m_downChoke); return true; case ProtocolBase::INTERESTED: if (type == Download::CONNECTION_LEECH && m_peerChunks.bitfield()->is_all_set()) return true; m_download->choke_group()->up_queue()->set_queued(this, &m_upChoke); return true; case ProtocolBase::NOT_INTERESTED: m_download->choke_group()->up_queue()->set_not_queued(this, &m_upChoke); return true; case ProtocolBase::HAVE: if (!m_down->can_read_have_body()) break; read_have_chunk(buf->read_32()); return true; case ProtocolBase::REQUEST: if (!m_down->can_read_request_body()) break; if (!m_upChoke.choked()) { write_insert_poll_safe(); read_request_piece(m_down->read_request()); } else { m_down->read_request(); } return true; case ProtocolBase::PIECE: if (type != Download::CONNECTION_LEECH) throw communication_error("Received a piece but the connection is strictly for seeding."); if (!m_down->can_read_piece_body()) break; if (!down_chunk_start(m_down->read_piece(length - 9))) { // We don't want this chunk. if (down_chunk_skip_from_buffer()) { m_tryRequest = true; down_chunk_finished(); return true; } else { m_down->set_state(ProtocolRead::READ_SKIP_PIECE); m_down->throttle()->insert(m_peerChunks.download_throttle()); return false; } } else { if (down_chunk_from_buffer()) { m_tryRequest = true; down_chunk_finished(); return true; } else { m_down->set_state(ProtocolRead::READ_PIECE); m_down->throttle()->insert(m_peerChunks.download_throttle()); return false; } } case ProtocolBase::CANCEL: if (!m_down->can_read_cancel_body()) break; read_cancel_piece(m_down->read_request()); return true; case ProtocolBase::PORT: if (!m_down->can_read_port_body()) break; manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16()); return true; case ProtocolBase::EXTENSION_PROTOCOL: if (!m_down->can_read_extension_body()) break; if (m_extensions->is_default()) { m_extensions = new ProtocolExtension(); m_extensions->set_info(m_peerInfo, m_download); } { int extension = m_down->buffer()->read_8(); m_extensions->read_start(extension, length - 2, (extension == ProtocolExtension::UT_PEX) && !m_download->want_pex_msg()); m_down->set_state(ProtocolRead::READ_EXTENSION); } if (!down_extension()) return false; if (m_extensions->has_pending_message()) write_insert_poll_safe(); m_down->set_state(ProtocolRead::IDLE); return true; default: throw communication_error("Received unsupported message type."); } // We were unsuccessfull in reading the message, need more data. buf->set_position_itr(beginning); return false; } template void PeerConnection::event_read() { m_timeLastRead = cachedTime; // Need to make sure ProtocolBuffer::end() is pointing to the end of // the unread data, and that the unread data starts from the // beginning of the buffer. Or do we use position? Propably best, // therefor ProtocolBuffer::position() points to the beginning of // the unused data. try { // Normal read. // // We rarely will read zero bytes as the read of 64 bytes will // almost always either not fill up or it will require additional // reads. // // Only loop when end hits 64. do { switch (m_down->get_state()) { case ProtocolRead::IDLE: if (m_down->buffer()->size_end() < read_size) { unsigned int length = read_stream_throws(m_down->buffer()->end(), read_size - m_down->buffer()->size_end()); m_down->throttle()->node_used_unthrottled(length); if (is_encrypted()) m_encryption.decrypt(m_down->buffer()->end(), length); m_down->buffer()->move_end(length); } while (read_message()); if (m_down->buffer()->size_end() == read_size) { m_down->buffer()->move_unused(); break; } else { m_down->buffer()->move_unused(); return; } case ProtocolRead::READ_PIECE: if (type != Download::CONNECTION_LEECH) return; if (!request_list()->is_downloading()) throw internal_error("ProtocolRead::READ_PIECE state but RequestList is not downloading."); if (!m_request_list.transfer()->is_valid() || !m_request_list.transfer()->is_leader()) { m_down->set_state(ProtocolRead::READ_SKIP_PIECE); break; } if (!down_chunk()) return; m_tryRequest = true; m_down->set_state(ProtocolRead::IDLE); down_chunk_finished(); break; case ProtocolRead::READ_SKIP_PIECE: if (type != Download::CONNECTION_LEECH) return; if (request_list()->transfer()->is_leader()) { m_down->set_state(ProtocolRead::READ_PIECE); break; } if (!down_chunk_skip()) return; m_tryRequest = true; m_down->set_state(ProtocolRead::IDLE); down_chunk_finished(); break; case ProtocolRead::READ_EXTENSION: if (!down_extension()) return; if (m_extensions->has_pending_message()) write_insert_poll_safe(); m_down->set_state(ProtocolRead::IDLE); break; default: throw internal_error("PeerConnection::event_read() wrong state."); } // Figure out how to get rid of the shouldLoop boolean. } while (true); // Exception handlers: } catch (close_connection& e) { m_download->connection_list()->erase(this, 0); } catch (blocked_connection& e) { m_download->connection_list()->erase(this, 0); } catch (network_error& e) { LT_LOG_NETWORK_ERRORS("%s network read error: %s", rak::socket_address::cast_from(m_peerInfo->socket_address())->address_str().c_str(), e.what()); m_download->connection_list()->erase(this, 0); } catch (storage_error& e) { LT_LOG_NETWORK_ERRORS("storage read error: %s", e.what()); m_download->connection_list()->erase(this, 0); } catch (base_error& e) { std::stringstream s; s << "Connection read fd(" << get_fd().get_fd() << ',' << m_down->get_state() << ',' << m_down->last_command() << ") \"" << e.what() << '"'; s << " '" << rak::copy_escape_html((char*)m_down->buffer()->begin(), (char*)m_down->buffer()->position()) << "'"; throw internal_error(s.str()); } } template inline void PeerConnection::fill_write_buffer() { ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); // No need to use delayed choke ever. if (m_sendChoked && m_up->can_write_choke()) { m_sendChoked = false; m_up->write_choke(m_upChoke.choked()); if (m_upChoke.choked()) { m_up->throttle()->erase(m_peerChunks.upload_throttle()); up_chunk_release(); m_peerChunks.upload_queue()->clear(); if (m_encryptBuffer != NULL) { if (m_encryptBuffer->remaining()) throw internal_error("Deleting encryptBuffer with encrypted data remaining."); delete m_encryptBuffer; m_encryptBuffer = NULL; } } else { m_up->throttle()->insert(m_peerChunks.upload_throttle()); } } // Send the interested state before any requests as some clients, // e.g. BitTornado 0.7.14 and uTorrent 0.3.0, disconnect if a // request has been received while uninterested. The problem arises // as they send unchoke before receiving interested. if (type == Download::CONNECTION_LEECH && m_sendInterested && m_up->can_write_interested()) { m_up->write_interested(m_downInterested); m_sendInterested = false; } if (type == Download::CONNECTION_LEECH && m_tryRequest) { if (!(m_tryRequest = !should_request()) && !(m_tryRequest = try_request_pieces()) && !request_list()->is_interested_in_active()) { m_sendInterested = true; m_downInterested = false; m_download->choke_group()->down_queue()->set_not_queued(this, &m_downChoke); } } DownloadMain::have_queue_type* haveQueue = m_download->have_queue(); if (type == Download::CONNECTION_LEECH && !haveQueue->empty() && m_peerChunks.have_timer() <= haveQueue->front().first && m_up->can_write_have()) { DownloadMain::have_queue_type::iterator last = std::find_if(haveQueue->begin(), haveQueue->end(), rak::greater(m_peerChunks.have_timer(), rak::mem_ref(&DownloadMain::have_queue_type::value_type::first))); do { m_up->write_have((--last)->second); } while (last != haveQueue->begin() && m_up->can_write_have()); m_peerChunks.set_have_timer(last->first + 1); } if (type == Download::CONNECTION_INITIAL_SEED && m_up->can_write_have()) offer_chunk(); while (type == Download::CONNECTION_LEECH && !m_peerChunks.cancel_queue()->empty() && m_up->can_write_cancel()) { m_up->write_cancel(m_peerChunks.cancel_queue()->front()); m_peerChunks.cancel_queue()->pop_front(); } if (m_sendPEXMask && m_up->can_write_extension() && send_pex_message()) { // Don't do anything else if send_pex_message() succeeded. } else if (m_extensions->has_pending_message() && m_up->can_write_extension() && send_ext_message()) { // Same. } else if (!m_upChoke.choked() && !m_peerChunks.upload_queue()->empty() && m_up->can_write_piece() && (type != Download::CONNECTION_INITIAL_SEED || should_upload())) { write_prepare_piece(); } if (is_encrypted()) m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); } template void PeerConnection::event_write() { try { do { switch (m_up->get_state()) { case ProtocolWrite::IDLE: fill_write_buffer(); if (m_up->buffer()->remaining() == 0) { manager->poll()->remove_write(this); return; } m_up->set_state(ProtocolWrite::MSG); case ProtocolWrite::MSG: if (!m_up->buffer()->consume(m_up->throttle()->node_used_unthrottled(write_stream_throws(m_up->buffer()->position(), m_up->buffer()->remaining())))) return; m_up->buffer()->reset(); if (m_up->last_command() == ProtocolBase::PIECE) { // We're uploading a piece. load_up_chunk(); m_up->set_state(ProtocolWrite::WRITE_PIECE); // fall through to WRITE_PIECE case below } else if (m_up->last_command() == ProtocolBase::EXTENSION_PROTOCOL) { m_up->set_state(ProtocolWrite::WRITE_EXTENSION); break; } else { // Break or loop? Might do an ifelse based on size of the // write buffer. Also the write buffer is relatively large. m_up->set_state(ProtocolWrite::IDLE); break; } case ProtocolWrite::WRITE_PIECE: if (!up_chunk()) return; m_up->set_state(ProtocolWrite::IDLE); break; case ProtocolWrite::WRITE_EXTENSION: if (!up_extension()) return; m_up->set_state(ProtocolWrite::IDLE); break; default: throw internal_error("PeerConnection::event_write() wrong state."); } } while (true); } catch (close_connection& e) { m_download->connection_list()->erase(this, 0); } catch (blocked_connection& e) { m_download->connection_list()->erase(this, 0); } catch (network_error& e) { LT_LOG_NETWORK_ERRORS("%s write error: %s", rak::socket_address::cast_from(m_peerInfo->socket_address())->address_str().c_str(), e.what()); m_download->connection_list()->erase(this, 0); } catch (storage_error& e) { LT_LOG_STORAGE_ERRORS("write error: %s", e.what()); m_download->connection_list()->erase(this, 0); } catch (base_error& e) { std::stringstream s; s << "Connection write fd(" << get_fd().get_fd() << ',' << m_up->get_state() << ',' << m_up->last_command() << ") \"" << e.what() << '"'; throw internal_error(s.str()); } } template void PeerConnection::read_have_chunk(uint32_t index) { if (index >= m_peerChunks.bitfield()->size_bits()) throw communication_error("Peer sent HAVE message with out-of-range index."); if (m_peerChunks.bitfield()->get(index)) return; m_download->chunk_statistics()->received_have_chunk(&m_peerChunks, index, m_download->file_list()->chunk_size()); if (type == Download::CONNECTION_INITIAL_SEED) m_download->initial_seeding()->chunk_seen(index, this); // Disconnect seeds when we are seeding (but not for initial seeding // so that we keep accurate chunk statistics until that is done). if (m_peerChunks.bitfield()->is_all_set()) { if (type == Download::CONNECTION_SEED || (type != Download::CONNECTION_INITIAL_SEED && m_download->file_list()->is_done())) throw close_connection(); m_download->choke_group()->up_queue()->set_not_queued(this, &m_upChoke); } if (type != Download::CONNECTION_LEECH || m_download->file_list()->is_done()) return; if (is_down_interested()) { if (!m_tryRequest && m_download->chunk_selector()->received_have_chunk(&m_peerChunks, index)) { m_tryRequest = true; write_insert_poll_safe(); } } else { if (m_download->chunk_selector()->received_have_chunk(&m_peerChunks, index)) { m_sendInterested = !m_downInterested; m_downInterested = true; // Ensure we get inserted into the choke manager queue in case // the peer keeps us unchoked even though we've said we're not // interested. if (m_downUnchoked) m_download->choke_group()->down_queue()->set_queued(this, &m_downChoke); // Is it enough to insert into write here? Make the interested // check branch to include insert_write, even when not sending // interested. m_tryRequest = true; write_insert_poll_safe(); } } } template<> void PeerConnection::offer_chunk() { // If bytes left to send in this chunk minus bytes about to be sent is zero, // assume the peer will have got the chunk completely. In that case we may // get another one to offer if not enough other peers are interested even // if the peer would otherwise still be blocked. uint32_t bytesLeft = m_data.bytesLeft; if (!m_peerChunks.upload_queue()->empty() && m_peerChunks.upload_queue()->front().index() == m_data.lastIndex) bytesLeft -= m_peerChunks.upload_queue()->front().length(); uint32_t index = m_download->initial_seeding()->chunk_offer(this, bytesLeft == 0 ? m_data.lastIndex : InitialSeeding::no_offer); if (index == InitialSeeding::no_offer || index == m_data.lastIndex) return; m_up->write_have(index); m_data.lastIndex = index; m_data.bytesLeft = m_download->file_list()->chunk_index_size(index); } template<> bool PeerConnection::should_upload() { // For initial seeding, check if chunk is well seeded now, and if so // remove it from the queue to better use our bandwidth on rare chunks. while (!m_peerChunks.upload_queue()->empty() && !m_download->initial_seeding()->should_upload(m_peerChunks.upload_queue()->front().index())) m_peerChunks.upload_queue()->pop_front(); // If queue ends up empty, choke peer to let it know that it // shouldn't wait for the cancelled pieces to be sent. if (m_peerChunks.upload_queue()->empty()) { m_download->choke_group()->up_queue()->set_not_queued(this, &m_upChoke); m_download->choke_group()->up_queue()->set_queued(this, &m_upChoke); // If we're sending the chunk we last offered, adjust bytes left in it. } else if (m_peerChunks.upload_queue()->front().index() == m_data.lastIndex) { m_data.bytesLeft -= m_peerChunks.upload_queue()->front().length(); if (!m_data.bytesLeft) m_data.lastIndex = InitialSeeding::no_offer; } return !m_peerChunks.upload_queue()->empty(); } // Explicit instantiation of the member functions and vtable. template class PeerConnection; template class PeerConnection; template class PeerConnection; } libtorrent-0.13.6/src/protocol/peer_connection_leech.h000066400000000000000000000054411257211073700231110ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_PEER_CONNECTION_LEECH_H #define LIBTORRENT_PROTOCOL_PEER_CONNECTION_LEECH_H #include "peer_connection_base.h" #include "torrent/download.h" namespace torrent { // Type-specific data. template struct PeerConnectionData; template<> struct PeerConnectionData { }; template<> struct PeerConnectionData { }; template<> struct PeerConnectionData { PeerConnectionData() : lastIndex(~uint32_t()) { } uint32_t lastIndex; uint32_t bytesLeft; }; template class PeerConnection : public PeerConnectionBase { public: ~PeerConnection(); virtual void initialize_custom(); virtual void update_interested(); virtual bool receive_keepalive(); virtual void event_read(); virtual void event_write(); private: inline bool read_message(); void read_have_chunk(uint32_t index); void offer_chunk(); bool should_upload(); inline void fill_write_buffer(); PeerConnectionData m_data; }; } #endif libtorrent-0.13.6/src/protocol/peer_connection_metadata.cc000066400000000000000000000351411257211073700237470ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "data/chunk_list_node.h" #include "download/chunk_selector.h" #include "download/chunk_statistics.h" #include "download/download_main.h" #include "torrent/dht_manager.h" #include "torrent/download_info.h" #include "torrent/download/choke_queue.h" #include "torrent/peer/connection_list.h" #include "torrent/peer/peer_info.h" #include "rak/functional.h" #include "torrent/utils/log.h" #include "extensions.h" #include "peer_connection_metadata.h" #define LT_LOG_METADATA_EVENTS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_METADATA_EVENTS, this->download()->info(), "metadata_events", "%40s " log_fmt, this->peer_info()->id_hex(), __VA_ARGS__); #define LT_LOG_STORAGE_ERRORS(log_fmt, ...) \ lt_log_print_info(LOG_PROTOCOL_STORAGE_ERRORS, this->download()->info(), "storage_errors", "%40s " log_fmt, this->peer_info()->id_hex(), __VA_ARGS__); namespace torrent { PeerConnectionMetadata::~PeerConnectionMetadata() { } void PeerConnectionMetadata::initialize_custom() { } void PeerConnectionMetadata::update_interested() { } bool PeerConnectionMetadata::receive_keepalive() { if (cachedTime - m_timeLastRead > rak::timer::from_seconds(240)) return false; m_tryRequest = true; // There's no point in adding ourselves to the write poll if the // buffer is full, as that will already have been taken care of. if (m_up->get_state() == ProtocolWrite::IDLE && m_up->can_write_keepalive()) { write_insert_poll_safe(); ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); m_up->write_keepalive(); if (is_encrypted()) m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); } return true; } // We keep the message in the buffer if it is incomplete instead of // keeping the state and remembering the read information. This // shouldn't happen very often compared to full reads. inline bool PeerConnectionMetadata::read_message() { ProtocolBuffer<512>* buf = m_down->buffer(); if (buf->remaining() < 4) return false; // Remember the start of the message so we may reset it if we don't // have the whole message. ProtocolBuffer<512>::iterator beginning = buf->position(); uint32_t length = buf->read_32(); if (length == 0) { // Keepalive message. m_down->set_last_command(ProtocolBase::KEEP_ALIVE); return true; } else if (buf->remaining() < 1) { buf->set_position_itr(beginning); return false; } else if (length > (1 << 20)) { throw communication_error("PeerConnection::read_message() got an invalid message length."); } m_down->set_last_command((ProtocolBase::Protocol)buf->peek_8()); // Ignore most messages, they aren't relevant for a metadata download. switch (buf->read_8()) { case ProtocolBase::CHOKE: case ProtocolBase::UNCHOKE: case ProtocolBase::INTERESTED: case ProtocolBase::NOT_INTERESTED: return true; case ProtocolBase::HAVE: if (!m_down->can_read_have_body()) break; buf->read_32(); return true; case ProtocolBase::REQUEST: if (!m_down->can_read_request_body()) break; m_down->read_request(); return true; case ProtocolBase::PIECE: throw communication_error("Received a piece but the connection is strictly for meta data."); case ProtocolBase::CANCEL: if (!m_down->can_read_cancel_body()) break; m_down->read_request(); return true; case ProtocolBase::PORT: if (!m_down->can_read_port_body()) break; manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16()); return true; case ProtocolBase::EXTENSION_PROTOCOL: LT_LOG_METADATA_EVENTS("protocol extension message", 0); if (!m_down->can_read_extension_body()) break; if (m_extensions->is_default()) { m_extensions = new ProtocolExtension(); m_extensions->set_info(m_peerInfo, m_download); } { int extension = m_down->buffer()->read_8(); m_extensions->read_start(extension, length - 2, (extension == ProtocolExtension::UT_PEX) && !m_download->want_pex_msg()); m_down->set_state(ProtocolRead::READ_EXTENSION); } if (!down_extension()) return false; LT_LOG_METADATA_EVENTS("protocol extension done", 0); // Drop peer if it disabled the metadata extension. if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) throw close_connection(); m_down->set_state(ProtocolRead::IDLE); m_tryRequest = true; write_insert_poll_safe(); return true; case ProtocolBase::BITFIELD: // Discard the bitfield sent by the peer. m_skipLength = length - 1; m_down->set_state(ProtocolRead::READ_SKIP_PIECE); return false; default: throw communication_error("Received unsupported message type."); } // We were unsuccessfull in reading the message, need more data. buf->set_position_itr(beginning); return false; } void PeerConnectionMetadata::event_read() { m_timeLastRead = cachedTime; // Need to make sure ProtocolBuffer::end() is pointing to the end of // the unread data, and that the unread data starts from the // beginning of the buffer. Or do we use position? Propably best, // therefor ProtocolBuffer::position() points to the beginning of // the unused data. try { // Normal read. // // We rarely will read zero bytes as the read of 64 bytes will // almost always either not fill up or it will require additional // reads. // // Only loop when end hits 64. do { switch (m_down->get_state()) { case ProtocolRead::IDLE: if (m_down->buffer()->size_end() < read_size) { unsigned int length = read_stream_throws(m_down->buffer()->end(), read_size - m_down->buffer()->size_end()); m_down->throttle()->node_used_unthrottled(length); if (is_encrypted()) m_encryption.decrypt(m_down->buffer()->end(), length); m_down->buffer()->move_end(length); } while (read_message()); if (m_down->buffer()->size_end() == read_size) { m_down->buffer()->move_unused(); break; } else { m_down->buffer()->move_unused(); return; } case ProtocolRead::READ_EXTENSION: if (!down_extension()) return; // Drop peer if it disabled the metadata extension. if (!m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) throw close_connection(); LT_LOG_METADATA_EVENTS("reading extension message", 0); m_down->set_state(ProtocolRead::IDLE); m_tryRequest = true; write_insert_poll_safe(); break; // Actually skipping the bitfield. // We never receive normal piece messages anyway. case ProtocolRead::READ_SKIP_PIECE: if (!read_skip_bitfield()) return; m_down->set_state(ProtocolRead::IDLE); break; default: throw internal_error("PeerConnection::event_read() wrong state."); } // Figure out how to get rid of the shouldLoop boolean. } while (true); // Exception handlers: } catch (close_connection& e) { m_download->connection_list()->erase(this, 0); } catch (blocked_connection& e) { m_download->connection_list()->erase(this, 0); } catch (network_error& e) { m_download->connection_list()->erase(this, 0); } catch (storage_error& e) { LT_LOG_STORAGE_ERRORS("read error: %s", e.what()); m_download->connection_list()->erase(this, 0); } catch (base_error& e) { std::stringstream s; s << "Connection read fd(" << get_fd().get_fd() << ',' << m_down->get_state() << ',' << m_down->last_command() << ") \"" << e.what() << '"'; throw internal_error(s.str()); } } inline void PeerConnectionMetadata::fill_write_buffer() { ProtocolBuffer<512>::iterator old_end = m_up->buffer()->end(); if (m_tryRequest) m_tryRequest = try_request_metadata_pieces(); if (m_sendPEXMask && m_up->can_write_extension() && send_pex_message()) { // Don't do anything else if send_pex_message() succeeded. } else if (m_extensions->has_pending_message() && m_up->can_write_extension() && send_ext_message()) { // Same. } if (is_encrypted()) m_encryption.encrypt(old_end, m_up->buffer()->end() - old_end); } void PeerConnectionMetadata::event_write() { try { do { switch (m_up->get_state()) { case ProtocolWrite::IDLE: fill_write_buffer(); if (m_up->buffer()->remaining() == 0) { manager->poll()->remove_write(this); return; } m_up->set_state(ProtocolWrite::MSG); case ProtocolWrite::MSG: if (!m_up->buffer()->consume(m_up->throttle()->node_used_unthrottled(write_stream_throws(m_up->buffer()->position(), m_up->buffer()->remaining())))) return; m_up->buffer()->reset(); if (m_up->last_command() != ProtocolBase::EXTENSION_PROTOCOL) { m_up->set_state(ProtocolWrite::IDLE); break; } m_up->set_state(ProtocolWrite::WRITE_EXTENSION); case ProtocolWrite::WRITE_EXTENSION: if (!up_extension()) return; m_up->set_state(ProtocolWrite::IDLE); break; default: throw internal_error("PeerConnection::event_write() wrong state."); } } while (true); } catch (close_connection& e) { m_download->connection_list()->erase(this, 0); } catch (blocked_connection& e) { m_download->connection_list()->erase(this, 0); } catch (network_error& e) { m_download->connection_list()->erase(this, 0); } catch (storage_error& e) { LT_LOG_STORAGE_ERRORS("read error: %s", e.what()); m_download->connection_list()->erase(this, 0); } catch (base_error& e) { std::stringstream s; s << "Connection write fd(" << get_fd().get_fd() << ',' << m_up->get_state() << ',' << m_up->last_command() << ") \"" << e.what() << '"'; throw internal_error(s.str()); } } bool PeerConnectionMetadata::read_skip_bitfield() { if (m_down->buffer()->remaining()) { uint32_t length = std::min(m_skipLength, (uint32_t)m_down->buffer()->remaining()); m_down->buffer()->consume(length); m_skipLength -= length; } if (m_skipLength) { uint32_t length = std::min(m_skipLength, (uint32_t)null_buffer_size); length = read_stream_throws(m_nullBuffer, length); if (!length) return false; m_skipLength -= length; } return !m_skipLength; } // Same as the PCB code, but only one at a time and with the extension protocol. bool PeerConnectionMetadata::try_request_metadata_pieces() { if (m_download->file_list()->chunk_size() == 1 || !m_extensions->is_remote_supported(ProtocolExtension::UT_METADATA)) return false; if (request_list()->queued_empty()) m_downStall = 0; uint32_t pipeSize = request_list()->calculate_pipe_size(m_peerChunks.download_throttle()->rate()->rate()); // Don't start requesting if we can't do it in large enough chunks. if (request_list()->pipe_size() >= (pipeSize + 10) / 2) return false; // DEBUG: // if (!request_list()->queued_size() < pipeSize || !m_up->can_write_extension() || if (!m_up->can_write_extension() || m_extensions->has_pending_message()) return false; const Piece* p = request_list()->delegate(); if (p == NULL) return false; if (!m_download->file_list()->is_valid_piece(*p) || !m_peerChunks.bitfield()->get(p->index())) throw internal_error("PeerConnectionMetadata::try_request_metadata_pieces() tried to use an invalid piece."); // DEBUG: if (m_extensions->request_metadata_piece(p)) { LT_LOG_METADATA_EVENTS("request metadata piece succeded", 0); return true; } else { LT_LOG_METADATA_EVENTS("request metadata piece failed", 0); return false; } } void PeerConnectionMetadata::receive_metadata_piece(uint32_t piece, const char* data, uint32_t length) { if (data == NULL) { // Length is not set in a reject message. length = ProtocolExtension::metadata_piece_size; if ((piece << ProtocolExtension::metadata_piece_shift) + ProtocolExtension::metadata_piece_size >= m_download->file_list()->size_bytes()) length = m_download->file_list()->chunk_size() % ProtocolExtension::metadata_piece_size; m_tryRequest = false; read_cancel_piece(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length)); LT_LOG_METADATA_EVENTS("rejected metadata piece", 0); return; } if (!down_chunk_start(Piece(0, piece << ProtocolExtension::metadata_piece_shift, length))) { LT_LOG_METADATA_EVENTS("skipped metadata piece", 0); down_chunk_skip_process(data, length); } else { LT_LOG_METADATA_EVENTS("processed metadata piece", 0); down_chunk_process(data, length); } if (m_request_list.transfer() != NULL && !m_request_list.transfer()->is_finished()) throw internal_error("PeerConnectionMetadata::receive_metadata_piece did not have complete piece."); m_tryRequest = true; down_chunk_finished(); } } libtorrent-0.13.6/src/protocol/peer_connection_metadata.h000066400000000000000000000046621257211073700236150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H #define LIBTORRENT_PROTOCOL_PEER_CONNECTION_METADATA_H #include "peer_connection_base.h" #include "torrent/download.h" namespace torrent { class PeerConnectionMetadata : public PeerConnectionBase { public: ~PeerConnectionMetadata(); virtual void initialize_custom(); virtual void update_interested(); virtual bool receive_keepalive(); virtual void event_read(); virtual void event_write(); virtual void receive_metadata_piece(uint32_t piece, const char* data, uint32_t length); private: inline bool read_message(); bool read_skip_bitfield(); bool try_request_metadata_pieces(); inline void fill_write_buffer(); uint32_t m_skipLength; }; } #endif libtorrent-0.13.6/src/protocol/peer_factory.cc000066400000000000000000000045611257211073700214210ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "peer_factory.h" #include "peer_connection_leech.h" #include "peer_connection_metadata.h" namespace torrent { PeerConnectionBase* createPeerConnectionDefault(bool encrypted) { PeerConnectionBase* pc = new PeerConnection; return pc; } PeerConnectionBase* createPeerConnectionSeed(bool encrypted) { PeerConnectionBase* pc = new PeerConnection; return pc; } PeerConnectionBase* createPeerConnectionInitialSeed(bool encrypted) { PeerConnectionBase* pc = new PeerConnection; return pc; } PeerConnectionBase* createPeerConnectionMetadata(bool encrypted) { PeerConnectionBase* pc = new PeerConnectionMetadata; return pc; } } libtorrent-0.13.6/src/protocol/peer_factory.h000066400000000000000000000040051257211073700212540ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_PEER_FACTORY_H #define LIBTORRENT_PEER_PEER_FACTORY_H namespace torrent { class PeerConnectionBase; PeerConnectionBase* createPeerConnectionDefault(bool encrypted); PeerConnectionBase* createPeerConnectionSeed(bool encrypted); PeerConnectionBase* createPeerConnectionInitialSeed(bool encrypted); PeerConnectionBase* createPeerConnectionMetadata(bool encrypted); } #endif libtorrent-0.13.6/src/protocol/protocol_base.h000066400000000000000000000206261257211073700214340ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_PROTOCOL_BASE_H #define LIBTORRENT_NET_PROTOCOL_BASE_H #include #include "net/protocol_buffer.h" namespace torrent { class Piece; class ProtocolBase { public: typedef ProtocolBuffer<512> Buffer; typedef uint32_t size_type; static const size_type buffer_size = 512; typedef enum { CHOKE = 0, UNCHOKE, INTERESTED, NOT_INTERESTED, HAVE, BITFIELD, REQUEST, PIECE, CANCEL, PORT, // = 9 EXTENSION_PROTOCOL = 20, NONE, // These are not part of the protocol KEEP_ALIVE // Last command was a keep alive } Protocol; typedef enum { IDLE, MSG, READ_PIECE, READ_SKIP_PIECE, READ_EXTENSION, WRITE_PIECE, WRITE_EXTENSION, INTERNAL_ERROR } State; ProtocolBase() : m_state(IDLE), m_lastCommand(NONE), m_throttle(NULL) { m_buffer.reset(); } Protocol last_command() const { return m_lastCommand; } void set_last_command(Protocol p) { m_lastCommand = p; } Buffer* buffer() { return &m_buffer; } ThrottleList* throttle() { return m_throttle; } void set_throttle(ThrottleList* t) { m_throttle = t; } State get_state() const { return m_state; } void set_state(State s) { m_state = s; } Piece read_request(); Piece read_piece(size_type length); void write_command(Protocol c) { m_buffer.write_8(m_lastCommand = c); } void write_keepalive(); void write_choke(bool s); void write_interested(bool s); void write_have(uint32_t index); void write_bitfield(size_type length); void write_request(const Piece& p); void write_cancel(const Piece& p); void write_piece(const Piece& p); void write_port(uint16_t port); void write_extension(uint8_t id, uint32_t length); static const size_type sizeof_keepalive = 4; static const size_type sizeof_choke = 5; static const size_type sizeof_interested = 5; static const size_type sizeof_have = 9; static const size_type sizeof_have_body = 4; static const size_type sizeof_bitfield = 5; static const size_type sizeof_request = 17; static const size_type sizeof_request_body = 12; static const size_type sizeof_cancel = 17; static const size_type sizeof_cancel_body = 12; static const size_type sizeof_piece = 13; static const size_type sizeof_piece_body = 8; static const size_type sizeof_port = 7; static const size_type sizeof_port_body = 2; static const size_type sizeof_extension = 6; static const size_type sizeof_extension_body=1; bool can_write_keepalive() const { return m_buffer.reserved_left() >= sizeof_keepalive; } bool can_write_choke() const { return m_buffer.reserved_left() >= sizeof_choke; } bool can_write_interested() const { return m_buffer.reserved_left() >= sizeof_interested; } bool can_write_have() const { return m_buffer.reserved_left() >= sizeof_have; } bool can_write_bitfield() const { return m_buffer.reserved_left() >= sizeof_bitfield; } bool can_write_request() const { return m_buffer.reserved_left() >= sizeof_request; } bool can_write_cancel() const { return m_buffer.reserved_left() >= sizeof_cancel; } bool can_write_piece() const { return m_buffer.reserved_left() >= sizeof_piece; } bool can_write_port() const { return m_buffer.reserved_left() >= sizeof_port; } bool can_write_extension() const { return m_buffer.reserved_left() >= sizeof_extension; } bool can_read_have_body() const { return m_buffer.remaining() >= sizeof_have_body; } bool can_read_request_body() const { return m_buffer.remaining() >= sizeof_request_body; } bool can_read_cancel_body() const { return m_buffer.remaining() >= sizeof_request_body; } bool can_read_piece_body() const { return m_buffer.remaining() >= sizeof_piece_body; } bool can_read_port_body() const { return m_buffer.remaining() >= sizeof_port_body; } bool can_read_extension_body() const { return m_buffer.remaining() >= sizeof_extension_body; } protected: State m_state; Protocol m_lastCommand; ThrottleList* m_throttle; Buffer m_buffer; }; inline Piece ProtocolBase::read_request() { uint32_t index = m_buffer.read_32(); uint32_t offset = m_buffer.read_32(); uint32_t length = m_buffer.read_32(); return Piece(index, offset, length); } inline Piece ProtocolBase::read_piece(size_type length) { uint32_t index = m_buffer.read_32(); uint32_t offset = m_buffer.read_32(); return Piece(index, offset, length); } inline void ProtocolBase::write_keepalive() { m_buffer.write_32(0); m_lastCommand = KEEP_ALIVE; } inline void ProtocolBase::write_choke(bool s) { m_buffer.write_32(1); write_command(s ? CHOKE : UNCHOKE); } inline void ProtocolBase::write_interested(bool s) { m_buffer.write_32(1); write_command(s ? INTERESTED : NOT_INTERESTED); } inline void ProtocolBase::write_have(uint32_t index) { m_buffer.write_32(5); write_command(HAVE); m_buffer.write_32(index); } inline void ProtocolBase::write_bitfield(size_type length) { m_buffer.write_32(1 + length); write_command(BITFIELD); } inline void ProtocolBase::write_request(const Piece& p) { m_buffer.write_32(13); write_command(REQUEST); m_buffer.write_32(p.index()); m_buffer.write_32(p.offset()); m_buffer.write_32(p.length()); } inline void ProtocolBase::write_cancel(const Piece& p) { m_buffer.write_32(13); write_command(CANCEL); m_buffer.write_32(p.index()); m_buffer.write_32(p.offset()); m_buffer.write_32(p.length()); } inline void ProtocolBase::write_piece(const Piece& p) { m_buffer.write_32(9 + p.length()); write_command(PIECE); m_buffer.write_32(p.index()); m_buffer.write_32(p.offset()); } inline void ProtocolBase::write_port(uint16_t port) { m_buffer.write_32(3); write_command(PORT); m_buffer.write_16(port); } inline void ProtocolBase::write_extension(uint8_t id, uint32_t length) { m_buffer.write_32(2 + length); write_command(EXTENSION_PROTOCOL); m_buffer.write_8(id); } } #endif libtorrent-0.13.6/src/protocol/request_list.cc000066400000000000000000000314301257211073700214550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include "torrent/data/block.h" #include "torrent/data/block_list.h" #include "torrent/exceptions.h" #include "download/delegator.h" #include "utils/instrumentation.h" #include "peer_chunks.h" #include "request_list.h" namespace torrent { const instrumentation_enum request_list_constants::instrumentation_added[bucket_count] = { INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_ADDED }; const instrumentation_enum request_list_constants::instrumentation_moved[bucket_count] = { INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_MOVED }; const instrumentation_enum request_list_constants::instrumentation_removed[bucket_count] = { INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_REMOVED }; const instrumentation_enum request_list_constants::instrumentation_total[bucket_count] = { INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_TOTAL }; // Make inline... template <> void request_list_constants::destroy(BlockTransfer*& obj) { Block::release(obj); } // TODO: Add a to-be-cancelled list, timer, and use that to avoid // cancelling pieces on CHOKE->UNCHOKE weirdness in some clients. // // Right after this the client should always be allowed to queue more // pieces, perhaps add a timer for last choke and check the request // time on the queued pieces. This makes it possible to get going // again if the remote queue got cleared. // It is assumed invalid transfers have been removed. struct request_list_same_piece { request_list_same_piece(const Piece& p) : m_piece(p) {} bool operator () (const BlockTransfer* d) { return m_piece.index() == d->piece().index() && m_piece.offset() == d->piece().offset(); } Piece m_piece; }; struct request_list_keep_request { bool operator () (const BlockTransfer* d) { return d->is_valid(); } }; RequestList::~RequestList() { if (m_transfer != NULL) throw internal_error("request dtor m_transfer != NULL"); if (!m_queues.empty()) throw internal_error("request dtor m_queues not empty"); priority_queue_erase(&taskScheduler, &m_delay_remove_choked); priority_queue_erase(&taskScheduler, &m_delay_process_unordered); } const Piece* RequestList::delegate() { BlockTransfer* transfer = m_delegator->delegate(m_peerChunks, m_affinity); instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_DELEGATED, 1); if (transfer == NULL) return NULL; m_affinity = transfer->index(); m_queues.push_back(bucket_queued, transfer); return &transfer->piece(); } void RequestList::stall_initial() { queue_bucket_for_all_in_queue(m_queues, bucket_queued, std::ptr_fun(&Block::stalled)); m_queues.move_all_to(bucket_queued, bucket_stalled); queue_bucket_for_all_in_queue(m_queues, bucket_unordered, std::ptr_fun(&Block::stalled)); m_queues.move_all_to(bucket_unordered, bucket_stalled); } void RequestList::stall_prolonged() { if (m_transfer != NULL) Block::stalled(m_transfer); queue_bucket_for_all_in_queue(m_queues, bucket_queued, std::ptr_fun(&Block::stalled)); m_queues.move_all_to(bucket_queued, bucket_stalled); queue_bucket_for_all_in_queue(m_queues, bucket_unordered, std::ptr_fun(&Block::stalled)); m_queues.move_all_to(bucket_unordered, bucket_stalled); // Currently leave the the requests until the peer gets disconnected. (?) } void RequestList::choked() { // Check if we want to update the choke timer; if non-zero and // updated within a short timespan? m_last_choke = cachedTime; if (m_queues.queue_empty(bucket_queued) && m_queues.queue_empty(bucket_unordered)) return; m_queues.move_all_to(bucket_queued, bucket_choked); m_queues.move_all_to(bucket_unordered, bucket_choked); m_queues.move_all_to(bucket_stalled, bucket_choked); if (!m_delay_remove_choked.is_queued()) priority_queue_insert(&taskScheduler, &m_delay_remove_choked, (cachedTime + rak::timer::from_seconds(timeout_remove_choked)).round_seconds()); } void RequestList::unchoked() { m_last_unchoke = cachedTime; priority_queue_erase(&taskScheduler, &m_delay_remove_choked); // Clear choked queue if the peer doesn't start sending previously // requested pieces. // // This handles the case where a peer does a choke immediately // followed unchoke before starting to send pieces. if (!m_queues.queue_empty(bucket_choked)) { priority_queue_insert(&taskScheduler, &m_delay_remove_choked, (cachedTime + rak::timer::from_seconds(timeout_remove_choked)).round_seconds()); } } void RequestList::delay_remove_choked() { m_queues.clear(bucket_choked); } void RequestList::prepare_process_unordered(queues_type::iterator itr) { m_queues.move_to(bucket_queued, m_queues.begin(bucket_queued), itr, bucket_unordered); if (m_delay_process_unordered.is_queued()) return; priority_queue_insert(&taskScheduler, &m_delay_process_unordered, (cachedTime + rak::timer::from_seconds(timeout_process_unordered)).round_seconds()); m_last_unordered_position = unordered_size(); } void RequestList::delay_process_unordered() { m_last_unordered_position = std::min(m_last_unordered_position, unordered_size()); instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_FINISHED, m_last_unordered_position); m_queues.destroy(bucket_unordered, m_queues.begin(bucket_unordered), m_queues.begin(bucket_unordered) + m_last_unordered_position); m_last_unordered_position = unordered_size(); if (m_last_unordered_position != 0) priority_queue_insert(&taskScheduler, &m_delay_process_unordered, (cachedTime + rak::timer::from_seconds(timeout_process_unordered / 2)).round_seconds()); } void RequestList::clear() { if (is_downloading()) skipped(); m_queues.clear(bucket_queued); m_queues.clear(bucket_unordered); m_queues.clear(bucket_stalled); m_queues.clear(bucket_choked); } bool RequestList::downloading(const Piece& piece) { if (m_transfer != NULL) throw internal_error("RequestList::downloading(...) m_transfer != NULL."); instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_DOWNLOADING, 1); std::pair itr = queue_bucket_find_if_in_any(m_queues, request_list_same_piece(piece)); switch (itr.first) { case bucket_queued: if (itr.second != m_queues.begin(bucket_queued)) prepare_process_unordered(itr.second); m_transfer = m_queues.take(itr.first, itr.second); break; case bucket_unordered: // Do unordered take of element here to avoid copy shifting the whole deque. (?) // // Alternatively, move back some elements to bucket_queued. if (std::distance(m_queues.begin(itr.first), itr.second) < m_last_unordered_position) m_last_unordered_position--; m_transfer = m_queues.take(itr.first, itr.second); break; case bucket_stalled: // Do special handling of unordered pieces. m_transfer = m_queues.take(itr.first, itr.second); break; case bucket_choked: m_transfer = m_queues.take(itr.first, itr.second); // We make sure that the choked queue eventually gets cleared if // the peer has skipped sending some pieces from the choked queue. priority_queue_erase(&taskScheduler, &m_delay_remove_choked); priority_queue_insert(&taskScheduler, &m_delay_remove_choked, (cachedTime + rak::timer::from_seconds(timeout_choked_received)).round_seconds()); break; default: goto downloading_error; }; // We received an invalid piece length, propably zero length due to // the peer not being able to transfer the requested piece. // // We need to replace the current BlockTransfer so Block can keep // the unmodified BlockTransfer. if (piece.length() != m_transfer->piece().length()) { if (piece.length() != 0) throw communication_error("Peer sent a piece with wrong, non-zero, length."); Block::release(m_transfer); goto downloading_error; } // Check if piece isn't wanted anymore. Do this after the length // check to ensure we return a correct BlockTransfer. if (!m_transfer->is_valid()) return false; m_transfer->block()->transfering(m_transfer); return true; downloading_error: // Create a dummy BlockTransfer object to hold the piece // information. m_transfer = new BlockTransfer(); Block::create_dummy(m_transfer, m_peerChunks->peer_info(), piece); instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_UNKNOWN, 1); return false; } // Must clear the downloading piece. void RequestList::finished() { if (!is_downloading()) throw internal_error("RequestList::finished() called but no transfer is in progress."); if (!m_transfer->is_valid()) throw internal_error("RequestList::finished() called but transfer is invalid."); BlockTransfer* transfer = m_transfer; m_transfer = NULL; m_delegator->transfer_list()->finished(transfer); instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_FINISHED, 1); } void RequestList::skipped() { if (!is_downloading()) throw internal_error("RequestList::skip() called but no transfer is in progress."); Block::release(m_transfer); m_transfer = NULL; instrumentation_update(INSTRUMENTATION_TRANSFER_REQUESTS_SKIPPED, 1); } // Data downloaded by this non-leading transfer does not match what we // already have. void RequestList::transfer_dissimilar() { if (!is_downloading()) throw internal_error("RequestList::transfer_dissimilar() called but no transfer is in progress."); BlockTransfer* dummy = new BlockTransfer(); Block::create_dummy(dummy, m_peerChunks->peer_info(), m_transfer->piece()); dummy->set_position(m_transfer->position()); // TODO.... peer_info still on a block we no longer control?.. m_transfer->block()->transfer_dissimilar(m_transfer); m_transfer = dummy; } struct equals_reservee : public std::binary_function { bool operator () (BlockTransfer* r, uint32_t index) const { return r->is_valid() && index == r->index(); } }; bool RequestList::is_interested_in_active() const { for (TransferList::const_iterator itr = m_delegator->transfer_list()->begin(), last = m_delegator->transfer_list()->end(); itr != last; ++itr) if (m_peerChunks->bitfield()->get((*itr)->index())) return true; return false; } uint32_t RequestList::calculate_pipe_size(uint32_t rate) { // Change into KB. rate /= 1024; if (!m_delegator->get_aggressive()) { if (rate < 20) return rate + 2; else return rate / 5 + 18; } else { if (rate < 10) return rate / 5 + 1; else return rate / 10 + 2; } } } libtorrent-0.13.6/src/protocol/request_list.h000066400000000000000000000140661257211073700213250ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_REQUEST_LIST_H #define LIBTORRENT_REQUEST_LIST_H #include #include "torrent/data/block_transfer.h" #include "utils/instrumentation.h" #include "utils/queue_buckets.h" #include "globals.h" namespace torrent { class PeerChunks; class Delegator; struct request_list_constants { static const int bucket_count = 4; static const torrent::instrumentation_enum instrumentation_added[bucket_count]; static const torrent::instrumentation_enum instrumentation_moved[bucket_count]; static const torrent::instrumentation_enum instrumentation_removed[bucket_count]; static const torrent::instrumentation_enum instrumentation_total[bucket_count]; template static void destroy(Type& obj); }; class RequestList { public: typedef torrent::queue_buckets queues_type; static const int bucket_queued = 0; static const int bucket_unordered = 1; static const int bucket_stalled = 2; static const int bucket_choked = 3; static const int timeout_remove_choked = 6; static const int timeout_choked_received = 60; static const int timeout_process_unordered = 60; RequestList(); ~RequestList(); // Some parameters here, like how fast we are downloading and stuff // when we start considering those. const Piece* delegate(); void stall_initial(); void stall_prolonged(); void choked(); void unchoked(); void clear(); // The returned transfer must still be valid. bool downloading(const Piece& piece); void finished(); void skipped(); void transfer_dissimilar(); bool is_downloading() { return m_transfer != NULL; } bool is_interested_in_active() const; // bool has_index(uint32_t i); const Piece& next_queued_piece() const { return m_queues.front(bucket_queued)->piece(); } bool queued_empty() const { return m_queues.queue_empty(bucket_queued); } size_t queued_size() const { return m_queues.queue_size(bucket_queued); } bool unordered_empty() const { return m_queues.queue_empty(bucket_unordered); } size_t unordered_size() const { return m_queues.queue_size(bucket_unordered); } bool stalled_empty() const { return m_queues.queue_empty(bucket_stalled); } size_t stalled_size() const { return m_queues.queue_size(bucket_stalled); } bool choked_empty() const { return m_queues.queue_empty(bucket_choked); } size_t choked_size() const { return m_queues.queue_size(bucket_choked); } uint32_t pipe_size() const; uint32_t calculate_pipe_size(uint32_t rate); void set_delegator(Delegator* d) { m_delegator = d; } void set_peer_chunks(PeerChunks* b) { m_peerChunks = b; } BlockTransfer* transfer() { return m_transfer; } const BlockTransfer* transfer() const { return m_transfer; } const BlockTransfer* queued_front() const { return m_queues.front(bucket_queued); } private: void delay_remove_choked(); void prepare_process_unordered(queues_type::iterator itr); void delay_process_unordered(); Delegator* m_delegator; PeerChunks* m_peerChunks; BlockTransfer* m_transfer; queues_type m_queues; int32_t m_affinity; rak::timer m_last_choke; rak::timer m_last_unchoke; size_t m_last_unordered_position; rak::priority_item m_delay_remove_choked; rak::priority_item m_delay_process_unordered; }; inline RequestList::RequestList() : m_delegator(NULL), m_peerChunks(NULL), m_transfer(NULL), m_affinity(-1), m_last_unordered_position(0) { m_delay_remove_choked.slot() = std::tr1::bind(&RequestList::delay_remove_choked, this); m_delay_process_unordered.slot() = std::tr1::bind(&RequestList::delay_process_unordered, this); } // TODO: Make sure queued_size is never too small. inline uint32_t RequestList::pipe_size() const { return queued_size() + stalled_size() + unordered_size() / 4; } } #endif libtorrent-0.13.6/src/thread_disk.cc000066400000000000000000000052671257211073700173630ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "thread_disk.h" #include "torrent/exceptions.h" #include "torrent/poll.h" #include "torrent/utils/log.h" #include "utils/instrumentation.h" namespace torrent { void thread_disk::init_thread() { if (!Poll::slot_create_poll()) throw internal_error("thread_disk::init_thread(): Poll::slot_create_poll() not valid."); m_poll = Poll::slot_create_poll()(); m_state = STATE_INITIALIZED; m_instrumentation_index = INSTRUMENTATION_POLLING_DO_POLL_DISK - INSTRUMENTATION_POLLING_DO_POLL; } void thread_disk::call_events() { // lt_log_print_locked(torrent::LOG_THREAD_NOTICE, "Got thread_disk tick."); // TODO: Consider moving this into timer events instead. if ((m_flags & flag_do_shutdown)) { if ((m_flags & flag_did_shutdown)) throw internal_error("Already trigged shutdown."); __sync_or_and_fetch(&m_flags, flag_did_shutdown); throw shutdown_exception(); } m_hash_queue.perform(); } int64_t thread_disk::next_timeout_usec() { return rak::timer::from_seconds(10).round_seconds().usec(); } } libtorrent-0.13.6/src/thread_disk.h000066400000000000000000000041731257211073700172200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_THREAD_DISK_H #define LIBTORRENT_THREAD_DISK_H #include "data/hash_check_queue.h" #include "torrent/utils/thread_base.h" namespace torrent { class thread_disk : public thread_base { public: const char* name() const { return "rtorrent disk"; } virtual void init_thread(); HashCheckQueue* hash_queue() { return &m_hash_queue; } protected: virtual void call_events(); virtual int64_t next_timeout_usec(); HashCheckQueue m_hash_queue; }; } #endif libtorrent-0.13.6/src/thread_main.cc000066400000000000000000000062031257211073700173440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "thread_main.h" #include "globals.h" #include "torrent/exceptions.h" #include "torrent/poll.h" #include "torrent/utils/log.h" #include "utils/instrumentation.h" namespace torrent { void thread_main::init_thread() { acquire_global_lock(); if (!Poll::slot_create_poll()) throw internal_error("thread_main::init_thread(): Poll::slot_create_poll() not valid."); m_poll = Poll::slot_create_poll()(); m_poll->set_flags(Poll::flag_waive_global_lock); m_state = STATE_INITIALIZED; m_thread = pthread_self(); m_flags |= flag_main_thread; m_instrumentation_index = INSTRUMENTATION_POLLING_DO_POLL_MAIN - INSTRUMENTATION_POLLING_DO_POLL; } void thread_main::call_events() { cachedTime = rak::timer::current(); // Ensure we don't call rak::timer::current() twice if there was no // scheduled tasks called. if (taskScheduler.empty() || taskScheduler.top()->time() > cachedTime) return; while (!taskScheduler.empty() && taskScheduler.top()->time() <= cachedTime) { rak::priority_item* v = taskScheduler.top(); taskScheduler.pop(); v->clear_time(); v->slot()(); } // Update the timer again to ensure we get accurate triggering of // msec timers. cachedTime = rak::timer::current(); } int64_t thread_main::next_timeout_usec() { cachedTime = rak::timer::current(); if (!taskScheduler.empty()) return std::max(taskScheduler.top()->time() - cachedTime, rak::timer()).usec(); else return rak::timer::from_seconds(60).usec(); } } libtorrent-0.13.6/src/thread_main.h000066400000000000000000000040301257211073700172020ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_THREAD_MAIN_H #define LIBTORRENT_THREAD_MAIN_H #include "data/hash_check_queue.h" #include "torrent/utils/thread_base.h" namespace torrent { class thread_main : public thread_base { public: const char* name() const { return "rtorrent main"; } virtual void init_thread(); protected: virtual void call_events(); virtual int64_t next_timeout_usec(); }; } #endif libtorrent-0.13.6/src/torrent/000077500000000000000000000000001257211073700162565ustar00rootroot00000000000000libtorrent-0.13.6/src/torrent/Makefile.am000066400000000000000000000026721257211073700203210ustar00rootroot00000000000000SUBDIRS = \ data \ download \ peer \ utils noinst_LTLIBRARIES = libsub_torrent.la libsub_torrent_la_SOURCES = \ bitfield.cc \ bitfield.h \ chunk_manager.cc \ chunk_manager.h \ common.h \ connection_manager.cc \ connection_manager.h \ dht_manager.cc \ dht_manager.h \ download.cc \ download.h \ download_info.h \ error.cc \ error.h \ event.h \ exceptions.cc \ exceptions.h \ hash_string.cc \ hash_string.h \ http.cc \ http.h \ object.cc \ object.h \ object_raw_bencode.h \ object_static_map.cc \ object_static_map.h \ object_stream.cc \ object_stream.h \ path.cc \ path.h \ poll.h \ poll_epoll.cc \ poll_epoll.h \ poll_kqueue.h \ poll_kqueue.cc \ poll_select.h \ poll_select.cc \ rate.cc \ rate.h \ throttle.cc \ throttle.h \ torrent.cc \ torrent.h \ tracker.cc \ tracker.h \ tracker_controller.cc \ tracker_controller.h \ tracker_list.cc \ tracker_list.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrentincludedir = $(includedir)/torrent libtorrentinclude_HEADERS = \ bitfield.h \ chunk_manager.h \ common.h \ connection_manager.h \ dht_manager.h \ download.h \ download_info.h \ error.h \ exceptions.h \ event.h \ hash_string.h \ http.h \ object.h \ object_raw_bencode.h \ object_static_map.h \ object_stream.h \ path.h \ poll.h \ poll_epoll.h \ poll_kqueue.h \ poll_select.h \ rate.h \ throttle.h \ torrent.h \ tracker.h \ tracker_controller.h \ tracker_list.h libtorrent-0.13.6/src/torrent/bitfield.cc000066400000000000000000000100101257211073700203370ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "rak/algorithm.h" #include "utils/instrumentation.h" #include "bitfield.h" #include "exceptions.h" namespace torrent { void Bitfield::set_size_bits(size_type s) { if (m_data != NULL) throw internal_error("Bitfield::set_size_bits(size_type s) m_data != NULL."); m_size = s; } void Bitfield::set_size_set(size_type s) { if (s > m_size || m_data != NULL) throw internal_error("Bitfield::set_size_set(size_type s) s > m_size."); m_set = s; } void Bitfield::allocate() { if (m_data != NULL) return; m_data = new value_type[size_bytes()]; instrumentation_update(INSTRUMENTATION_MEMORY_BITFIELDS, (int64_t)size_bytes()); } void Bitfield::unallocate() { if (m_data == NULL) return; delete [] m_data; m_data = NULL; instrumentation_update(INSTRUMENTATION_MEMORY_BITFIELDS, -(int64_t)size_bytes()); } void Bitfield::update() { // Clears the unused bits. clear_tail(); m_set = 0; iterator itr = m_data; iterator last = end(); while (itr + sizeof(unsigned int) <= last) { m_set += rak::popcount_wrapper(*reinterpret_cast(itr)); itr += sizeof(unsigned int); } while (itr != last) { m_set += rak::popcount_wrapper(*itr++); } } void Bitfield::copy(const Bitfield& bf) { unallocate(); m_size = bf.m_size; m_set = bf.m_set; if (bf.m_data == NULL) { m_data = NULL; } else { allocate(); std::memcpy(m_data, bf.m_data, size_bytes()); } } void Bitfield::swap(Bitfield& bf) { std::swap(m_size, bf.m_size); std::swap(m_set, bf.m_set); std::swap(m_data, bf.m_data); } void Bitfield::set_all() { m_set = m_size; std::memset(m_data, ~value_type(), size_bytes()); clear_tail(); } void Bitfield::unset_all() { m_set = 0; std::memset(m_data, value_type(), size_bytes()); } // Quick hack. Speed improvements would require that m_set is kept // up-to-date. void Bitfield::set_range(size_type first, size_type last) { while (first != last) set(first++); } void Bitfield::unset_range(size_type first, size_type last) { while (first != last) unset(first++); } // size_type // Bitfield::count_range(size_type first, size_type last) { // size_type count = 0; // // Some archs have bitcounting instructions, look into writing a // // wrapper for those. // for (iterator itr = m_data, last = end(); itr != last; ++itr) // m_set += bit_count_256[*itr]; // } } libtorrent-0.13.6/src/torrent/bitfield.h000066400000000000000000000117531257211073700202200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_BITFIELD_H #define LIBTORRENT_BITFIELD_H #include #include namespace torrent { class LIBTORRENT_EXPORT Bitfield { public: typedef uint32_t size_type; typedef uint8_t value_type; typedef const uint8_t const_value_type; typedef value_type* iterator; typedef const value_type* const_iterator; Bitfield() : m_size(0), m_set(0), m_data(NULL) {} ~Bitfield() { clear(); } bool empty() const { return m_data == NULL; } bool is_all_set() const { return m_set == m_size; } bool is_all_unset() const { return m_set == 0; } bool is_tail_cleared() const { return m_size % 8 == 0 || !((*(end() - 1) & mask_from(m_size % 8))); } size_type size_bits() const { return m_size; } size_type size_bytes() const { return (m_size + 7) / 8; } size_type size_set() const { return m_set; } size_type size_unset() const { return m_size - m_set; } void set_size_bits(size_type s); void set_size_set(size_type s); // Call update if you've changed the data directly and want to // update the counters and unset the last unused bits. // // Resize clears the data? void update(); void allocate(); void unallocate(); void clear() { unallocate(); m_size = 0; m_set = 0; } void clear_tail() { if (m_size % 8) *(end() - 1) &= mask_before(m_size % 8); } void copy(const Bitfield& bf); void swap(Bitfield& bf); void set_all(); void set_range(size_type first, size_type last); void unset_all(); void unset_range(size_type first, size_type last); // size_type count_range(size_type first, size_type last) const; bool get(size_type idx) const { return m_data[idx / 8] & mask_at(idx % 8); } void set(size_type idx) { m_set += !get(idx); m_data[idx / 8] |= mask_at(idx % 8); } void unset(size_type idx) { m_set -= get(idx); m_data[idx / 8] &= ~mask_at(idx % 8); } iterator begin() { return m_data; } const_iterator begin() const { return m_data; } iterator end() { return m_data + size_bytes(); } const_iterator end() const { return m_data + size_bytes(); } size_type position(const_iterator itr) const { return (itr - m_data) * 8; } void from_c_str(const char* str) { std::memcpy(m_data, str, size_bytes()); update(); } // Remember to use modulo. static value_type mask_at(size_type idx) { return 1 << (7 - idx); } static value_type mask_before(size_type idx) { return (value_type)~0 << (8 - idx); } static value_type mask_from(size_type idx) { return (value_type)~0 >> idx; } private: Bitfield(const Bitfield& bf); Bitfield& operator = (const Bitfield& bf); size_type m_size; size_type m_set; value_type* m_data; }; } #endif libtorrent-0.13.6/src/torrent/chunk_manager.cc000066400000000000000000000147301257211073700213740ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "data/chunk_list.h" #include "utils/instrumentation.h" #include "exceptions.h" #include "chunk_manager.h" #include "globals.h" namespace torrent { ChunkManager::ChunkManager() : m_memoryUsage(0), m_memoryBlockCount(0), m_safeSync(false), m_timeoutSync(600), m_timeoutSafeSync(900), m_preloadType(0), m_preloadMinSize(256 << 10), m_preloadRequiredRate(5 << 10), m_statsPreloaded(0), m_statsNotPreloaded(0), m_timerStarved(0), m_lastFreed(0) { // 1/5 of the available memory should be enough for the client. If // the client really requires alot more memory it should call this // itself. m_maxMemoryUsage = (estimate_max_memory_usage() * 4) / 5; } ChunkManager::~ChunkManager() { if (m_memoryUsage != 0 || m_memoryBlockCount != 0) throw internal_error("ChunkManager::~ChunkManager() m_memoryUsage != 0 || m_memoryBlockCount != 0."); } uint64_t ChunkManager::sync_queue_memory_usage() const { uint64_t size = 0; for (const_iterator itr = begin(), last = end(); itr != last; itr++) size += (*itr)->queue_size() * (*itr)->chunk_size(); return size; } uint32_t ChunkManager::sync_queue_size() const { uint32_t size = 0; for (const_iterator itr = begin(), last = end(); itr != last; itr++) size += (*itr)->queue_size(); return size; } uint64_t ChunkManager::estimate_max_memory_usage() { rlimit rlp; #ifdef RLIMIT_AS if (getrlimit(RLIMIT_AS, &rlp) == 0 && rlp.rlim_cur != RLIM_INFINITY) #else if (getrlimit(RLIMIT_DATA, &rlp) == 0 && rlp.rlim_cur != RLIM_INFINITY) #endif return rlp.rlim_cur; return (uint64_t)DEFAULT_ADDRESS_SPACE_SIZE << 20; } uint64_t ChunkManager::safe_free_diskspace() const { return m_memoryUsage + ((uint64_t)512 << 20); } void ChunkManager::insert(ChunkList* chunkList) { chunkList->set_manager(this); base_type::push_back(chunkList); } void ChunkManager::erase(ChunkList* chunkList) { if (chunkList->queue_size() != 0) throw internal_error("ChunkManager::erase(...) chunkList->queue_size() != 0."); iterator itr = std::find(base_type::begin(), base_type::end(), chunkList); if (itr == base_type::end()) throw internal_error("ChunkManager::erase(...) itr == base_type::end()."); std::iter_swap(itr, --base_type::end()); base_type::pop_back(); chunkList->set_manager(NULL); } bool ChunkManager::allocate(uint32_t size, int flags) { if (m_memoryUsage + size > (3 * m_maxMemoryUsage) / 4) try_free_memory((1 * m_maxMemoryUsage) / 4); if (m_memoryUsage + size > m_maxMemoryUsage) { if (!(flags & allocate_dont_log)) instrumentation_update(INSTRUMENTATION_MINCORE_ALLOC_FAILED, 1); return false; } if (!(flags & allocate_dont_log)) instrumentation_update(INSTRUMENTATION_MINCORE_ALLOCATIONS, size); m_memoryUsage += size; m_memoryBlockCount++; instrumentation_update(INSTRUMENTATION_MEMORY_CHUNK_COUNT, 1); instrumentation_update(INSTRUMENTATION_MEMORY_CHUNK_USAGE, size); return true; } void ChunkManager::deallocate(uint32_t size, int flags) { if (size > m_memoryUsage) throw internal_error("ChunkManager::deallocate(...) size > m_memoryUsage."); if (!(flags & allocate_dont_log)) { if (flags & allocate_revert_log) instrumentation_update(INSTRUMENTATION_MINCORE_ALLOCATIONS, -size); else instrumentation_update(INSTRUMENTATION_MINCORE_DEALLOCATIONS, size); } m_memoryUsage -= size; m_memoryBlockCount--; instrumentation_update(INSTRUMENTATION_MEMORY_CHUNK_COUNT, -1); instrumentation_update(INSTRUMENTATION_MEMORY_CHUNK_USAGE, -(int64_t)size); } void ChunkManager::try_free_memory(uint64_t size) { // Ensure that we don't call this function too often when futile as // it might be somewhat expensive. // // Note that it won't be able to free chunks that are scheduled for // hash checking, so a too low max memory setting will give problem // at high transfer speed. if (m_timerStarved + 10 >= cachedTime.seconds()) return; sync_all(0, size <= m_memoryUsage ? (m_memoryUsage - size) : 0); // The caller must ensure he tries to free a sufficiently large // amount of memory to ensure it, and other users, has enough memory // space for at least 10 seconds. m_timerStarved = cachedTime.seconds(); } void ChunkManager::periodic_sync() { sync_all(ChunkList::sync_use_timeout, 0); } void ChunkManager::sync_all(int flags, uint64_t target) { if (empty()) return; // Start from the next entry so that we don't end up repeatedly // syncing the same torrent. m_lastFreed = m_lastFreed % base_type::size() + 1; iterator itr = base_type::begin() + m_lastFreed; do { if (itr == base_type::end()) itr = base_type::begin(); (*itr)->sync_chunks(flags); } while (++itr != base_type::begin() + m_lastFreed && m_memoryUsage >= target); m_lastFreed = itr - begin(); } } libtorrent-0.13.6/src/torrent/chunk_manager.h000066400000000000000000000152771257211073700212450ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Add some helpfull words here. #ifndef LIBTORRENT_CHUNK_MANAGER_H #define LIBTORRENT_CHUNK_MANAGER_H #include #include namespace torrent { // TODO: Currently all chunk lists are inserted, despite the download // not being open/active. class LIBTORRENT_EXPORT ChunkManager : private std::vector { public: typedef std::vector base_type; typedef uint32_t size_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::const_iterator; using base_type::begin; using base_type::end; using base_type::size; using base_type::empty; ChunkManager(); ~ChunkManager(); uint64_t memory_usage() const { return m_memoryUsage; } uint64_t sync_queue_memory_usage() const; uint32_t memory_block_count() const { return m_memoryBlockCount; } uint32_t sync_queue_size() const; // Should we allow the client to reserve some memory? // The client should set this automatically if ulimit is set. uint64_t max_memory_usage() const { return m_maxMemoryUsage; } void set_max_memory_usage(uint64_t bytes) { m_maxMemoryUsage = bytes; } // Estimate the max memory usage possible, capped at 1GB. static uint64_t estimate_max_memory_usage(); uint64_t safe_free_diskspace() const; bool safe_sync() const { return m_safeSync; } void set_safe_sync(uint32_t state) { m_safeSync = state; } // Set the interval to wait after the last write to a chunk before // trying to sync it. By not forcing a sync too early it should give // the kernel an oppertunity to sync at its convenience. uint32_t timeout_sync() const { return m_timeoutSync; } void set_timeout_sync(uint32_t seconds) { m_timeoutSync = seconds; } uint32_t timeout_safe_sync() const { return m_timeoutSafeSync; } void set_timeout_safe_sync(uint32_t seconds) { m_timeoutSafeSync = seconds; } // Set to 0 to disable preloading. // // How the value is used is yet to be determined, but it won't be // able to use actual requests in the request queue as we can easily // stay ahead of it causing preloading to fail. uint32_t preload_type() const { return m_preloadType; } void set_preload_type(uint32_t t) { m_preloadType = t; } uint32_t preload_min_size() const { return m_preloadMinSize; } void set_preload_min_size(uint32_t bytes) { m_preloadMinSize = bytes; } // Required rate before attempting to preload chunk, per whole // megabyte of chunk size. uint32_t preload_required_rate() const { return m_preloadRequiredRate; } void set_preload_required_rate(uint32_t bytes) { m_preloadRequiredRate = bytes; } void insert(ChunkList* chunkList); void erase(ChunkList* chunkList); // The client may use these functions to affect the library's memory // usage by indicating how much it uses. This shouldn't really be // nessesary unless the client maps large amounts of memory. // // If the caller finds out the allocated memory quota isn't needed // due to e.g. other errors then 'deallocate_unused' must be called // within the context of the original 'allocate' caller in order to // properly be reflected when logging. // // The primary user of these functions is ChunkList. static const int allocate_revert_log = (1 << 0); static const int allocate_dont_log = (1 << 1); bool allocate(uint32_t size, int flags = 0); void deallocate(uint32_t size, int flags = 0); void try_free_memory(uint64_t size); void periodic_sync(); // Not sure if I wnt these here. Consider implementing a generic // statistics API. uint32_t stats_preloaded() const { return m_statsPreloaded; } void inc_stats_preloaded() { m_statsPreloaded++; } uint32_t stats_not_preloaded() const { return m_statsNotPreloaded; } void inc_stats_not_preloaded() { m_statsNotPreloaded++; } private: ChunkManager(const ChunkManager&); void operator = (const ChunkManager&); void sync_all(int flags, uint64_t target) LIBTORRENT_NO_EXPORT; uint64_t m_memoryUsage; uint64_t m_maxMemoryUsage; uint32_t m_memoryBlockCount; bool m_safeSync; uint32_t m_timeoutSync; uint32_t m_timeoutSafeSync; uint32_t m_preloadType; uint32_t m_preloadMinSize; uint32_t m_preloadRequiredRate; uint32_t m_statsPreloaded; uint32_t m_statsNotPreloaded; int32_t m_timerStarved; size_type m_lastFreed; }; } #endif libtorrent-0.13.6/src/torrent/common.h000066400000000000000000000057631257211073700177320ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_COMMON_H #define LIBTORRENT_COMMON_H #include #include struct sockaddr; namespace torrent { enum priority_enum { PRIORITY_OFF = 0, PRIORITY_NORMAL, PRIORITY_HIGH }; typedef priority_enum priority_t; // Just forward declare everything here so we can keep the actual // headers clean. class AvailableList; class Bitfield; class Block; class BlockFailed; class BlockList; class BlockTransfer; class Chunk; class ChunkList; class ChunkManager; class ChunkSelector; class ClientInfo; class ClientList; class ConnectionList; class ConnectionManager; class DhtManager; class DhtRouter; class Download; class DownloadMain; class DownloadWrapper; class FileList; class Event; class File; class FileList; class Handshake; class HandshakeManager; class HashString; class Listen; class MemoryChunk; class Object; class Path; class Peer; class PeerConnectionBase; class PeerInfo; class PeerList; class Piece; class Poll; class ProtocolExtension; class Rate; class SocketSet; class Throttle; class Tracker; class TrackerList; class TransferList; // This should only need to be set when compiling libtorrent. #ifdef SUPPORT_ATTRIBUTE_VISIBILITY #define LIBTORRENT_NO_EXPORT __attribute__ ((visibility("hidden"))) #define LIBTORRENT_EXPORT __attribute__ ((visibility("default"))) #else #define LIBTORRENT_NO_EXPORT #define LIBTORRENT_EXPORT #endif } #endif libtorrent-0.13.6/src/torrent/connection_manager.cc000066400000000000000000000136631257211073700224270ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "net/listen.h" #include "connection_manager.h" #include "error.h" #include "exceptions.h" #include "manager.h" namespace tr1 { using namespace std::tr1; } namespace torrent { // Fix TrackerUdp, etc, if this is made async. static ConnectionManager::slot_resolver_result_type* resolve_host(const char* host, int family, int socktype, ConnectionManager::slot_resolver_result_type slot) { if (manager->main_thread_main()->is_current()) thread_base::release_global_lock(); rak::address_info* ai; int err; if ((err = rak::address_info::get_address_info(host, family, socktype, &ai)) != 0) { if (manager->main_thread_main()->is_current()) thread_base::acquire_global_lock(); slot(NULL, err); return NULL; } rak::socket_address sa; sa.copy(*ai->address(), ai->length()); rak::address_info::free_address_info(ai); if (manager->main_thread_main()->is_current()) thread_base::acquire_global_lock(); slot(sa.c_sockaddr(), 0); return NULL; } ConnectionManager::ConnectionManager() : m_size(0), m_maxSize(0), m_priority(iptos_throughput), m_sendBufferSize(0), m_receiveBufferSize(0), m_encryptionOptions(encryption_none), m_listen(new Listen), m_listen_port(0), m_listen_backlog(SOMAXCONN) { m_bindAddress = (new rak::socket_address())->c_sockaddr(); rak::socket_address::cast_from(m_bindAddress)->sa_inet()->clear(); m_localAddress = (new rak::socket_address())->c_sockaddr(); rak::socket_address::cast_from(m_localAddress)->sa_inet()->clear(); m_proxyAddress = (new rak::socket_address())->c_sockaddr(); rak::socket_address::cast_from(m_proxyAddress)->sa_inet()->clear(); m_slot_resolver = tr1::bind(&resolve_host, tr1::placeholders::_1, tr1::placeholders::_2, tr1::placeholders::_3, tr1::placeholders::_4); } ConnectionManager::~ConnectionManager() { delete m_listen; delete m_bindAddress; delete m_localAddress; delete m_proxyAddress; } bool ConnectionManager::can_connect() const { return m_size < m_maxSize; } void ConnectionManager::set_send_buffer_size(uint32_t s) { m_sendBufferSize = s; } void ConnectionManager::set_receive_buffer_size(uint32_t s) { m_receiveBufferSize = s; } void ConnectionManager::set_encryption_options(uint32_t options) { #ifdef USE_OPENSSL m_encryptionOptions = options; #else throw input_error("Compiled without encryption support."); #endif } void ConnectionManager::set_bind_address(const sockaddr* sa) { const rak::socket_address* rsa = rak::socket_address::cast_from(sa); if (rsa->family() != rak::socket_address::af_inet) throw input_error("Tried to set a bind address that is not an af_inet address."); rak::socket_address::cast_from(m_bindAddress)->copy(*rsa, rsa->length()); } void ConnectionManager::set_local_address(const sockaddr* sa) { const rak::socket_address* rsa = rak::socket_address::cast_from(sa); if (rsa->family() != rak::socket_address::af_inet) throw input_error("Tried to set a local address that is not an af_inet address."); rak::socket_address::cast_from(m_localAddress)->copy(*rsa, rsa->length()); } void ConnectionManager::set_proxy_address(const sockaddr* sa) { const rak::socket_address* rsa = rak::socket_address::cast_from(sa); if (rsa->family() != rak::socket_address::af_inet) throw input_error("Tried to set a proxy address that is not an af_inet address."); rak::socket_address::cast_from(m_proxyAddress)->copy(*rsa, rsa->length()); } uint32_t ConnectionManager::filter(const sockaddr* sa) { if (!m_slot_filter) return 1; else return m_slot_filter(sa); } bool ConnectionManager::listen_open(port_type begin, port_type end) { if (!m_listen->open(begin, end, m_listen_backlog, rak::socket_address::cast_from(m_bindAddress))) return false; m_listen_port = m_listen->port(); return true; } void ConnectionManager::listen_close() { m_listen->close(); } void ConnectionManager::set_listen_backlog(int v) { if (v < 1 || v >= (1 << 16)) throw input_error("backlog value out of bounds"); if (m_listen->is_open()) throw input_error("backlog value must be set before listen port is opened"); m_listen_backlog = v; } } libtorrent-0.13.6/src/torrent/connection_manager.h000066400000000000000000000200621257211073700222600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Add some helpfull words here. (These are some words, hope they are // helpful) #ifndef LIBTORRENT_CONNECTION_MANAGER_H #define LIBTORRENT_CONNECTION_MANAGER_H #include #include #include #include #include #include #include #include namespace torrent { // Standard pair of up/down throttles. // First element is upload throttle, second element is download throttle. typedef std::pair ThrottlePair; class LIBTORRENT_EXPORT ConnectionManager { public: typedef uint32_t size_type; typedef uint16_t port_type; typedef uint8_t priority_type; static const priority_type iptos_default = 0; static const priority_type iptos_lowdelay = IPTOS_LOWDELAY; static const priority_type iptos_throughput = IPTOS_THROUGHPUT; static const priority_type iptos_reliability = IPTOS_RELIABILITY; #if defined IPTOS_MINCOST static const priority_type iptos_mincost = IPTOS_MINCOST; #elif defined IPTOS_LOWCOST static const priority_type iptos_mincost = IPTOS_LOWCOST; #else static const priority_type iptos_mincost = iptos_throughput; #endif static const uint32_t encryption_none = 0; static const uint32_t encryption_allow_incoming = (1 << 0); static const uint32_t encryption_try_outgoing = (1 << 1); static const uint32_t encryption_require = (1 << 2); static const uint32_t encryption_require_RC4 = (1 << 3); static const uint32_t encryption_enable_retry = (1 << 4); static const uint32_t encryption_prefer_plaintext = (1 << 5); // Internal to libtorrent. static const uint32_t encryption_use_proxy = (1 << 6); static const uint32_t encryption_retrying = (1 << 7); enum { handshake_incoming = 1, handshake_outgoing = 2, handshake_outgoing_encrypted = 3, handshake_outgoing_proxy = 4, handshake_success = 5, handshake_dropped = 6, handshake_failed = 7, handshake_retry_plaintext = 8, handshake_retry_encrypted = 9 }; typedef std::tr1::function slot_filter_type; typedef std::tr1::function slot_throttle_type; // The sockaddr argument in the result slot call is NULL if the resolve failed, and the int holds the errno. typedef std::tr1::function slot_resolver_result_type; typedef std::tr1::function slot_resolver_type; ConnectionManager(); ~ConnectionManager(); // Check that we have not surpassed the max number of open sockets // and that we're allowed to connect to the socket address. // // Consider only checking max number of open sockets. bool can_connect() const; // Call this to keep the socket count up to date. void inc_socket_count() { m_size++; } void dec_socket_count() { m_size--; } size_type size() const { return m_size; } size_type max_size() const { return m_maxSize; } priority_type priority() const { return m_priority; } uint32_t send_buffer_size() const { return m_sendBufferSize; } uint32_t receive_buffer_size() const { return m_receiveBufferSize; } uint32_t encryption_options() { return m_encryptionOptions; } void set_max_size(size_type s) { m_maxSize = s; } void set_priority(priority_type p) { m_priority = p; } void set_send_buffer_size(uint32_t s); void set_receive_buffer_size(uint32_t s); void set_encryption_options(uint32_t options); // Setting the addresses creates a copy of the address. const sockaddr* bind_address() const { return m_bindAddress; } const sockaddr* local_address() const { return m_localAddress; } const sockaddr* proxy_address() const { return m_proxyAddress; } void set_bind_address(const sockaddr* sa); void set_local_address(const sockaddr* sa); void set_proxy_address(const sockaddr* sa); uint32_t filter(const sockaddr* sa); void set_filter(const slot_filter_type& s) { m_slot_filter = s; } bool listen_open(port_type begin, port_type end); void listen_close(); // Since trackers need our port number, it doesn't get cleared after // 'listen_close()'. The client may change the reported port number, // but do note that it gets overwritten after 'listen_open(...)'. port_type listen_port() const { return m_listen_port; } int listen_backlog() const { return m_listen_backlog; } void set_listen_port(port_type p) { m_listen_port = p; } void set_listen_backlog(int v); // The resolver returns a pointer to its copy of the result slot // which the caller may set blocked to prevent the slot from being // called. The pointer must be NULL if the result slot was already // called because the resolve was synchronous. slot_resolver_type& resolver() { return m_slot_resolver; } // The slot returns a ThrottlePair to use for the given address, or // NULLs to use the default throttle. slot_throttle_type& address_throttle() { return m_slot_address_throttle; } // For internal usage. Listen* listen() { return m_listen; } private: ConnectionManager(const ConnectionManager&); void operator = (const ConnectionManager&); size_type m_size; size_type m_maxSize; priority_type m_priority; uint32_t m_sendBufferSize; uint32_t m_receiveBufferSize; int m_encryptionOptions; sockaddr* m_bindAddress; sockaddr* m_localAddress; sockaddr* m_proxyAddress; Listen* m_listen; port_type m_listen_port; uint32_t m_listen_backlog; slot_filter_type m_slot_filter; slot_resolver_type m_slot_resolver; slot_throttle_type m_slot_address_throttle; }; } #endif libtorrent-0.13.6/src/torrent/data/000077500000000000000000000000001257211073700171675ustar00rootroot00000000000000libtorrent-0.13.6/src/torrent/data/Makefile.am000066400000000000000000000014601257211073700212240ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_torrentdata.la libsub_torrentdata_la_SOURCES = \ block.cc \ block.h \ block_failed.h \ block_list.cc \ block_list.h \ block_transfer.h \ chunk_utils.cc \ chunk_utils.h \ download_data.cc \ download_data.h \ file.cc \ file.h \ file_list.cc \ file_list.h \ file_list_iterator.cc \ file_list_iterator.h \ file_manager.cc \ file_manager.h \ file_utils.cc \ file_utils.h \ piece.h \ transfer_list.cc \ transfer_list.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir) libtorrentincludedir = $(includedir)/torrent/data libtorrentinclude_HEADERS = \ block.h \ block_list.h \ block_transfer.h \ chunk_utils.h \ download_data.h \ file.h \ file_list.h \ file_list_iterator.h \ file_manager.h \ file_utils.h \ piece.h \ transfer_list.h libtorrent-0.13.6/src/torrent/data/block.cc000066400000000000000000000325141257211073700205750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "peer/peer_info.h" #include "protocol/peer_connection_base.h" #include "block.h" #include "block_failed.h" #include "block_list.h" #include "block_transfer.h" #include "exceptions.h" namespace torrent { Block::~Block() { if (m_state != STATE_INCOMPLETE && m_state != STATE_COMPLETED) throw internal_error("Block dtor with 'm_state != STATE_INCOMPLETE && m_state != STATE_COMPLETED'"); if (m_state == STATE_COMPLETED) { if (m_leader == NULL) throw internal_error("Block dtor with 'm_state == STATE_COMPLETED && m_leader == NULL'"); m_leader->set_peer_info(NULL); } m_leader = NULL; m_state = STATE_INVALID; std::for_each(m_queued.begin(), m_queued.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this)); m_queued.clear(); std::for_each(m_transfers.begin(), m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this)); m_transfers.clear(); if (m_notStalled != 0) throw internal_error("Block::clear() m_stalled != 0."); delete m_failedList; } BlockTransfer* Block::insert(PeerInfo* peerInfo) { if (find_queued(peerInfo) || find_transfer(peerInfo)) throw internal_error("Block::insert(...) find_queued(peerInfo) || find_transfer(peerInfo)."); m_notStalled++; transfer_list_type::iterator itr = m_queued.insert(m_queued.end(), new BlockTransfer()); (*itr)->set_peer_info(peerInfo); (*itr)->set_block(this); (*itr)->set_piece(m_piece); (*itr)->set_state(BlockTransfer::STATE_QUEUED); (*itr)->set_request_time(cachedTime.seconds()); (*itr)->set_position(0); (*itr)->set_stall(0); (*itr)->set_failed_index(BlockFailed::invalid_index); return (*itr); } void Block::erase(BlockTransfer* transfer) { if (transfer->is_erased()) throw internal_error("Block::erase(...) transfer already erased"); if (transfer->peer_info() != NULL) throw internal_error("Block::erase(...) transfer has non-null peer info"); m_notStalled -= transfer->stall() == 0; if (transfer->is_queued()) { transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer); if (itr == m_queued.end()) throw internal_error("Block::erase(...) Could not find transfer."); m_queued.erase(itr); } else if (!transfer->is_finished()) { transfer_list_type::iterator itr = std::find(m_transfers.begin(), m_transfers.end(), transfer); if (itr == m_transfers.end()) throw internal_error("Block::erase(...) Could not find transfer."); // Need to do something different here for now, i think. m_transfers.erase(itr); if (transfer == m_leader) { if (m_state == STATE_COMPLETED) throw internal_error("Block::erase with 'transfer == m_transfer && m_state == STATE_COMPLETED'"); // When the leader is erased then any non-leading transfer must // be promoted. These non-leading transfers are guaranteed to // have the same data up to their position. PeerConnectionBase // assumes that a Block with non-leaders have a leader. // Create a range containing transfers with // is_not_leader(). Erased transfer will end up in the back. transfer_list_type::iterator first = std::find_if(m_transfers.begin(), m_transfers.end(), std::not1(std::mem_fun(&BlockTransfer::is_leader))); transfer_list_type::iterator last = std::stable_partition(first, m_transfers.end(), std::mem_fun(&BlockTransfer::is_not_leader)); transfer_list_type::iterator newLeader = std::max_element(first, last, rak::less2(std::mem_fun(&BlockTransfer::position), std::mem_fun(&BlockTransfer::position))); if (newLeader != last) { m_leader = *newLeader; m_leader->set_state(BlockTransfer::STATE_LEADER); } else { m_leader = NULL; // If we have no new leader, remove the erased (dissimilar) // transfers so they can get another shot. They cannot be // removed when found dissimilar as that would result in them // being queued immediately. remove_erased_transfers(); } } } else { throw internal_error("Block::erase(...) Transfer is finished."); } // Check if we need to check for peer_info not being null. transfer->set_block(NULL); delete transfer; } bool Block::transfering(BlockTransfer* transfer) { if (!transfer->is_valid()) throw internal_error("Block::transfering(...) transfer->block() == NULL."); transfer_list_type::iterator itr = std::find(m_queued.begin(), m_queued.end(), transfer); if (itr == m_queued.end()) throw internal_error("Block::transfering(...) not queued."); m_queued.erase(itr); m_transfers.insert(m_transfers.end(), transfer); // If this block already has an active transfer, make this transfer // skip the piece. If this transfer gets ahead of the currently // transfering, it will (a) take over as the leader if the data is // the same or (b) erase itself from this block if the data does not // match. if (m_leader != NULL) { transfer->set_state(BlockTransfer::STATE_NOT_LEADER); return false; } else { m_leader = transfer; transfer->set_state(BlockTransfer::STATE_LEADER); return true; } } // TODO: Don't depend on m_leader for access to block transfer data of // done peer_info, etc. bool Block::completed(BlockTransfer* transfer) { if (!transfer->is_valid()) throw internal_error("Block::completed(...) transfer->block() == NULL."); if (!transfer->is_leader()) throw internal_error("Block::completed(...) transfer is not the leader."); // Special case where another ignored transfer finished before the // leader? // // Perhaps do magic to the transfer, erase it or something. if (!is_finished()) throw internal_error("Block::completed(...) !is_finished()."); if (transfer != m_leader) throw internal_error("Block::completed(...) transfer != m_leader."); m_parent->inc_finished(); if ((Block::size_type)std::count_if(m_parent->begin(), m_parent->end(), std::mem_fun_ref(&Block::is_finished)) < m_parent->finished()) throw internal_error("Block::completed(...) Finished blocks too large."); m_notStalled -= transfer->stall() == 0; transfer->set_block(NULL); transfer->set_stall(~uint32_t()); // Currently just throw out the queued transfers. In case the hash // check fails, we might consider telling pcb during the call to // Block::transfering(...). But that would propably not be correct // as we want to trigger cancel messages from here, as hash fail is // a rare occurrence. std::for_each(m_queued.begin(), m_queued.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this)); m_queued.clear(); // We need to invalidate those unfinished and keep the one that // finished for later reference. remove_non_leader_transfers(); // We now know that all transfers except the current leader we're // handling has been invalidated. if (m_transfers.empty() || m_transfers.back() != transfer) throw internal_error("Block::completed(...) m_transfers.empty() || m_transfers.back() != transfer."); m_state = STATE_COMPLETED; return m_parent->is_all_finished(); } void Block::retry_transfer() { m_state = STATE_INCOMPLETE; } // Mark a non-leading transfer as having received dissimilar data to // the leader. It is then marked as erased so that we know its data // was not used, yet keep it in m_transfers so as not to cause a // re-download. void Block::transfer_dissimilar(BlockTransfer* transfer) { if (!transfer->is_not_leader() || m_leader == transfer) throw internal_error("Block::transfer_dissimilar(...) transfer is the leader."); m_notStalled -= transfer->stall() == 0; // Why not just delete? Gets done by completed(), though when // erasing the leader we need to remove dissimilar unless we have // another leader. transfer->set_state(BlockTransfer::STATE_ERASED); transfer->set_position(0); transfer->set_block(NULL); } void Block::stalled_transfer(BlockTransfer* transfer) { if (transfer->stall() == 0) { if (m_notStalled == 0) throw internal_error("Block::stalled(...) m_notStalled == 0."); m_notStalled--; // Do magic here. } transfer->set_stall(transfer->stall() + 1); } void Block::change_leader(BlockTransfer* transfer) { if (m_leader == transfer) throw internal_error("Block::change_leader(...) m_leader == transfer."); if (is_finished()) throw internal_error("Block::change_leader(...) is_finished()."); if (m_leader != NULL) m_leader->set_state(BlockTransfer::STATE_NOT_LEADER); m_leader = transfer; m_leader->set_state(BlockTransfer::STATE_LEADER); } void Block::failed_leader() { if (!is_finished()) throw internal_error("Block::failed_leader(...) !is_finished()."); m_leader = NULL; if (m_failedList != NULL) m_failedList->set_current(BlockFailed::invalid_index); } void Block::create_dummy(BlockTransfer* transfer, PeerInfo* peerInfo, const Piece& piece) { transfer->set_peer_info(peerInfo); transfer->set_block(NULL); transfer->set_piece(piece); transfer->set_state(BlockTransfer::STATE_ERASED); transfer->set_position(0); transfer->set_stall(0); transfer->set_failed_index(BlockTransfer::invalid_index); } void Block::release(BlockTransfer* transfer) { transfer->set_peer_info(NULL); if (!transfer->is_valid()) delete transfer; else // TODO: Do we need to verify that the block is 'this'? transfer->block()->erase(transfer); } // // Private: // void Block::invalidate_transfer(BlockTransfer* transfer) { if (transfer == m_leader) throw internal_error("Block::invalidate_transfer(...) transfer == m_leader."); // Check if the block is this. transfer->set_block(NULL); if (transfer->peer_info() == NULL) { delete transfer; return; // Consider if this should be an exception. } m_notStalled -= (transfer->stall() == 0); // Do the canceling magic here. if (transfer->peer_info()->connection() != NULL) transfer->peer_info()->connection()->cancel_transfer(transfer); } void Block::remove_erased_transfers() { transfer_list_type::iterator split = std::stable_partition(m_transfers.begin(), m_transfers.end(), std::not1(std::mem_fun(&BlockTransfer::is_erased))); std::for_each(split, m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this)); m_transfers.erase(split, m_transfers.end()); } void Block::remove_non_leader_transfers() { transfer_list_type::iterator split = std::stable_partition(m_transfers.begin(), m_transfers.end(), std::mem_fun(&BlockTransfer::is_leader)); std::for_each(split, m_transfers.end(), std::bind1st(std::mem_fun(&Block::invalidate_transfer), this)); m_transfers.erase(split, m_transfers.end()); } BlockTransfer* Block::find_queued(const PeerInfo* p) { transfer_list_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info))); if (itr == m_queued.end()) return NULL; else return *itr; } const BlockTransfer* Block::find_queued(const PeerInfo* p) const { transfer_list_type::const_iterator itr = std::find_if(m_queued.begin(), m_queued.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info))); if (itr == m_queued.end()) return NULL; else return *itr; } BlockTransfer* Block::find_transfer(const PeerInfo* p) { transfer_list_type::iterator itr = std::find_if(m_transfers.begin(), m_transfers.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info))); if (itr == m_transfers.end()) return NULL; else return *itr; } const BlockTransfer* Block::find_transfer(const PeerInfo* p) const { transfer_list_type::const_iterator itr = std::find_if(m_transfers.begin(), m_transfers.end(), rak::equal(p, std::mem_fun(&BlockTransfer::peer_info))); if (itr == m_transfers.end()) return NULL; else return *itr; } } libtorrent-0.13.6/src/torrent/data/block.h000066400000000000000000000156611257211073700204430ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_BLOCK_H #define LIBTORRENT_BLOCK_H #include #include #include #include namespace torrent { // If you start adding slots, make sure the rest of the code creates // copies and clears the original variables before calls to erase etc. class LIBTORRENT_EXPORT Block { public: // Using vectors as they will remain small, thus the cost of erase // should be small. Later we can do faster erase by ignoring the // ordering. typedef std::vector transfer_list_type; typedef uint32_t size_type; typedef enum { STATE_INCOMPLETE, STATE_COMPLETED, STATE_INVALID } state_type; Block(); ~Block(); bool is_stalled() const { return m_notStalled == 0; } bool is_finished() const { return m_leader != NULL && m_leader->is_finished(); } bool is_transfering() const { return m_leader != NULL && !m_leader->is_finished(); } bool is_peer_queued(const PeerInfo* p) const { return find_queued(p) != NULL; } bool is_peer_transfering(const PeerInfo* p) const { return find_transfer(p) != NULL; } size_type size_all() const { return m_queued.size() + m_transfers.size(); } size_type size_not_stalled() const { return m_notStalled; } BlockList* parent() { return m_parent; } const BlockList* parent() const { return m_parent; } void set_parent(BlockList* p) { m_parent = p; } const Piece& piece() const { return m_piece; } void set_piece(const Piece& p) { m_piece = p; } uint32_t index() const { return m_piece.index(); } const transfer_list_type* queued() const { return &m_queued; } const transfer_list_type* transfers() const { return &m_transfers; } // The leading transfer, whom's data we're currently using. BlockTransfer* leader() { return m_leader; } const BlockTransfer* leader() const { return m_leader; } BlockTransfer* find(const PeerInfo* p); const BlockTransfer* find(const PeerInfo* p) const; BlockTransfer* find_queued(const PeerInfo* p); const BlockTransfer* find_queued(const PeerInfo* p) const; BlockTransfer* find_transfer(const PeerInfo* p); const BlockTransfer* find_transfer(const PeerInfo* p) const; // Internal to libTorrent: BlockTransfer* insert(PeerInfo* peerInfo); void erase(BlockTransfer* transfer); bool transfering(BlockTransfer* transfer); void transfer_dissimilar(BlockTransfer* transfer); bool completed(BlockTransfer* transfer); void retry_transfer(); static inline void stalled(BlockTransfer* transfer); void stalled_transfer(BlockTransfer* transfer); void change_leader(BlockTransfer* transfer); void failed_leader(); BlockFailed* failed_list() { return m_failedList; } void set_failed_list(BlockFailed* f) { m_failedList = f; } static void create_dummy(BlockTransfer* transfer, PeerInfo* peerInfo, const Piece& piece); // If the queued or transfering is already removed from the block it // will just delete the object. Made static so it can be called when // block == NULL. static void release(BlockTransfer* transfer); private: Block(const Block&); void operator = (const Block&); void invalidate_transfer(BlockTransfer* transfer) LIBTORRENT_NO_EXPORT; void remove_erased_transfers() LIBTORRENT_NO_EXPORT; void remove_non_leader_transfers() LIBTORRENT_NO_EXPORT; BlockList* m_parent; Piece m_piece; state_type m_state; uint32_t m_notStalled; transfer_list_type m_queued; transfer_list_type m_transfers; BlockTransfer* m_leader; BlockFailed* m_failedList; }; inline Block::Block() : m_state(STATE_INCOMPLETE), m_notStalled(0), m_leader(NULL), m_failedList(NULL) { } inline BlockTransfer* Block::find(const PeerInfo* p) { BlockTransfer* transfer; if ((transfer = find_queued(p)) != NULL) return transfer; else return find_transfer(p); } inline const BlockTransfer* Block::find(const PeerInfo* p) const { const BlockTransfer* transfer; if ((transfer = find_queued(p)) != NULL) return transfer; else return find_transfer(p); } inline void Block::stalled(BlockTransfer* transfer) { if (!transfer->is_valid()) return; transfer->block()->stalled_transfer(transfer); } } #endif libtorrent-0.13.6/src/torrent/data/block_failed.h000066400000000000000000000074421257211073700217450ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_BLOCK_FAILED_H #define LIBTORRENT_BLOCK_FAILED_H #include #include #include #include namespace torrent { class BlockFailed : public std::vector > { public: typedef std::vector > base_type; using base_type::value_type; using base_type::reference; using base_type::size_type; using base_type::difference_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::size; using base_type::empty; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::operator[]; static const uint32_t invalid_index = ~uint32_t(); BlockFailed() : m_current(invalid_index) {} ~BlockFailed(); size_type current() const { return m_current; } iterator current_iterator() { return begin() + m_current; } reverse_iterator current_reverse_iterator() { return reverse_iterator(begin() + m_current + 1); } void set_current(size_type idx) { m_current = idx; } void set_current(iterator itr) { m_current = itr - begin(); } void set_current(reverse_iterator itr) { m_current = itr.base() - begin() - 1; } iterator max_element(); reverse_iterator reverse_max_element(); private: BlockFailed(const BlockFailed&); void operator = (const BlockFailed&); static void delete_entry(const reference e) { delete [] e.first; } static bool compare_entries(const reference e1, const reference e2) { return e1.second < e2.second; } size_type m_current; }; inline BlockFailed::~BlockFailed() { std::for_each(begin(), end(), std::ptr_fun(&BlockFailed::delete_entry)); } inline BlockFailed::iterator BlockFailed::max_element() { return std::max_element(begin(), end(), std::ptr_fun(&BlockFailed::compare_entries)); } inline BlockFailed::reverse_iterator BlockFailed::reverse_max_element() { return std::max_element(rbegin(), rend(), std::ptr_fun(&BlockFailed::compare_entries)); } } #endif libtorrent-0.13.6/src/torrent/data/block_list.cc000066400000000000000000000060531257211073700216270ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "block_transfer.h" #include "block_list.h" #include "exceptions.h" namespace torrent { BlockList::BlockList(const Piece& piece, uint32_t blockLength) : m_piece(piece), m_priority(PRIORITY_OFF), m_finished(0), m_failed(0), m_attempt(0), m_bySeeder(false) { if (piece.length() == 0) throw internal_error("BlockList::BlockList(...) received zero length piece."); // Look into optimizing this by using input iterators in the ctor. base_type::resize((m_piece.length() + blockLength - 1) / blockLength); // ATM assume offset of 0. // uint32_t offset = m_piece.offset(); uint32_t offset = 0; for (iterator itr = begin(), last = end() - 1; itr != last; ++itr, offset += blockLength) { itr->set_parent(this); itr->set_piece(Piece(m_piece.index(), offset, blockLength)); } base_type::back().set_parent(this); base_type::back().set_piece(Piece(m_piece.index(), offset, (m_piece.length() % blockLength) ? m_piece.length() % blockLength : blockLength)); } BlockList::~BlockList() { // The default dtor's handles cleaning up the blocks and block transfers. } void BlockList::do_all_failed() { clear_finished(); set_attempt(0); // Clear leaders when we want to redownload the chunk. std::for_each(begin(), end(), std::mem_fun_ref(&Block::failed_leader)); std::for_each(begin(), end(), std::mem_fun_ref(&Block::retry_transfer)); } } libtorrent-0.13.6/src/torrent/data/block_list.h000066400000000000000000000122031257211073700214630ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_BLOCK_LIST_H #define LIBTORRENT_BLOCK_LIST_H #include #include #include #include namespace torrent { // Temporary workaround until we can use C++11's std::vector::emblace_back. template class no_copy_vector { public: typedef Type value_type; typedef size_t size_type; typedef value_type& reference; typedef ptrdiff_t difference_type; typedef value_type* iterator; typedef const value_type* const_iterator; no_copy_vector() : m_size(0), m_values(NULL) {} ~no_copy_vector() { clear(); } size_type size() const { return m_size; } bool empty() const { return m_size == 0; } void resize(size_type s) { clear(); m_size = s; m_values = new value_type[s]; } void clear() { delete [] m_values; m_values = NULL; m_size = 0; } iterator begin() { return m_values; } const_iterator begin() const { return m_values; } iterator end() { return m_values + m_size; } const_iterator end() const { return m_values + m_size; } value_type& back() { return *(m_values + m_size - 1); } value_type& operator[](size_type idx) { return m_values[idx]; } private: no_copy_vector(const no_copy_vector&); void operator = (const no_copy_vector&); size_type m_size; Block* m_values; }; class LIBTORRENT_EXPORT BlockList : public no_copy_vector { public: typedef no_copy_vector base_type; typedef uint32_t size_type; using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; // using base_type::reverse_iterator; using base_type::size; using base_type::empty; using base_type::begin; using base_type::end; // using base_type::rbegin; // using base_type::rend; using base_type::operator[]; BlockList(const Piece& piece, uint32_t blockLength); ~BlockList(); bool is_all_finished() const { return m_finished == size(); } const Piece& piece() const { return m_piece; } uint32_t index() const { return m_piece.index(); } priority_t priority() const { return m_priority; } void set_priority(priority_t p) { m_priority = p; } size_type finished() const { return m_finished; } void inc_finished() { m_finished++; } void clear_finished() { m_finished = 0; } uint32_t failed() const { return m_failed; } // Temporary, just increment for now. void inc_failed() { m_failed++; } uint32_t attempt() const { return m_attempt; } void set_attempt(uint32_t a) { m_attempt = a; } // Set when the chunk was initially requested from a seeder. This // allows us to quickly determine if it is a suitable chunk to // request from another seeder, e.g by already knowing it is a rare // piece. bool by_seeder() const { return m_bySeeder; } void set_by_seeder(bool state) { m_bySeeder = state; } void do_all_failed(); private: BlockList(const BlockList&); void operator = (const BlockList&); Piece m_piece; priority_t m_priority; size_type m_finished; uint32_t m_failed; uint32_t m_attempt; bool m_bySeeder; }; } #endif libtorrent-0.13.6/src/torrent/data/block_transfer.h000066400000000000000000000121731257211073700223420ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_BLOCK_TRANSFER_H #define LIBTORRENT_BLOCK_TRANSFER_H #include #include #include #include #include namespace torrent { class LIBTORRENT_EXPORT BlockTransfer { public: static const uint32_t invalid_index = ~uint32_t(); typedef PeerInfo* key_type; typedef enum { STATE_ERASED, STATE_QUEUED, STATE_LEADER, STATE_NOT_LEADER } state_type; BlockTransfer(); ~BlockTransfer(); // TODO: Do we need to also check for peer_info?... bool is_valid() const { return m_block != NULL; } bool is_erased() const { return m_state == STATE_ERASED; } bool is_queued() const { return m_state == STATE_QUEUED; } bool is_leader() const { return m_state == STATE_LEADER; } bool is_not_leader() const { return m_state == STATE_NOT_LEADER; } bool is_finished() const { return m_position == m_piece.length(); } key_type peer_info() { return m_peer_info; } const key_type const_peer_info() const { return m_peer_info; } Block* block() { return m_block; } const Block* const_block() const { return m_block; } const Piece& piece() const { return m_piece; } uint32_t index() const { return m_piece.index(); } state_type state() const { return m_state; } int32_t request_time() const { return m_request_time; } // Adjust the position after any actions like erasing it from a // Block, but before if finishing. uint32_t position() const { return m_position; } uint32_t stall() const { return m_stall; } uint32_t failed_index() const { return m_failedIndex; } void set_peer_info(key_type p); void set_block(Block* b) { m_block = b; } void set_piece(const Piece& p) { m_piece = p; } void set_state(state_type s) { m_state = s; } void set_request_time(int32_t t) { m_request_time = t; } void set_position(uint32_t p) { m_position = p; } void adjust_position(uint32_t p) { m_position += p; } void set_stall(uint32_t s) { m_stall = s; } void set_failed_index(uint32_t i) { m_failedIndex = i; } private: BlockTransfer(const BlockTransfer&); void operator = (const BlockTransfer&); key_type m_peer_info; Block* m_block; Piece m_piece; state_type m_state; int32_t m_request_time; uint32_t m_position; uint32_t m_stall; uint32_t m_failedIndex; }; inline BlockTransfer::BlockTransfer() : m_peer_info(NULL), m_block(NULL) { } inline BlockTransfer::~BlockTransfer() { if (m_block != NULL) throw internal_error("BlockTransfer::~BlockTransfer() block not NULL"); if (m_peer_info != NULL) throw internal_error("BlockTransfer::~BlockTransfer() peer_info not NULL"); } inline void BlockTransfer::set_peer_info(key_type p) { if (m_peer_info != NULL) m_peer_info->dec_transfer_counter(); m_peer_info = p; if (m_peer_info != NULL) m_peer_info->inc_transfer_counter(); } } #endif libtorrent-0.13.6/src/torrent/data/chunk_utils.cc000066400000000000000000000071001257211073700220240ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "chunk_utils.h" #include "download.h" #include "exceptions.h" #include "manager.h" #include "chunk_manager.h" #include "download/download_wrapper.h" #include "torrent/download/download_manager.h" #include "data/chunk.h" #include "data/chunk_list.h" namespace torrent { std::vector chunk_list_mapping(Download* download) { ChunkList* chunk_list = download->ptr()->main()->chunk_list(); std::vector mappings; for (ChunkList::const_iterator itr = chunk_list->begin(), last = chunk_list->end(); itr != last; itr++) { if (!itr->is_valid()) continue; for (Chunk::const_iterator itr2 = itr->chunk()->begin(), last2 = itr->chunk()->end(); itr2 != last2; itr2++) { if (itr2->mapped() != ChunkPart::MAPPED_MMAP) continue; vm_mapping val = { itr2->chunk().ptr(), itr2->chunk().size_aligned() }; mappings.push_back(val); } } return mappings; } chunk_info_result chunk_list_address_info(void* address) { ChunkManager::iterator first = manager->chunk_manager()->begin(); ChunkManager::iterator last = manager->chunk_manager()->begin(); while (first != last) { ChunkList::chunk_address_result result = (*first)->find_address(address); if (result.first != (*first)->end()) { DownloadManager::iterator d_itr = manager->download_manager()->find_chunk_list(*first); if (d_itr == manager->download_manager()->end()) return chunk_info_result(); chunk_info_result ci; ci.download = Download(*d_itr); ci.chunk_index = result.first->index(); ci.chunk_offset = result.second->position() + std::distance(result.second->chunk().begin(), (char*)address); ci.file_path = result.second->file()->frozen_path().c_str(); ci.file_offset = result.second->file_offset() + std::distance(result.second->chunk().begin(), (char*)address); return ci; } first++; } return chunk_info_result(); } } libtorrent-0.13.6/src/torrent/data/chunk_utils.h000066400000000000000000000044531257211073700216760ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_CHUNK_UTILS_H #define LIBTORRENT_CHUNK_UTILS_H #include #include #include namespace torrent { class ChunkList; struct vm_mapping { void* ptr; uint64_t length; }; // Change to ChunkList* when that becomes part of the public API. std::vector chunk_list_mapping(Download* download) LIBTORRENT_EXPORT; struct chunk_info_result { Download download; uint32_t chunk_index; uint32_t chunk_offset; const char* file_path; uint64_t file_offset; // void* chunk_begin; // void* chunk_end; // int prot; }; chunk_info_result chunk_list_address_info(void* address) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/data/download_data.cc000066400000000000000000000056001257211073700222770ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "download_data.h" namespace torrent { // Calculate the number of chunks remaining to be downloaded. // // Doing it the slow and safe way, optimize this at some point. uint32_t download_data::calc_wanted_chunks() const { if (m_completed_bitfield.is_all_set()) return 0; priority_ranges wanted_ranges = priority_ranges::create_union(m_normal_priority, m_high_priority); if (m_completed_bitfield.is_all_unset()) return wanted_ranges.intersect_distance(0, m_completed_bitfield.size_bits()); if (m_completed_bitfield.empty()) throw internal_error("download_data::update_wanted_chunks() m_completed_bitfield.empty()."); uint32_t result = 0; for (download_data::priority_ranges::const_iterator itr = wanted_ranges.begin(), last = wanted_ranges.end(); itr != last; itr++) { //remaining = completed->count_range(itr->first, itr->second); uint32_t idx = itr->first; while (idx != itr->second) result += !m_completed_bitfield.get(idx++); } return result; } void download_data::verify_wanted_chunks(const char* where) const { if (m_wanted_chunks != calc_wanted_chunks()) throw internal_error("Invalid download_data::wanted_chunks() value in " + std::string(where) + "."); } } libtorrent-0.13.6/src/torrent/data/download_data.h000066400000000000000000000115751257211073700221510ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_DOWNLOAD_DATA_H #define LIBTORRENT_DATA_DOWNLOAD_DATA_H #include #include #include #include #include namespace torrent { class ChunkSelector; class Download; class DownloadWrapper; class FileList; class download_data { public: typedef ranges priority_ranges; typedef void (function_void)(void); typedef std::tr1::function slot_void; download_data() : m_wanted_chunks(0) {} const HashString& hash() const { return m_hash; } bool is_partially_done() const { return m_wanted_chunks == 0; } bool is_not_partially_done() const { return m_wanted_chunks != 0; } const Bitfield* completed_bitfield() const { return &m_completed_bitfield; } const Bitfield* untouched_bitfield() const { return &m_untouched_bitfield; } const priority_ranges* high_priority() const { return &m_high_priority; } const priority_ranges* normal_priority() const { return &m_normal_priority; } uint32_t wanted_chunks() const { return m_wanted_chunks; } uint32_t calc_wanted_chunks() const; void verify_wanted_chunks(const char* where) const; slot_void& slot_initial_hash() const { return m_slot_initial_hash; } slot_void& slot_download_done() const { return m_slot_download_done; } slot_void& slot_partially_done() const { return m_slot_partially_done; } slot_void& slot_partially_restarted() const { return m_slot_partially_restarted; } protected: friend class ChunkList; friend class ChunkSelector; friend class Download; friend class DownloadWrapper; friend class FileList; HashString& mutable_hash() { return m_hash; } Bitfield* mutable_completed_bitfield() { return &m_completed_bitfield; } Bitfield* mutable_untouched_bitfield() { return &m_untouched_bitfield; } priority_ranges* mutable_high_priority() { return &m_high_priority; } priority_ranges* mutable_normal_priority() { return &m_normal_priority; } void update_wanted_chunks() { m_wanted_chunks = calc_wanted_chunks(); } void set_wanted_chunks(uint32_t n) { m_wanted_chunks = n; } void call_download_done() { if (m_slot_download_done) m_slot_download_done(); } void call_partially_done() { if (m_slot_partially_done) m_slot_partially_done(); } void call_partially_restarted() { if (m_slot_partially_restarted) m_slot_partially_restarted(); } private: HashString m_hash; Bitfield m_completed_bitfield; Bitfield m_untouched_bitfield; priority_ranges m_high_priority; priority_ranges m_normal_priority; uint32_t m_wanted_chunks; mutable slot_void m_slot_initial_hash; mutable slot_void m_slot_download_done; mutable slot_void m_slot_partially_done; mutable slot_void m_slot_partially_restarted; }; } #endif // LIBTORRENT_DATA_DOWNLOAD_DATA_H libtorrent-0.13.6/src/torrent/data/file.cc000066400000000000000000000117511257211073700204220ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "data/memory_chunk.h" #include "data/socket_file.h" #include "torrent/exceptions.h" #include "file.h" #include "file_manager.h" #include "globals.h" #include "manager.h" namespace torrent { File::File() : m_fd(-1), m_protection(0), m_flags(0), m_offset(0), m_size(0), m_lastTouched(cachedTime.usec()), m_completed(0), m_priority(PRIORITY_NORMAL), m_matchDepthPrev(0), m_matchDepthNext(0) { } File::~File() { if (is_open()) throw internal_error("File::~File() called on an open file."); } bool File::is_created() const { rak::file_stat fs; // If we can't even get permission to do fstat, we might as well // consider the file as not created. This function is to be used by // the client to check that the torrent files are present and ok, // rather than as a way to find out if it is starting on a blank // slate. if (!fs.update(frozen_path())) // return rak::error_number::current() == rak::error_number::e_access; return false; return fs.is_regular(); } bool File::is_correct_size() const { rak::file_stat fs; if (!fs.update(frozen_path())) return false; return fs.is_regular() && (uint64_t)fs.size() == m_size; } // At some point we should pass flags for deciding if the correct size // is necessary, etc. bool File::prepare(int prot, int flags) { m_lastTouched = cachedTime.usec(); // Check if we got write protection and flag_resize_queued is // set. If so don't quit as we need to try re-sizing, instead call // resize_file. if (is_open() && has_permissions(prot)) return true; // For now don't allow overridding this check in prepare. if (m_flags & flag_create_queued) flags |= SocketFile::o_create; else flags &= ~SocketFile::o_create; if (!manager->file_manager()->open(this, prot, flags)) return false; m_flags |= flag_previously_created; m_flags &= ~flag_create_queued; // Replace PROT_WRITE with something prettier. if ((m_flags & flag_resize_queued) && has_permissions(PROT_WRITE)) { m_flags &= ~flag_resize_queued; return resize_file(); } return true; } void File::set_range(uint32_t chunkSize) { if (chunkSize == 0) m_range = range_type(0, 0); else if (m_size == 0) m_range = File::range_type(m_offset / chunkSize, m_offset / chunkSize); else m_range = File::range_type(m_offset / chunkSize, (m_offset + m_size + chunkSize - 1) / chunkSize); } void File::set_match_depth(File* left, File* right) { uint32_t level = 0; Path::const_iterator itrLeft = left->path()->begin(); Path::const_iterator itrRight = right->path()->begin(); while (itrLeft != left->path()->end() && itrRight != right->path()->end() && *itrLeft == *itrRight) { itrLeft++; itrRight++; level++; } left->m_matchDepthNext = level; right->m_matchDepthPrev = level; } bool File::resize_file() { if (!is_open()) return false; // This doesn't try to re-open it as rw. if (m_size == SocketFile(m_fd).size()) return true; // For now make it so that the fallocate flag indicates if we want // to do potentially blocking allocation, while FS supported // non-blocking allocation is done always. int flags = SocketFile::flag_fallocate; if (m_flags & flag_fallocate) flags |= SocketFile::flag_fallocate_blocking; return SocketFile(m_fd).set_size(m_size, flags); } } libtorrent-0.13.6/src/torrent/data/file.h000066400000000000000000000162011257211073700202570ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_FILE_H #define LIBTORRENT_FILE_H #include #include namespace torrent { class LIBTORRENT_EXPORT lt_cacheline_aligned File { public: friend class FileList; typedef std::pair range_type; static const int flag_active = (1 << 0); static const int flag_create_queued = (1 << 1); static const int flag_resize_queued = (1 << 2); static const int flag_fallocate = (1 << 3); static const int flag_previously_created = (1 << 4); static const int flag_prioritize_first = (1 << 5); static const int flag_prioritize_last = (1 << 6); File(); ~File(); bool is_created() const; bool is_open() const { return m_fd != -1; } bool is_correct_size() const; bool is_valid_position(uint64_t p) const; bool is_create_queued() const { return m_flags & flag_create_queued; } bool is_resize_queued() const { return m_flags & flag_resize_queued; } bool is_previously_created() const { return m_flags & flag_previously_created; } bool has_flags(int flags) { return m_flags & flags; } void set_flags(int flags); void unset_flags(int flags); bool has_permissions(int prot) const { return !(prot & ~m_protection); } uint64_t offset() const { return m_offset; } uint64_t size_bytes() const { return m_size; } uint32_t size_chunks() const { return m_range.second - m_range.first; } uint32_t completed_chunks() const { return m_completed; } void set_completed_chunks(uint32_t v); const range_type& range() const { return m_range; } uint32_t range_first() const { return m_range.first; } uint32_t range_second() const { return m_range.second; } priority_t priority() const { return m_priority; } void set_priority(priority_t t) { m_priority = t; } const Path* path() const { return &m_path; } Path* mutable_path() { return &m_path; } const std::string& frozen_path() const { return m_frozenPath; } uint32_t match_depth_prev() const { return m_matchDepthPrev; } uint32_t match_depth_next() const { return m_matchDepthNext; } // This should only be changed by libtorrent. int file_descriptor() const { return m_fd; } void set_file_descriptor(int fd) { m_fd = fd; } // This might actually be wanted, as it would be nice to allow the // File to decide if it needs to try creating the underlying file or // not. bool prepare(int prot, int flags = 0); int protection() const { return m_protection; } void set_protection(int prot) { m_protection = prot; } uint64_t last_touched() const { return m_lastTouched; } void set_last_touched(uint64_t t) { m_lastTouched = t; } protected: void set_flags_protected(int flags) { m_flags |= flags; } void unset_flags_protected(int flags) { m_flags &= ~flags; } void set_frozen_path(const std::string& path) { m_frozenPath = path; } void set_offset(uint64_t off) { m_offset = off; } void set_size_bytes(uint64_t size) { m_size = size; } void set_range(uint32_t chunkSize); void set_completed_protected(uint32_t v) { m_completed = v; } void inc_completed_protected() { m_completed++; } static void set_match_depth(File* left, File* right); void set_match_depth_prev(uint32_t l) { m_matchDepthPrev = l; } void set_match_depth_next(uint32_t l) { m_matchDepthNext = l; } private: File(const File&); void operator = (const File&); bool resize_file(); int m_fd; int m_protection; int m_flags; Path m_path; std::string m_frozenPath; uint64_t m_offset; uint64_t m_size; uint64_t m_lastTouched; range_type m_range; uint32_t m_completed; priority_t m_priority; uint32_t m_matchDepthPrev; uint32_t m_matchDepthNext; }; inline bool File::is_valid_position(uint64_t p) const { return p >= m_offset && p < m_offset + m_size; } inline void File::set_flags(int flags) { set_flags_protected(flags & (flag_create_queued | flag_resize_queued | flag_fallocate | flag_prioritize_first | flag_prioritize_last)); } inline void File::unset_flags(int flags) { unset_flags_protected(flags & (flag_create_queued | flag_resize_queued | flag_fallocate | flag_prioritize_first | flag_prioritize_last)); } inline void File::set_completed_chunks(uint32_t v) { if (!has_flags(flag_active) && v <= size_chunks()) m_completed = v; } } #endif libtorrent-0.13.6/src/torrent/data/file_list.cc000066400000000000000000000516651257211073700214650ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include "data/chunk.h" #include "data/memory_chunk.h" #include "data/socket_file.h" #include "torrent/exceptions.h" #include "torrent/path.h" #include "torrent/utils/log.h" #include "file.h" #include "file_list.h" #include "file_manager.h" #include "manager.h" #include "piece.h" #define LT_LOG_FL(log_level, log_fmt, ...) \ lt_log_print_data(LOG_STORAGE_##log_level, (&m_data), "file_list", log_fmt, __VA_ARGS__); namespace torrent { void verify_file_list(const FileList* fl) { if (fl->empty()) throw internal_error("verify_file_list() 1."); if ((*fl->begin())->match_depth_prev() != 0 || (*fl->rbegin())->match_depth_next() != 0) throw internal_error("verify_file_list() 2."); for (FileList::const_iterator itr = fl->begin(), last = fl->end() - 1; itr != last; itr++) if ((*itr)->match_depth_next() != (*(itr + 1))->match_depth_prev() || (*itr)->match_depth_next() >= (*itr)->path()->size()) throw internal_error("verify_file_list() 3."); } FileList::FileList() : m_isOpen(false), m_torrentSize(0), m_chunkSize(0), m_maxFileSize(~uint64_t()) { } FileList::~FileList() { // Can we skip close()? close(); std::for_each(begin(), end(), rak::call_delete()); base_type::clear(); m_torrentSize = 0; } bool FileList::is_valid_piece(const Piece& piece) const { return piece.index() < size_chunks() && piece.length() != 0 && // Make sure offset does not overflow 32 bits. piece.offset() + piece.length() >= piece.offset() && piece.offset() + piece.length() <= chunk_index_size(piece.index()); } bool FileList::is_root_dir_created() const { rak::file_stat fs; if (!fs.update(m_rootDir)) // return rak::error_number::current() == rak::error_number::e_access; return false; return fs.is_directory(); } bool FileList::is_multi_file() const { // Currently only check if we got just one file. In the future this // should be a bool, which will be set based on what flags are // passed when the torrent was loaded. return m_isMultiFile; } uint64_t FileList::completed_bytes() const { // Chunk size needs to be cast to a uint64_t for the below to work. uint64_t cs = chunk_size(); if (bitfield()->empty()) return bitfield()->is_all_set() ? size_bytes() : (completed_chunks() * cs); if (!bitfield()->get(size_chunks() - 1) || size_bytes() % cs == 0) { // The last chunk is not done, or the last chunk is the same size // as the others. return completed_chunks() * cs; } else { if (completed_chunks() == 0) throw internal_error("FileList::bytes_completed() completed_chunks() == 0."); return (completed_chunks() - 1) * cs + size_bytes() % cs; } } uint64_t FileList::left_bytes() const { uint64_t left = size_bytes() - completed_bytes(); if (left > ((uint64_t)1 << 60)) throw internal_error("FileList::bytes_left() is too large."); if (completed_chunks() == size_chunks() && left != 0) throw internal_error("FileList::bytes_left() has an invalid size."); return left; } uint32_t FileList::chunk_index_size(uint32_t index) const { if (index + 1 != size_chunks() || size_bytes() % chunk_size() == 0) return chunk_size(); else return size_bytes() % chunk_size(); } void FileList::set_root_dir(const std::string& path) { if (is_open()) throw input_error("Tried to change the root directory on an open download."); std::string::size_type last = path.find_last_not_of('/'); if (last == std::string::npos) m_rootDir = "."; else m_rootDir = path.substr(0, last + 1); } void FileList::set_max_file_size(uint64_t size) { if (is_open()) throw input_error("Tried to change the max file size for an open download."); m_maxFileSize = size; } // This function should really ensure that we arn't dealing files // spread over multiple mount-points. uint64_t FileList::free_diskspace() const { uint64_t freeDiskspace = std::numeric_limits::max(); for (path_list::const_iterator itr = m_indirectLinks.begin(), last = m_indirectLinks.end(); itr != last; ++itr) { rak::fs_stat stat; if (!stat.update(*itr)) continue; freeDiskspace = std::min(freeDiskspace, stat.bytes_avail()); } return freeDiskspace != std::numeric_limits::max() ? freeDiskspace : 0; } FileList::iterator_range FileList::split(iterator position, split_type* first, split_type* last) { if (is_open()) throw internal_error("FileList::split(...) is_open()."); if (first == last || position == end()) throw internal_error("FileList::split(...) invalid arguments."); if (position != begin()) (*(position - 1))->set_match_depth_next(0); if (position + 1 != end()) (*(position + 1))->set_match_depth_prev(0); File* oldFile = *position; uint64_t offset = oldFile->offset(); size_type index = std::distance(begin(), position); size_type length = std::distance(first, last); base_type::insert(position, length - 1, NULL); position = begin() + index; iterator itr = position; while (first != last) { File* newFile = new File(); newFile->set_offset(offset); newFile->set_size_bytes(first->first); newFile->set_range(m_chunkSize); *newFile->mutable_path() = first->second; offset += first->first; *itr = newFile; itr++; first++; } if (offset != oldFile->offset() + oldFile->size_bytes()) throw internal_error("FileList::split(...) split size does not match the old size."); delete oldFile; return iterator_range(position, itr); } FileList::iterator FileList::merge(iterator first, iterator last, const Path& path) { File* newFile = new File; // Set the path before deleting any iterators in case it refers to // one of the objects getting deleted. *newFile->mutable_path() = path; if (first == last) { if (first == end()) newFile->set_offset(m_torrentSize); else newFile->set_offset((*first)->offset()); first = base_type::insert(first, newFile); } else { newFile->set_offset((*first)->offset()); for (iterator itr = first; itr != last; ++itr) { newFile->set_size_bytes(newFile->size_bytes() + (*itr)->size_bytes()); delete *itr; } first = base_type::erase(first + 1, last) - 1; *first = newFile; } newFile->set_range(m_chunkSize); if (first == begin()) newFile->set_match_depth_prev(0); else File::set_match_depth(*(first - 1), newFile); if (first + 1 == end()) newFile->set_match_depth_next(0); else File::set_match_depth(newFile, *(first + 1)); return first; } // If the user supplies an invalid range, it will bork in weird ways. void FileList::update_paths(iterator first, iterator last) { // Check if we're open? if (first == last) return; if (first != begin()) File::set_match_depth(*(first - 1), *first); while (first != last && ++first != end()) File::set_match_depth(*(first - 1), *first); verify_file_list(this); } bool FileList::make_root_path() { if (!is_open()) return false; return ::mkdir(m_rootDir.c_str(), 0777) == 0 || errno == EEXIST; } bool FileList::make_all_paths() { if (!is_open()) return false; Path dummyPath; const Path* lastPath = &dummyPath; for (iterator itr = begin(), last = end(); itr != last; ++itr) { File* entry = *itr; // No need to create directories if the entry has already been // opened. if (entry->is_open()) continue; if (entry->path()->empty()) throw storage_error("Found an empty filename."); Path::const_iterator lastPathItr = lastPath->begin(); Path::const_iterator firstMismatch = entry->path()->begin(); // Couldn't find a suitable stl algo, need to write my own. while (firstMismatch != entry->path()->end() && lastPathItr != lastPath->end() && *firstMismatch == *lastPathItr) { lastPathItr++; firstMismatch++; } rak::error_number::clear_global(); make_directory(entry->path()->begin(), entry->path()->end(), firstMismatch); lastPath = entry->path(); } return true; } // Initialize FileList and add a dummy file that may be split into // multiple files. void FileList::initialize(uint64_t torrentSize, uint32_t chunkSize) { if (sizeof(off_t) != 8) throw internal_error("Last minute panic; sizeof(off_t) != 8."); if (chunkSize == 0) throw internal_error("FileList::initialize() chunk_size() == 0."); m_chunkSize = chunkSize; m_torrentSize = torrentSize; m_rootDir = "."; m_data.mutable_completed_bitfield()->set_size_bits((size_bytes() + chunk_size() - 1) / chunk_size()); m_data.mutable_normal_priority()->insert(0, size_chunks()); m_data.set_wanted_chunks(size_chunks()); File* newFile = new File(); newFile->set_offset(0); newFile->set_size_bytes(torrentSize); newFile->set_range(m_chunkSize); base_type::push_back(newFile); } struct file_list_cstr_less { bool operator () (const char* c1, const char* c2) const { return std::strcmp(c1, c2) < 0; } }; void FileList::open(int flags) { typedef std::set path_set; LT_LOG_FL(INFO, "Opening.", 0); if (m_rootDir.empty()) throw internal_error("FileList::open() m_rootDir.empty()."); m_indirectLinks.push_back(m_rootDir); Path lastPath; path_set pathSet; iterator itr = end(); try { if (!(flags & open_no_create) && !make_root_path()) throw storage_error("Could not create directory '" + m_rootDir + "': " + std::strerror(errno)); for (itr = begin(); itr != end(); ++itr) { File* entry = *itr; // We no longer consider it an error to open a previously opened // FileList as we now use the same function to create // non-existent files. // // Since m_isOpen is set, we know root dir wasn't changed, thus // we can keep the previously opened file. if (entry->is_open()) continue; // Update the path during open so that any changes to root dir // and file paths are properly handled. if (entry->path()->back().empty()) entry->set_frozen_path(std::string()); else entry->set_frozen_path(m_rootDir + entry->path()->as_string()); if (!pathSet.insert(entry->frozen_path().c_str()).second) throw storage_error("Duplicate filename found."); if (entry->size_bytes() > m_maxFileSize) throw storage_error("File exceedes the configured max file size."); if (entry->path()->empty()) throw storage_error("Empty filename is not allowed."); // Handle directory creation outside of open_file, so we can do // it here if necessary. entry->set_flags_protected(File::flag_active); if (!open_file(&*entry, lastPath, flags)) { // This needs to check if the error was due to open_no_create // being set or not. if (!(flags & open_no_create)) // Also check if open_require_all_open is set. throw storage_error("Could not open file: " + std::string(rak::error_number::current().c_str())); // Don't set the lastPath as we haven't created the directory. continue; } lastPath = *entry->path(); } } catch (local_error& e) { for (iterator itr2 = begin(), last = end(); itr2 != last; ++itr2) { (*itr2)->unset_flags_protected(File::flag_active); manager->file_manager()->close(*itr2); } if (itr == end()) { LT_LOG_FL(ERROR, "Failed to prepare file list: %s", e.what()); } else { LT_LOG_FL(ERROR, "Failed to prepare file '%s': %s", (*itr)->path()->as_string().c_str(), e.what()); } // Set to false here in case we tried to open the FileList for the // second time. m_isOpen = false; throw; } m_isOpen = true; m_frozenRootDir = m_rootDir; // For meta-downloads, if the file exists, we have to assume that // it is either 0 or 1 length or the correct size. If the size // turns out wrong later, a storage_error will be thrown elsewhere // to alert the user in this (unlikely) case. // // DEBUG: Make this depend on a flag... if (size_bytes() < 2) { rak::file_stat stat; // This probably recurses into open() once, but that is harmless. if (stat.update((*begin())->frozen_path()) && stat.size() > 1) return reset_filesize(stat.size()); } } void FileList::close() { if (!is_open()) return; LT_LOG_FL(INFO, "Closing.", 0); for (iterator itr = begin(), last = end(); itr != last; ++itr) { (*itr)->unset_flags_protected(File::flag_active); manager->file_manager()->close(*itr); } m_isOpen = false; m_indirectLinks.clear(); m_data.mutable_completed_bitfield()->unallocate(); } void FileList::make_directory(Path::const_iterator pathBegin, Path::const_iterator pathEnd, Path::const_iterator startItr) { std::string path = m_rootDir; while (pathBegin != pathEnd) { path += "/" + *pathBegin; if (pathBegin++ != startItr) continue; startItr++; rak::file_stat fileStat; if (fileStat.update_link(path) && fileStat.is_link() && std::find(m_indirectLinks.begin(), m_indirectLinks.end(), path) == m_indirectLinks.end()) m_indirectLinks.push_back(path); if (pathBegin == pathEnd) break; if (::mkdir(path.c_str(), 0777) != 0 && errno != EEXIST) throw storage_error("Could not create directory '" + path + "': " + std::strerror(errno)); } } bool FileList::open_file(File* node, const Path& lastPath, int flags) { rak::error_number::clear_global(); if (!(flags & open_no_create)) { const Path* path = node->path(); Path::const_iterator lastItr = lastPath.begin(); Path::const_iterator firstMismatch = path->begin(); // Couldn't find a suitable stl algo, need to write my own. while (firstMismatch != path->end() && lastItr != lastPath.end() && *firstMismatch == *lastItr) { lastItr++; firstMismatch++; } make_directory(path->begin(), path->end(), firstMismatch); } // Some torrents indicate an empty directory by having a path with // an empty last element. This entry must be zero length. if (node->path()->back().empty()) return node->size_bytes() == 0; rak::file_stat fileStat; if (fileStat.update(node->frozen_path()) && !fileStat.is_regular() && !fileStat.is_link()) { // Might also bork on other kinds of file types, but there's no // suitable errno for all cases. rak::error_number::set_global(rak::error_number::e_isdir); return false; } return node->prepare(MemoryChunk::prot_read, 0); } MemoryChunk FileList::create_chunk_part(FileList::iterator itr, uint64_t offset, uint32_t length, int prot) { offset -= (*itr)->offset(); length = std::min(length, (*itr)->size_bytes() - offset); if ((int64_t)offset < 0) throw internal_error("FileList::chunk_part(...) caught a negative offset"); // Check that offset != length of file. if (!(*itr)->prepare(prot)) return MemoryChunk(); return SocketFile((*itr)->file_descriptor()).create_chunk(offset, length, prot, MemoryChunk::map_shared); } Chunk* FileList::create_chunk(uint64_t offset, uint32_t length, int prot) { if (offset + length > m_torrentSize) throw internal_error("Tried to access chunk out of range in FileList"); std::auto_ptr chunk(new Chunk); for (iterator itr = std::find_if(begin(), end(), std::bind2nd(std::mem_fun(&File::is_valid_position), offset)); length != 0; ++itr) { if (itr == end()) throw internal_error("FileList could not find a valid file for chunk"); if ((*itr)->size_bytes() == 0) continue; MemoryChunk mc = create_chunk_part(itr, offset, length, prot); if (!mc.is_valid()) return NULL; if (mc.size() == 0) throw internal_error("FileList::create_chunk(...) mc.size() == 0."); if (mc.size() > length) throw internal_error("FileList::create_chunk(...) mc.size() > length."); chunk->push_back(ChunkPart::MAPPED_MMAP, mc); chunk->back().set_file(*itr, offset - (*itr)->offset()); offset += mc.size(); length -= mc.size(); } if (chunk->empty()) return NULL; return chunk.release(); } Chunk* FileList::create_chunk_index(uint32_t index, int prot) { return create_chunk((uint64_t)index * chunk_size(), chunk_index_size(index), prot); } void FileList::mark_completed(uint32_t index) { if (index >= size_chunks() || completed_chunks() >= size_chunks()) throw internal_error("FileList::mark_completed(...) received an invalid index."); if (bitfield()->empty()) throw internal_error("FileList::mark_completed(...) bitfield is empty."); if (bitfield()->size_bits() != size_chunks()) throw internal_error("FileList::mark_completed(...) bitfield is not the right size."); if (bitfield()->get(index)) throw internal_error("FileList::mark_completed(...) received a chunk that has already been finished."); if (bitfield()->size_set() >= bitfield()->size_bits()) throw internal_error("FileList::mark_completed(...) bitfield()->size_set() >= bitfield()->size_bits()."); LT_LOG_FL(DEBUG, "Done chunk: index:%" PRIu32 ".", index); m_data.mutable_completed_bitfield()->set(index); inc_completed(begin(), index); // TODO: Remember to validate 'wanted_chunks'. if (m_data.normal_priority()->has(index) || m_data.high_priority()->has(index)) { if (m_data.wanted_chunks() == 0) throw internal_error("FileList::mark_completed(...) m_data.wanted_chunks() == 0."); m_data.set_wanted_chunks(m_data.wanted_chunks() - 1); } } FileList::iterator FileList::inc_completed(iterator firstItr, uint32_t index) { firstItr = std::find_if(firstItr, end(), rak::less(index, std::mem_fun(&File::range_second))); iterator lastItr = std::find_if(firstItr, end(), rak::less(index + 1, std::mem_fun(&File::range_second))); if (firstItr == end()) throw internal_error("FileList::inc_completed() first == m_entryList->end()."); // TODO: Check if this works right for zero-length files. std::for_each(firstItr, lastItr == end() ? end() : (lastItr + 1), std::mem_fun(&File::inc_completed_protected)); return lastItr; } void FileList::update_completed() { if (!bitfield()->is_tail_cleared()) throw internal_error("Content::update_done() called but m_bitfield's tail isn't cleared."); m_data.update_wanted_chunks(); if (bitfield()->is_all_set()) { for (iterator itr = begin(), last = end(); itr != last; ++itr) (*itr)->set_completed_protected((*itr)->size_chunks()); } else { // Clear any old progress data from the entries as we don't clear // this on close, etc. for (iterator itr = begin(), last = end(); itr != last; ++itr) (*itr)->set_completed_protected(0); if (bitfield()->is_all_unset()) return; iterator entryItr = begin(); for (Bitfield::size_type index = 0; index < bitfield()->size_bits(); ++index) if (bitfield()->get(index)) entryItr = inc_completed(entryItr, index); } } void FileList::reset_filesize(int64_t size) { LT_LOG_FL(INFO, "Resetting torrent size: size:%" PRIi64 ".", size); close(); m_chunkSize = size; m_torrentSize = size; (*begin())->set_size_bytes(size); (*begin())->set_range(m_chunkSize); m_data.mutable_completed_bitfield()->allocate(); m_data.mutable_completed_bitfield()->unset_all(); open(open_no_create); } } libtorrent-0.13.6/src/torrent/data/file_list.h000066400000000000000000000204051257211073700213130ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_FILE_LIST_H #define LIBTORRENT_FILE_LIST_H #include #include #include #include #include #include #include namespace torrent { class Content; class Download; class DownloadConstructor; class DownloadMain; class DownloadWrapper; class Handshake; class LIBTORRENT_EXPORT FileList : private std::vector { public: friend class Content; friend class Download; friend class DownloadConstructor; friend class DownloadMain; friend class DownloadWrapper; friend class Handshake; typedef std::vector base_type; typedef std::vector path_list; typedef std::pair split_type; // The below are using-directives that make visible functions and // typedefs in the parent std::vector, only those listed below are // accessible. If you don't understand how this works, use google, // don't ask me. using base_type::value_type; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::const_reverse_iterator; typedef std::pair iterator_range; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::front; using base_type::back; using base_type::empty; using base_type::reserve; using base_type::at; using base_type::operator[]; FileList() LIBTORRENT_NO_EXPORT; ~FileList() LIBTORRENT_NO_EXPORT; bool is_open() const { return m_isOpen; } bool is_done() const { return completed_chunks() == size_chunks(); } bool is_valid_piece(const Piece& piece) const; bool is_root_dir_created() const; // Check if the torrent is loaded as a multi-file torrent. This may // return true even for a torrent with just one file. bool is_multi_file() const; void set_multi_file(bool state) { m_isMultiFile = state; } size_t size_files() const { return base_type::size(); } uint64_t size_bytes() const { return m_torrentSize; } uint32_t size_chunks() const { return bitfield()->size_bits(); } uint32_t completed_chunks() const { return bitfield()->size_set(); } uint64_t completed_bytes() const; uint64_t left_bytes() const; uint32_t chunk_size() const { return m_chunkSize; } uint32_t chunk_index_size(uint32_t index) const; uint64_t chunk_index_position(uint32_t index) const { return index * chunk_size(); } const download_data* data() const { return &m_data; } const Bitfield* bitfield() const { return m_data.completed_bitfield(); } // You may only call set_root_dir after all nodes have been added. const std::string& root_dir() const { return m_rootDir; } const std::string& frozen_root_dir() const { return m_frozenRootDir; } void set_root_dir(const std::string& path); uint64_t max_file_size() const { return m_maxFileSize; } void set_max_file_size(uint64_t size); // If the files span multiple disks, the one with the least amount // of free diskspace will be returned. uint64_t free_diskspace() const; // List of directories in the torrent that might be on different // volumes as they are links, including the root directory. Used by // 'free_diskspace()'. const path_list* indirect_links() const { return &m_indirectLinks; } // The sum of the sizes in the range [first,last> must be equal to // the size of 'position'. Do not use the old pointer in 'position' // after this call. iterator_range split(iterator position, split_type* first, split_type* last); // Use an empty range to insert a zero length file. iterator merge(iterator first, iterator last, const Path& path); iterator merge(iterator_range range, const Path& path) { return merge(range.first, range.second, path); } void update_paths(iterator first, iterator last); bool make_root_path(); bool make_all_paths(); protected: static const int open_no_create = (1 << 0); static const int open_require_all_open = (1 << 1); void initialize(uint64_t torrentSize, uint32_t chunkSize) LIBTORRENT_NO_EXPORT; void open(int flags) LIBTORRENT_NO_EXPORT; void close() LIBTORRENT_NO_EXPORT; download_data* mutable_data() { return &m_data; } // Before calling this function, make sure you clear errno. If // creating the chunk failed, NULL is returned and errno is set. Chunk* create_chunk(uint64_t offset, uint32_t length, int prot) LIBTORRENT_NO_EXPORT; Chunk* create_chunk_index(uint32_t index, int prot) LIBTORRENT_NO_EXPORT; void mark_completed(uint32_t index) LIBTORRENT_NO_EXPORT; iterator inc_completed(iterator firstItr, uint32_t index) LIBTORRENT_NO_EXPORT; void update_completed() LIBTORRENT_NO_EXPORT; // Used for meta downloads; we only know the // size after the first extension handshake. void reset_filesize(int64_t) LIBTORRENT_NO_EXPORT; private: bool open_file(File* node, const Path& lastPath, int flags) LIBTORRENT_NO_EXPORT; void make_directory(Path::const_iterator pathBegin, Path::const_iterator pathEnd, Path::const_iterator startItr) LIBTORRENT_NO_EXPORT; MemoryChunk create_chunk_part(FileList::iterator itr, uint64_t offset, uint32_t length, int prot) LIBTORRENT_NO_EXPORT; download_data m_data; bool m_isOpen; uint64_t m_torrentSize; uint32_t m_chunkSize; uint64_t m_maxFileSize; std::string m_rootDir; path_list m_indirectLinks; // Reorder next minor version bump: bool m_isMultiFile; std::string m_frozenRootDir; }; inline FileList::iterator file_list_contains_position(FileList* file_list, uint64_t pos) { return std::find_if(file_list->begin(), file_list->end(), std::bind2nd(std::mem_fun(&File::is_valid_position), pos)); } } #endif libtorrent-0.13.6/src/torrent/data/file_list_iterator.cc000066400000000000000000000103421257211073700233610ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "file.h" #include "file_list_iterator.h" namespace torrent { bool FileListIterator::is_file() const { return m_depth >= 0 && m_depth + 1 == (int32_t)(*m_position)->path()->size(); } bool FileListIterator::is_empty() const { return (*m_position)->path()->size() == 0; } bool FileListIterator::is_entering() const { return m_depth >= 0 && m_depth + 1 != (int32_t)(*m_position)->path()->size(); } FileListIterator& FileListIterator::operator ++() { int32_t size = (*m_position)->path()->size(); if (size == 0) { m_position++; return *this; } m_depth++; if (m_depth > size) throw internal_error("FileListIterator::operator ++() m_depth > size."); if (m_depth == size) m_depth = -(size - 1); if (m_depth == -(int32_t)(*m_position)->match_depth_next()) { m_depth = (*m_position)->match_depth_next(); m_position++; } return *this; } FileListIterator& FileListIterator::operator --() { // We're guaranteed that if m_depth != 0 then so is the path size, // so there's no need to check for it. if (m_depth == 0) { m_position--; // This ensures we properly start iterating the paths in a tree // without failing badly when size == 0. if ((*m_position)->path()->size() > 1) m_depth = -1; } else if (m_depth == (int32_t)(*m_position)->match_depth_prev()) { m_position--; // If only the last element differs, then we don't switch to // negative depth. Also make sure we skip the negative of the // current depth, as we index by the depth we're exiting from. if (m_depth + 1 != (int32_t)(*m_position)->path()->size()) m_depth = -(m_depth + 1); } else { int32_t size = (int32_t)(*m_position)->path()->size(); m_depth--; if (m_depth < -size) throw internal_error("FileListIterator::operator --() m_depth < -size."); if (m_depth == -size) m_depth = size - 1; } return *this; } FileListIterator& FileListIterator::forward_current_depth() { uint32_t baseDepth = depth(); if (!is_entering()) return ++(*this); // If the above test was false then we know there must be a // 'leaving' at baseDepth before the end of the list. do { ++(*this); } while (depth() > baseDepth); return *this; } FileListIterator& FileListIterator::backward_current_depth() { --(*this); if (is_entering() || is_file() || is_empty()) return *this; if (depth() == 0) throw internal_error("FileListIterator::backward_current_depth() depth() == 0."); uint32_t baseDepth = depth(); while (depth() >= baseDepth) --(*this); return *this; } } libtorrent-0.13.6/src/torrent/data/file_list_iterator.h000066400000000000000000000134521257211073700232300ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_FILE_LIST_ITERATOR_H #define LIBTORRENT_FILE_LIST_ITERATOR_H #include #include #include namespace torrent { class File; // A special purpose iterator class for iterating through FileList as // a dired structure. class LIBTORRENT_EXPORT FileListIterator { public: typedef FileList::iterator iterator; typedef File* reference; typedef File** pointer; FileListIterator() {} explicit FileListIterator(iterator pos, uint32_t depth = 0) : m_position(pos), m_depth(depth) {} bool is_file() const; bool is_empty() const; bool is_entering() const; bool is_leaving() const { return m_depth < 0; } uint32_t depth() const { return std::abs(m_depth); } iterator base() const { return m_position; } reference file() const { return *m_position; } reference operator *() const { return *m_position; } pointer operator ->() const { return &*m_position; } FileListIterator& operator ++(); FileListIterator& operator --(); FileListIterator operator ++(int); FileListIterator operator --(int); FileListIterator& forward_current_depth(); FileListIterator& backward_current_depth(); friend bool operator ==(const FileListIterator& left, const FileListIterator& right); friend bool operator !=(const FileListIterator& left, const FileListIterator& right); private: iterator m_position; int32_t m_depth; }; inline FileListIterator FileListIterator::operator ++(int) { FileListIterator tmp = *this; ++(*this); return tmp; } inline FileListIterator FileListIterator::operator --(int) { FileListIterator tmp = *this; --(*this); return tmp; } inline bool operator ==(const FileListIterator& left, const FileListIterator& right) { return left.m_position == right.m_position && left.m_depth == right.m_depth; } inline bool operator !=(const FileListIterator& left, const FileListIterator& right) { return left.m_position != right.m_position || left.m_depth != right.m_depth; } // Take a range as input and return the next entry at the same // directory depth as first. If the returned iterator equals 'last' or // is_leaving() == true then the search failed. class LIBTORRENT_EXPORT file_list_collapsed_iterator : private FileListIterator { public: typedef FileListIterator base_type; typedef file_list_collapsed_iterator this_type; using base_type::iterator; using base_type::reference; using base_type::pointer; using base_type::is_file; using base_type::is_empty; using base_type::is_entering; using base_type::is_leaving; using base_type::depth; using base_type::file; file_list_collapsed_iterator() {} file_list_collapsed_iterator(const FileListIterator& src) : FileListIterator(src) {} explicit file_list_collapsed_iterator(iterator pos, uint32_t depth = 0) : FileListIterator(pos, depth) {} base_type base() const { return *static_cast(this); } this_type& operator ++() { base_type::forward_current_depth(); return *this; } this_type& operator --() { base_type::backward_current_depth(); return *this; } this_type operator ++(int); this_type operator --(int); friend bool operator ==(const file_list_collapsed_iterator& left, const file_list_collapsed_iterator& right); friend bool operator !=(const file_list_collapsed_iterator& left, const file_list_collapsed_iterator& right); }; inline bool operator ==(const file_list_collapsed_iterator& left, const file_list_collapsed_iterator& right) { return left.base() == right.base(); } inline bool operator !=(const file_list_collapsed_iterator& left, const file_list_collapsed_iterator& right) { return left.base() != right.base(); } inline file_list_collapsed_iterator file_list_collapsed_iterator::operator ++(int) { this_type tmp = *this; ++(*this); return tmp; } inline file_list_collapsed_iterator file_list_collapsed_iterator::operator --(int) { this_type tmp = *this; --(*this); return tmp; } } #endif libtorrent-0.13.6/src/torrent/data/file_manager.cc000066400000000000000000000072761257211073700221230ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "data/socket_file.h" #include "torrent/exceptions.h" #include "file.h" #include "file_manager.h" #include "manager.h" namespace torrent { FileManager::FileManager() : m_maxOpenFiles(0), m_filesOpenedCounter(0), m_filesClosedCounter(0), m_filesFailedCounter(0) {} FileManager::~FileManager() { if (!empty()) throw internal_error("FileManager::~FileManager() called but empty() != true."); } void FileManager::set_max_open_files(size_type s) { if (s < 4 || s > (1 << 16)) throw input_error("Max open files must be between 4 and 2^16."); m_maxOpenFiles = s; while (size() > m_maxOpenFiles) close_least_active(); } bool FileManager::open(value_type file, int prot, int flags) { if (file->is_open()) close(file); if (size() > m_maxOpenFiles) throw internal_error("FileManager::open_file(...) m_openSize > m_maxOpenFiles."); if (size() == m_maxOpenFiles) close_least_active(); SocketFile fd; if (!fd.open(file->frozen_path(), prot, flags)) { m_filesFailedCounter++; return false; } file->set_protection(prot); file->set_file_descriptor(fd.fd()); base_type::push_back(file); // Consider storing the position of the file here. m_filesOpenedCounter++; return true; } void FileManager::close(value_type file) { if (!file->is_open()) return; SocketFile(file->file_descriptor()).close(); file->set_protection(0); file->set_file_descriptor(-1); iterator itr = std::find(begin(), end(), file); if (itr == end()) throw internal_error("FileManager::close_file(...) itr == end()."); *itr = back(); base_type::pop_back(); m_filesClosedCounter++; } struct FileManagerActivity { FileManagerActivity() : m_last(rak::timer::max().usec()), m_file(NULL) {} void operator ()(File* f) { if (f->is_open() && f->last_touched() <= m_last) { m_last = f->last_touched(); m_file = f; } } uint64_t m_last; File* m_file; }; void FileManager::close_least_active() { close(std::for_each(begin(), end(), FileManagerActivity()).m_file); } } libtorrent-0.13.6/src/torrent/data/file_manager.h000066400000000000000000000061401257211073700217520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DATA_FILE_MANAGER_H #define LIBTORRENT_DATA_FILE_MANAGER_H #include #include namespace torrent { class File; class LIBTORRENT_EXPORT FileManager : private std::vector { public: typedef std::vector base_type; typedef uint32_t size_type; using base_type::value_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; FileManager(); ~FileManager(); size_type open_files() const { return base_type::size(); } size_type max_open_files() const { return m_maxOpenFiles; } void set_max_open_files(size_type s); bool open(value_type file, int prot, int flags); void close(value_type file); void close_least_active(); // Statistics: uint64_t files_opened_counter() const { return m_filesOpenedCounter; } uint64_t files_closed_counter() const { return m_filesClosedCounter; } uint64_t files_failed_counter() const { return m_filesFailedCounter; } private: FileManager(const FileManager&) LIBTORRENT_NO_EXPORT; void operator = (const FileManager&) LIBTORRENT_NO_EXPORT; size_type m_maxOpenFiles; uint64_t m_filesOpenedCounter; uint64_t m_filesClosedCounter; uint64_t m_filesFailedCounter; }; } #endif libtorrent-0.13.6/src/torrent/data/file_utils.cc000066400000000000000000000071071257211073700216420ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "exceptions.h" #include "file.h" #include "file_utils.h" namespace torrent { FileList::iterator file_split(FileList* fileList, FileList::iterator position, uint64_t maxSize, const std::string& suffix) { const Path* srcPath = (*position)->path(); uint64_t splitSize = ((*position)->size_bytes() + maxSize - 1) / maxSize; if (srcPath->empty() || (*position)->size_bytes() == 0) throw input_error("Tried to split a file with an empty path or zero length file."); if (splitSize > 1000) throw input_error("Tried to split a file into more than 1000 parts."); // Also replace dwnlctor's vector. FileList::split_type* splitList = new FileList::split_type[splitSize]; FileList::split_type* splitItr = splitList; unsigned int nameSize = srcPath->back().size() + suffix.size(); char name[nameSize + 4]; std::memcpy(name, srcPath->back().c_str(), srcPath->back().size()); std::memcpy(name + srcPath->back().size(), suffix.c_str(), suffix.size()); for (unsigned int i = 0; i != splitSize; ++i, ++splitItr) { if (i == splitSize - 1 && (*position)->size_bytes() % maxSize != 0) splitItr->first = (*position)->size_bytes() % maxSize; else splitItr->first = maxSize; name[nameSize + 0] = '0' + (i / 100) % 10; name[nameSize + 1] = '0' + (i / 10) % 10; name[nameSize + 2] = '0' + (i / 1) % 10; name[nameSize + 3] = '\0'; splitItr->second = *srcPath; splitItr->second.back() = name; } return fileList->split(position, splitList, splitItr).second; } void file_split_all(FileList* fileList, uint64_t maxSize, const std::string& suffix) { if (maxSize == 0) throw input_error("Tried to split torrent files into zero sized chunks."); FileList::iterator itr = fileList->begin(); while (itr != fileList->end()) if ((*itr)->size_bytes() > maxSize && !(*itr)->path()->empty()) itr = file_split(fileList, itr, maxSize, suffix); else itr++; } } libtorrent-0.13.6/src/torrent/data/file_utils.h000066400000000000000000000041631257211073700215030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_FILE_UTILS_H #define LIBTORRENT_FILE_UTILS_H #include #include namespace torrent { // Split 'position' into 'maxSize' sized files and return the iterator // after the last new entry. FileList::iterator file_split(FileList* fileList, FileList::iterator position, uint64_t maxSize, const std::string& suffix) LIBTORRENT_EXPORT; void file_split_all(FileList* fileList, uint64_t maxSize, const std::string& suffix) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/data/piece.h000066400000000000000000000055611257211073700204340ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PIECE_H #define LIBTORRENT_PIECE_H #include namespace torrent { class LIBTORRENT_EXPORT Piece { public: static const uint32_t invalid_index = ~uint32_t(); Piece() : m_index(invalid_index), m_offset(0), m_length(0) {} Piece(uint32_t index, uint32_t offset, uint32_t length) : m_index(index), m_offset(offset), m_length(length) {} bool is_valid() const { return m_index != invalid_index; } uint32_t index() const { return m_index; } void set_index(uint32_t v) { m_index = v; } uint32_t offset() const { return m_offset; } void set_offset(uint32_t v) { m_offset = v; } uint32_t length() const { return m_length; } void set_length(uint32_t v) { m_length = v; } bool operator == (const Piece& p) const { return m_index == p.m_index && m_offset == p.m_offset && m_length == p.m_length; } bool operator != (const Piece& p) const { return m_index != p.m_index || m_offset != p.m_offset || m_length != p.m_length; } private: uint32_t m_index; uint32_t m_offset; uint32_t m_length; }; } #endif libtorrent-0.13.6/src/torrent/data/transfer_list.cc000066400000000000000000000243661257211073700223700ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include "data/chunk.h" #include "peer/peer_info.h" #include "block_failed.h" #include "block_transfer.h" #include "block_list.h" #include "exceptions.h" #include "piece.h" #include "transfer_list.h" namespace torrent { TransferList::TransferList() : m_succeededCount(0), m_failedCount(0) { } // TODO: Derp if transfer list isn't cleared... TransferList::~TransferList() { if (!base_type::empty()) throw internal_error("TransferList::~TransferList() called on an non-empty object"); } TransferList::iterator TransferList::find(uint32_t index) { return std::find_if(begin(), end(), rak::equal(index, std::mem_fun(&BlockList::index))); } TransferList::const_iterator TransferList::find(uint32_t index) const { return std::find_if(begin(), end(), rak::equal(index, std::mem_fun(&BlockList::index))); } void TransferList::clear() { std::for_each(begin(), end(), std::tr1::bind(m_slot_canceled, std::tr1::bind(&BlockList::index, std::tr1::placeholders::_1))); std::for_each(begin(), end(), rak::call_delete()); base_type::clear(); } TransferList::iterator TransferList::insert(const Piece& piece, uint32_t blockSize) { if (find(piece.index()) != end()) throw internal_error("Delegator::new_chunk(...) received an index that is already delegated."); BlockList* blockList = new BlockList(piece, blockSize); m_slot_queued(piece.index()); return base_type::insert(end(), blockList); } // TODO: Create a destructor to ensure all blocklists have been cleared/invaldiated? TransferList::iterator TransferList::erase(iterator itr) { if (itr == end()) throw internal_error("TransferList::erase(...) itr == m_chunks.end()."); delete *itr; return base_type::erase(itr); } void TransferList::finished(BlockTransfer* transfer) { if (!transfer->is_valid()) throw internal_error("TransferList::finished(...) got transfer with wrong state."); uint32_t index = transfer->block()->index(); // Marks the transfer as complete and erases it. if (transfer->block()->completed(transfer)) m_slot_completed(index); } void TransferList::hash_succeeded(uint32_t index, Chunk* chunk) { iterator blockListItr = find(index); if ((Block::size_type)std::count_if((*blockListItr)->begin(), (*blockListItr)->end(), std::mem_fun_ref(&Block::is_finished)) != (*blockListItr)->size()) throw internal_error("TransferList::hash_succeeded(...) Finished blocks does not match size."); // The chunk should also be marked here or by the caller so that it // gets priority for syncing back to disk. if ((*blockListItr)->failed() != 0) mark_failed_peers(*blockListItr, chunk); // Add to a list of finished chunks indices with timestamps. This is // mainly used for torrent resume data on which chunks need to be // rehashed on crashes. // // We assume the chunk gets sync'ed within 10 minutes, so minimum // retention time of 30 minutes should be enough. The list only gets // pruned every 60 minutes, so any timer that reads values once // every 30 minutes is guaranteed to get them all as long as it is // ordered properly. m_completedList.push_back(std::make_pair(rak::timer::current().usec(), index)); if (rak::timer(m_completedList.front().first) + rak::timer::from_minutes(60) < rak::timer::current()) { completed_list_type::iterator itr = std::find_if(m_completedList.begin(), m_completedList.end(), rak::less_equal(rak::timer::current() - rak::timer::from_minutes(30), rak::mem_ref(&completed_list_type::value_type::first))); m_completedList.erase(m_completedList.begin(), itr); } m_succeededCount++; erase(blockListItr); } struct transfer_list_compare_data { transfer_list_compare_data(Chunk* chunk, const Piece& p) : m_chunk(chunk), m_piece(p) { } bool operator () (const BlockFailed::reference failed) { return m_chunk->compare_buffer(failed.first, m_piece.offset(), m_piece.length()); } Chunk* m_chunk; Piece m_piece; }; void TransferList::hash_failed(uint32_t index, Chunk* chunk) { iterator blockListItr = find(index); if (blockListItr == end()) throw internal_error("TransferList::hash_failed(...) Could not find index."); if ((Block::size_type)std::count_if((*blockListItr)->begin(), (*blockListItr)->end(), std::mem_fun_ref(&Block::is_finished)) != (*blockListItr)->size()) throw internal_error("TransferList::hash_failed(...) Finished blocks does not match size."); m_failedCount++; // Could propably also check promoted against size of the block // list. if ((*blockListItr)->attempt() == 0) { unsigned int promoted = update_failed(*blockListItr, chunk); if (promoted > 0 || promoted < (*blockListItr)->size()) { // Retry with the most popular blocks. (*blockListItr)->set_attempt(1); retry_most_popular(*blockListItr, chunk); // Also consider various other schemes, like using blocks from // only/mainly one peer. return; } } // Should we check if there's any peers whom have sent us bad data // before, and just clear those first? // Re-download the blocks. (*blockListItr)->do_all_failed(); } // update_failed(...) either increments the reference count of a // failed entry, or creates a new one if the data differs. unsigned int TransferList::update_failed(BlockList* blockList, Chunk* chunk) { unsigned int promoted = 0; blockList->inc_failed(); for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) { if (itr->failed_list() == NULL) itr->set_failed_list(new BlockFailed()); BlockFailed::iterator failedItr = std::find_if(itr->failed_list()->begin(), itr->failed_list()->end(), transfer_list_compare_data(chunk, itr->piece())); if (failedItr == itr->failed_list()->end()) { // We've never encountered this data before, make a new entry. char* buffer = new char[itr->piece().length()]; chunk->to_buffer(buffer, itr->piece().offset(), itr->piece().length()); itr->failed_list()->push_back(BlockFailed::value_type(buffer, 1)); failedItr = itr->failed_list()->end() - 1; // Count how many new data sets? } else { // Increment promoted when the entry's reference count becomes // larger than others, but not if it previously was the largest. BlockFailed::iterator maxItr = itr->failed_list()->max_element(); if (maxItr->second == failedItr->second && maxItr != (itr->failed_list()->reverse_max_element().base() - 1)) promoted++; failedItr->second++; } itr->failed_list()->set_current(failedItr); itr->leader()->set_failed_index(failedItr - itr->failed_list()->begin()); } return promoted; } void TransferList::mark_failed_peers(BlockList* blockList, Chunk* chunk) { std::set badPeers; for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) { // This chunk data is good, set it as current and // everyone who sent something else is a bad peer. itr->failed_list()->set_current(std::find_if(itr->failed_list()->begin(), itr->failed_list()->end(), transfer_list_compare_data(chunk, itr->piece()))); for (Block::transfer_list_type::const_iterator itr2 = itr->transfers()->begin(), last2 = itr->transfers()->end(); itr2 != last2; ++itr2) if ((*itr2)->failed_index() != itr->failed_list()->current() && (*itr2)->failed_index() != ~uint32_t()) badPeers.insert((*itr2)->peer_info()); } std::for_each(badPeers.begin(), badPeers.end(), m_slot_corrupt); } // Copy the stored data to the chunk from the failed entries with // largest reference counts. void TransferList::retry_most_popular(BlockList* blockList, Chunk* chunk) { for (BlockList::iterator itr = blockList->begin(), last = blockList->end(); itr != last; ++itr) { BlockFailed::reverse_iterator failedItr = itr->failed_list()->reverse_max_element(); if (failedItr == itr->failed_list()->rend()) throw internal_error("TransferList::retry_most_popular(...) No failed list entry found."); // The data is the same, so no need to copy. if (failedItr == itr->failed_list()->current_reverse_iterator()) continue; // Change the leader to the currently held buffer? chunk->from_buffer(failedItr->first, itr->piece().offset(), itr->piece().length()); itr->failed_list()->set_current(failedItr); } m_slot_completed(blockList->index()); } } libtorrent-0.13.6/src/torrent/data/transfer_list.h000066400000000000000000000100711257211073700222160ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRANSFER_LIST_H #define LIBTORRENT_TRANSFER_LIST_H #include #include #include namespace torrent { class TransferList : public std::vector { public: typedef std::vector base_type; typedef std::vector > completed_list_type; using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::const_reverse_iterator; using base_type::size; using base_type::empty; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; TransferList(); ~TransferList(); iterator find(uint32_t index); const_iterator find(uint32_t index) const; const completed_list_type& completed_list() const { return m_completedList; } uint32_t succeeded_count() const { return m_succeededCount; } uint32_t failed_count() const { return m_failedCount; } // // Internal to libTorrent: // void clear(); iterator insert(const Piece& piece, uint32_t blockSize); iterator erase(iterator itr); void finished(BlockTransfer* transfer); void hash_succeeded(uint32_t index, Chunk* chunk); void hash_failed(uint32_t index, Chunk* chunk); typedef std::tr1::function slot_chunk_index; typedef std::tr1::function slot_peer_info; slot_chunk_index& slot_canceled() { return m_slot_canceled; } slot_chunk_index& slot_completed() { return m_slot_completed; } slot_chunk_index& slot_queued() { return m_slot_queued; } slot_peer_info& slot_corrupt() { return m_slot_corrupt; } private: TransferList(const TransferList&); void operator = (const TransferList&); unsigned int update_failed(BlockList* blockList, Chunk* chunk); void mark_failed_peers(BlockList* blockList, Chunk* chunk); void retry_most_popular(BlockList* blockList, Chunk* chunk); slot_chunk_index m_slot_canceled; slot_chunk_index m_slot_completed; slot_chunk_index m_slot_queued; slot_peer_info m_slot_corrupt; completed_list_type m_completedList; uint32_t m_succeededCount; uint32_t m_failedCount; }; } #endif libtorrent-0.13.6/src/torrent/dht_manager.cc000066400000000000000000000071501257211073700210410ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "manager.h" #include "dht/dht_router.h" #include "dht_manager.h" namespace torrent { DhtManager::~DhtManager() { stop(); delete m_router; } void DhtManager::initialize(const Object& dhtCache) { if (m_router != NULL) throw internal_error("DhtManager::initialize called with DHT already active."); m_router = new DhtRouter(dhtCache, rak::socket_address::cast_from(manager->connection_manager()->bind_address())); } void DhtManager::start(port_type port) { if (m_router == NULL) throw internal_error("DhtManager::start called without initializing first."); m_port = port; m_router->start(port); } void DhtManager::stop() { if (m_router != NULL) m_router->stop(); } bool DhtManager::is_active() const { return m_router != NULL && m_router->is_active(); } void DhtManager::add_node(const sockaddr* addr, int port) { if (m_router != NULL) m_router->contact(rak::socket_address::cast_from(addr), port); } void DhtManager::add_node(const std::string& host, int port) { if (m_router != NULL) m_router->add_contact(host, port); } Object* DhtManager::store_cache(Object* container) const { if (m_router == NULL) throw internal_error("DhtManager::store_cache called but DHT not initialized."); return m_router->store_cache(container); } DhtManager::statistics_type DhtManager::get_statistics() const { return m_router->get_statistics(); } void DhtManager::reset_statistics() { m_router->reset_statistics(); } void DhtManager::set_upload_throttle(Throttle* t) { if (m_router->is_active()) throw internal_error("DhtManager::set_upload_throttle() called while DHT server active."); m_router->set_upload_throttle(t->throttle_list()); } void DhtManager::set_download_throttle(Throttle* t) { if (m_router->is_active()) throw internal_error("DhtManager::set_download_throttle() called while DHT server active."); m_router->set_download_throttle(t->throttle_list()); } } libtorrent-0.13.6/src/torrent/dht_manager.h000066400000000000000000000107661257211073700207120ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Add some helpfull words here. #ifndef LIBTORRENT_DHT_MANAGER_H #define LIBTORRENT_DHT_MANAGER_H #include #include namespace torrent { class ThrottleList; class LIBTORRENT_EXPORT DhtManager { public: typedef ConnectionManager::port_type port_type; struct statistics_type { // Cycle; 0=inactive, 1=initial bootstrapping, 2 and up=normal operation unsigned int cycle; // UDP transfer rates. const Rate& up_rate; const Rate& down_rate; // DHT query statistics. unsigned int queries_received; unsigned int queries_sent; unsigned int replies_received; unsigned int errors_received; unsigned int errors_caught; // DHT node info. unsigned int num_nodes; unsigned int num_buckets; // DHT tracker info. unsigned int num_peers; unsigned int max_peers; unsigned int num_trackers; statistics_type(const Rate& up, const Rate& down) : up_rate(up), down_rate(down) { } }; DhtManager() : m_router(NULL), m_portSent(0), m_canReceive(true) { }; ~DhtManager(); void initialize(const Object& dhtCache); void start(port_type port); void stop(); // Store DHT cache in the given container and return the container. Object* store_cache(Object* container) const; bool is_valid() const { return m_router; } bool is_active() const; port_type port() const { return m_port; } bool can_receive_queries() const { return m_canReceive; } // Call this after sending the port to a client, so the router knows // that it should be getting requests. void port_sent() { m_portSent++; } // Add a node by host (from a torrent file), or by address from explicit add_node // command or the BT PORT message. void add_node(const std::string& host, int port); void add_node(const sockaddr* addr, int port); statistics_type get_statistics() const; void reset_statistics(); void set_upload_throttle(Throttle* t); void set_download_throttle(Throttle* t); // To be called if upon examining the statistics, the client decides that // we can't receive outside requests and therefore shouldn't advertise our // UDP port after the BT handshake. void set_can_receive(bool can) { m_canReceive = can; } // Internal libTorrent use only DhtRouter* router() { return m_router; } private: DhtRouter* m_router; port_type m_port; int m_portSent; bool m_canReceive; }; } #endif libtorrent-0.13.6/src/torrent/download.cc000066400000000000000000000426721257211073700204070ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include "data/block.h" #include "data/block_list.h" #include "data/chunk_list.h" #include "data/hash_queue.h" #include "data/hash_torrent.h" #include "download/available_list.h" #include "download/chunk_selector.h" #include "download/chunk_statistics.h" #include "download/download_wrapper.h" #include "protocol/peer_connection_base.h" #include "protocol/peer_factory.h" #include "peer/peer_info.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "torrent/download_info.h" #include "torrent/data/file.h" #include "torrent/peer/connection_list.h" #include "torrent/tracker_controller.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "exceptions.h" #include "download.h" #include "object.h" #include "throttle.h" #include "tracker_list.h" #define LT_LOG_THIS(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TORRENT_##log_level, m_ptr->info(), "download", log_fmt, __VA_ARGS__); namespace torrent { const DownloadInfo* Download::info() const { return m_ptr->info(); } const download_data* Download::data() const { return m_ptr->data(); } void Download::open(int flags) { if (m_ptr->info()->is_open()) return; LT_LOG_THIS(INFO, "Opening torrent: flags:%0x.", flags); // Currently always open with no_create, as start will make sure // they are created. Need to fix this. m_ptr->main()->open(FileList::open_no_create); m_ptr->hash_checker()->hashing_ranges().insert(0, m_ptr->main()->file_list()->size_chunks()); // Mark the files by default to be created and resized. The client // should be allowed to pass a flag that will keep the old settings, // although loading resume data should really handle everything // properly. int fileFlags = File::flag_create_queued | File::flag_resize_queued; if (flags & open_enable_fallocate) fileFlags |= File::flag_fallocate; for (FileList::iterator itr = m_ptr->main()->file_list()->begin(), last = m_ptr->main()->file_list()->end(); itr != last; itr++) (*itr)->set_flags(fileFlags); } void Download::close(int flags) { if (m_ptr->info()->is_active()) stop(0); LT_LOG_THIS(INFO, "Closing torrent: flags:%0x.", flags); m_ptr->close(); } void Download::start(int flags) { DownloadInfo* info = m_ptr->info(); if (!m_ptr->hash_checker()->is_checked()) throw internal_error("Tried to start an unchecked download."); if (!info->is_open()) throw internal_error("Tried to start a closed download."); if (m_ptr->data()->mutable_completed_bitfield()->empty()) throw internal_error("Tried to start a download with empty bitfield."); if (info->is_active()) return; LT_LOG_THIS(INFO, "Starting torrent: flags:%0x.", flags); m_ptr->data()->verify_wanted_chunks("Download::start(...)"); // file_list()->open(flags); // If the FileList::open_no_create flag was not set, our new // behavior is to create all zero-length files with // flag_queued_create set. file_list()->open(flags & ~FileList::open_no_create); if (m_ptr->connection_type() == CONNECTION_INITIAL_SEED) { if (!m_ptr->main()->start_initial_seeding()) set_connection_type(CONNECTION_SEED); } m_ptr->main()->start(); m_ptr->main()->tracker_controller()->enable((flags & start_skip_tracker) ? TrackerController::enable_dont_reset_stats : 0); // Reset the uploaded/download baseline when we restart the download // so that broken trackers get the right uploaded ratio. if (!(flags & start_keep_baseline)) { info->set_uploaded_baseline(info->up_rate()->total()); info->set_completed_baseline(m_ptr->main()->file_list()->completed_bytes()); lt_log_print_info(LOG_TRACKER_INFO, info, "download", "Setting new baseline on start: uploaded:%" PRIu64 " completed:%" PRIu64 ".", info->uploaded_baseline(), info->completed_baseline()); } if (!(flags & start_skip_tracker)) m_ptr->main()->tracker_controller()->send_start_event(); } void Download::stop(int flags) { if (!m_ptr->info()->is_active()) return; LT_LOG_THIS(INFO, "Stopping torrent: flags:%0x.", flags); m_ptr->main()->stop(); if (!(flags & stop_skip_tracker)) m_ptr->main()->tracker_controller()->send_stop_event(); m_ptr->main()->tracker_controller()->disable(); } bool Download::hash_check(bool tryQuick) { if (m_ptr->hash_checker()->is_checking()) throw internal_error("Download::hash_check(...) called but the hash is already being checked."); if (!m_ptr->info()->is_open() || m_ptr->info()->is_active()) throw internal_error("Download::hash_check(...) called on a closed or active download."); if (m_ptr->hash_checker()->is_checked()) throw internal_error("Download::hash_check(...) called but already hash checked."); Bitfield* bitfield = m_ptr->data()->mutable_completed_bitfield(); LT_LOG_THIS(INFO, "Checking hash: allocated:%i try_quick:%i.", !bitfield->empty(), (int)tryQuick); if (bitfield->empty()) { // The bitfield still hasn't been allocated, so no resume data was // given. bitfield->allocate(); bitfield->unset_all(); m_ptr->hash_checker()->hashing_ranges().insert(0, m_ptr->main()->file_list()->size_chunks()); } m_ptr->main()->file_list()->update_completed(); return m_ptr->hash_checker()->start(tryQuick); } // Propably not correct, need to clear content, etc. void Download::hash_stop() { if (!m_ptr->hash_checker()->is_checking()) return; LT_LOG_THIS(INFO, "Hashing stopped.", 0); m_ptr->hash_checker()->hashing_ranges().erase(0, m_ptr->hash_checker()->position()); m_ptr->hash_queue()->remove(m_ptr->data()); m_ptr->hash_checker()->clear(); } bool Download::is_hash_checked() const { return m_ptr->hash_checker()->is_checked(); } bool Download::is_hash_checking() const { return m_ptr->hash_checker()->is_checking(); } void Download::set_pex_enabled(bool enabled) { if (enabled) m_ptr->info()->set_pex_enabled(); else m_ptr->info()->unset_flags(DownloadInfo::flag_pex_enabled); } Object* Download::bencode() { return m_ptr->bencode(); } const Object* Download::bencode() const { return m_ptr->bencode(); } FileList* Download::file_list() const { return m_ptr->main()->file_list(); } TrackerController* Download::tracker_controller() const { return m_ptr->main()->tracker_controller(); } TrackerList* Download::tracker_list() const { return m_ptr->main()->tracker_list(); } PeerList* Download::peer_list() { return m_ptr->main()->peer_list(); } const PeerList* Download::peer_list() const { return m_ptr->main()->peer_list(); } const TransferList* Download::transfer_list() const { return m_ptr->main()->delegator()->transfer_list(); } ConnectionList* Download::connection_list() { return m_ptr->main()->connection_list(); } const ConnectionList* Download::connection_list() const { return m_ptr->main()->connection_list(); } uint64_t Download::bytes_done() const { uint64_t a = 0; Delegator* d = m_ptr->main()->delegator(); for (TransferList::const_iterator itr1 = d->transfer_list()->begin(), last1 = d->transfer_list()->end(); itr1 != last1; ++itr1) for (BlockList::const_iterator itr2 = (*itr1)->begin(), last2 = (*itr1)->end(); itr2 != last2; ++itr2) if (itr2->is_finished()) a += itr2->piece().length(); return a + m_ptr->main()->file_list()->completed_bytes(); } uint32_t Download::chunks_hashed() const { return m_ptr->hash_checker()->position(); } const uint8_t* Download::chunks_seen() const { return !m_ptr->main()->chunk_statistics()->empty() ? &*m_ptr->main()->chunk_statistics()->begin() : NULL; } void Download::set_chunks_done(uint32_t chunks_done, uint32_t chunks_wanted) { if (m_ptr->info()->is_open() || !m_ptr->data()->mutable_completed_bitfield()->empty()) throw input_error("Download::set_chunks_done(...) Invalid state."); chunks_done = std::min(chunks_done, m_ptr->file_list()->size_chunks()); chunks_wanted = std::min(chunks_wanted, m_ptr->file_list()->size_chunks() - chunks_done); m_ptr->data()->mutable_completed_bitfield()->set_size_set(chunks_done); m_ptr->data()->set_wanted_chunks(chunks_wanted); } void Download::set_bitfield(bool allSet) { if (m_ptr->hash_checker()->is_checked() || m_ptr->hash_checker()->is_checking()) throw input_error("Download::set_bitfield(...) Download in invalid state."); Bitfield* bitfield = m_ptr->data()->mutable_completed_bitfield(); bitfield->allocate(); if (allSet) bitfield->set_all(); else bitfield->unset_all(); m_ptr->data()->update_wanted_chunks(); m_ptr->hash_checker()->hashing_ranges().clear(); } void Download::set_bitfield(uint8_t* first, uint8_t* last) { if (m_ptr->hash_checker()->is_checked() || m_ptr->hash_checker()->is_checking()) throw input_error("Download::set_bitfield(...) Download in invalid state."); if (std::distance(first, last) != (ptrdiff_t)m_ptr->main()->file_list()->bitfield()->size_bytes()) throw input_error("Download::set_bitfield(...) Invalid length."); Bitfield* bitfield = m_ptr->data()->mutable_completed_bitfield(); bitfield->allocate(); std::memcpy(bitfield->begin(), first, bitfield->size_bytes()); bitfield->update(); m_ptr->data()->update_wanted_chunks(); m_ptr->hash_checker()->hashing_ranges().clear(); } void Download::update_range(int flags, uint32_t first, uint32_t last) { if (m_ptr->hash_checker()->is_checked() || m_ptr->hash_checker()->is_checking() || m_ptr->main()->file_list()->bitfield()->empty()) throw input_error("Download::clear_range(...) Download in invalid state."); if (flags & update_range_recheck) m_ptr->hash_checker()->hashing_ranges().insert(first, last); if (flags & (update_range_clear | update_range_recheck)) { m_ptr->data()->mutable_completed_bitfield()->unset_range(first, last); m_ptr->data()->update_wanted_chunks(); } } void Download::sync_chunks() { m_ptr->main()->chunk_list()->sync_chunks(ChunkList::sync_all | ChunkList::sync_force); } uint32_t Download::peers_complete() const { return m_ptr->main()->chunk_statistics()->complete(); } uint32_t Download::peers_accounted() const { return m_ptr->main()->chunk_statistics()->accounted(); } uint32_t Download::peers_currently_unchoked() const { return m_ptr->main()->choke_group()->up_queue()->size_unchoked(); } uint32_t Download::peers_currently_interested() const { return m_ptr->main()->choke_group()->up_queue()->size_total(); } uint32_t Download::size_pex() const { return m_ptr->main()->info()->size_pex(); } uint32_t Download::max_size_pex() const { return m_ptr->main()->info()->max_size_pex(); } bool Download::accepting_new_peers() const { return m_ptr->info()->is_accepting_new_peers(); } // DEPRECATE uint32_t Download::uploads_max() const { if (m_ptr->main()->up_group_entry()->max_slots() == DownloadInfo::unlimited) return 0; return m_ptr->main()->up_group_entry()->max_slots(); } uint32_t Download::uploads_min() const { // if (m_ptr->main()->up_group_entry()->min_slots() == DownloadInfo::unlimited) // return 0; return m_ptr->main()->up_group_entry()->min_slots(); } uint32_t Download::downloads_max() const { if (m_ptr->main()->down_group_entry()->max_slots() == DownloadInfo::unlimited) return 0; return m_ptr->main()->down_group_entry()->max_slots(); } uint32_t Download::downloads_min() const { // if (m_ptr->main()->down_group_entry()->min_slots() == DownloadInfo::unlimited) // return 0; return m_ptr->main()->down_group_entry()->min_slots(); } void Download::set_upload_throttle(Throttle* t) { if (m_ptr->info()->is_active()) throw internal_error("Download::set_upload_throttle() called on active download."); m_ptr->main()->set_upload_throttle(t->throttle_list()); } void Download::set_download_throttle(Throttle* t) { if (m_ptr->info()->is_active()) throw internal_error("Download::set_download_throttle() called on active download."); m_ptr->main()->set_download_throttle(t->throttle_list()); } void Download::send_completed() { m_ptr->main()->tracker_controller()->send_completed_event(); } void Download::manual_request(bool force) { m_ptr->main()->tracker_controller()->manual_request(force); } void Download::manual_cancel() { m_ptr->main()->tracker_controller()->close(); } // DEPRECATE void Download::set_uploads_max(uint32_t v) { if (v > (1 << 16)) throw input_error("Max uploads must be between 0 and 2^16."); // For the moment, treat 0 as unlimited. m_ptr->main()->up_group_entry()->set_max_slots(v == 0 ? DownloadInfo::unlimited : v); m_ptr->main()->choke_group()->up_queue()->balance_entry(m_ptr->main()->up_group_entry()); } void Download::set_uploads_min(uint32_t v) { if (v > (1 << 16)) throw input_error("Min uploads must be between 0 and 2^16."); // For the moment, treat 0 as unlimited. m_ptr->main()->up_group_entry()->set_min_slots(v); m_ptr->main()->choke_group()->up_queue()->balance_entry(m_ptr->main()->up_group_entry()); } void Download::set_downloads_max(uint32_t v) { if (v > (1 << 16)) throw input_error("Max downloads must be between 0 and 2^16."); // For the moment, treat 0 as unlimited. m_ptr->main()->down_group_entry()->set_max_slots(v == 0 ? DownloadInfo::unlimited : v); m_ptr->main()->choke_group()->down_queue()->balance_entry(m_ptr->main()->down_group_entry()); } void Download::set_downloads_min(uint32_t v) { if (v > (1 << 16)) throw input_error("Min downloads must be between 0 and 2^16."); // For the moment, treat 0 as unlimited. m_ptr->main()->down_group_entry()->set_min_slots(v); m_ptr->main()->choke_group()->down_queue()->balance_entry(m_ptr->main()->down_group_entry()); } Download::ConnectionType Download::connection_type() const { return (ConnectionType)m_ptr->connection_type(); } void Download::set_connection_type(ConnectionType t) { if (m_ptr->info()->is_meta_download()) { m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionMetadata); return; } switch (t) { case CONNECTION_LEECH: m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionDefault); break; case CONNECTION_SEED: m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionSeed); break; case CONNECTION_INITIAL_SEED: if (info()->is_active() && m_ptr->main()->initial_seeding() == NULL) throw input_error("Can't switch to initial seeding: download is active."); m_ptr->main()->connection_list()->slot_new_connection(&createPeerConnectionInitialSeed); break; default: throw input_error("torrent::Download::set_connection_type(...) received an unknown type."); }; m_ptr->set_connection_type(t); } Download::HeuristicType Download::upload_choke_heuristic() const { return (Download::HeuristicType)m_ptr->main()->choke_group()->up_queue()->heuristics(); } void Download::set_upload_choke_heuristic(HeuristicType t) { if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE) throw input_error("Invalid heuristics value."); m_ptr->main()->choke_group()->up_queue()->set_heuristics((choke_queue::heuristics_enum)t); } Download::HeuristicType Download::download_choke_heuristic() const { return (Download::HeuristicType)m_ptr->main()->choke_group()->down_queue()->heuristics(); } void Download::set_download_choke_heuristic(HeuristicType t) { if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE) throw input_error("Invalid heuristics value."); m_ptr->main()->choke_group()->down_queue()->set_heuristics((choke_queue::heuristics_enum)t); } void Download::update_priorities() { m_ptr->receive_update_priorities(); } void Download::add_peer(const sockaddr* sa, int port) { if (m_ptr->info()->is_private()) return; rak::socket_address sa_port = *rak::socket_address::cast_from(sa); sa_port.set_port(port); m_ptr->main()->add_peer(sa_port); } DownloadMain* Download::main() { return m_ptr->main(); } } libtorrent-0.13.6/src/torrent/download.h000066400000000000000000000156311257211073700202440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_H #define LIBTORRENT_DOWNLOAD_H #include #include #include #include #include namespace torrent { class ConnectionList; class DownloadInfo; class DownloadMain; class download_data; class TrackerController; // Download is safe to copy and destory as it is just a pointer to an // internal class. class LIBTORRENT_EXPORT Download { public: static const uint32_t numwanted_diabled = ~uint32_t(); // Start and open flags can be stored in the same integer, same for // stop and close flags. static const int open_enable_fallocate = (1 << 0); static const int start_no_create = (1 << 1); static const int start_keep_baseline = (1 << 2); static const int start_skip_tracker = (1 << 3); static const int stop_skip_tracker = (1 << 0); Download(DownloadWrapper* d = NULL) : m_ptr(d) {} const DownloadInfo* info() const; const download_data* data() const; // Not active atm. Opens and prepares/closes the files. void open(int flags = 0); void close(int flags = 0); // When 'tryQuick' is true, it will only check if the chunks can be // mmaped and stops if one is encountered. If it doesn't find any // mappable chunks it will return true to indicate that it is // finished and a hash done signal has been queued. // // Chunk ranges that have valid resume data won't be checked. bool hash_check(bool tryQuick); void hash_stop(); // Start/stop the download. The torrent must be open. void start(int flags = 0); void stop(int flags = 0); // Does not check if the download has been removed. bool is_valid() const { return m_ptr; } bool is_hash_checked() const; bool is_hash_checking() const; void set_pex_enabled(bool enabled); Object* bencode(); const Object* bencode() const; TrackerController* tracker_controller() const; TrackerList* tracker_list() const; FileList* file_list() const; PeerList* peer_list(); const PeerList* peer_list() const; const TransferList* transfer_list() const; ConnectionList* connection_list(); const ConnectionList* connection_list() const; // Bytes completed. uint64_t bytes_done() const; uint32_t chunks_hashed() const; const uint8_t* chunks_seen() const; // Set the number of finished chunks for closed torrents. void set_chunks_done(uint32_t chunks_done, uint32_t chunks_wanted); // Use the below to set the resume data and what chunk ranges need // to be hash checked. If they arn't called then by default it will // use an cleared bitfield and check the whole range. // // These must be called when is_open, !is_checked and !is_checking. void set_bitfield(bool allSet); void set_bitfield(uint8_t* first, uint8_t* last); static const int update_range_recheck = (1 << 0); static const int update_range_clear = (1 << 1); void update_range(int flags, uint32_t first, uint32_t last); // Temporary hack for syncing chunks to disk before hash resume is // saved. void sync_chunks(); uint32_t peers_complete() const; uint32_t peers_accounted() const; uint32_t peers_currently_unchoked() const; uint32_t peers_currently_interested() const; uint32_t size_pex() const; uint32_t max_size_pex() const; bool accepting_new_peers() const; uint32_t uploads_max() const; void set_uploads_max(uint32_t v); uint32_t uploads_min() const; void set_uploads_min(uint32_t v); uint32_t downloads_max() const; void set_downloads_max(uint32_t v); uint32_t downloads_min() const; void set_downloads_min(uint32_t v); void set_upload_throttle(Throttle* t); void set_download_throttle(Throttle* t); // Some temporary functions that are routed to // TrackerManager... Clean this up. void send_completed(); void manual_request(bool force); void manual_cancel(); typedef enum { CONNECTION_LEECH, CONNECTION_SEED, CONNECTION_INITIAL_SEED, CONNECTION_METADATA, } ConnectionType; ConnectionType connection_type() const; void set_connection_type(ConnectionType t); typedef enum { } HeuristicType; HeuristicType upload_choke_heuristic() const; void set_upload_choke_heuristic(HeuristicType t); HeuristicType download_choke_heuristic() const; void set_download_choke_heuristic(HeuristicType t); // Call this when you want the modifications of the download priorities // in the entries to take effect. It is slightly expensive as it rechecks // all the peer bitfields to see if we are still interested. void update_priorities(); void add_peer(const sockaddr* addr, int port); DownloadWrapper* ptr() { return m_ptr; } DownloadMain* main(); private: DownloadWrapper* m_ptr; }; } #endif libtorrent-0.13.6/src/torrent/download/000077500000000000000000000000001257211073700200655ustar00rootroot00000000000000libtorrent-0.13.6/src/torrent/download/Makefile.am000066400000000000000000000010031257211073700221130ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_torrentdownload.la libsub_torrentdownload_la_SOURCES = \ choke_group.cc \ choke_group.h \ choke_queue.cc \ choke_queue.h \ download_manager.cc \ download_manager.h \ group_entry.h \ resource_manager.cc \ resource_manager.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir) libtorrentincludedir = $(includedir)/torrent/download libtorrentinclude_HEADERS = \ choke_group.h \ choke_queue.h \ download_manager.h \ group_entry.h \ resource_manager.h libtorrent-0.13.6/src/torrent/download/choke_group.cc000066400000000000000000000051031257211073700227000ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "choke_group.h" #include "choke_queue.h" // TODO: Put resource_manager_entry in a separate file. #include "resource_manager.h" #include "torrent/exceptions.h" #include "download/download_main.h" namespace tr1 { using namespace std::tr1; } namespace torrent { choke_group::choke_group() : m_tracker_mode(TRACKER_MODE_NORMAL), m_down_queue(choke_queue::flag_unchoke_all_new), m_first(NULL), m_last(NULL) { } uint64_t choke_group::up_rate() const { return std::for_each(m_first, m_last, rak::accumulate((uint64_t)0, tr1::bind(&Rate::rate, tr1::bind(&resource_manager_entry::up_rate, tr1::placeholders::_1)))).result; } uint64_t choke_group::down_rate() const { return std::for_each(m_first, m_last, rak::accumulate((uint64_t)0, tr1::bind(&Rate::rate, tr1::bind(&resource_manager_entry::down_rate, tr1::placeholders::_1)))).result; } } libtorrent-0.13.6/src/torrent/download/choke_group.h000066400000000000000000000074701257211073700225530ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_CHOKE_GROUP_H #define LIBTORRENT_DOWNLOAD_CHOKE_GROUP_H #include #include #include #include #include // TODO: Separate out resource_manager_entry. #include namespace torrent { class choke_queue; class resource_manager_entry; class LIBTORRENT_EXPORT choke_group { public: enum tracker_mode_enum { TRACKER_MODE_NORMAL, TRACKER_MODE_AGGRESSIVE }; choke_group(); const std::string& name() const { return m_name; } void set_name(const std::string& name) { m_name = name; } tracker_mode_enum tracker_mode() const { return m_tracker_mode; } void set_tracker_mode(tracker_mode_enum tm) { m_tracker_mode = tm; } choke_queue* up_queue() { return &m_up_queue; } choke_queue* down_queue() { return &m_down_queue; } const choke_queue* c_up_queue() const { return &m_up_queue; } const choke_queue* c_down_queue() const { return &m_down_queue; } uint32_t up_requested() const { return std::min(m_up_queue.size_total(), m_up_queue.max_unchoked()); } uint32_t down_requested() const { return std::min(m_down_queue.size_total(), m_down_queue.max_unchoked()); } bool empty() const { return m_first == m_last; } uint32_t size() const { return std::distance(m_first, m_last); } uint64_t up_rate() const; uint64_t down_rate() const; // Internal: resource_manager_entry* first() { return m_first; } resource_manager_entry* last() { return m_last; } void set_first(resource_manager_entry* first) { m_first = first; } void set_last(resource_manager_entry* last) { m_last = last; } void inc_iterators() { m_first++; m_last++; } void dec_iterators() { m_first--; m_last--; } private: std::string m_name; tracker_mode_enum m_tracker_mode; choke_queue m_up_queue; choke_queue m_down_queue; resource_manager_entry* m_first; resource_manager_entry* m_last; }; } #endif libtorrent-0.13.6/src/torrent/download/choke_queue.cc000066400000000000000000000575151257211073700227060ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include #include "protocol/peer_connection_base.h" #include "torrent/download/group_entry.h" #include "torrent/peer/connection_list.h" #include "torrent/peer/choke_status.h" #include "torrent/utils/log.h" #include "choke_queue.h" namespace torrent { struct choke_manager_less { bool operator () (choke_queue::value_type v1, choke_queue::value_type v2) const { return v1.weight < v2.weight; } }; static inline bool should_connection_unchoke(choke_queue* cq, PeerConnectionBase* pcb) { return pcb->should_connection_unchoke(cq); } static inline void log_choke_changes_func_new(void* address, const char* title, int quota, int adjust) { lt_log_print(LOG_INSTRUMENTATION_CHOKE, "%p %i %s %i %i", address, 0, //lf->last_update(), title, quota, adjust); } choke_queue::~choke_queue() { if (m_currently_unchoked != 0) throw internal_error("choke_queue::~choke_queue() called but m_currentlyUnchoked != 0."); if (m_currently_queued != 0) throw internal_error("choke_queue::~choke_queue() called but m_currentlyQueued != 0."); } // 1 > 1 // 9 > 2 // 17 > 3 < 21 // 25 > 4 < 31 // 33 > 5 < 41 // 65 > 9 < 81 inline uint32_t choke_queue::max_alternate() const { if (m_currently_unchoked < 31) return (m_currently_unchoked + 7) / 8; else return (m_currently_unchoked + 9) / 10; } group_stats choke_queue::prepare_weights(group_stats gs) { // gs.sum_min_needed = 0; // gs.sum_max_needed = 0; // gs.sum_max_leftovers = 0; // Needs to reflect how many we can optimistically unchoke after choking unchoked connections? // // also remember to clear the queue/unchoked thingies. for (group_container_type::iterator itr = m_group_container.begin(), last = m_group_container.end(); itr != last; itr++) { m_heuristics_list[m_heuristics].slot_choke_weight((*itr)->mutable_unchoked()->begin(), (*itr)->mutable_unchoked()->end()); std::sort((*itr)->mutable_unchoked()->begin(), (*itr)->mutable_unchoked()->end(), choke_manager_less()); m_heuristics_list[m_heuristics].slot_unchoke_weight((*itr)->mutable_queued()->begin(), (*itr)->mutable_queued()->end()); std::sort((*itr)->mutable_queued()->begin(), (*itr)->mutable_queued()->end(), choke_manager_less()); // Aggregate the statistics... Remember to update them after // optimistic/pessimistic unchokes. gs.sum_min_needed += std::min((*itr)->size_connections(), std::min((*itr)->min_slots(), (*itr)->max_slots())); uint32_t max_slots = std::min((*itr)->size_connections(), (*itr)->max_slots()); gs.sum_max_needed += max_slots; gs.sum_max_leftovers += (*itr)->size_connections() - max_slots; // Counter for how many can choke/unchoke based on weights? // However one should never have zero weight for any weight group. } return gs; } group_stats choke_queue::retrieve_connections(group_stats gs, container_type* queued, container_type* unchoked) { for (group_container_type::iterator itr = m_group_container.begin(), last = m_group_container.end(); itr != last; itr++) { group_entry* entry = *itr; unsigned int min_slots = std::min(entry->min_slots(), entry->max_slots()); lt_log_print(LOG_PEER_DEBUG, "Choke queue retrieve_connections; queued:%u unchoked:%u min_slots:%u max_slots:%u.", (unsigned)entry->queued()->size(), (unsigned)entry->unchoked()->size(), min_slots, entry->max_slots()); // Disable this after finding the flaw... ? // if (entry->unchoked()->size() > entry->max_slots()) { // unsigned int count = 0; // // Still needing choke/unchoke to fill min_size. // while (entry->unchoked()->size() > entry->max_slots() && !entry->unchoked()->empty()) // count += m_slotConnection(entry->unchoked()->back().connection, true); // // m_slotUnchoke(-count); // Move this? // gs.now_choked += entry->unchoked()->size(); // Need to add this... // } if (entry->unchoked()->size() < min_slots) { // Currently unchoked is less than min_slots, so don't give any // candidates for choking and also check if we can fill the // requirement by unchoking queued connections. unsigned int count = 0; // Still needing choke/unchoke to fill min_size. while (!entry->queued()->empty() && entry->unchoked()->size() < min_slots) count += m_slotConnection(entry->queued()->back().connection, false); gs.changed_unchoked += count; gs.now_unchoked += entry->unchoked()->size(); } else { // TODO: This only handles a single weight group, fixme. group_entry::container_type::const_iterator first = entry->unchoked()->begin() + min_slots; group_entry::container_type::const_iterator last = entry->unchoked()->end(); unchoked->insert(unchoked->end(), first, last); gs.now_unchoked += min_slots; } // TODO: Does not do optimistic unchokes if min_slots >= max_slots. if (entry->unchoked()->size() < entry->max_slots()) { // We can assume that at either we have no queued connections or // 'min_slots' has been reached. queued->insert(queued->end(), entry->queued()->end() - std::min(entry->queued()->size(), entry->max_slots() - entry->unchoked()->size()), entry->queued()->end()); } } return gs; } void choke_queue::rebuild_containers(container_type* queued, container_type* unchoked) { queued->clear(); unchoked->clear(); for (group_container_type::iterator itr = m_group_container.begin(), last = m_group_container.end(); itr != last; itr++) { queued->insert(queued->end(), (*itr)->queued()->begin(), (*itr)->queued()->end()); unchoked->insert(unchoked->end(), (*itr)->unchoked()->begin(), (*itr)->unchoked()->end()); } } void choke_queue::balance() { // Return if no balancing is needed. Don't return if is_unlimited() // as we might have just changed the value and have interested that // can be unchoked. // // TODO: Check if unlimited, in that case we don't need to balance // if we got no queued connections. if (m_currently_unchoked == m_maxUnchoked) return; container_type queued; container_type unchoked; group_stats gs; std::memset(&gs, 0, sizeof(group_stats)); gs = prepare_weights(gs); gs = retrieve_connections(gs, &queued, &unchoked); if (gs.changed_unchoked != 0) m_slotUnchoke(gs.changed_unchoked); // If we have more unchoked than max global slots allow for, // 'can_unchoke' will be negative. int can_unchoke = m_slotCanUnchoke(); int max_unchoked = std::min(m_maxUnchoked, (uint32_t)(1 << 20)); int adjust = max_unchoked - (int)(unchoked.size() + gs.now_unchoked); adjust = std::min(adjust, can_unchoke); log_choke_changes_func_new(this, "balance", m_maxUnchoked, adjust); int result = 0; if (adjust > 0) { result = adjust_choke_range(queued.begin(), queued.end(), &queued, &unchoked, adjust, false); } else if (adjust < 0) { // We can do the choking before the slot is called as this // choke_queue won't be unchoking the same peers due to the // call-back. result = -adjust_choke_range(unchoked.begin(), unchoked.end(), &unchoked, &queued, -adjust, true); } if (result != 0) m_slotUnchoke(result); lt_log_print(LOG_PEER_DEBUG, "Called balance; adjust:%i can_unchoke:%i queued:%u unchoked:%u result:%i.", adjust, can_unchoke, (unsigned)queued.size(), (unsigned)unchoked.size(), result); } void choke_queue::balance_entry(group_entry* entry) { m_heuristics_list[m_heuristics].slot_choke_weight(entry->mutable_unchoked()->begin(), entry->mutable_unchoked()->end()); std::sort(entry->mutable_unchoked()->begin(), entry->mutable_unchoked()->end(), choke_manager_less()); m_heuristics_list[m_heuristics].slot_unchoke_weight(entry->mutable_queued()->begin(), entry->mutable_queued()->end()); std::sort(entry->mutable_queued()->begin(), entry->mutable_queued()->end(), choke_manager_less()); int count = 0; unsigned int min_slots = std::min(entry->min_slots(), entry->max_slots()); while (!entry->unchoked()->empty() && entry->unchoked()->size() > entry->max_slots()) count -= m_slotConnection(entry->unchoked()->back().connection, true); while (!entry->queued()->empty() && entry->unchoked()->size() < min_slots) count += m_slotConnection(entry->queued()->back().connection, false); m_slotUnchoke(count); } int choke_queue::cycle(uint32_t quota) { // TODO: This should not use the old values, but rather the number // of unchoked this round. // HACKKKKKK container_type queued; container_type unchoked; rebuild_containers(&queued, &unchoked); int oldSize = unchoked.size(); uint32_t alternate = max_alternate(); queued.clear(); unchoked.clear(); group_stats gs; std::memset(&gs, 0, sizeof(group_stats)); gs = prepare_weights(gs); gs = retrieve_connections(gs, &queued, &unchoked); quota = std::min(quota, m_maxUnchoked); quota = quota - std::min(quota, gs.now_unchoked); uint32_t adjust = (unchoked.size() < quota) ? (quota - unchoked.size()) : 0; adjust = std::max(adjust, alternate); adjust = std::min(adjust, quota); log_choke_changes_func_new(this, "cycle", quota, adjust); lt_log_print(LOG_PEER_DEBUG, "Called cycle; quota:%u adjust:%i alternate:%i queued:%u unchoked:%u.", quota, adjust, alternate, (unsigned)queued.size(), (unsigned)unchoked.size()); uint32_t unchoked_count = adjust_choke_range(queued.begin(), queued.end(), &queued, &unchoked, adjust, false); if (unchoked.size() > quota) adjust_choke_range(unchoked.begin(), unchoked.end() - unchoked_count, &unchoked, &queued, unchoked.size() - quota, true); if (unchoked.size() > quota) throw internal_error("choke_queue::cycle() unchoked.size() > quota."); rebuild_containers(&queued, &unchoked); // Remove... lt_log_print(LOG_PEER_DEBUG, "After cycle; queued:%u unchoked:%u unchoked_count:%i old_size:%i.", (unsigned)queued.size(), (unsigned)unchoked.size(), unchoked_count, oldSize); return ((int)unchoked.size() - (int)oldSize); // + gs.changed_unchoke } void choke_queue::set_queued(PeerConnectionBase* pc, choke_status* base) { if (base->queued() || base->unchoked()) return; base->set_queued(true); if (base->snubbed()) return; base->entry()->connection_queued(pc); modify_currently_queued(1); if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke() > 0) && should_connection_unchoke(this, pc) && rak::timer(base->time_last_choke()) + rak::timer::from_seconds(10) < cachedTime) { m_slotConnection(pc, false); m_slotUnchoke(1); } } void choke_queue::set_not_queued(PeerConnectionBase* pc, choke_status* base) { if (!base->queued()) return; base->set_queued(false); if (base->snubbed()) return; if (base->unchoked()) { m_slotConnection(pc, true); m_slotUnchoke(-1); } base->entry()->connection_unqueued(pc); modify_currently_queued(-1); } void choke_queue::set_snubbed(PeerConnectionBase* pc, choke_status* base) { if (base->snubbed()) return; base->set_snubbed(true); if (base->unchoked()) { m_slotConnection(pc, true); m_slotUnchoke(-1); } else if (!base->queued()) { return; } base->entry()->connection_unqueued(pc); modify_currently_queued(-1); base->set_queued(false); } void choke_queue::set_not_snubbed(PeerConnectionBase* pc, choke_status* base) { if (!base->snubbed()) return; base->set_snubbed(false); if (!base->queued()) return; if (base->unchoked()) throw internal_error("choke_queue::set_not_snubbed(...) base->unchoked()."); base->entry()->connection_queued(pc); modify_currently_queued(1); if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke() > 0) && should_connection_unchoke(this, pc) && rak::timer(base->time_last_choke()) + rak::timer::from_seconds(10) < cachedTime) { m_slotConnection(pc, false); m_slotUnchoke(1); } } // We are no longer in m_connectionList. void choke_queue::disconnected(PeerConnectionBase* pc, choke_status* base) { if (base->snubbed()) { // Do nothing. } else if (base->unchoked()) { m_slotUnchoke(-1); base->entry()->connection_choked(pc); modify_currently_unchoked(-1); } else if (base->queued()) { base->entry()->connection_unqueued(pc); modify_currently_queued(-1); } base->set_queued(false); } // No need to do any choking as the next choke balancing will take // care of things. void choke_queue::move_connections(choke_queue* src, choke_queue* dest, DownloadMain* download, group_entry* base) { if (src != NULL) { group_container_type::iterator itr = std::find(src->m_group_container.begin(), src->m_group_container.end(), base); if (itr == src->m_group_container.end()) throw internal_error("choke_queue::move_connections(...) could not find group."); std::swap(*itr, src->m_group_container.back()); src->m_group_container.pop_back(); } if (dest != NULL) { dest->m_group_container.push_back(base); } if (src == NULL || dest == NULL) return; src->modify_currently_queued(-base->queued()->size()); src->modify_currently_unchoked(-base->unchoked()->size()); dest->modify_currently_queued(base->queued()->size()); dest->modify_currently_unchoked(base->unchoked()->size()); } // // Heuristics: // void choke_manager_allocate_slots(choke_queue::iterator first, choke_queue::iterator last, uint32_t max, uint32_t* weights, choke_queue::target_type* target) { // Sorting the connections from the lowest to highest value. // TODO: std::sort(first, last, choke_manager_less()); // 'weightTotal' only contains the weight of targets that have // connections to unchoke. When all connections are in a group are // to be unchoked, then the group's weight is removed. uint32_t weightTotal = 0; uint32_t unchoke = max; target[0].second = first; for (uint32_t i = 0; i < choke_queue::order_max_size; i++) { target[i].first = 0; target[i + 1].second = std::find_if(target[i].second, last, rak::less(i * choke_queue::order_base + (choke_queue::order_base - 1), rak::mem_ref(&choke_queue::value_type::weight))); if (std::distance(target[i].second, target[i + 1].second) != 0) weightTotal += weights[i]; } // Spread available unchoke slots as long as we can give everyone an // equal share. while (weightTotal != 0 && unchoke / weightTotal > 0) { uint32_t base = unchoke / weightTotal; for (uint32_t itr = 0; itr < choke_queue::order_max_size; itr++) { uint32_t s = std::distance(target[itr].second, target[itr + 1].second); if (weights[itr] == 0 || target[itr].first >= s) continue; uint32_t u = std::min(s - target[itr].first, base * weights[itr]); unchoke -= u; target[itr].first += u; if (target[itr].first >= s) weightTotal -= weights[itr]; } } // Spread the remainder starting from a random position based on the // total weight. This will ensure that aggregated over time we // spread the unchokes equally according to the weight table. if (weightTotal != 0 && unchoke != 0) { uint32_t start = ::random() % weightTotal; unsigned int itr = 0; for ( ; ; itr++) { uint32_t s = std::distance(target[itr].second, target[itr + 1].second); if (weights[itr] == 0 || target[itr].first >= s) continue; if (start < weights[itr]) break; start -= weights[itr]; } for ( ; weightTotal != 0 && unchoke != 0; itr = (itr + 1) % choke_queue::order_max_size) { uint32_t s = std::distance(target[itr].second, target[itr + 1].second); if (weights[itr] == 0 || target[itr].first >= s) continue; uint32_t u = std::min(unchoke, std::min(s - target[itr].first, weights[itr] - start)); start = 0; unchoke -= u; target[itr].first += u; if (target[itr].first >= s) weightTotal -= weights[itr]; } } } template bool range_is_contained(Itr first, Itr last, Itr lower_bound, Itr upper_bound) { return first >= lower_bound && last <= upper_bound && first <= last; } uint32_t choke_queue::adjust_choke_range(iterator first, iterator last, container_type* src_container, container_type* dest_container, uint32_t max, bool is_choke) { target_type target[order_max_size + 1]; if (is_choke) { // TODO: m_heuristics_list[m_heuristics].slot_choke_weight(first, last); choke_manager_allocate_slots(first, last, max, m_heuristics_list[m_heuristics].choke_weight, target); } else { // m_heuristics_list[m_heuristics].slot_unchoke_weight(first, last); choke_manager_allocate_slots(first, last, max, m_heuristics_list[m_heuristics].unchoke_weight, target); } if (lt_log_is_valid(LOG_INSTRUMENTATION_CHOKE)) { for (uint32_t i = 0; i < choke_queue::order_max_size; i++) lt_log_print(LOG_INSTRUMENTATION_CHOKE, "%p %i %s %u %u %i", this, 0, //lf->last_update(), (const char*)"unchoke" + 2*is_choke, i, target[i].first, std::distance(target[i].second, target[i + 1].second)); } // Now do the actual unchoking. uint32_t count = 0; uint32_t skipped = 0; for (target_type* itr = target + order_max_size; itr != target; itr--) { uint32_t order_size = std::distance((itr - 1)->second, itr->second); uint32_t order_remaining = order_size - (itr - 1)->first; if ((itr - 1)->first > order_size) throw internal_error("choke_queue::adjust_choke_range(...) itr->first > std::distance((itr - 1)->second, itr->second)."); (itr - 1)->first += std::min(skipped, order_remaining); skipped -= std::min(skipped, order_remaining); iterator first_adjust = itr->second - (itr - 1)->first; iterator last_adjust = itr->second; if (!range_is_contained(first_adjust, last_adjust, src_container->begin(), src_container->end())) throw internal_error("choke_queue::adjust_choke_range(...) bad iterator range."); // We start by unchoking the highest priority in this group, and // if we find any peers we can't choke/unchoke we'll move them to // the last spot in the container and decrement 'last_adjust'. iterator itr_adjust = last_adjust; while (itr_adjust != first_adjust) { itr_adjust--; // if (!is_choke && !should_connection_unchoke(this, itr_adjust->first)) { // // Swap with end and continue if not done with group. Count how many? // std::iter_swap(itr_adjust, --last_adjust); // if (first_adjust == (itr - 1)->second) // skipped++; // else // first_adjust--; // continue; // } m_slotConnection(itr_adjust->connection, is_choke); count++; lt_log_print(LOG_INSTRUMENTATION_CHOKE, "%p %i %s %p %X %llu %llu", this, 0, //lf->last_update(), (const char*)"unchoke" + 2*is_choke, itr_adjust->connection, itr_adjust->weight, (long long unsigned int)itr_adjust->connection->up_rate()->rate(), (long long unsigned int)itr_adjust->connection->down_rate()->rate()); } // The 'target' iterators remain valid after erase since we're // removing them in reverse order. dest_container->insert(dest_container->end(), first_adjust, last_adjust); src_container->erase(first_adjust, last_adjust); } if (count > max) throw internal_error("choke_queue::adjust_choke_range(...) count > max."); return count; } // Note that these algorithms fail if the rate >= 2^30. // Need to add the recently unchoked check here? void calculate_upload_choke(choke_queue::iterator first, choke_queue::iterator last) { while (first != last) { // Very crude version for now. // // This needs to give more weight to peers that haven't had time to unchoke us. uint32_t downloadRate = first->connection->peer_chunks()->download_throttle()->rate()->rate() / 16; first->weight = choke_queue::order_base - 1 - downloadRate; first++; } } void calculate_upload_unchoke(choke_queue::iterator first, choke_queue::iterator last) { while (first != last) { if (first->connection->is_down_local_unchoked()) { uint32_t downloadRate = first->connection->peer_chunks()->download_throttle()->rate()->rate(); // If the peer transmits at less than 1KB, we should consider it // to be a rather stingy peer, and should look for new ones. if (downloadRate < 1000) first->weight = downloadRate; else first->weight = 2 * choke_queue::order_base + downloadRate; } else { // This will be our optimistic unchoke queue, should be // semi-random. Give lower weights to known stingy peers. first->weight = 1 * choke_queue::order_base + ::random() % (1 << 10); } first++; } } // Fix this, but for now just use something simple. void calculate_download_choke(choke_queue::iterator first, choke_queue::iterator last) { while (first != last) { // Very crude version for now. uint32_t downloadRate = first->connection->peer_chunks()->download_throttle()->rate()->rate(); first->weight = choke_queue::order_base - 1 - downloadRate; first++; } } void calculate_download_unchoke(choke_queue::iterator first, choke_queue::iterator last) { while (first != last) { // Very crude version for now. uint32_t downloadRate = first->connection->peer_chunks()->download_throttle()->rate()->rate(); first->weight = downloadRate; first++; } } choke_queue::heuristics_type choke_queue::m_heuristics_list[HEURISTICS_MAX_SIZE] = { { &calculate_upload_choke, &calculate_upload_unchoke, { 1, 1, 1, 1 }, { 1, 3, 9, 0 } }, { &calculate_upload_choke, &calculate_upload_unchoke, { 1, 1, 1, 1 }, { 1, 3, 9, 0 } }, { &calculate_download_choke, &calculate_download_unchoke, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } }, { &calculate_download_choke, &calculate_download_unchoke, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } }, }; } libtorrent-0.13.6/src/torrent/download/choke_queue.h000066400000000000000000000162211257211073700225350ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_CHOKE_QUEUE_H #define LIBTORRENT_DOWNLOAD_CHOKE_QUEUE_H #include #include #include #include #include #include namespace torrent { class choke_status; class group_entry; class ConnectionList; class PeerConnectionBase; class DownloadMain; struct group_stats { unsigned int sum_min_needed; unsigned int sum_max_needed; unsigned int sum_max_leftovers; unsigned int changed_choked; unsigned int changed_unchoked; unsigned int now_choked; unsigned int now_unchoked; }; class LIBTORRENT_EXPORT choke_queue { public: typedef std::tr1::function slot_unchoke; typedef std::tr1::function slot_can_unchoke; typedef std::tr1::function slot_connection; typedef std::vector container_type; typedef container_type::value_type value_type; typedef container_type::iterator iterator; typedef std::pair target_type; typedef std::vector group_container_type; typedef void (*slot_weight)(iterator first, iterator last); static const int flag_unchoke_all_new = 0x1; static const uint32_t order_base = (1 << 30); static const uint32_t order_max_size = 4; static const uint32_t weight_size_bytes = order_max_size * sizeof(uint32_t); static const uint32_t unlimited = ~uint32_t(); struct heuristics_type { slot_weight slot_choke_weight; slot_weight slot_unchoke_weight; uint32_t choke_weight[order_max_size]; uint32_t unchoke_weight[order_max_size]; }; enum heuristics_enum { HEURISTICS_UPLOAD_LEECH, HEURISTICS_UPLOAD_LEECH_DUMMY, HEURISTICS_DOWNLOAD_LEECH, HEURISTICS_DOWNLOAD_LEECH_DUMMY, HEURISTICS_MAX_SIZE }; choke_queue(int flags = 0) : m_flags(flags), m_heuristics(HEURISTICS_MAX_SIZE), m_maxUnchoked(unlimited), m_currently_queued(0), m_currently_unchoked(0), m_slotConnection(NULL) {} ~choke_queue(); bool is_full() const { return !is_unlimited() && size_unchoked() >= m_maxUnchoked; } bool is_unlimited() const { return m_maxUnchoked == unlimited; } uint32_t size_unchoked() const { return m_currently_unchoked; } uint32_t size_queued() const { return m_currently_queued; } uint32_t size_total() const { return size_unchoked() + size_queued(); } // This must be unsigned. uint32_t max_unchoked() const { return m_maxUnchoked; } int32_t max_unchoked_signed() const { return m_maxUnchoked; } void set_max_unchoked(uint32_t v) { m_maxUnchoked = v; } void balance(); void balance_entry(group_entry* entry); int cycle(uint32_t quota); // Assume interested state is already updated for the PCB and that // this gets called once every time the status changes. void set_queued(PeerConnectionBase* pc, choke_status* base); void set_not_queued(PeerConnectionBase* pc, choke_status* base); void set_snubbed(PeerConnectionBase* pc, choke_status* base); void set_not_snubbed(PeerConnectionBase* pc, choke_status* base); void disconnected(PeerConnectionBase* pc, choke_status* base); static void move_connections(choke_queue* src, choke_queue* dest, DownloadMain* download, group_entry* base); heuristics_enum heuristics() const { return m_heuristics; } void set_heuristics(heuristics_enum hs) { m_heuristics = hs; } void set_slot_unchoke(slot_unchoke s) { m_slotUnchoke = s; } void set_slot_can_unchoke(slot_can_unchoke s) { m_slotCanUnchoke = s; } void set_slot_connection(slot_connection s) { m_slotConnection = s; } // TODO: Consider putting this in queue_group. group_container_type& group_container() { return m_group_container; } void modify_currently_queued(int value) { m_currently_queued += value; } void modify_currently_unchoked(int value) { m_currently_unchoked += value; } private: group_stats prepare_weights(group_stats gs); group_stats retrieve_connections(group_stats gs, container_type* queued, container_type* unchoked); void rebuild_containers(container_type* queued, container_type* unchoked); inline uint32_t max_alternate() const; uint32_t adjust_choke_range(iterator first, iterator last, container_type* src_container, container_type* dest_container, uint32_t max, bool is_choke); static heuristics_type m_heuristics_list[HEURISTICS_MAX_SIZE]; int m_flags; heuristics_enum m_heuristics; uint32_t m_maxUnchoked; uint32_t m_currently_queued; uint32_t m_currently_unchoked; slot_unchoke m_slotUnchoke; slot_can_unchoke m_slotCanUnchoke; slot_connection m_slotConnection; group_container_type m_group_container; }; } #endif libtorrent-0.13.6/src/torrent/download/download_manager.cc000066400000000000000000000077131257211073700237050ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "torrent/exceptions.h" #include "download/download_wrapper.h" #include "download_manager.h" namespace torrent { DownloadManager::iterator DownloadManager::insert(DownloadWrapper* d) { if (find(d->info()->hash()) != end()) throw internal_error("Could not add torrent as it already exists."); return base_type::insert(end(), d); } DownloadManager::iterator DownloadManager::erase(DownloadWrapper* d) { iterator itr = std::find(begin(), end(), d); if (itr == end()) throw internal_error("Tried to remove a torrent that doesn't exist"); delete *itr; return base_type::erase(itr); } void DownloadManager::clear() { while (!empty()) { delete base_type::back(); base_type::pop_back(); } } DownloadManager::iterator DownloadManager::find(const std::string& hash) { return std::find_if(begin(), end(), rak::equal(*HashString::cast_from(hash), rak::on(std::mem_fun(&DownloadWrapper::info), std::mem_fun(&DownloadInfo::hash)))); } DownloadManager::iterator DownloadManager::find(const HashString& hash) { return std::find_if(begin(), end(), rak::equal(hash, rak::on(std::mem_fun(&DownloadWrapper::info), std::mem_fun(&DownloadInfo::hash)))); } DownloadManager::iterator DownloadManager::find(DownloadInfo* info) { return std::find_if(begin(), end(), rak::equal(info, std::mem_fun(&DownloadWrapper::info))); } DownloadManager::iterator DownloadManager::find_chunk_list(ChunkList* cl) { return std::find_if(begin(), end(), rak::equal(cl, std::mem_fun(&DownloadWrapper::chunk_list))); } DownloadMain* DownloadManager::find_main(const char* hash) { iterator itr = std::find_if(begin(), end(), rak::equal(*HashString::cast_from(hash), rak::on(std::mem_fun(&DownloadWrapper::info), std::mem_fun(&DownloadInfo::hash)))); if (itr == end()) return NULL; else return (*itr)->main(); } DownloadMain* DownloadManager::find_main_obfuscated(const char* hash) { iterator itr = std::find_if(begin(), end(), rak::equal(*HashString::cast_from(hash), rak::on(std::mem_fun(&DownloadWrapper::info), std::mem_fun(&DownloadInfo::hash_obfuscated)))); if (itr == end()) return NULL; else return (*itr)->main(); } } libtorrent-0.13.6/src/torrent/download/download_manager.h000066400000000000000000000062741257211073700235500ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_MANAGER_H #define LIBTORRENT_DOWNLOAD_MANAGER_H #include #include namespace torrent { class ChunkList; class DownloadWrapper; class DownloadInfo; class DownloadMain; class LIBTORRENT_EXPORT DownloadManager : private std::vector { public: typedef std::vector base_type; typedef base_type::value_type value_type; typedef base_type::pointer pointer; typedef base_type::const_pointer const_pointer; typedef base_type::reference reference; typedef base_type::const_reference const_reference; typedef base_type::size_type size_type; typedef base_type::iterator iterator; typedef base_type::reverse_iterator reverse_iterator; typedef base_type::const_iterator const_iterator; typedef base_type::const_reverse_iterator const_reverse_iterator; using base_type::empty; using base_type::size; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; ~DownloadManager() { clear(); } iterator find(const std::string& hash); iterator find(const HashString& hash); iterator find(DownloadInfo* info); iterator find_chunk_list(ChunkList* cl); DownloadMain* find_main(const char* hash); DownloadMain* find_main_obfuscated(const char* hash); // // Don't export: // iterator insert(DownloadWrapper* d) LIBTORRENT_NO_EXPORT; iterator erase(DownloadWrapper* d) LIBTORRENT_NO_EXPORT; void clear() LIBTORRENT_NO_EXPORT; }; } #endif libtorrent-0.13.6/src/torrent/download/group_entry.h000066400000000000000000000130541257211073700226160ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_GROUP_ENTRY_H #define LIBTORRENT_DOWNLOAD_GROUP_ENTRY_H #include #include #include #include #include namespace torrent { class choke_queue; class PeerConnectionBase; struct weighted_connection { weighted_connection(PeerConnectionBase* pcb, uint32_t w) : connection(pcb), weight(w) {} bool operator == (const PeerConnectionBase* pcb) { return pcb == connection; } bool operator != (const PeerConnectionBase* pcb) { return pcb != connection; } PeerConnectionBase* connection; uint32_t weight; }; // TODO: Rename to choke_entry, create an new class called group entry? class group_entry { public: typedef std::vector container_type; static const uint32_t unlimited = ~uint32_t(); group_entry() : m_max_slots(unlimited), m_min_slots(0) {} uint32_t size_connections() const { return m_queued.size() + m_unchoked.size(); } uint32_t max_slots() const { return m_max_slots; } uint32_t min_slots() const { return m_min_slots; } void set_max_slots(uint32_t s) { m_max_slots = s; } void set_min_slots(uint32_t s) { m_min_slots = s; } const container_type* queued() { return &m_queued; } const container_type* unchoked() { return &m_unchoked; } protected: friend class choke_queue; friend class PeerConnectionBase; container_type* mutable_queued() { return &m_queued; } container_type* mutable_unchoked() { return &m_unchoked; } void connection_unchoked(PeerConnectionBase* pcb); void connection_choked(PeerConnectionBase* pcb); void connection_queued(PeerConnectionBase* pcb); void connection_unqueued(PeerConnectionBase* pcb); private: uint32_t m_max_slots; uint32_t m_min_slots; // After a cycle the end of the vector should have the // highest-priority connections, and any new connections get put at // the back so they should always good candidates for unchoking. container_type m_queued; container_type m_unchoked; }; inline void group_entry::connection_unchoked(PeerConnectionBase* pcb) { container_type::iterator itr = std::find_if(m_unchoked.begin(), m_unchoked.end(), std::tr1::bind(&weighted_connection::operator==, std::tr1::placeholders::_1, pcb)); if (itr != m_unchoked.end()) throw internal_error("group_entry::connection_unchoked(pcb) failed."); m_unchoked.push_back(weighted_connection(pcb, uint32_t())); } inline void group_entry::connection_queued(PeerConnectionBase* pcb) { container_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(), std::tr1::bind(&weighted_connection::operator==, std::tr1::placeholders::_1, pcb)); if (itr != m_queued.end()) throw internal_error("group_entry::connection_queued(pcb) failed."); m_queued.push_back(weighted_connection(pcb, uint32_t())); } inline void group_entry::connection_choked(PeerConnectionBase* pcb) { container_type::iterator itr = std::find_if(m_unchoked.begin(), m_unchoked.end(), std::tr1::bind(&weighted_connection::operator==, std::tr1::placeholders::_1, pcb)); if (itr == m_unchoked.end()) throw internal_error("group_entry::connection_choked(pcb) failed."); std::swap(*itr, m_unchoked.back()); m_unchoked.pop_back(); } inline void group_entry::connection_unqueued(PeerConnectionBase* pcb) { container_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(), std::tr1::bind(&weighted_connection::operator==, std::tr1::placeholders::_1, pcb)); if (itr == m_queued.end()) throw internal_error("group_entry::connection_unqueued(pcb) failed."); std::swap(*itr, m_queued.back()); m_queued.pop_back(); } } #endif libtorrent-0.13.6/src/torrent/download/resource_manager.cc000066400000000000000000000246371257211073700237310ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include "torrent/exceptions.h" #include "torrent/download/choke_group.h" #include "torrent/utils/log.h" #include "download/download_main.h" #include "protocol/peer_connection_base.h" #include "choke_queue.h" #include "resource_manager.h" namespace tr1 { using namespace std::tr1; } namespace torrent { const Rate* resource_manager_entry::up_rate() const { return m_download->info()->up_rate(); } const Rate* resource_manager_entry::down_rate() const { return m_download->info()->down_rate(); } ResourceManager::ResourceManager() : m_currentlyUploadUnchoked(0), m_currentlyDownloadUnchoked(0), m_maxUploadUnchoked(0), m_maxDownloadUnchoked(0) { choke_base_type::push_back(new choke_group()); choke_base_type::back()->set_name("default"); choke_base_type::back()->set_first(&*base_type::end()); choke_base_type::back()->set_last(&*base_type::end()); choke_base_type::back()->up_queue()->set_heuristics(choke_queue::HEURISTICS_UPLOAD_LEECH); choke_base_type::back()->down_queue()->set_heuristics(choke_queue::HEURISTICS_DOWNLOAD_LEECH); choke_base_type::back()->up_queue()->set_slot_unchoke(tr1::bind(&ResourceManager::receive_upload_unchoke, this, tr1::placeholders::_1)); choke_base_type::back()->down_queue()->set_slot_unchoke(tr1::bind(&ResourceManager::receive_download_unchoke, this, tr1::placeholders::_1)); choke_base_type::back()->up_queue()->set_slot_can_unchoke(tr1::bind(&ResourceManager::retrieve_upload_can_unchoke, this)); choke_base_type::back()->down_queue()->set_slot_can_unchoke(tr1::bind(&ResourceManager::retrieve_download_can_unchoke, this)); choke_base_type::back()->up_queue()->set_slot_connection(tr1::bind(&PeerConnectionBase::receive_upload_choke, tr1::placeholders::_1, tr1::placeholders::_2)); choke_base_type::back()->down_queue()->set_slot_connection(tr1::bind(&PeerConnectionBase::receive_download_choke, tr1::placeholders::_1, tr1::placeholders::_2)); } ResourceManager::~ResourceManager() { if (m_currentlyUploadUnchoked != 0) throw internal_error("ResourceManager::~ResourceManager() called but m_currentlyUploadUnchoked != 0."); if (m_currentlyDownloadUnchoked != 0) throw internal_error("ResourceManager::~ResourceManager() called but m_currentlyDownloadUnchoked != 0."); std::for_each(choke_base_type::begin(), choke_base_type::end(), rak::call_delete()); } // If called directly ensure a valid group has been selected. ResourceManager::iterator ResourceManager::insert(const resource_manager_entry& entry) { iterator itr = base_type::insert(base_type::end(), entry); DownloadMain* download = itr->download(); download->set_choke_group(choke_base_type::at(entry.group())); update_group_iterators(); choke_queue::move_connections(NULL, download->choke_group()->up_queue(), download, download->up_group_entry()); choke_queue::move_connections(NULL, download->choke_group()->down_queue(), download, download->down_group_entry()); return itr; } void ResourceManager::update_group_iterators() { base_type::iterator entry_itr = base_type::begin(); choke_base_type::iterator group_itr = choke_base_type::begin(); while (group_itr != choke_base_type::end()) { (*group_itr)->set_first(&*entry_itr); entry_itr = std::find_if(entry_itr, end(), rak::less(std::distance(choke_base_type::begin(), group_itr), std::mem_fun_ref(&value_type::group))); (*group_itr)->set_last(&*entry_itr); group_itr++; } } void ResourceManager::validate_group_iterators() { base_type::iterator entry_itr = base_type::begin(); choke_base_type::iterator group_itr = choke_base_type::begin(); while (group_itr != choke_base_type::end()) { if ((*group_itr)->first() != &*entry_itr) throw internal_error("ResourceManager::receive_tick() invalid first iterator."); entry_itr = std::find_if(entry_itr, end(), rak::less(std::distance(choke_base_type::begin(), group_itr), std::mem_fun_ref(&value_type::group))); if ((*group_itr)->last() != &*entry_itr) throw internal_error("ResourceManager::receive_tick() invalid last iterator."); group_itr++; } } void ResourceManager::erase(DownloadMain* d) { iterator itr = std::find_if(begin(), end(), rak::equal(d, std::mem_fun_ref(&value_type::download))); if (itr == end()) throw internal_error("ResourceManager::erase() itr == end()."); choke_queue::move_connections(d->choke_group()->up_queue(), NULL, d, d->up_group_entry()); choke_queue::move_connections(d->choke_group()->down_queue(), NULL, d, d->down_group_entry()); choke_base_type::iterator group_itr = choke_base_type::begin() + itr->group(); (*group_itr)->set_last((*group_itr)->last() - 1); base_type::erase(itr); } ResourceManager::iterator ResourceManager::find(DownloadMain* d) { return std::find_if(begin(), end(), rak::equal(d, std::mem_fun_ref(&value_type::download))); } ResourceManager::iterator ResourceManager::find_throw(DownloadMain* d) { iterator itr = std::find_if(begin(), end(), rak::equal(d, std::mem_fun_ref(&value_type::download))); if (itr == end()) throw input_error("Could not find download in resource manager."); return itr; } void ResourceManager::set_priority(iterator itr, uint16_t pri) { itr->set_priority(pri); } void ResourceManager::set_group(iterator itr, uint16_t grp) { if (itr->group() == grp) return; // if (grp >= choke_base_type::size()) throw input_error("Choke group not found."); } void ResourceManager::set_max_upload_unchoked(unsigned int m) { if (m > (1 << 16)) throw input_error("Max unchoked must be between 0 and 2^16."); m_maxUploadUnchoked = m; } void ResourceManager::set_max_download_unchoked(unsigned int m) { if (m > (1 << 16)) throw input_error("Max unchoked must be between 0 and 2^16."); m_maxDownloadUnchoked = m; } // The choking choke manager won't updated it's count until after // possibly multiple calls of this function. void ResourceManager::receive_upload_unchoke(int num) { lt_log_print(LOG_PEER_INFO, "Upload unchoked slots adjust; currently:%u adjust:%i", m_currentlyUploadUnchoked, num); if ((int)m_currentlyUploadUnchoked + num < 0) throw internal_error("ResourceManager::receive_upload_unchoke(...) received an invalid value."); m_currentlyUploadUnchoked += num; } void ResourceManager::receive_download_unchoke(int num) { lt_log_print(LOG_PEER_INFO, "Download unchoked slots adjust; currently:%u adjust:%i", m_currentlyDownloadUnchoked, num); if ((int)m_currentlyDownloadUnchoked + num < 0) throw internal_error("ResourceManager::receive_download_unchoke(...) received an invalid value."); m_currentlyDownloadUnchoked += num; } int ResourceManager::retrieve_upload_can_unchoke() { if (m_maxUploadUnchoked == 0) return std::numeric_limits::max(); return (int)m_maxUploadUnchoked - (int)m_currentlyUploadUnchoked; } int ResourceManager::retrieve_download_can_unchoke() { if (m_maxDownloadUnchoked == 0) return std::numeric_limits::max(); return (int)m_maxDownloadUnchoked - (int)m_currentlyDownloadUnchoked; } void ResourceManager::receive_tick() { validate_group_iterators(); m_currentlyUploadUnchoked += balance_unchoked(choke_base_type::size(), m_maxUploadUnchoked, true); m_currentlyDownloadUnchoked += balance_unchoked(choke_base_type::size(), m_maxDownloadUnchoked, false); if (m_currentlyUploadUnchoked != choke_base_type::back()->up_queue()->size_unchoked()) throw torrent::internal_error("m_currentlyUploadUnchoked != choke_base_type::back()->up_queue()->size_unchoked()"); if (m_currentlyDownloadUnchoked != choke_base_type::back()->down_queue()->size_unchoked()) throw torrent::internal_error("m_currentlyDownloadUnchoked != choke_base_type::back()->down_queue()->size_unchoked()"); } unsigned int ResourceManager::total_weight() const { // TODO: This doesn't take into account inactive downloads. return std::for_each(begin(), end(), rak::accumulate((unsigned int)0, std::mem_fun_ref(&value_type::priority))).result; } int ResourceManager::balance_unchoked(unsigned int weight, unsigned int max_unchoked, bool is_up) { int change = 0; if (max_unchoked == 0) max_unchoked = std::numeric_limits::max(); if (is_up) { change += choke_base_type::back()->up_queue()->cycle(max_unchoked); lt_log_print(LOG_PEER_DEBUG, "Upload unchoked slots cycle; currently:%u adjusted:%i max_unchoked:%u", m_currentlyUploadUnchoked, change, max_unchoked); } else { change += choke_base_type::back()->down_queue()->cycle(max_unchoked); lt_log_print(LOG_PEER_DEBUG, "Download unchoked slots cycle; currently:%u adjusted:%i max_unchoked:%u", m_currentlyDownloadUnchoked, change, max_unchoked); } return change; } } libtorrent-0.13.6/src/torrent/download/resource_manager.h000066400000000000000000000127421257211073700235650ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_RESOURCE_MANAGER_H #define LIBTORRENT_PEER_RESOURCE_MANAGER_H #include #include #include #include namespace torrent { // This class will handle the division of various resources like // uploads. For now the weight is equal to the value of the priority. // // Although the ConnectionManager class keeps a tally of open sockets, // we still need to balance them across the different downloads so // ResourceManager will also keep track of those. // // Add unlimited handling later. class choke_group; class DownloadMain; class Rate; class ResourceManager; class LIBTORRENT_EXPORT resource_manager_entry { public: friend class ResourceManager; resource_manager_entry(DownloadMain* d = NULL, uint16_t pri = 0, uint16_t grp = 0) : m_download(d), m_priority(pri), m_group(grp) {} DownloadMain* download() { return m_download; } const DownloadMain* c_download() const { return m_download; } uint16_t priority() const { return m_priority; } uint16_t group() const { return m_group; } const Rate* up_rate() const; const Rate* down_rate() const; protected: void set_priority(uint16_t pri) { m_priority = pri; } void set_group(uint16_t grp) { m_group = grp; } private: DownloadMain* m_download; uint16_t m_priority; uint16_t m_group; }; class LIBTORRENT_EXPORT ResourceManager : private std::vector, private std::vector { public: typedef std::vector base_type; typedef std::vector choke_base_type; typedef base_type::value_type value_type; typedef base_type::iterator iterator; using base_type::begin; using base_type::end; using base_type::size; using base_type::capacity; ResourceManager(); ~ResourceManager(); void insert(DownloadMain* d, uint16_t priority) { insert(value_type(d, priority)); } void erase(DownloadMain* d); iterator find(DownloadMain* d); iterator find_throw(DownloadMain* d); iterator find_group_end(uint16_t group); choke_group* group_at(uint16_t grp); resource_manager_entry& entry_at(DownloadMain* d) { return *find_throw(d); } void set_priority(iterator itr, uint16_t pri); void set_group(iterator itr, uint16_t grp); // When setting this, make sure you choke peers, else change // receive_can_unchoke. unsigned int currently_upload_unchoked() const { return m_currentlyUploadUnchoked; } unsigned int currently_download_unchoked() const { return m_currentlyDownloadUnchoked; } unsigned int max_upload_unchoked() const { return m_maxUploadUnchoked; } unsigned int max_download_unchoked() const { return m_maxDownloadUnchoked; } void set_max_upload_unchoked(unsigned int m); void set_max_download_unchoked(unsigned int m); void receive_upload_unchoke(int num); void receive_download_unchoke(int num); int retrieve_upload_can_unchoke(); int retrieve_download_can_unchoke(); void receive_tick(); private: iterator insert(const resource_manager_entry& entry); void update_group_iterators(); void validate_group_iterators(); unsigned int total_weight() const; int balance_unchoked(unsigned int weight, unsigned int max_unchoked, bool is_up); unsigned int m_currentlyUploadUnchoked; unsigned int m_currentlyDownloadUnchoked; unsigned int m_maxUploadUnchoked; unsigned int m_maxDownloadUnchoked; }; } #endif libtorrent-0.13.6/src/torrent/download_info.h000066400000000000000000000231171257211073700212550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_INFO_H #define LIBTORRENT_DOWNLOAD_INFO_H #include #include #include #include #include #include namespace torrent { class FileList; class DownloadMain; // This will become a Download 'handle' of kinds. class DownloadInfo { public: typedef std::tr1::function slot_stat_type; typedef std::list > signal_void_type; typedef std::list > signal_string_type; enum State { NONE, COMPLETED, STARTED, STOPPED }; static const int flag_open = (1 << 0); static const int flag_active = (1 << 1); static const int flag_compact = (1 << 2); static const int flag_accepting_new_peers = (1 << 3); static const int flag_accepting_seeders = (1 << 4); // Only used during leeching. static const int flag_private = (1 << 5); static const int flag_meta_download = (1 << 6); static const int flag_pex_enabled = (1 << 7); static const int flag_pex_active = (1 << 8); static const int public_flags = flag_accepting_seeders; static const uint32_t unlimited = ~uint32_t(); DownloadInfo(); const std::string& name() const { return m_name; } void set_name(const std::string& s) { m_name = s; } const HashString& hash() const { return m_hash; } HashString& mutable_hash() { return m_hash; } const HashString& hash_obfuscated() const { return m_hashObfuscated; } HashString& mutable_hash_obfuscated() { return m_hashObfuscated; } const HashString& local_id() const { return m_localId; } HashString& mutable_local_id() { return m_localId; } bool is_open() const { return m_flags & flag_open; } bool is_active() const { return m_flags & flag_active; } bool is_compact() const { return m_flags & flag_compact; } bool is_accepting_new_peers() const { return m_flags & flag_accepting_new_peers; } bool is_accepting_seeders() const { return m_flags & flag_accepting_seeders; } bool is_private() const { return m_flags & flag_private; } bool is_meta_download() const { return m_flags & flag_meta_download; } bool is_pex_enabled() const { return m_flags & flag_pex_enabled; } bool is_pex_active() const { return m_flags & flag_pex_active; } int flags() const { return m_flags; } void set_flags(int flags) { m_flags |= flags; } void unset_flags(int flags) { m_flags &= ~flags; } void change_flags(int flags, bool state) { if (state) set_flags(flags); else unset_flags(flags); } void public_set_flags(int flags) const { m_flags |= (flags & public_flags); } void public_unset_flags(int flags) const { m_flags &= ~(flags & public_flags); } void public_change_flags(int flags, bool state) const { if (state) public_set_flags(flags); else public_unset_flags(flags); } void set_private() { set_flags(flag_private); unset_flags(flag_pex_enabled); } void set_pex_enabled() { if (!is_private()) set_flags(flag_pex_enabled); } const Rate* up_rate() const { return &m_upRate; } const Rate* down_rate() const { return &m_downRate; } const Rate* skip_rate() const { return &m_skipRate; } Rate* mutable_up_rate() const { return &m_upRate; } Rate* mutable_down_rate() const { return &m_downRate; } Rate* mutable_skip_rate() const { return &m_skipRate; } uint64_t uploaded_baseline() const { return m_uploadedBaseline; } uint64_t uploaded_adjusted() const { return std::max(m_upRate.total() - uploaded_baseline(), 0); } void set_uploaded_baseline(uint64_t b) { m_uploadedBaseline = b; } uint64_t completed_baseline() const { return m_completedBaseline; } uint64_t completed_adjusted() const { return std::max(m_slotStatCompleted() - completed_baseline(), 0); } void set_completed_baseline(uint64_t b) { m_completedBaseline = b; } size_t metadata_size() const { return m_metadataSize; } void set_metadata_size(size_t size) { m_metadataSize = size; } uint32_t size_pex() const { return m_sizePex; } void set_size_pex(uint32_t b) { m_sizePex = b; } uint32_t max_size_pex() const { return m_maxSizePex; } void set_max_size_pex(uint32_t b) { m_maxSizePex = b; } uint32_t max_size_pex_list() const { return 200; } // Unix epoche, 0 == unknown. uint32_t creation_date() const { return m_creationDate; } uint32_t load_date() const { return m_loadDate; } uint32_t http_timeout() const { return 60; } uint32_t udp_timeout() const { return 30; } uint32_t udp_tries() const { return 2; } uint32_t upload_unchoked() const { return m_upload_unchoked; } uint32_t download_unchoked() const { return m_download_unchoked; } // The list of addresses is guaranteed to be sorted and unique. signal_void_type& signal_tracker_success() const { return m_signalTrackerSuccess; } signal_string_type& signal_tracker_failed() const { return m_signalTrackerFailed; } // // Libtorrent internal: // void set_creation_date(uint32_t d) { m_creationDate = d; } void set_upload_unchoked(uint32_t num) { m_upload_unchoked = num; } void set_download_unchoked(uint32_t num) { m_download_unchoked = num; } slot_stat_type& slot_left() { return m_slotStatLeft; } slot_stat_type& slot_completed() { return m_slotStatCompleted; } private: std::string m_name; HashString m_hash; HashString m_hashObfuscated; HashString m_localId; mutable int m_flags; mutable Rate m_upRate; mutable Rate m_downRate; mutable Rate m_skipRate; uint64_t m_uploadedBaseline; uint64_t m_completedBaseline; uint32_t m_sizePex; uint32_t m_maxSizePex; size_t m_metadataSize; uint32_t m_creationDate; uint32_t m_loadDate; uint32_t m_upload_unchoked; uint32_t m_download_unchoked; slot_stat_type m_slotStatLeft; slot_stat_type m_slotStatCompleted; mutable signal_void_type m_signalTrackerSuccess; mutable signal_string_type m_signalTrackerFailed; }; } #endif libtorrent-0.13.6/src/torrent/error.cc000066400000000000000000000062131257211073700177200ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "error.h" namespace torrent { static const char* errorStrings[e_last + 1] = { "unknown error", // e_none "not BitTorrent protocol", // eh_not_bittorrent "not accepting connections", // eh_not_accepting_connections "duplicate peer ID", // eh_duplicate "unknown download", // eh_unknown_download "download inactive", // eh_inactive_download "seeder rejected", // eh_unwanted_connection "is self", // eh_is_self "invalid value received", // eh_invalid_value "unencrypted connection rejected", // eh_unencrypted_rejected "invalid encryption method", // eh_invalid_encryption "encryption sync failed", // eh_encryption_sync_failed "network error", // eh_network_error "network unreachable", // eh_network_unreachable "network timeout", // eh_network_timeout "invalid message order", // eh_invalid_order "too many failed chunks", // eh_toomanyfailed // "", // e_handshake_incoming // "", // e_handshake_outgoing // "", // e_handshake_outgoing_encrypted // "", // e_handshake_outgoing_proxy // "", // e_handshake_success // "", // e_handshake_retry_plaintext // "" // e_handshake_retry_encrypted }; const char* strerror(int err) { if (err < 0 || err > e_last) return "Unknown error"; return errorStrings[err]; } } libtorrent-0.13.6/src/torrent/error.h000066400000000000000000000062541257211073700175670ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_ERROR_H #define LIBTORRENT_TORRENT_ERROR_H #include namespace torrent { const int e_none = 0; // Handshake errors passed to signal handler const int e_handshake_not_bittorrent = 1; const int e_handshake_not_accepting_connections = 2; const int e_handshake_duplicate = 3; const int e_handshake_unknown_download = 4; const int e_handshake_inactive_download = 5; const int e_handshake_unwanted_connection = 6; const int e_handshake_is_self = 7; const int e_handshake_invalid_value = 8; const int e_handshake_unencrypted_rejected = 9; const int e_handshake_invalid_encryption = 10; const int e_handshake_encryption_sync_failed = 11; const int e_handshake_network_error = 12; const int e_handshake_network_unreachable = 13; const int e_handshake_network_timeout = 14; const int e_handshake_invalid_order = 15; const int e_handshake_toomanyfailed = 16; // const int e_handshake_incoming = 13; // const int e_handshake_outgoing = 14; // const int e_handshake_outgoing_encrypted = 15; // const int e_handshake_outgoing_proxy = 16; // const int e_handshake_success = 17; // const int e_handshake_retry_plaintext = 18; // const int e_handshake_retry_encrypted = 19; const int e_last = 16; const char* strerror(int err) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/event.h000066400000000000000000000044621257211073700175560ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_EVENT_H #define LIBTORRENT_TORRENT_EVENT_H #include namespace torrent { class LIBTORRENT_EXPORT Event { public: virtual ~Event() {} // These are not virtual as the fd is heavily used in select based // polling, thus fast access is critical to performance. int file_descriptor() const { return m_fileDesc; } virtual void event_read() = 0; virtual void event_write() = 0; virtual void event_error() = 0; // Require all event types to define this function. virtual const char* type_name() const { return "default"; } // Event closed? protected: int m_fileDesc; }; } #endif libtorrent-0.13.6/src/torrent/exceptions.cc000066400000000000000000000056541257211073700207600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #ifdef USE_EXECINFO #include #endif #include "exceptions.h" namespace torrent { // Use actual functions, instead of inlined, for the ctor of // exceptions. This allows us to create breakpoints at throws. This is // limited to rarely thrown exceptions. void communication_error::initialize(const std::string& msg) { m_msg = msg; } void storage_error::initialize(const std::string& msg) { m_msg = msg; } void resource_error::initialize(const std::string& msg) { m_msg = msg; } void input_error::initialize(const std::string& msg) { m_msg = msg; } const char* connection_error::what() const throw() { return std::strerror(m_errno); } const char* address_info_error::what() const throw() { return ::gai_strerror(m_errno); } void internal_error::initialize(const std::string& msg) { m_msg = msg; std::stringstream output; #ifdef USE_EXECINFO void* stackPtrs[20]; // Print the stack and exit. int stackSize = ::backtrace(stackPtrs, 20); char** stackStrings = backtrace_symbols(stackPtrs, stackSize); for (int i = 0; i < stackSize; ++i) output << stackStrings[i] << std::endl; #else output << "Stack dump not enabled." << std::endl; #endif m_backtrace = output.str(); } } libtorrent-0.13.6/src/torrent/exceptions.h000066400000000000000000000137771257211073700206270ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Note that some of the exception classes below are strictly speaking // _NOT DOING THE RIGHT THING_. One is really not supposed to use any // calls to malloc/new in an exception's ctor. // // Will be fixed next API update. #ifndef LIBTORRENT_EXCEPTIONS_H #define LIBTORRENT_EXCEPTIONS_H #include #include #include namespace torrent { // All exceptions inherit from std::exception to make catching // everything libtorrent related at the root easier. class LIBTORRENT_EXPORT base_error : public std::exception { public: virtual ~base_error() throw() {} }; // The library or application did some borking it shouldn't have, bug // tracking time! class LIBTORRENT_EXPORT internal_error : public base_error { public: internal_error(const char* msg) { initialize(msg); } internal_error(const std::string& msg) { initialize(msg); } virtual ~internal_error() throw() {} virtual const char* what() const throw() { return m_msg.c_str(); } const std::string& backtrace() const throw() { return m_backtrace; } private: // Use this function for breaking on throws. void initialize(const std::string& msg); std::string m_msg; std::string m_backtrace; }; // For some reason we couldn't talk with a protocol/tracker, migth be a // library bug, connection problem or bad input. class LIBTORRENT_EXPORT network_error : public base_error { public: virtual ~network_error() throw() {} }; class LIBTORRENT_EXPORT communication_error : public network_error { public: communication_error(const char* msg) { initialize(msg); } communication_error(const std::string& msg) { initialize(msg); } virtual ~communication_error() throw() {} virtual const char* what() const throw() { return m_msg.c_str(); } private: // Use this function for breaking on throws. void initialize(const std::string& msg); std::string m_msg; }; class LIBTORRENT_EXPORT connection_error : public network_error { public: connection_error(int err) : m_errno(err) {} virtual ~connection_error() throw() {} virtual const char* what() const throw(); int get_errno() const { return m_errno; } private: int m_errno; }; class LIBTORRENT_EXPORT address_info_error : public network_error { public: address_info_error(int err) : m_errno(err) {} virtual ~address_info_error() throw() {} virtual const char* what() const throw(); int get_errno() const { return m_errno; } private: int m_errno; }; class LIBTORRENT_EXPORT close_connection : public network_error { public: virtual ~close_connection() throw() {} }; class LIBTORRENT_EXPORT blocked_connection : public network_error { public: virtual ~blocked_connection() throw() {} }; // Stuff like bad torrent file, disk space and permissions. class LIBTORRENT_EXPORT local_error : public base_error { public: virtual ~local_error() throw() {} }; class LIBTORRENT_EXPORT storage_error : public local_error { public: storage_error(const char* msg) { initialize(msg); } storage_error(const std::string& msg) { initialize(msg); } virtual ~storage_error() throw() {} virtual const char* what() const throw() { return m_msg.c_str(); } private: // Use this function for breaking on throws. void initialize(const std::string& msg); std::string m_msg; }; class LIBTORRENT_EXPORT resource_error : public local_error { public: resource_error(const char* msg) { initialize(msg); } resource_error(const std::string& msg) { initialize(msg); } virtual ~resource_error() throw() {} virtual const char* what() const throw() { return m_msg.c_str(); } private: // Use this function for breaking on throws. void initialize(const std::string& msg); std::string m_msg; }; class LIBTORRENT_EXPORT input_error : public local_error { public: input_error(const char* msg) { initialize(msg); } input_error(const std::string& msg) { initialize(msg); } virtual ~input_error() throw() {} virtual const char* what() const throw() { return m_msg.c_str(); } private: // Use this function for breaking on throws. void initialize(const std::string& msg); std::string m_msg; }; class LIBTORRENT_EXPORT bencode_error : public input_error { public: bencode_error(const char* msg) : input_error(msg) {} bencode_error(const std::string& msg) : input_error(msg) {} virtual ~bencode_error() throw() {} }; class LIBTORRENT_EXPORT shutdown_exception : public base_error { public: virtual ~shutdown_exception() throw() {} }; } #endif libtorrent-0.13.6/src/torrent/hash_string.cc000066400000000000000000000047201257211073700211010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "hash_string.h" namespace torrent { const char* hash_string_from_hex_c_str(const char* first, HashString& hash) { const char* hash_first = first; torrent::HashString::iterator itr = hash.begin(); while (itr != hash.end()) { if (!std::isxdigit(*first) || !std::isxdigit(*(first + 1))) return hash_first; *itr++ = (rak::hexchar_to_value(*first) << 4) + rak::hexchar_to_value(*(first + 1)); first += 2; } return first; } char* hash_string_to_hex(const HashString& hash, char* first) { return rak::transform_hex(hash.begin(), hash.end(), first); } std::string hash_string_to_hex_str(const HashString& hash) { std::string str(HashString::size_data * 2, '\0'); rak::transform_hex(hash.begin(), hash.end(), str.begin()); return str; } } libtorrent-0.13.6/src/torrent/hash_string.h000066400000000000000000000131461257211073700207450ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // A fixed with char array used to store 20 byte with hashes. This // should really be replaced with std::array<20>. #ifndef LIBTORRENT_HASH_STRING_H #define LIBTORRENT_HASH_STRING_H #include #include #include #include namespace torrent { class LIBTORRENT_EXPORT HashString { public: typedef char value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; static const size_type size_data = 20; size_type size() const { return size_data; } iterator begin() { return m_data; } const_iterator begin() const { return m_data; } iterator end() { return m_data + size(); } const_iterator end() const { return m_data + size(); } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } reference operator [] (size_type n) { return *(m_data + n); } const_reference operator [] (size_type n) const { return *(m_data + n); } value_type* data() { return m_data; } const value_type* data() const { return m_data; } const value_type* c_str() const { return m_data; } std::string str() const { return std::string(m_data, size_data); } void clear(int v = 0) { std::memset(data(), v, size()); } void assign(const value_type* src) { std::memcpy(data(), src, size()); } bool equal_to(const char* hash) const { return std::memcmp(m_data, hash, size()) == 0; } bool not_equal_to(const char* hash) const { return std::memcmp(m_data, hash, size()) != 0; } // It is the users responsibility to ensure src.length() >= // size_data. static const HashString* cast_from(const char* src) { return (const HashString*)src; } static const HashString* cast_from(const std::string& src) { return (const HashString*)src.c_str(); } static HashString* cast_from(char* src) { return (HashString*)src; } private: char m_data[size_data]; }; const char* hash_string_from_hex_c_str(const char* first, HashString& hash) LIBTORRENT_EXPORT; char* hash_string_to_hex(const HashString& hash, char* first) LIBTORRENT_EXPORT; std::string hash_string_to_hex_str(const HashString& hash) LIBTORRENT_EXPORT; inline const char* hash_string_to_hex_first(const HashString& hash, char* first) { hash_string_to_hex(hash, first); return first; } inline bool operator == (const HashString& one, const HashString& two) { return std::memcmp(one.begin(), two.begin(), HashString::size_data) == 0; } inline bool operator != (const HashString& one, const HashString& two) { return std::memcmp(one.begin(), two.begin(), HashString::size_data) != 0; } inline bool operator < (const HashString& one, const HashString& two) { return std::memcmp(one.begin(), two.begin(), HashString::size_data) < 0; } inline bool operator <= (const HashString& one, const HashString& two) { return std::memcmp(one.begin(), two.begin(), HashString::size_data) <= 0; } } #endif libtorrent-0.13.6/src/torrent/http.cc000066400000000000000000000053431257211073700175510ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "rak/functional.h" #include "torrent/exceptions.h" #include "torrent/http.h" #include "torrent/utils/log.h" namespace torrent { Http::slot_http Http::m_factory; Http::~Http() { } void Http::trigger_done() { if (signal_done().empty()) lt_log_print(LOG_TRACKER_INFO, "Disowned tracker done: url:'%s'.", m_url.c_str()); bool should_delete_self = (m_flags & flag_delete_self); bool should_delete_stream = (m_flags & flag_delete_stream); rak::slot_list_call(signal_done()); if (should_delete_stream) { delete m_stream; m_stream = NULL; } if (should_delete_self) delete this; } void Http::trigger_failed(const std::string& message) { if (signal_done().empty()) lt_log_print(LOG_TRACKER_INFO, "Disowned tracker failed: url:'%s'.", m_url.c_str()); bool should_delete_self = (m_flags & flag_delete_self); bool should_delete_stream = (m_flags & flag_delete_stream); rak::slot_list_call(signal_failed(), message); if (should_delete_stream) { delete m_stream; m_stream = NULL; } if (should_delete_self) delete this; } } libtorrent-0.13.6/src/torrent/http.h000066400000000000000000000106301257211073700174060ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_HTTP_H #define LIBTORRENT_HTTP_H #include #include #include #include #include namespace torrent { // The client should set the user agent to something like // "CLIENT_NAME/CLIENT_VERSION/LIBRARY_VERSION". // Keep in mind that these objects get reused. class LIBTORRENT_EXPORT Http { public: typedef std::tr1::function slot_void; typedef std::tr1::function slot_string; typedef std::tr1::function slot_http; typedef std::list signal_void; typedef std::list signal_string; static const int flag_delete_self = 0x1; static const int flag_delete_stream = 0x2; Http() : m_flags(0), m_stream(NULL), m_timeout(0) {} virtual ~Http(); // Start must never throw on bad input. Calling start/stop on an // object in the wrong state should throw a torrent::internal_error. virtual void start() = 0; virtual void close() = 0; int flags() const { return m_flags; } void set_delete_self() { m_flags |= flag_delete_self; } void set_delete_stream() { m_flags |= flag_delete_stream; } const std::string& url() const { return m_url; } void set_url(const std::string& url) { m_url = url; } // Make sure the output stream does not have any bad/failed bits set. std::iostream* stream() { return m_stream; } void set_stream(std::iostream* str) { m_stream = str; } uint32_t timeout() const { return m_timeout; } void set_timeout(uint32_t seconds) { m_timeout = seconds; } // The owner of the Http object must close it as soon as possible // after receiving the signal, as the implementation may allocate // limited resources during its lifetime. signal_void& signal_done() { return m_signal_done; } signal_string& signal_failed() { return m_signal_failed; } // Guaranteed to return a valid object or throw a internal_error. The // caller takes ownership of the returned object. static slot_http& slot_factory() { return m_factory; } protected: void trigger_done(); void trigger_failed(const std::string& message); int m_flags; std::string m_url; std::iostream* m_stream; uint32_t m_timeout; signal_void m_signal_done; signal_string m_signal_failed; private: Http(const Http&); void operator = (const Http&); static slot_http m_factory; }; } #endif libtorrent-0.13.6/src/torrent/object.cc000066400000000000000000000170401257211073700200350ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "object.h" #include "object_stream.h" namespace torrent { Object& Object::get_key(const std::string& k) { check_throw(TYPE_MAP); map_type::iterator itr = _map().find(k); if (itr == _map().end()) throw bencode_error("Object operator [" + k + "] could not find element"); return itr->second; } const Object& Object::get_key(const std::string& k) const { check_throw(TYPE_MAP); map_type::const_iterator itr = _map().find(k); if (itr == _map().end()) throw bencode_error("Object operator [" + k + "] could not find element"); return itr->second; } Object& Object::get_key(const char* k) { check_throw(TYPE_MAP); map_type::iterator itr = _map().find(std::string(k)); if (itr == _map().end()) throw bencode_error("Object operator [" + std::string(k) + "] could not find element"); return itr->second; } const Object& Object::get_key(const char* k) const { check_throw(TYPE_MAP); map_type::const_iterator itr = _map().find(std::string(k)); if (itr == _map().end()) throw bencode_error("Object operator [" + std::string(k) + "] could not find element"); return itr->second; } Object::map_insert_type Object::insert_preserve_type(const key_type& k, Object& b) { check_throw(TYPE_MAP); map_insert_type result = _map().insert(map_type::value_type(k, b)); if (!result.second && result.first->second.type() != b.type()) { result.first->second.move(b); result.second = true; } return result; } Object& Object::move(Object& src) { if (this == &src) return *this; *this = create_empty(src.type()); swap_same_type(*this, src); return *this; } Object& Object::swap(Object& src) { if (this == &src) return *this; if (type() != src.type()) { torrent::Object tmp = create_empty(src.type()); swap_same_type(tmp, src); src = create_empty(this->type()); swap_same_type(src, *this); *this = create_empty(tmp.type()); swap_same_type(*this, tmp); } else { swap_same_type(*this, src); } return *this; } Object& Object::merge_copy(const Object& object, uint32_t skip_mask, uint32_t maxDepth) { if (maxDepth == 0 || m_flags & skip_mask) return (*this = object); if (object.is_map()) { if (!is_map()) *this = create_map(); map_type& dest = as_map(); map_type::iterator destItr = dest.begin(); map_type::const_iterator srcItr = object.as_map().begin(); map_type::const_iterator srcLast = object.as_map().end(); while (srcItr != srcLast) { destItr = std::find_if(destItr, dest.end(), rak::less_equal(srcItr->first, rak::mem_ref(&map_type::value_type::first))); if (srcItr->first < destItr->first) // destItr remains valid and pointing to the next possible // position. dest.insert(destItr, *srcItr); else destItr->second.merge_copy(srcItr->second, maxDepth - 1); srcItr++; } // } else if (object.is_list()) { // if (!is_list()) // *this = create_list(); // list_type& dest = as_list(); // list_type::iterator destItr = dest.begin(); // list_type::const_iterator srcItr = object.as_list().begin(); // list_type::const_iterator srcLast = object.as_list().end(); // while (srcItr != srcLast) { // if (destItr == dest.end()) // destItr = dest.insert(destItr, *srcItr); // else // destItr->merge_copy(*srcItr, maxDepth - 1); // destItr++; // } } else { *this = object; } return *this; } Object& Object::operator = (const Object& src) { if (&src == this) return *this; clear(); // Need some more magic here? m_flags = src.m_flags & (mask_type | mask_public); switch (type()) { case TYPE_STRING: new (&_string()) string_type(src._string()); break; case TYPE_LIST: new (&_list()) list_type(src._list()); break; case TYPE_MAP: _map_ptr() = new map_type(src._map()); break; case TYPE_DICT_KEY: new (&_dict_key()) dict_key_type(src._dict_key()); _dict_key().second = new Object(*src._dict_key().second); break; default: t_pod = src.t_pod; break; } return *this; } Object object_create_normal(const raw_bencode& obj) { torrent::Object result; if (object_read_bencode_c(obj.begin(), obj.end(), &result, 128) != obj.end()) throw bencode_error("Invalid bencode data."); return result; } Object object_create_normal(const raw_list& obj) { torrent::Object result = Object::create_list(); raw_list::iterator first = obj.begin(); raw_list::iterator last = obj.end(); while (first != last) { Object::list_iterator new_entry = result.as_list().insert(result.as_list().end(), Object()); first = object_read_bencode_c(first, last, &*new_entry, 128); // The unordered flag is inherited also from list elements who // have been marked as unordered, though e.g. unordered strings // in the list itself does not cause this flag to be set. if (new_entry->flags() & Object::flag_unordered) result.set_internal_flags(Object::flag_unordered); } return result; } Object object_create_normal(const raw_map& obj) { torrent::Object result = Object::create_map(); raw_list::iterator first = obj.begin(); raw_list::iterator last = obj.end(); Object::string_type prev; while (first != last) { raw_string raw_str = object_read_bencode_c_string(first, last); first = raw_str.end(); Object::string_type key_str = raw_str.as_string(); // We do not set flag_unordered if the first key was zero // length, while multiple zero length keys will trigger the // unordered_flag. if (key_str <= prev && !result.as_map().empty()) result.set_internal_flags(Object::flag_unordered); Object* value = &result.as_map()[key_str]; first = object_read_bencode_c(first, last, value, 128); if (value->flags() & Object::flag_unordered) result.set_internal_flags(Object::flag_unordered); key_str.swap(prev); } return result; } } libtorrent-0.13.6/src/torrent/object.h000066400000000000000000000573061257211073700177100ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_OBJECT_H #define LIBTORRENT_OBJECT_H #include #include #include #include #include #include namespace torrent { class LIBTORRENT_EXPORT Object { public: typedef int64_t value_type; typedef std::string string_type; typedef std::vector list_type; typedef std::map map_type; typedef map_type* map_ptr_type; typedef map_type::key_type key_type; typedef std::pair dict_key_type; typedef list_type::iterator list_iterator; typedef list_type::const_iterator list_const_iterator; typedef list_type::reverse_iterator list_reverse_iterator; typedef list_type::const_reverse_iterator list_const_reverse_iterator; typedef map_type::iterator map_iterator; typedef map_type::const_iterator map_const_iterator; typedef map_type::reverse_iterator map_reverse_iterator; typedef map_type::const_reverse_iterator map_const_reverse_iterator; typedef std::pair map_insert_type; // Flags in the range of 0xffff0000 may be set by the user, however // 0x00ff0000 are reserved for keywords defined by libtorrent. static const uint32_t mask_type = 0xff; static const uint32_t mask_flags = ~mask_type; static const uint32_t mask_internal = 0xffff; static const uint32_t mask_public = ~mask_internal; static const uint32_t flag_unordered = 0x100; // bencode dictionary was not sorted static const uint32_t flag_static_data = 0x010000; // Object does not change across sessions. static const uint32_t flag_session_data = 0x020000; // Object changes between sessions. static const uint32_t flag_function = 0x040000; // A function object. static const uint32_t flag_function_q1 = 0x080000; // A quoted function object. static const uint32_t flag_function_q2 = 0x100000; // A double-quoted function object. static const uint32_t mask_function = 0x1C0000; // Mask for function objects. enum type_type { TYPE_NONE, TYPE_RAW_BENCODE, TYPE_RAW_STRING, TYPE_RAW_LIST, TYPE_RAW_MAP, TYPE_VALUE, TYPE_STRING, TYPE_LIST, TYPE_MAP, TYPE_DICT_KEY }; Object() : m_flags(TYPE_NONE) {} Object(const value_type v) : m_flags(TYPE_VALUE) { new (&_value()) value_type(v); } Object(const char* s) : m_flags(TYPE_STRING) { new (&_string()) string_type(s); } Object(const string_type& s) : m_flags(TYPE_STRING) { new (&_string()) string_type(s); } Object(const raw_bencode& r) : m_flags(TYPE_RAW_BENCODE) { new (&_raw_bencode()) raw_bencode(r); } Object(const raw_string& r) : m_flags(TYPE_RAW_STRING) { new (&_raw_string()) raw_string(r); } Object(const raw_list& r) : m_flags(TYPE_RAW_LIST) { new (&_raw_list()) raw_list(r); } Object(const raw_map& r) : m_flags(TYPE_RAW_MAP) { new (&_raw_map()) raw_map(r); } Object(const Object& b); ~Object() { clear(); } // TODO: Move this out of the class namespace, call them // make_object_. static Object create_empty(type_type t); static Object create_value() { return Object(value_type()); } static Object create_string() { return Object(string_type()); } static Object create_list() { Object tmp; tmp.m_flags = TYPE_LIST; new (&tmp._list()) list_type(); return tmp; } static Object create_map() { Object tmp; tmp.m_flags = TYPE_MAP; tmp._map_ptr() = new map_type(); return tmp; } static Object create_dict_key(); static Object create_raw_bencode(raw_bencode obj = raw_bencode()); static Object create_raw_string(raw_string obj = raw_string()); static Object create_raw_list(raw_list obj = raw_list()); static Object create_raw_map(raw_map obj = raw_map()); template static Object create_list_range(ForwardIterator first, ForwardIterator last); static Object from_list(const list_type& src) { Object tmp; tmp.m_flags = TYPE_LIST; new (&tmp._list()) list_type(src); return tmp; } // Clear should probably not be inlined due to size and not being // optimized away in pretty much any case. Might not work well in // cases where we pass constant rvalues. void clear(); type_type type() const { return (type_type)(m_flags & mask_type); } uint32_t flags() const { return m_flags & mask_flags; } void set_flags(uint32_t f) { m_flags |= f & mask_public; } void unset_flags(uint32_t f) { m_flags &= ~(f & mask_public); } void set_internal_flags(uint32_t f) { m_flags |= f & (mask_internal & ~mask_type); } void unset_internal_flags(uint32_t f) { m_flags &= ~(f & (mask_internal & ~mask_type)); } // Add functions for setting/clearing the public flags. bool is_empty() const { return type() == TYPE_NONE; } bool is_not_empty() const { return type() != TYPE_NONE; } bool is_value() const { return type() == TYPE_VALUE; } bool is_string() const { return type() == TYPE_STRING; } bool is_string_empty() const { return type() != TYPE_STRING || _string().empty(); } bool is_list() const { return type() == TYPE_LIST; } bool is_map() const { return type() == TYPE_MAP; } bool is_dict_key() const { return type() == TYPE_DICT_KEY; } bool is_raw_bencode() const { return type() == TYPE_RAW_BENCODE; } bool is_raw_string() const { return type() == TYPE_RAW_STRING; } bool is_raw_list() const { return type() == TYPE_RAW_LIST; } bool is_raw_map() const { return type() == TYPE_RAW_MAP; } value_type& as_value() { check_throw(TYPE_VALUE); return _value(); } const value_type& as_value() const { check_throw(TYPE_VALUE); return _value(); } string_type& as_string() { check_throw(TYPE_STRING); return _string(); } const string_type& as_string() const { check_throw(TYPE_STRING); return _string(); } const string_type& as_string_c() const { check_throw(TYPE_STRING); return _string(); } list_type& as_list() { check_throw(TYPE_LIST); return _list(); } const list_type& as_list() const { check_throw(TYPE_LIST); return _list(); } map_type& as_map() { check_throw(TYPE_MAP); return _map(); } const map_type& as_map() const { check_throw(TYPE_MAP); return _map(); } string_type& as_dict_key() { check_throw(TYPE_DICT_KEY); return _dict_key().first; } const string_type& as_dict_key() const { check_throw(TYPE_DICT_KEY); return _dict_key().first; } Object& as_dict_obj() { check_throw(TYPE_DICT_KEY); return *_dict_key().second; } const Object& as_dict_obj() const { check_throw(TYPE_DICT_KEY); return *_dict_key().second; } raw_bencode& as_raw_bencode() { check_throw(TYPE_RAW_BENCODE); return _raw_bencode(); } const raw_bencode& as_raw_bencode() const { check_throw(TYPE_RAW_BENCODE); return _raw_bencode(); } raw_string& as_raw_string() { check_throw(TYPE_RAW_STRING); return _raw_string(); } const raw_string& as_raw_string() const { check_throw(TYPE_RAW_STRING); return _raw_string(); } raw_list& as_raw_list() { check_throw(TYPE_RAW_LIST); return _raw_list(); } const raw_list& as_raw_list() const { check_throw(TYPE_RAW_LIST); return _raw_list(); } raw_map& as_raw_map() { check_throw(TYPE_RAW_MAP); return _raw_map(); } const raw_map& as_raw_map() const { check_throw(TYPE_RAW_MAP); return _raw_map(); } bool has_key(const key_type& k) const { check_throw(TYPE_MAP); return _map().find(k) != _map().end(); } bool has_key_value(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_VALUE); } bool has_key_string(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_STRING); } bool has_key_list(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_LIST); } bool has_key_map(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_MAP); } bool has_key_raw_bencode(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_RAW_BENCODE); } bool has_key_raw_string(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_RAW_STRING); } bool has_key_raw_list(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_RAW_LIST); } bool has_key_raw_map(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_RAW_MAP); } // Should have an interface for that returns pointer or something, // so we don't need to search twice. // Make these inline... Object& get_key(const key_type& k); const Object& get_key(const key_type& k) const; Object& get_key(const char* k); const Object& get_key(const char* k) const; template value_type& get_key_value(const T& k) { return get_key(k).as_value(); } template const value_type& get_key_value(const T& k) const { return get_key(k).as_value(); } template string_type& get_key_string(const T& k) { return get_key(k).as_string(); } template const string_type& get_key_string(const T& k) const { return get_key(k).as_string(); } template list_type& get_key_list(const T& k) { return get_key(k).as_list(); } template const list_type& get_key_list(const T& k) const { return get_key(k).as_list(); } template map_type& get_key_map(const T& k) { return get_key(k).as_map(); } template const map_type& get_key_map(const T& k) const { return get_key(k).as_map(); } Object& insert_key(const key_type& k, const Object& b) { check_throw(TYPE_MAP); return _map()[k] = b; } Object& insert_key_move(const key_type& k, Object& b) { check_throw(TYPE_MAP); return _map()[k].move(b); } // 'insert_preserve_*' inserts the object 'b' if the key 'k' does // not exist, else it returns the old entry. The type specific // versions also require the old entry to be of the same type. // // Consider making insert_preserve_* return std::pair or // something similar. map_insert_type insert_preserve_any(const key_type& k, const Object& b) { check_throw(TYPE_MAP); return _map().insert(map_type::value_type(k, b)); } map_insert_type insert_preserve_type(const key_type& k, Object& b); map_insert_type insert_preserve_copy(const key_type& k, Object b) { return insert_preserve_type(k, b); } void erase_key(const key_type& k) { check_throw(TYPE_MAP); _map().erase(k); } Object& insert_front(const Object& b) { check_throw(TYPE_LIST); return *_list().insert(_list().begin(), b); } Object& insert_back(const Object& b) { check_throw(TYPE_LIST); return *_list().insert(_list().end(), b); } // Copy and merge operations: Object& move(Object& b); Object& swap(Object& b); Object& swap_same_type(Object& b); // Only map entries are merged. Object& merge_move(Object& object, uint32_t maxDepth = ~uint32_t()); Object& merge_copy(const Object& object, uint32_t skip_mask = flag_static_data, uint32_t maxDepth = ~uint32_t()); Object& operator = (const Object& b); // Internal: void swap_same_type(Object& left, Object& right); private: inline bool check(map_type::const_iterator itr, type_type t) const { return itr != _map().end() && itr->second.type() == t; } inline void check_throw(type_type t) const { if (t != type()) throw bencode_error("Wrong object type."); } uint32_t m_flags; #ifdef HAVE_CXX11 value_type& _value() { return t_value; } const value_type& _value() const { return t_value; } string_type& _string() { return t_string; } const string_type& _string() const { return t_string; } list_type& _list() { return t_list; } const list_type& _list() const { return t_list; } map_type& _map() { return *t_map; } const map_type& _map() const { return *t_map; } map_ptr_type& _map_ptr() { return t_map; } const map_ptr_type& _map_ptr() const { return t_map; } dict_key_type& _dict_key() { return t_dict_key; } const dict_key_type& _dict_key() const { return t_dict_key; } raw_object& _raw_object() { return t_raw_object; } const raw_object& _raw_object() const { return t_raw_object; } raw_bencode& _raw_bencode() { return t_raw_bencode; } const raw_bencode& _raw_bencode() const { return t_raw_bencode; } raw_string& _raw_string() { return t_raw_string; } const raw_string& _raw_string() const { return t_raw_string; } raw_list& _raw_list() { return t_raw_list; } const raw_list& _raw_list() const { return t_raw_list; } raw_map& _raw_map() { return t_raw_map; } const raw_map& _raw_map() const { return t_raw_map; } union pod_types { value_type t_value; raw_object t_raw_object; raw_bencode t_raw_bencode; raw_string t_raw_string; raw_list t_raw_list; raw_map t_raw_map; }; union { pod_types t_pod; value_type t_value; string_type t_string; list_type t_list; map_type* t_map; dict_key_type t_dict_key; raw_object t_raw_object; raw_bencode t_raw_bencode; raw_string t_raw_string; raw_list t_raw_list; raw_map t_raw_map; }; #else // #error "WTF we're testing C++11 now." value_type& _value() { return reinterpret_cast(t_pod); } const value_type& _value() const { return reinterpret_cast(t_pod); } string_type& _string() { return reinterpret_cast(t_string); } const string_type& _string() const { return reinterpret_cast(t_string); } list_type& _list() { return reinterpret_cast(t_list); } const list_type& _list() const { return reinterpret_cast(t_list); } map_type& _map() { return *reinterpret_cast(t_pod); } const map_type& _map() const { return *reinterpret_cast(t_pod); } map_ptr_type& _map_ptr() { return reinterpret_cast(t_pod); } const map_ptr_type& _map_ptr() const { return reinterpret_cast(t_pod); } dict_key_type& _dict_key() { return reinterpret_cast(t_pod); } const dict_key_type& _dict_key() const { return reinterpret_cast(t_pod); } raw_object& _raw_object() { return reinterpret_cast(t_pod); } const raw_object& _raw_object() const { return reinterpret_cast(t_pod); } raw_bencode& _raw_bencode() { return reinterpret_cast(t_pod); } const raw_bencode& _raw_bencode() const { return reinterpret_cast(t_pod); } raw_string& _raw_string() { return reinterpret_cast(t_pod); } const raw_string& _raw_string() const { return reinterpret_cast(t_pod); } raw_list& _raw_list() { return reinterpret_cast(t_pod); } const raw_list& _raw_list() const { return reinterpret_cast(t_pod); } raw_map& _raw_map() { return reinterpret_cast(t_pod); } const raw_map& _raw_map() const { return reinterpret_cast(t_pod); } union pod_types { value_type t_value; map_type* t_map; char t_raw_object[sizeof(raw_object)]; }; union { pod_types t_pod; char t_string[sizeof(string_type)]; char t_list[sizeof(list_type)]; char t_dict_key[sizeof(dict_key_type)]; }; #endif }; inline Object::Object(const Object& b) { m_flags = b.m_flags & (mask_type | mask_public); switch (type()) { case TYPE_NONE: case TYPE_RAW_BENCODE: case TYPE_RAW_STRING: case TYPE_RAW_LIST: case TYPE_RAW_MAP: case TYPE_VALUE: t_pod = b.t_pod; break; case TYPE_STRING: new (&_string()) string_type(b._string()); break; case TYPE_LIST: new (&_list()) list_type(b._list()); break; case TYPE_MAP: _map_ptr() = new map_type(b._map()); break; case TYPE_DICT_KEY: new (&_dict_key().first) string_type(b._dict_key().first); _dict_key().second = new Object(*b._dict_key().second); break; } } inline Object Object::create_empty(type_type t) { switch (t) { case TYPE_RAW_BENCODE: return create_raw_bencode(); case TYPE_RAW_STRING: return create_raw_string(); case TYPE_RAW_LIST: return create_raw_list(); case TYPE_RAW_MAP: return create_raw_map(); case TYPE_VALUE: return create_value(); case TYPE_STRING: return create_string(); case TYPE_LIST: return create_list(); case TYPE_MAP: return create_map(); case TYPE_DICT_KEY: return create_dict_key(); case TYPE_NONE: default: return torrent::Object(); } } inline Object object_create_raw_bencode_c_str(const char str[]) { return Object::create_raw_bencode(raw_bencode(str, strlen(str))); } // TODO: These do not preserve the flag... Object object_create_normal(const raw_bencode& obj) LIBTORRENT_EXPORT; Object object_create_normal(const raw_list& obj) LIBTORRENT_EXPORT; Object object_create_normal(const raw_map& obj) LIBTORRENT_EXPORT; inline Object object_create_normal(const raw_string& obj) { return torrent::Object(obj.as_string()); } inline Object Object::create_dict_key() { Object tmp; tmp.m_flags = TYPE_DICT_KEY; new (&tmp._dict_key()) dict_key_type(); tmp._dict_key().second = new Object(); return tmp; } inline Object Object::create_raw_bencode(raw_bencode obj) { Object tmp; tmp.m_flags = TYPE_RAW_BENCODE; new (&tmp._raw_bencode()) raw_bencode(obj); return tmp; } inline Object Object::create_raw_string(raw_string obj) { Object tmp; tmp.m_flags = TYPE_RAW_STRING; new (&tmp._raw_string()) raw_string(obj); return tmp; } inline Object Object::create_raw_list(raw_list obj) { Object tmp; tmp.m_flags = TYPE_RAW_LIST; new (&tmp._raw_list()) raw_list(obj); return tmp; } inline Object Object::create_raw_map(raw_map obj) { Object tmp; tmp.m_flags = TYPE_RAW_MAP; new (&tmp._raw_map()) raw_map(obj); return tmp; } inline Object object_create_normal(const Object& obj) { switch (obj.type()) { case Object::TYPE_RAW_BENCODE: return object_create_normal(obj.as_raw_bencode()); case Object::TYPE_RAW_STRING: return object_create_normal(obj.as_raw_string()); case Object::TYPE_RAW_LIST: return object_create_normal(obj.as_raw_list()); case Object::TYPE_RAW_MAP: return object_create_normal(obj.as_raw_map()); default: return obj; } } inline std::string object_create_string(const torrent::Object& obj) { switch (obj.type()) { case Object::TYPE_RAW_BENCODE: return obj.as_raw_bencode().as_raw_string().as_string(); case Object::TYPE_RAW_STRING: return obj.as_raw_string().as_string(); default: return obj.as_string(); } } template inline Object Object::create_list_range(ForwardIterator first, ForwardIterator last) { Object tmp; tmp.m_flags = TYPE_LIST; new (&tmp._list()) list_type(first, last); return tmp; } inline void Object::clear() { switch (type()) { case TYPE_STRING: _string().~string_type(); break; case TYPE_LIST: _list().~list_type(); break; case TYPE_MAP: delete _map_ptr(); break; case TYPE_DICT_KEY: delete _dict_key().second; _dict_key().~dict_key_type(); break; default: break; } // Only clear type? m_flags = TYPE_NONE; } inline void Object::swap_same_type(Object& left, Object& right) { std::swap(left.m_flags, right.m_flags); switch (left.type()) { case Object::TYPE_STRING: left._string().swap(right._string()); break; case Object::TYPE_LIST: left._list().swap(right._list()); break; case Object::TYPE_DICT_KEY: std::swap(left._dict_key().first, right._dict_key().first); std::swap(left._dict_key().second, right._dict_key().second); break; default: std::swap(left.t_pod, right.t_pod); break; } } inline void swap(Object& left, Object& right) { left.swap(right); } inline bool object_equal(const Object& left, const Object& right) { if (left.type() != right.type()) return false; switch (left.type()) { case Object::TYPE_NONE: return true; case Object::TYPE_VALUE: return left.as_value() == right.as_value(); case Object::TYPE_STRING: return left.as_string() == right.as_string(); default: return false; } } } #endif libtorrent-0.13.6/src/torrent/object_raw_bencode.h000066400000000000000000000155771257211073700222440ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_OBJECT_RAW_BENCODE_H #define LIBTORRENT_OBJECT_RAW_BENCODE_H #include #include #include #include namespace torrent { class raw_bencode; class raw_string; class raw_list; class raw_map; // The base class for static constant version of Objects. This class // should never be used directly. class raw_object { public: typedef const char value_type; typedef const char* iterator; typedef const char* const_iterator; typedef uint32_t size_type; raw_object() : m_data(), m_size() {} raw_object(value_type* src_data, size_type src_size) : m_data(src_data), m_size(src_size) {} bool empty() const { return m_size == 0; } size_type size() const { return m_size; } iterator begin() const { return m_data; } iterator end() const { return m_data + m_size; } value_type* data() const { return m_data; } bool operator == (const raw_object& rhs) const { return m_size == rhs.m_size && std::memcmp(m_data, rhs.m_data, m_size) == 0; } bool operator != (const raw_object& rhs) const { return m_size != rhs.m_size || std::memcmp(m_data, rhs.m_data, m_size) != 0; } protected: iterator m_data; size_type m_size; }; #define RAW_BENCODE_SET_USING \ using raw_object::value_type; \ using raw_object::iterator; \ using raw_object::const_iterator; \ using raw_object::size_type; \ using raw_object::empty; \ using raw_object::size; \ using raw_object::begin; \ using raw_object::end; \ using raw_object::data; \ \ bool operator == (const this_type& rhs) const { return raw_object::operator==(rhs); } \ bool operator != (const this_type& rhs) const { return raw_object::operator!=(rhs); } \ // A raw_bencode object shall always contain valid bencode data or be // empty. class raw_bencode : protected raw_object { public: typedef raw_bencode this_type; RAW_BENCODE_SET_USING raw_bencode() : raw_object() {} raw_bencode(value_type* src_data, size_type src_size) : raw_object(src_data, src_size) {} bool is_empty() const { return m_size == 0; } bool is_value() const { return m_size >= 3 && m_data[0] >= 'i'; } bool is_raw_string() const { return m_size >= 2 && m_data[0] >= '0' && m_data[0] <= '9'; } bool is_raw_list() const { return m_size >= 2 && m_data[0] >= 'l'; } bool is_raw_map() const { return m_size >= 2 && m_data[0] >= 'd'; } std::string as_value_string() const; raw_string as_raw_string() const; raw_list as_raw_list() const; raw_map as_raw_map() const; static raw_bencode from_c_str(const char* str) { return raw_bencode(str, std::strlen(str)); } }; class raw_string : protected raw_object { public: typedef raw_string this_type; RAW_BENCODE_SET_USING raw_string() {} raw_string(value_type* src_data, size_type src_size) : raw_object(src_data, src_size) {} std::string as_string() const { return std::string(m_data, m_size); } static raw_string from_c_str(const char* str) { return raw_string(str, std::strlen(str)); } static raw_string from_string(const std::string& str) { return raw_string(str.data(), str.size()); } }; class raw_list : protected raw_object { public: typedef raw_list this_type; RAW_BENCODE_SET_USING raw_list() {} raw_list(value_type* src_data, size_type src_size) : raw_object(src_data, src_size) {} static raw_list from_c_str(const char* str) { return raw_list(str, std::strlen(str)); } }; class raw_map : protected raw_object { public: typedef raw_map this_type; RAW_BENCODE_SET_USING raw_map() {} raw_map(value_type* src_data, size_type src_size) : raw_object(src_data, src_size) {} }; // // // inline std::string raw_bencode::as_value_string() const { if (!is_value()) throw bencode_error("Wrong object type."); return std::string(data() + 1, size() - 2); } inline raw_string raw_bencode::as_raw_string() const { if (!is_raw_string()) throw bencode_error("Wrong object type."); const_iterator itr = std::find(begin(), end(), ':'); if (itr == end()) throw internal_error("Invalid bencode in raw_bencode."); return raw_string(itr + 1, std::distance(itr + 1, end())); } inline raw_list raw_bencode::as_raw_list() const { if (!is_raw_list()) throw bencode_error("Wrong object type."); return raw_list(m_data + 1, m_size - 2); } inline raw_map raw_bencode::as_raw_map() const { if (!is_raw_map()) throw bencode_error("Wrong object type."); return raw_map(m_data + 1, m_size - 2); } // // Redo... // inline bool raw_bencode_equal(const raw_bencode& left, const raw_bencode& right) { return left.size() == right.size() && std::memcmp(left.begin(), right.begin(), left.size()) == 0; } template inline bool raw_bencode_equal(const tmpl_raw_object& left, const char* right, size_t right_size) { return left.size() == right_size && std::memcmp(left.begin(), right, right_size) == 0; } template inline bool raw_bencode_equal_c_str(const tmpl_raw_object& left, const char* right) { return raw_bencode_equal(left, right, strlen(right)); } } #endif libtorrent-0.13.6/src/torrent/object_static_map.cc000066400000000000000000000051021257211073700222350ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "object_static_map.h" namespace torrent { const static_map_key_search_result find_key_match(const static_map_mapping_type* first, const static_map_mapping_type* last, const char* key_first, const char* key_last) { // unsigned int key_length = strlen(key); const static_map_mapping_type* itr = first; while (itr != last) { unsigned int base = rak::count_base(key_first, key_last, itr->key, itr->key + itr->max_key_size); if (key_first[base] != '\0') { // Return not found here if we know the entry won't come after // this. itr++; continue; } if (itr->key[base] == '\0' || itr->key[base] == '*' || (itr->key[base] == ':' && itr->key[base + 1] == ':') || (itr->key[base] == '[' && itr->key[base + 1] == ']')) return static_map_key_search_result(itr, base); break; } return static_map_key_search_result(first, 0); } } libtorrent-0.13.6/src/torrent/object_static_map.h000066400000000000000000000101441257211073700221010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_OBJECT_STATIC_MAP_H #define LIBTORRENT_OBJECT_STATIC_MAP_H #include #include #include namespace torrent { struct static_map_mapping_type { static const size_t max_key_size = 16; bool is_end() const { return key[0] == '\0'; } static bool is_not_key_char(char c) { return c == '\0' || c == ':' || c == '[' || c == '*'; } const char* find_key_end(const char* pos) const { return std::find_if(pos, key + max_key_size, &is_not_key_char); } uint32_t index; const char key[max_key_size]; }; struct static_map_entry_type { torrent::Object object; }; template class static_map_type { public: typedef Object value_type; typedef tmpl_key_type key_type; typedef static_map_entry_type entry_type; typedef static_map_mapping_type mapping_type; typedef mapping_type key_list_type[tmpl_length]; typedef entry_type value_list_type[tmpl_length]; static const size_t size = tmpl_length; static const key_list_type keys; entry_type* values() { return m_values; } const entry_type* values() const { return m_values; } Object& operator [] (key_type key) { return m_values[key].object; } const Object& operator [] (key_type key) const { return m_values[key].object; } private: value_list_type m_values; }; // // Helper functions/classes for parsing keys: // struct static_map_stack_type { void set_key_index(uint32_t start_index, uint32_t end_index, uint32_t delim_size, torrent::Object::type_type type = Object::TYPE_MAP) { key_index = start_index; next_key = end_index + delim_size; obj_type = type; } void clear() { key_index = 0; next_key = 0; obj_type = Object::TYPE_MAP; } uint32_t key_index; uint32_t next_key; Object::type_type obj_type; }; typedef std::pair static_map_key_search_result; // Note that the key for both functions must be null-terminated at // 'key_last'. const static_map_key_search_result find_key_match(const static_map_mapping_type* first, const static_map_mapping_type* last, const char* key_first, const char* key_last) LIBTORRENT_EXPORT; inline const static_map_key_search_result find_key_match(const static_map_mapping_type* first, const static_map_mapping_type* last, const char* key) { return find_key_match(first, last, key, key + strlen(key)); } } #endif libtorrent-0.13.6/src/torrent/object_stream.cc000066400000000000000000000626401257211073700214160ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include #include "utils/sha1.h" #include "object.h" #include "object_stream.h" #include "object_static_map.h" namespace torrent { bool object_read_string(std::istream* input, std::string& str) { uint32_t size; *input >> size; if (input->fail() || input->get() != ':') return false; str.resize(size); for (std::string::iterator itr = str.begin(); itr != str.end() && input->good(); ++itr) *itr = input->get(); return !input->fail(); } const char* object_read_bencode_c_value(const char* first, const char* last, int64_t& value) { if (first == last) return first; bool neg = false; if (*first == '-') { // Don't allow '-0', or '-' followed by non-numeral. if ((first + 1) == last || *(first + 1) <= '0' || *(first + 1) > '9') return first; neg = true; first++; } value = 0; while (first != last && *first >= '0' && *first <= '9') value = value * 10 + (*first++ - '0'); if (neg) value = -value; return first; } raw_string object_read_bencode_c_string(const char* first, const char* last) { // Set the most-significant bit so that if there are no numbers in // the input it will fail the length check, while "0" will shift the // bit out. unsigned int length = 0x1 << (std::numeric_limits::digits - 1); while (first != last && *first >= '0' && *first <= '9') length = length * 10 + (*first++ - '0'); if (length + 1 > (unsigned int)std::distance(first, last) || *first++ != ':') throw torrent::bencode_error("Invalid bencode data."); return raw_string(first, length); } // Could consider making this non-recursive, but they seldomly are // deep enough to make that worth-while. void object_read_bencode(std::istream* input, Object* object, uint32_t depth) { int c; switch ((c = input->peek())) { case 'i': input->get(); *object = Object::create_value(); *input >> object->as_value(); if (input->get() != 'e') break; return; case 'l': input->get(); *object = Object::create_list(); if (++depth >= 1024) break; while (input->good()) { if (input->peek() == 'e') { input->get(); return; } Object::list_iterator itr = object->as_list().insert(object->as_list().end(), Object()); object_read_bencode(input, &*itr, depth); // The unordered flag is inherited also from list elements who // have been marked as unordered, though e.g. unordered strings // in the list itself does not cause this flag to be set. if (itr->flags() & Object::flag_unordered) object->set_internal_flags(Object::flag_unordered); } break; case 'd': { input->get(); *object = Object::create_map(); if (++depth >= 1024) break; Object::string_type last; while (input->good()) { if (input->peek() == 'e') { input->get(); return; } Object::string_type str; if (!object_read_string(input, str)) break; // We do not set flag_unordered if the first key was zero // length, while multiple zero length keys will trigger the // unordered_flag. if (str <= last && !object->as_map().empty()) object->set_internal_flags(Object::flag_unordered); Object* value = &object->as_map()[str]; object_read_bencode(input, value, depth); if (value->flags() & Object::flag_unordered) object->set_internal_flags(Object::flag_unordered); str.swap(last); } break; } default: if (c >= '0' && c <= '9') { *object = Object::create_string(); if (object_read_string(input, object->as_string())) return; } break; } input->setstate(std::istream::failbit); object->clear(); } const char* object_read_bencode_c(const char* first, const char* last, Object* object, uint32_t depth) { if (first == last) throw torrent::bencode_error("Invalid bencode data."); switch (*first) { case 'i': *object = Object::create_value(); first = object_read_bencode_c_value(first + 1, last, object->as_value()); if (first == last || *first++ != 'e') break; return first; case 'l': if (++depth >= 1024) break; first++; *object = Object::create_list(); while (first != last) { if (*first == 'e') return first + 1; Object::list_iterator itr = object->as_list().insert(object->as_list().end(), Object()); first = object_read_bencode_c(first, last, &*itr, depth); // The unordered flag is inherited also from list elements who // have been marked as unordered, though e.g. unordered strings // in the list itself does not cause this flag to be set. if (itr->flags() & Object::flag_unordered) object->set_internal_flags(Object::flag_unordered); } break; case 'd': { if (++depth >= 1024) break; first++; *object = Object::create_map(); Object::string_type prev; while (first != last) { if (*first == 'e') return first + 1; raw_string raw_str = object_read_bencode_c_string(first, last); first = raw_str.end(); Object::string_type str = raw_str.as_string(); // We do not set flag_unordered if the first key was zero // length, while multiple zero length keys will trigger the // unordered_flag. if (str <= prev && !object->as_map().empty()) object->set_internal_flags(Object::flag_unordered); Object* value = &object->as_map()[str]; first = object_read_bencode_c(first, last, value, depth); if (value->flags() & Object::flag_unordered) object->set_internal_flags(Object::flag_unordered); str.swap(prev); } break; } default: if (*first < '0' || *first > '9') throw torrent::bencode_error("Invalid bencode data."); raw_string raw_str = object_read_bencode_c_string(first, last); *object = raw_str.as_string(); return raw_str.end(); } object->clear(); throw torrent::bencode_error("Invalid bencode data."); } inline bool object_is_not_digit(char c) { return c < '0' || c > '9'; } const char* object_read_bencode_skip_c(const char* first, const char* last) { char stack[128] = { 0 }; char* stack_itr = stack; while (first != last) { if (*first == 'e') { if (stack_itr-- == stack) throw torrent::bencode_error("Invalid bencode data."); first++; if (stack_itr == stack) return first; continue; } // Currently reading a dictionary, so ensure the first entry is a // string. if (*stack_itr && (first = object_read_bencode_c_string(first, last).end()) == last) break; switch (*first) { case 'i': if (first != last && *++first == '-') { if (first != last && *++first == '0') throw torrent::bencode_error("Invalid bencode data."); } first = std::find_if(first, last, &object_is_not_digit); if (first == last || *first++ != 'e') throw torrent::bencode_error("Invalid bencode data."); break; case 'l': case 'd': if (++stack_itr == stack + 128) throw torrent::bencode_error("Invalid bencode data."); *stack_itr = (*first++ == 'd'); continue; default: first = object_read_bencode_c_string(first, last).end(); }; if (stack_itr == stack) return first; } throw torrent::bencode_error("Invalid bencode data."); } const char* object_read_bencode_raw_c(const char* first, const char* last, torrent::Object* object, char type) { const char* tmp = first; first = object_read_bencode_skip_c(first, last); raw_bencode obj = raw_bencode(tmp, std::distance(tmp, first)); // if (obj.is_empty()) // throw torrent::bencode_error("Invalid bencode data."); switch (type) { case 'S': if (obj.is_raw_string()) *object = obj.as_raw_string(); break; case 'L': if (obj.is_raw_list()) *object = obj.as_raw_list(); break; case 'M': if (obj.is_raw_map()) *object = obj.as_raw_map(); break; default: *object = obj; }; return first; } // Would be nice to have a straight stream to hash conversion. std::string object_sha1(const Object* object) { Sha1 sha; char buffer[1024]; sha.init(); object_write_bencode_c(&object_write_to_sha1, &sha, object_buffer_t(buffer, buffer + 1024), object); sha.final_c(buffer); return std::string(buffer, 20); } std::istream& operator >> (std::istream& input, Object& object) { std::locale locale = input.imbue(std::locale::classic()); object.clear(); object_read_bencode(&input, &object); input.imbue(locale); return input; } std::ostream& operator << (std::ostream& output, const Object& object) { object_write_bencode(&output, &object); return output; } struct object_write_data_t { object_write_t writeFunc; void* data; object_buffer_t buffer; char* pos; }; void object_write_bencode_c_string(object_write_data_t* output, const char* srcData, uint32_t srcLength) { while (srcLength != 0) { uint32_t len = std::min(srcLength, std::distance(output->pos, output->buffer.second)); std::memcpy(output->pos, srcData, len); output->pos += len; if (output->pos == output->buffer.second) { output->buffer = output->writeFunc(output->data, output->buffer); output->pos = output->buffer.first; if (output->buffer.first == output->buffer.second) return; } srcData += len; srcLength -= len; } } void object_write_bencode_c_char(object_write_data_t* output, char src) { if (output->pos == output->buffer.second) { output->buffer = output->writeFunc(output->data, output->buffer); output->pos = output->buffer.first; if (output->buffer.first == output->buffer.second) return; } *output->pos++ = src; } // A new wheel. Look, how shiny and new. void object_write_bencode_c_value(object_write_data_t* output, int64_t src) { if (src == 0) return object_write_bencode_c_char(output, '0'); if (src < 0) { object_write_bencode_c_char(output, '-'); src = -src; } char buffer[20]; char* first = buffer + 20; // We don't need locale support, so just do this directly. while (src != 0) { *--first = '0' + src % 10; src /= 10; } object_write_bencode_c_string(output, first, 20 - std::distance(buffer, first)); } inline void object_write_bencode_c_obj_value(object_write_data_t* output, int64_t value) { object_write_bencode_c_char(output, 'i'); object_write_bencode_c_value(output, value); object_write_bencode_c_char(output, 'e'); } inline void object_write_bencode_c_obj_string(object_write_data_t* output, const char* data, uint32_t size) { object_write_bencode_c_value(output, size); object_write_bencode_c_char(output, ':'); object_write_bencode_c_string(output, data, size); } inline void object_write_bencode_c_obj_string(object_write_data_t* output, const std::string& str) { object_write_bencode_c_obj_string(output, str.c_str(), str.size()); } void object_write_bencode_c_object(object_write_data_t* output, const Object* object, uint32_t skip_mask) { switch (object->type()) { case Object::TYPE_NONE: break; case Object::TYPE_RAW_BENCODE: { raw_bencode raw = object->as_raw_bencode(); object_write_bencode_c_string(output, raw.begin(), raw.size()); break; } case Object::TYPE_RAW_STRING: { raw_string raw = object->as_raw_string(); object_write_bencode_c_value(output, raw.size()); object_write_bencode_c_char(output, ':'); object_write_bencode_c_string(output, raw.begin(), raw.size()); break; } case Object::TYPE_RAW_LIST: { raw_list raw = object->as_raw_list(); object_write_bencode_c_char(output, 'l'); object_write_bencode_c_string(output, raw.begin(), raw.size()); object_write_bencode_c_char(output, 'e'); break; } case Object::TYPE_RAW_MAP: { raw_map raw = object->as_raw_map(); object_write_bencode_c_char(output, 'd'); object_write_bencode_c_string(output, raw.begin(), raw.size()); object_write_bencode_c_char(output, 'e'); break; } case Object::TYPE_VALUE: object_write_bencode_c_obj_value(output, object->as_value()); break; case Object::TYPE_STRING: object_write_bencode_c_obj_string(output, object->as_string()); break; case Object::TYPE_LIST: object_write_bencode_c_char(output, 'l'); for (Object::list_const_iterator itr = object->as_list().begin(), last = object->as_list().end(); itr != last; ++itr) { if (itr->is_empty() || itr->flags() & skip_mask) continue; object_write_bencode_c_object(output, &*itr, skip_mask); } object_write_bencode_c_char(output, 'e'); break; case Object::TYPE_MAP: object_write_bencode_c_char(output, 'd'); for (Object::map_const_iterator itr = object->as_map().begin(), last = object->as_map().end(); itr != last; ++itr) { if (itr->second.is_empty() || itr->second.flags() & skip_mask) continue; object_write_bencode_c_obj_string(output, itr->first); object_write_bencode_c_object(output, &itr->second, skip_mask); } object_write_bencode_c_char(output, 'e'); break; case Object::TYPE_DICT_KEY: throw torrent::bencode_error("Cannot bencode internal dict_key type."); break; } } void object_write_bencode(std::ostream* output, const Object* object, uint32_t skip_mask) { char buffer[1024]; object_write_bencode_c(&object_write_to_stream, output, object_buffer_t(buffer, buffer + 1024), object, skip_mask); } object_buffer_t object_write_bencode(char* first, char* last, const Object* object, uint32_t skip_mask) { object_buffer_t buffer = object_buffer_t(first, last); return object_write_bencode_c(&object_write_to_buffer, &buffer, buffer, object, skip_mask); } object_buffer_t object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object, uint32_t skip_mask) { object_write_data_t output; output.writeFunc = writeFunc; output.data = data; output.buffer = buffer; output.pos = buffer.first; if (!(object->flags() & skip_mask)) object_write_bencode_c_object(&output, object, skip_mask); // Don't flush the buffer. if (output.pos == output.buffer.first) return output.buffer; return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos)); } object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) { if (buffer.first == buffer.second) throw internal_error("object_write_to_buffer(...) buffer overflow."); // Hmm... this does something weird... return object_buffer_t(buffer.second, buffer.second); } object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) { reinterpret_cast(data)->update(buffer.first, std::distance(buffer.first, buffer.second)); return buffer; } object_buffer_t object_write_to_stream(void* data, object_buffer_t buffer) { reinterpret_cast(data)->write(buffer.first, std::distance(buffer.first, buffer.second)); if (reinterpret_cast(data)->bad()) return object_buffer_t(buffer.first, buffer.first); return buffer; } object_buffer_t object_write_to_size(void* data, object_buffer_t buffer) { *reinterpret_cast(data) += std::distance(buffer.first, buffer.second); return buffer; } // // static_map operations: // const char* static_map_read_bencode_c(const char* first, const char* last, static_map_entry_type* entry_values, const static_map_mapping_type* first_key, const static_map_mapping_type* last_key) { // Temp hack... validate that we got valid bencode data... // { // torrent::Object obj; // if (object_read_bencode_c(first, last, &obj) != last) { // std::string escaped = rak::copy_escape_html(first, last); // char buffer[1024]; // sprintf(buffer, "Verified wrong, %u, '%u', '%s'.", std::distance(first, last), (unsigned int)*first, escaped.c_str()); // throw torrent::internal_error("Invalid bencode data."); // } // } if (first == last || *first++ != 'd') throw torrent::bencode_error("Invalid bencode data."); static_map_stack_type stack[8]; static_map_stack_type* stack_itr = stack; stack_itr->clear(); char current_key[static_map_mapping_type::max_key_size + 2] = ""; while (first != last) { // End a dictionary/list or the whole stream. if (*first == 'e') { first++; if (stack_itr == stack) return first; stack_itr--; continue; } raw_string raw_key = object_read_bencode_c_string(first, last); first = raw_key.end(); // Optimze this buy directly copying into 'current_key'. // // The max length of 'current_key' is one char more than the // mapping key so any bencode which exceeds that will always fail // to find a match. if (raw_key.size() >= static_map_mapping_type::max_key_size - stack_itr->next_key) { first = object_read_bencode_skip_c(first, last); continue; } memcpy(current_key + stack_itr->next_key, raw_key.data(), raw_key.size()); current_key[stack_itr->next_key + raw_key.size()] = '\0'; // Locate the right key. Optimize this by remembering previous // position... static_map_key_search_result key_search = find_key_match(first_key, last_key, current_key); // We're not interest in this object, skip it. if (key_search.second == 0) { first = object_read_bencode_skip_c(first, last); continue; } // Check if we're interested in any dictionaries/lists entries // under this key. // // Note that 'find_key_match' only returns 'key_search.second != // 0' for keys where the next characters are '\0', '::' or '[]'. switch (key_search.first->key[key_search.second]) { case '\0': first = object_read_bencode_c(first, last, &entry_values[key_search.first->index].object); first_key = key_search.first + 1; break; case '*': first = object_read_bencode_raw_c(first, last, &entry_values[key_search.first->index].object, key_search.first->key[key_search.second + 1]); first_key = key_search.first + 1; break; case ':': if (first == last) break; // The bencode object isn't a list. This should either skip it // or produce an error. if (*first++ != 'd') { first = object_read_bencode_skip_c(first - 1, last); break; } stack_itr++; stack_itr->set_key_index((stack_itr - 1)->next_key, key_search.second, 2); current_key[key_search.second] = ':'; current_key[key_search.second + 1] = ':'; break; case '[': { if (first == last) break; // The bencode object isn't a list. This should either skip it // or produce an error. if (*first++ != 'l') { first = object_read_bencode_skip_c(first - 1, last); break; } first_key = key_search.first; while (first != last) { if (*first == 'e') { first++; break; } if (first_key->key[key_search.second + 2] == '*') { first = object_read_bencode_raw_c(first, last, &entry_values[first_key->index].object, key_search.first->key[key_search.second + 1]); } else { first = object_read_bencode_c(first, last, &entry_values[first_key->index].object); } if (++first_key == last_key || strcmp(first_key->key, (first_key - 1)->key) != 0) { while (first != last) { if (*first == 'e') { first++; break; } first = object_read_bencode_skip_c(first, last); } break; } } break; } default: throw internal_error("static_map_read_bencode_c: key_search.first->key[base] returned invalid character."); }; } throw torrent::bencode_error("Invalid bencode data."); } void static_map_write_bencode_c_values(object_write_data_t* output, const static_map_entry_type* entry_values, const static_map_mapping_type* first_key, const static_map_mapping_type* last_key) { const char* prev_key = NULL; static_map_stack_type stack[8]; static_map_stack_type* stack_itr = stack; stack_itr->clear(); object_write_bencode_c_char(output, 'd'); while (first_key != last_key) { if (entry_values[first_key->index].object.is_empty()) { first_key++; continue; } // Compare the keys to see if they are part of the same // dictionaries/lists. unsigned int base_size = rak::count_base(first_key->key, first_key->key + stack_itr->next_key, prev_key, prev_key + stack_itr->next_key); while (base_size < stack_itr->next_key) { object_write_bencode_c_char(output, 'e'); stack_itr--; } const char* key_begin = first_key->key + stack_itr->next_key; do { const char* key_end = first_key->find_key_end(key_begin); if (stack_itr->obj_type == Object::TYPE_MAP) object_write_bencode_c_obj_string(output, key_begin, std::distance(key_begin, key_end)); // Check if '::' or '[' were found... if (*key_end == ':' && *(key_end + 1) == ':') { (++stack_itr)->set_key_index(std::distance(first_key->key, key_begin), std::distance(first_key->key, key_end), 2); key_begin = key_end + 2; object_write_bencode_c_char(output, 'd'); continue; } // Handle "foo[]..." entries. We iterate once for each "foo[]" // found in the key list. if (*key_end == '[' && *(key_end + 1) == ']') { (++stack_itr)->set_key_index(std::distance(first_key->key, key_begin), std::distance(first_key->key, key_end), 2, Object::TYPE_LIST); key_begin = key_end + 2; object_write_bencode_c_char(output, 'l'); continue; } // We have a leaf object. if (*key_end != '\0' && *key_end != '*') throw internal_error("static_map_type key is invalid."); object_write_bencode_c_object(output, &entry_values[first_key->index].object, 0); break; } while (true); prev_key = (first_key++)->key; } do { object_write_bencode_c_char(output, 'e'); } while (stack_itr-- != stack); } object_buffer_t static_map_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const static_map_entry_type* entry_values, const static_map_mapping_type* first_key, const static_map_mapping_type* last_key) { object_write_data_t output; output.writeFunc = writeFunc; output.data = data; output.buffer = buffer; output.pos = buffer.first; static_map_write_bencode_c_values(&output, entry_values, first_key, last_key); // DEBUG: Remove this. // { // torrent::Object obj; // if (object_read_bencode_c(output.buffer.first, output.pos, &obj) != output.pos) { // std::string escaped = rak::copy_escape_html(output.buffer.first, output.pos); // //char buffer[1024]; // // sprintf(buffer, "Verified wrong, %u, '%u', '%s'.", std::distanescaped.c_str()); // throw torrent::internal_error("Invalid bencode data generated: '" + escaped + "'"); // } // } // Don't flush the buffer. if (output.pos == output.buffer.first) return output.buffer; return output.writeFunc(output.data, object_buffer_t(output.buffer.first, output.pos)); } } libtorrent-0.13.6/src/torrent/object_stream.h000066400000000000000000000130351257211073700212520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_OBJECT_STREAM_H #define LIBTORRENT_OBJECT_STREAM_H #include #include #include namespace torrent { class raw_string; std::string object_sha1(const Object* object) LIBTORRENT_EXPORT; raw_string object_read_bencode_c_string(const char* first, const char* last) LIBTORRENT_EXPORT; // Assumes the stream's locale has been set to POSIX or C. Max depth // is 1024, this ensures files consisting of only 'l' don't segfault // the client. void object_read_bencode(std::istream* input, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT; const char* object_read_bencode_c(const char* first, const char* last, Object* object, uint32_t depth = 0) LIBTORRENT_EXPORT; const char* object_read_bencode_skip_c(const char* first, const char* last) LIBTORRENT_EXPORT; std::istream& operator >> (std::istream& input, Object& object) LIBTORRENT_EXPORT; std::ostream& operator << (std::ostream& output, const Object& object) LIBTORRENT_EXPORT; // object_buffer_t contains the start and end of the buffer. typedef std::pair object_buffer_t; typedef object_buffer_t (*object_write_t)(void* data, object_buffer_t buffer); // Assumes the stream's locale has been set to POSIX or C. void object_write_bencode(std::ostream* output, const Object* object, uint32_t skip_mask = 0) LIBTORRENT_EXPORT; object_buffer_t object_write_bencode(char* first, char* last, const Object* object, uint32_t skip_mask = 0) LIBTORRENT_EXPORT; object_buffer_t object_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const Object* object, uint32_t skip_mask = 0) LIBTORRENT_EXPORT; // To char buffer. 'data' is NULL. object_buffer_t object_write_to_buffer(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; object_buffer_t object_write_to_sha1(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; object_buffer_t object_write_to_stream(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; // Measures bencode size, 'data' is uint64_t*. object_buffer_t object_write_to_size(void* data, object_buffer_t buffer) LIBTORRENT_EXPORT; // // static_map operations: // template class static_map_type; struct static_map_mapping_type; struct static_map_entry_type; // Convert buffer to static key map. Inlined because we don't want // a separate wrapper function for each template argument. template inline const char* static_map_read_bencode(const char* first, const char* last, static_map_type& object) { return static_map_read_bencode_c(first, last, object.values(), object.keys, object.keys + object.size); }; template inline object_buffer_t static_map_write_bencode_c(object_write_t writeFunc, void* data, object_buffer_t buffer, const static_map_type& object) { return static_map_write_bencode_c_wrap(writeFunc, data, buffer, object.values(), object.keys, object.keys + object.size); } const char* static_map_read_bencode_c(const char* first, const char* last, static_map_entry_type* entry_values, const static_map_mapping_type* first_key, const static_map_mapping_type* last_key) LIBTORRENT_EXPORT; object_buffer_t static_map_write_bencode_c_wrap(object_write_t writeFunc, void* data, object_buffer_t buffer, const static_map_entry_type* entry_values, const static_map_mapping_type* first_key, const static_map_mapping_type* last_key) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/path.cc000066400000000000000000000044231257211073700175240ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "exceptions.h" #include "path.h" namespace torrent { void Path::insert_path(iterator pos, const std::string& path) { std::string::const_iterator first = path.begin(); std::string::const_iterator last; while (first != path.end()) { pos = insert(pos, std::string(first, (last = std::find(first, path.end(), '/')))); if (last == path.end()) return; first = last; first++; } } std::string Path::as_string() const { if (empty()) return std::string(); std::string s; for (const_iterator itr = begin(); itr != end(); ++itr) { s += '/'; s += *itr; } return s; } } libtorrent-0.13.6/src/torrent/path.h000066400000000000000000000070721257211073700173710ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PATH_H #define LIBTORRENT_PATH_H #include #include #include namespace torrent { // Use a blank first path to get root and "." to get current dir. class LIBTORRENT_EXPORT Path : private std::vector { public: typedef std::vector base_type; typedef base_type::value_type value_type; typedef base_type::pointer pointer; typedef base_type::const_pointer const_pointer; typedef base_type::reference reference; typedef base_type::const_reference const_reference; typedef base_type::size_type size_type; typedef base_type::difference_type difference_type; typedef base_type::allocator_type allocator_type; typedef base_type::iterator iterator; typedef base_type::reverse_iterator reverse_iterator; typedef base_type::const_iterator const_iterator; typedef base_type::const_reverse_iterator const_reverse_iterator; using base_type::clear; using base_type::empty; using base_type::size; using base_type::reserve; using base_type::front; using base_type::back; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::push_back; using base_type::at; using base_type::operator[]; void insert_path(iterator pos, const std::string& path); // Return the path as a string with '/' deliminator. The deliminator // is only inserted between path elements. std::string as_string() const; const std::string encoding() const { return m_encoding; } void set_encoding(const std::string& enc) { m_encoding = enc; } base_type* base() { return this; } const base_type* base() const { return this; } private: std::string m_encoding; }; } #endif libtorrent-0.13.6/src/torrent/peer/000077500000000000000000000000001257211073700172115ustar00rootroot00000000000000libtorrent-0.13.6/src/torrent/peer/Makefile.am000066400000000000000000000011031257211073700212400ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_torrentpeer.la libsub_torrentpeer_la_SOURCES = \ choke_status.h \ client_info.cc \ client_info.h \ client_list.cc \ client_list.h \ connection_list.cc \ connection_list.h \ peer.cc \ peer.h \ peer_info.cc \ peer_info.h \ peer_list.cc \ peer_list.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir) libtorrentincludedir = $(includedir)/torrent/peer libtorrentinclude_HEADERS = \ choke_status.h \ client_info.h \ client_list.h \ connection_list.h \ peer.h \ peer_info.h \ peer_list.h libtorrent-0.13.6/src/torrent/peer/choke_status.h000066400000000000000000000061051257211073700220600ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DOWNLOAD_CHOKE_STATUS_H #define LIBTORRENT_DOWNLOAD_CHOKE_STATUS_H #include namespace torrent { class group_entry; class choke_status { public: choke_status() : m_group_entry(NULL), m_queued(false), m_unchoked(false), m_snubbed(false), m_timeLastChoke(0) {} group_entry* entry() const { return m_group_entry; } void set_entry(group_entry* grp_ent) { m_group_entry = grp_ent; } bool queued() const { return m_queued; } void set_queued(bool s) { m_queued = s; } bool choked() const { return !m_unchoked; } bool unchoked() const { return m_unchoked; } void set_unchoked(bool s) { m_unchoked = s; } bool snubbed() const { return m_snubbed; } void set_snubbed(bool s) { m_snubbed = s; } int64_t time_last_choke() const { return m_timeLastChoke; } void set_time_last_choke(int64_t t) { m_timeLastChoke = t; } private: // TODO: Use flags. group_entry* m_group_entry; bool m_queued; bool m_unchoked; bool m_snubbed; int64_t m_timeLastChoke; }; } #endif libtorrent-0.13.6/src/torrent/peer/client_info.cc000066400000000000000000000076141257211073700220210ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "client_info.h" namespace torrent { unsigned int ClientInfo::key_size(id_type id) { switch (id) { case TYPE_AZUREUS: return 2; case TYPE_COMPACT: case TYPE_MAINLINE: return 1; default: return 0; } } unsigned int ClientInfo::version_size(id_type id) { switch (id) { case TYPE_AZUREUS: return 4; case TYPE_COMPACT: case TYPE_MAINLINE: return 3; default: return 0; } } bool ClientInfo::less_intersects(const ClientInfo& left, const ClientInfo& right) { if (left.type() > right.type()) return false; else if (left.type() < right.type()) return true; int keyComp = std::memcmp(left.key(), right.key(), ClientInfo::max_key_size); return keyComp < 0 || (keyComp == 0 && std::memcmp(left.upper_version(), right.version(), ClientInfo::max_version_size) < 0); } bool ClientInfo::less_disjoint(const ClientInfo& left, const ClientInfo& right) { if (left.type() > right.type()) return false; else if (left.type() < right.type()) return true; int keyComp = std::memcmp(left.key(), right.key(), ClientInfo::max_key_size); return keyComp < 0 || (keyComp == 0 && std::memcmp(left.version(), right.upper_version(), ClientInfo::max_version_size) < 0); } bool ClientInfo::greater_intersects(const ClientInfo& left, const ClientInfo& right) { return less_intersects(right, left); } bool ClientInfo::greater_disjoint(const ClientInfo& left, const ClientInfo& right) { return less_disjoint(right, left); } bool ClientInfo::intersects(const ClientInfo& left, const ClientInfo& right) { return left.type() == right.type() && std::memcmp(left.key(), right.key(), ClientInfo::max_key_size) == 0 && std::memcmp(left.version(), right.upper_version(), ClientInfo::max_version_size) <= 0 && std::memcmp(left.upper_version(), right.version(), ClientInfo::max_version_size) >= 0; } inline bool ClientInfo::equal_to(const ClientInfo& left, const ClientInfo& right) { return left.type() == right.type() && std::memcmp(left.key(), right.key(), ClientInfo::max_key_size) == 0 && std::memcmp(left.version(), right.version(), ClientInfo::max_version_size) == 0 && std::memcmp(left.upper_version(), right.upper_version(), ClientInfo::max_version_size) == 0; } } libtorrent-0.13.6/src/torrent/peer/client_info.h000066400000000000000000000106231257211073700216550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_CLIENT_INFO_H #define LIBTORRENT_PEER_CLIENT_INFO_H #include namespace torrent { class LIBTORRENT_EXPORT ClientInfo { public: friend class ClientList; typedef enum { TYPE_UNKNOWN, TYPE_AZUREUS, TYPE_COMPACT, TYPE_MAINLINE, TYPE_MAX_SIZE } id_type; struct info_type { const char* m_shortDescription; }; static const uint32_t max_key_size = 2; static const uint32_t max_version_size = 4; id_type type() const { return m_type; } const char* key() const { return m_key; } const char* version() const { return m_version; } const char* upper_version() const { return m_upperVersion; } const char* short_description() const { return m_info->m_shortDescription; } void set_short_description(const char* str) { m_info->m_shortDescription = str; } static unsigned int key_size(id_type id); static unsigned int version_size(id_type id); // The intersect/disjoint postfix indicates what kind of equivalence // comparison we get when using !less && !greater. static bool less_intersects(const ClientInfo& left, const ClientInfo& right); static bool less_disjoint(const ClientInfo& left, const ClientInfo& right); static bool greater_intersects(const ClientInfo& left, const ClientInfo& right); static bool greater_disjoint(const ClientInfo& left, const ClientInfo& right); static bool intersects(const ClientInfo& left, const ClientInfo& right); static bool equal_to(const ClientInfo& left, const ClientInfo& right); protected: void set_type(id_type t) { m_type = t; } info_type* info() const { return m_info; } void set_info(info_type* ptr) { m_info = ptr; } char* mutable_key() { return m_key; } char* mutable_version() { return m_version; } char* mutable_upper_version() { return m_upperVersion; } private: id_type m_type; char m_key[max_key_size]; // The client version. The ClientInfo object in ClientList bounds // the versions that this object applies to. When the user searches // the ClientList for a client id, m_version will be set to the // actual client version. char m_version[max_version_size]; char m_upperVersion[max_version_size]; // We don't really care about cleaning up this as deleting an entry // form ClientList shouldn't happen. info_type* m_info; }; } #endif libtorrent-0.13.6/src/torrent/peer/client_list.cc000066400000000000000000000243031257211073700220330ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "client_list.h" #include "exceptions.h" #include "hash_string.h" namespace torrent { ClientList::ClientList() { insert(ClientInfo::TYPE_UNKNOWN, NULL, NULL, NULL); // Move this to a seperate initialize function in libtorrent. // Sorted by popularity to optimize search. This list is heavily // biased by my own prejudices, and not at all based on facts. // First batch of clients. insert_helper(ClientInfo::TYPE_AZUREUS, "AZ", NULL, NULL, "Azureus"); insert_helper(ClientInfo::TYPE_AZUREUS, "BC", NULL, NULL, "BitComet"); insert_helper(ClientInfo::TYPE_AZUREUS, "CD", NULL, NULL, "Enhanced CTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "KT", NULL, NULL, "KTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "LT", NULL, NULL, "libtorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "lt", NULL, NULL, "libTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "UT", NULL, NULL, "uTorrent"); insert_helper(ClientInfo::TYPE_MAINLINE, "M", NULL, NULL, "Mainline"); insert_helper(ClientInfo::TYPE_COMPACT, "T", NULL, NULL, "BitTornado"); // Second batch of clients. insert_helper(ClientInfo::TYPE_AZUREUS, "AR", NULL, NULL, "Arctic"); insert_helper(ClientInfo::TYPE_AZUREUS, "BB", NULL, NULL, "BitBuddy"); insert_helper(ClientInfo::TYPE_AZUREUS, "BX", NULL, NULL, "Bittorrent X"); insert_helper(ClientInfo::TYPE_AZUREUS, "BS", NULL, NULL, "BTSlave"); insert_helper(ClientInfo::TYPE_AZUREUS, "CT", NULL, NULL, "CTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "DE", NULL, NULL, "DelugeTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "ES", NULL, NULL, "Electric Sheep"); insert_helper(ClientInfo::TYPE_AZUREUS, "LP", NULL, NULL, "Lphant"); insert_helper(ClientInfo::TYPE_AZUREUS, "MT", NULL, NULL, "MoonlightTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "MP", NULL, NULL, "MooPolice"); insert_helper(ClientInfo::TYPE_AZUREUS, "QT", NULL, NULL, "Qt 4 Torrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "RT", NULL, NULL, "Retriever"); insert_helper(ClientInfo::TYPE_AZUREUS, "SZ", NULL, NULL, "Shareaza"); insert_helper(ClientInfo::TYPE_AZUREUS, "SS", NULL, NULL, "SwarmScope"); insert_helper(ClientInfo::TYPE_AZUREUS, "SB", NULL, NULL, "Swiftbit"); insert_helper(ClientInfo::TYPE_AZUREUS, "TN", NULL, NULL, "TorrentDotNET"); insert_helper(ClientInfo::TYPE_AZUREUS, "TS", NULL, NULL, "Torrentstorm"); insert_helper(ClientInfo::TYPE_AZUREUS, "TR", NULL, NULL, "Transmission"); insert_helper(ClientInfo::TYPE_AZUREUS, "XT", NULL, NULL, "XanTorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "ZT", NULL, NULL, "ZipTorrent"); insert_helper(ClientInfo::TYPE_COMPACT, "A", NULL, NULL, "ABC"); insert_helper(ClientInfo::TYPE_COMPACT, "S", NULL, NULL, "Shadow's client"); insert_helper(ClientInfo::TYPE_COMPACT, "U", NULL, NULL, "UPnP NAT BitTorrent"); insert_helper(ClientInfo::TYPE_COMPACT, "O", NULL, NULL, "Osprey Permaseed"); // Third batch of clients. insert_helper(ClientInfo::TYPE_AZUREUS, "AX", NULL, NULL, "BitPump"); insert_helper(ClientInfo::TYPE_AZUREUS, "BF", NULL, NULL, "BitFlu"); insert_helper(ClientInfo::TYPE_AZUREUS, "BG", NULL, NULL, "BTG"); insert_helper(ClientInfo::TYPE_AZUREUS, "BR", NULL, NULL, "BitRocket"); insert_helper(ClientInfo::TYPE_AZUREUS, "EB", NULL, NULL, "EBit"); insert_helper(ClientInfo::TYPE_AZUREUS, "HL", NULL, NULL, "Halite"); insert_helper(ClientInfo::TYPE_AZUREUS, "qB", NULL, NULL, "qBittorrent"); insert_helper(ClientInfo::TYPE_AZUREUS, "UL", NULL, NULL, "uLeecher!"); insert_helper(ClientInfo::TYPE_AZUREUS, "XL", NULL, NULL, "XeiLun"); insert_helper(ClientInfo::TYPE_COMPACT, "R", NULL, NULL, "Tribler"); } ClientList::~ClientList() { for (iterator itr = begin(), last = end(); itr != last; ++itr) delete itr->info(); } ClientList::iterator ClientList::insert(ClientInfo::id_type type, const char* key, const char* version, const char* upperVersion) { if (type >= ClientInfo::TYPE_MAX_SIZE) throw input_error("Invalid client info id type."); ClientInfo clientInfo; clientInfo.set_type(type); clientInfo.set_info(new ClientInfo::info_type); clientInfo.set_short_description("Unknown"); std::memset(clientInfo.mutable_key(), 0, ClientInfo::max_key_size); if (key == NULL) std::memset(clientInfo.mutable_key(), 0, ClientInfo::max_key_size); else std::strncpy(clientInfo.mutable_key(), key, ClientInfo::max_key_size); if (version != NULL) std::memcpy(clientInfo.mutable_version(), version, ClientInfo::max_version_size); else std::memset(clientInfo.mutable_version(), 0, ClientInfo::max_version_size); if (upperVersion != NULL) std::memcpy(clientInfo.mutable_upper_version(), upperVersion, ClientInfo::max_version_size); else std::memset(clientInfo.mutable_upper_version(), -1, ClientInfo::max_version_size); return base_type::insert(end(), clientInfo); } ClientList::iterator ClientList::insert_helper(ClientInfo::id_type type, const char* key, const char* version, const char* upperVersion, const char* shortDescription) { char newKey[ClientInfo::max_key_size]; std::memset(newKey, 0, ClientInfo::max_key_size); std::memcpy(newKey, key, ClientInfo::key_size(type)); iterator itr = insert(type, newKey, version, upperVersion); itr->set_short_description(shortDescription); return itr; } // Make this properly honor const-ness. bool ClientList::retrieve_id(ClientInfo* dest, const HashString& id) const { if (id[0] == '-' && id[7] == '-' && std::isalpha(id[1]) && std::isalpha(id[2]) && std::isxdigit(id[3]) && std::isxdigit(id[4]) && std::isxdigit(id[5]) && std::isxdigit(id[6])) { dest->set_type(ClientInfo::TYPE_AZUREUS); dest->mutable_key()[0] = id[1]; dest->mutable_key()[1] = id[2]; for (int i = 0; i < 4; i++) dest->mutable_version()[i] = dest->mutable_upper_version()[i] = rak::hexchar_to_value(id[3 + i]); } else if (std::isalpha(id[0]) && id[4] == '-' && std::isxdigit(id[1]) && std::isxdigit(id[2]) && std::isxdigit(id[3])) { dest->set_type(ClientInfo::TYPE_COMPACT); dest->mutable_key()[0] = id[0]; dest->mutable_key()[1] = '\0'; dest->mutable_version()[0] = dest->mutable_upper_version()[0] = rak::hexchar_to_value(id[1]); dest->mutable_version()[1] = dest->mutable_upper_version()[1] = rak::hexchar_to_value(id[2]); dest->mutable_version()[2] = dest->mutable_upper_version()[2] = rak::hexchar_to_value(id[3]); dest->mutable_version()[3] = dest->mutable_upper_version()[3] = '\0'; } else if (std::isalpha(id[0]) && std::isdigit(id[1]) && id[2] == '-' && std::isdigit(id[3]) && (id[6] == '-' || id[7] == '-')) { dest->set_type(ClientInfo::TYPE_MAINLINE); dest->mutable_key()[0] = id[0]; dest->mutable_key()[1] = '\0'; dest->mutable_version()[0] = dest->mutable_upper_version()[0] = rak::hexchar_to_value(id[1]); if (id[4] == '-' && std::isdigit(id[5]) && id[6] == '-') { dest->mutable_version()[1] = dest->mutable_upper_version()[1] = rak::hexchar_to_value(id[3]); dest->mutable_version()[2] = dest->mutable_upper_version()[2] = rak::hexchar_to_value(id[5]); dest->mutable_version()[3] = dest->mutable_upper_version()[3] = '\0'; } else if (std::isdigit(id[4]) && id[5] == '-' && std::isdigit(id[6]) && id[7] == '-') { dest->mutable_version()[1] = dest->mutable_upper_version()[1] = rak::hexchar_to_value(id[3]) * 10 + rak::hexchar_to_value(id[4]); dest->mutable_version()[2] = dest->mutable_upper_version()[2] = rak::hexchar_to_value(id[6]); dest->mutable_version()[3] = dest->mutable_upper_version()[3] = '\0'; } else { *dest = *begin(); std::memset(dest->mutable_upper_version(), 0, ClientInfo::max_version_size); return false; } } else { // And then the incompatible idiots that make life difficult for us // others. (There's '3' schemes to choose from already...) // // Or not... // The first entry always contains the default ClientInfo. *dest = *begin(); std::memset(dest->mutable_upper_version(), 0, ClientInfo::max_version_size); return false; } const_iterator itr = std::find_if(begin() + 1, end(), std::tr1::bind(&ClientInfo::intersects, *dest, std::tr1::placeholders::_1)); if (itr == end()) dest->set_info(begin()->info()); else dest->set_info(itr->info()); return true; } void ClientList::retrieve_unknown(ClientInfo* dest) const { *dest = *begin(); std::memset(dest->mutable_upper_version(), 0, ClientInfo::max_version_size); } } libtorrent-0.13.6/src/torrent/peer/client_list.h000066400000000000000000000057151257211073700217030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_CLIENT_LIST_H #define LIBTORRENT_PEER_CLIENT_LIST_H #include #include namespace torrent { class LIBTORRENT_EXPORT ClientList : private std::vector { public: typedef std::vector base_type; typedef uint32_t size_type; using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::size; using base_type::empty; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; ClientList(); ~ClientList(); iterator insert(ClientInfo::id_type type, const char* key, const char* version, const char* upperVersion); // Helper functions which only require the key to be as long as the // key for that specific id type. iterator insert_helper(ClientInfo::id_type type, const char* key, const char* version, const char* upperVersion, const char* shortDescription); bool retrieve_id(ClientInfo* dest, const HashString& id) const; void retrieve_unknown(ClientInfo* dest) const; }; } #endif libtorrent-0.13.6/src/torrent/peer/connection_list.cc000066400000000000000000000205131257211073700227130ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "download/download_main.h" #include "net/address_list.h" #include "protocol/peer_connection_base.h" #include "torrent/exceptions.h" #include "torrent/download_info.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "connection_list.h" #include "peer.h" #include "peer_info.h" // When a peer is connected it should be removed from the list of // available peers. // // When a peer is disconnected the torrent should rebalance the choked // peers and connect to new ones if possible. namespace torrent { ConnectionList::ConnectionList(DownloadMain* download) : m_download(download), m_minSize(50), m_maxSize(100) { } void ConnectionList::clear() { std::for_each(begin(), end(), rak::on(std::mem_fun(&Peer::m_ptr), rak::call_delete())); base_type::clear(); m_disconnectQueue.clear(); } bool ConnectionList::want_connection(PeerInfo* p, Bitfield* bitfield) { if (m_download->file_list()->is_done() || m_download->initial_seeding() != NULL) return !bitfield->is_all_set(); if (!m_download->info()->is_accepting_seeders()) return !bitfield->is_all_set(); return true; } PeerConnectionBase* ConnectionList::insert(PeerInfo* peerInfo, const SocketFd& fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions) { if (size() >= m_maxSize) return NULL; PeerConnectionBase* peerConnection = m_slotNewConnection(encryptionInfo->is_encrypted()); if (peerConnection == NULL || bitfield == NULL) throw internal_error("ConnectionList::insert(...) received a NULL pointer."); peerInfo->set_connection(peerConnection); peerInfo->set_last_connection(cachedTime.seconds()); peerConnection->initialize(m_download, peerInfo, fd, bitfield, encryptionInfo, extensions); if (!peerConnection->get_fd().is_valid()) { delete peerConnection; return NULL; } base_type::push_back(peerConnection); m_download->info()->change_flags(DownloadInfo::flag_accepting_new_peers, size() < m_maxSize); rak::slot_list_call(m_signalConnected, peerConnection); return peerConnection; } ConnectionList::iterator ConnectionList::erase(iterator pos, int flags) { if (pos < begin() || pos >= end()) throw internal_error("ConnectionList::erase(...) iterator out or range."); if (flags & disconnect_delayed) { m_disconnectQueue.push_back((*pos)->id()); if (!m_download->delay_disconnect_peers().is_queued()) priority_queue_insert(&taskScheduler, &m_download->delay_disconnect_peers(), cachedTime); return pos; } PeerConnectionBase* peerConnection = (*pos)->m_ptr(); // The connection must be erased from the list before the signal is // emited otherwise some listeners might do stuff with the // assumption that the connection will remain in the list. *pos = base_type::back(); base_type::pop_back(); m_download->info()->change_flags(DownloadInfo::flag_accepting_new_peers, size() < m_maxSize); rak::slot_list_call(m_signalDisconnected, peerConnection); // Before of after the signal? peerConnection->cleanup(); peerConnection->mutable_peer_info()->set_connection(NULL); m_download->peer_list()->disconnected(peerConnection->mutable_peer_info(), PeerList::disconnect_set_time); // Delete after the signal to ensure the address of 'v' doesn't get // allocated for a different PCB in the signal. delete peerConnection; return pos; } void ConnectionList::erase(Peer* p, int flags) { erase(std::find(begin(), end(), p), flags); } void ConnectionList::erase(PeerInfo* peerInfo, int flags) { iterator itr = std::find(begin(), end(), peerInfo->connection()); if (itr == end()) return; erase(itr, flags); } void ConnectionList::erase_remaining(iterator pos, int flags) { flags |= disconnect_quick; // Need to do it one connection at the time to ensure that when the // signal is emited everything is in a valid state. while (pos != end()) erase(--end(), flags); m_download->info()->change_flags(DownloadInfo::flag_accepting_new_peers, size() < m_maxSize); } void ConnectionList::erase_seeders() { erase_remaining(std::partition(begin(), end(), rak::on(std::mem_fun(&Peer::c_ptr), std::mem_fun(&PeerConnectionBase::is_not_seeder))), disconnect_unwanted); } void ConnectionList::disconnect_queued() { for (queue_type::const_iterator itr = m_disconnectQueue.begin(), last = m_disconnectQueue.end(); itr != last; itr++) { ConnectionList::iterator conn_itr = find(m_disconnectQueue.back().c_str()); if (conn_itr != end()) erase(conn_itr, 0); } m_disconnectQueue = queue_type(); } struct connection_list_less { bool operator () (const Peer* p1, const Peer* p2) const { return *rak::socket_address::cast_from(p1->peer_info()->socket_address()) < *rak::socket_address::cast_from(p2->peer_info()->socket_address()); } bool operator () (const rak::socket_address& sa1, const Peer* p2) const { return sa1 < *rak::socket_address::cast_from(p2->peer_info()->socket_address()); } bool operator () (const Peer* p1, const rak::socket_address& sa2) const { return *rak::socket_address::cast_from(p1->peer_info()->socket_address()) < sa2; } }; ConnectionList::iterator ConnectionList::find(const char* id) { return std::find_if(begin(), end(), rak::equal(*HashString::cast_from(id), rak::on(std::mem_fun(&Peer::m_ptr), rak::on(std::mem_fun(&PeerConnectionBase::peer_info), std::mem_fun(&PeerInfo::id))))); } ConnectionList::iterator ConnectionList::find(const sockaddr* sa) { return std::find_if(begin(), end(), rak::equal_ptr(rak::socket_address::cast_from(sa), rak::on(std::mem_fun(&Peer::m_ptr), rak::on(std::mem_fun(&PeerConnectionBase::peer_info), std::mem_fun(&PeerInfo::socket_address))))); } void ConnectionList::set_difference(AddressList* l) { std::sort(begin(), end(), connection_list_less()); l->erase(std::set_difference(l->begin(), l->end(), begin(), end(), l->begin(), connection_list_less()), l->end()); } void ConnectionList::set_min_size(size_type v) { if (v > (1 << 16)) throw input_error("Min peer connections must be between 0 and 2^16."); m_minSize = v; } void ConnectionList::set_max_size(size_type v) { if (v > (1 << 16)) throw input_error("Max peer connections must be between 0 and 2^16."); if (v == 0) v = choke_queue::unlimited; m_maxSize = v; m_download->info()->change_flags(DownloadInfo::flag_accepting_new_peers, size() < m_maxSize); //m_download->choke_group()->up_queue()->balance(); } } libtorrent-0.13.6/src/torrent/peer/connection_list.h000066400000000000000000000127431257211073700225630ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_CONNECTION_LIST_H #define LIBTORRENT_PEER_CONNECTION_LIST_H #include #include #include #include #include namespace torrent { class AddressList; class Bitfield; class DownloadMain; class DownloadWrapper; class Peer; class PeerConnectionBase; class PeerInfo; class ProtocolExtension; class SocketFd; class EncryptionInfo; class HandshakeManager; class LIBTORRENT_EXPORT ConnectionList : private std::vector { public: friend class DownloadMain; friend class DownloadWrapper; friend class HandshakeManager; typedef std::vector base_type; typedef std::vector queue_type; typedef uint32_t size_type; typedef std::tr1::function slot_peer_type; typedef std::list signal_peer_type; typedef PeerConnectionBase* (*slot_new_conn_type)(bool encrypted); using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::const_iterator; using base_type::const_reverse_iterator; // using base_type::size; using base_type::empty; using base_type::front; using base_type::back; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; // Make sure any change here match PeerList's flags. static const int disconnect_available = (1 << 0); static const int disconnect_quick = (1 << 1); static const int disconnect_unwanted = (1 << 2); static const int disconnect_delayed = (1 << 3); ConnectionList(DownloadMain* download); // Make these protected? iterator erase(iterator pos, int flags); void erase(Peer* p, int flags); void erase(PeerInfo* peerInfo, int flags); void erase_remaining(iterator pos, int flags); void erase_seeders(); iterator find(const char* id); iterator find(const sockaddr* sa); size_type min_size() const { return m_minSize; } void set_min_size(size_type v); size_type size() const { return base_type::size(); } size_type max_size() const { return m_maxSize; } void set_max_size(size_type v); // Removes from 'l' addresses that are already connected to. Assumes // 'l' is sorted and unique. void set_difference(AddressList* l); signal_peer_type& signal_connected() { return m_signalConnected; } signal_peer_type& signal_disconnected() { return m_signalDisconnected; } // Move to protected: void slot_new_connection(slot_new_conn_type s) { m_slotNewConnection = s; } protected: // Does not do the usual cleanup done by 'erase'. void clear() LIBTORRENT_NO_EXPORT; bool want_connection(PeerInfo* p, Bitfield* bitfield); // Returns NULL if the connection was not added, the caller is then // responsible for cleaning up 'fd'. // // Clean this up, don't use this many arguments. PeerConnectionBase* insert(PeerInfo* p, const SocketFd& fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions) LIBTORRENT_NO_EXPORT; void disconnect_queued() LIBTORRENT_NO_EXPORT; private: ConnectionList(const ConnectionList&) LIBTORRENT_NO_EXPORT; void operator = (const ConnectionList&) LIBTORRENT_NO_EXPORT; DownloadMain* m_download; size_type m_minSize; size_type m_maxSize; signal_peer_type m_signalConnected; signal_peer_type m_signalDisconnected; slot_new_conn_type m_slotNewConnection; queue_type m_disconnectQueue; }; } #endif libtorrent-0.13.6/src/torrent/peer/peer.cc000066400000000000000000000100401257211073700204460ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "data/block.h" #include "data/block_transfer.h" #include "download/download_main.h" #include "protocol/peer_chunks.h" #include "protocol/peer_connection_base.h" #include "torrent/download/choke_queue.h" #include "exceptions.h" #include "connection_list.h" #include "peer.h" #include "peer_info.h" #include "rate.h" namespace torrent { bool Peer::is_encrypted() const { return c_ptr()->is_encrypted(); } bool Peer::is_obfuscated() const { return c_ptr()->is_obfuscated(); } bool Peer::is_up_choked() const { return c_ptr()->is_up_choked(); } bool Peer::is_up_interested() const { return c_ptr()->is_down_interested(); } bool Peer::is_down_choked() const { return !c_ptr()->is_down_remote_unchoked(); } bool Peer::is_down_choked_limited() const { return !c_ptr()->is_down_local_unchoked(); } bool Peer::is_down_queued() const { return c_ptr()->is_down_queued(); } bool Peer::is_down_interested() const { return c_ptr()->is_up_interested(); } bool Peer::is_snubbed() const { return c_ptr()->is_up_snubbed(); } bool Peer::is_banned() const { return m_peerInfo->failed_counter() >= 64; } void Peer::set_snubbed(bool v) { m_ptr()->set_upload_snubbed(v); } void Peer::set_banned(bool v) { m_peerInfo->set_failed_counter(v ? 64 : 0); } const Rate* Peer::down_rate() const { return c_ptr()->c_peer_chunks()->download_throttle()->rate(); } const Rate* Peer::up_rate() const { return c_ptr()->c_peer_chunks()->upload_throttle()->rate(); } const Rate* Peer::peer_rate() const { return c_ptr()->c_peer_chunks()->peer_rate(); } const Bitfield* Peer::bitfield() const { return c_ptr()->c_peer_chunks()->bitfield(); } uint32_t Peer::incoming_queue_size() const { return c_ptr()->request_list()->queued_size(); } uint32_t Peer::outgoing_queue_size() const { return c_ptr()->c_peer_chunks()->upload_queue()->size(); } uint32_t Peer::chunks_done() const { return c_ptr()->c_peer_chunks()->bitfield()->size_set(); } const BlockTransfer* Peer::transfer() const { if (c_ptr()->request_list()->transfer() != NULL) return c_ptr()->request_list()->transfer(); else if (!c_ptr()->request_list()->queued_empty()) return c_ptr()->request_list()->queued_front(); else return NULL; } void Peer::disconnect(int flags) { m_ptr()->download()->connection_list()->erase(this, flags); } } libtorrent-0.13.6/src/torrent/peer/peer.h000066400000000000000000000102111257211073700203100ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_H #define LIBTORRENT_PEER_H #include #include #include namespace torrent { class PeerConnectionBase; // == and = operators works as expected. // The Peer class is a wrapper around the internal peer class. This // peer class may be invalidated during a torrent::work call. So if // you keep a list or single instances in the client, you need to // listen to the appropriate signals from the download to keep up to // date. class LIBTORRENT_EXPORT Peer { public: // Does not check if it has been removed from the download. bool is_incoming() const { return peer_info()->is_incoming(); } bool is_encrypted() const; bool is_obfuscated() const; bool is_up_choked() const; bool is_up_interested() const; bool is_down_choked() const; bool is_down_choked_limited() const; bool is_down_queued() const; bool is_down_interested() const; bool is_snubbed() const; bool is_banned() const; void set_snubbed(bool v); void set_banned(bool v); const HashString& id() const { return peer_info()->id(); } const char* options() const { return peer_info()->options(); } const sockaddr* address() const { return peer_info()->socket_address(); } const Rate* down_rate() const; const Rate* up_rate() const; const Rate* peer_rate() const; const Bitfield* bitfield() const; const BlockTransfer* transfer() const; uint32_t incoming_queue_size() const; uint32_t outgoing_queue_size() const; uint32_t chunks_done() const; uint32_t failed_counter() const { return peer_info()->failed_counter(); } void disconnect(int flags); // // New interface: // const PeerInfo* peer_info() const { return m_peerInfo; } PeerConnectionBase* m_ptr() { return reinterpret_cast(this); } const PeerConnectionBase* c_ptr() const { return reinterpret_cast(this); } protected: Peer() {} virtual ~Peer() {} Peer(const Peer&); void operator = (const Peer&); bool operator == (const Peer& p) const; PeerInfo* m_peerInfo; }; } #endif libtorrent-0.13.6/src/torrent/peer/peer_info.cc000066400000000000000000000054351257211073700214750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "protocol/extensions.h" #include "protocol/peer_connection_base.h" #include "utils/instrumentation.h" #include "exceptions.h" #include "peer_info.h" namespace torrent { // Move this to peer_info.cc when these are made into the public API. // // TODO: Use a safer socket address parameter. PeerInfo::PeerInfo(const sockaddr* address) : m_flags(0), m_failedCounter(0), m_transferCounter(0), m_lastConnection(0), m_lastHandshake(0), m_listenPort(0), m_connection(NULL) { rak::socket_address* sa = new rak::socket_address(); *sa = *rak::socket_address::cast_from(address); m_address = sa->c_sockaddr(); } PeerInfo::~PeerInfo() { // if (m_transferCounter != 0) // throw internal_error("PeerInfo::~PeerInfo() m_transferCounter != 0."); instrumentation_update(INSTRUMENTATION_TRANSFER_PEER_INFO_UNACCOUNTED, m_transferCounter); if (is_blocked()) throw internal_error("PeerInfo::~PeerInfo() peer is blocked."); delete rak::socket_address::cast_from(m_address); } void PeerInfo::set_port(uint16_t port) { rak::socket_address::cast_from(m_address)->set_port(port); } } libtorrent-0.13.6/src/torrent/peer/peer_info.h000066400000000000000000000150161257211073700213330ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_INFO_H #define LIBTORRENT_PEER_INFO_H #include #include #include namespace torrent { class LIBTORRENT_EXPORT PeerInfo { public: friend class ConnectionList; friend class Handshake; friend class HandshakeManager; friend class InitialSeeding; friend class PeerList; friend class ProtocolExtension; static const int flag_connected = (1 << 0); static const int flag_incoming = (1 << 1); static const int flag_handshake = (1 << 2); static const int flag_blocked = (1 << 3); // For initial seeding. static const int flag_restart = (1 << 4); static const int flag_unwanted = (1 << 5); static const int flag_preferred = (1 << 6); static const int mask_ip_table = flag_unwanted | flag_preferred; PeerInfo(const sockaddr* address); ~PeerInfo(); bool is_connected() const { return m_flags & flag_connected; } bool is_incoming() const { return m_flags & flag_incoming; } bool is_handshake() const { return m_flags & flag_handshake; } bool is_blocked() const { return m_flags & flag_blocked; } bool is_restart() const { return m_flags & flag_restart; } bool is_unwanted() const { return m_flags & flag_unwanted; } bool is_preferred() const { return m_flags & flag_preferred; } int flags() const { return m_flags; } const HashString& id() const { return m_id; } const char* id_hex() const { return m_id_hex; } const ClientInfo& client_info() const { return m_clientInfo; } const char* options() const { return m_options; } const sockaddr* socket_address() const { return m_address; } uint16_t listen_port() const { return m_listenPort; } uint32_t failed_counter() const { return m_failedCounter; } void set_failed_counter(uint32_t c) { m_failedCounter = c; } uint32_t transfer_counter() const { return m_transferCounter; } uint32_t last_connection() const { return m_lastConnection; } void set_last_connection(uint32_t tvsec) { m_lastConnection = tvsec; } uint32_t last_handshake() const { return m_lastHandshake; } void set_last_handshake(uint32_t tvsec) { m_lastHandshake = tvsec; } bool supports_dht() const { return m_options[7] & 0x01; } bool supports_extensions() const { return m_options[5] & 0x10; } // // Internal to libTorrent: // PeerConnectionBase* connection() { return m_connection; } void inc_transfer_counter(); void dec_transfer_counter(); protected: void set_flags(int flags) { m_flags |= flags; } void unset_flags(int flags) { m_flags &= ~flags; } HashString& mutable_id() { return m_id; } char* mutable_id_hex() { return m_id_hex; } ClientInfo& mutable_client_info() { return m_clientInfo; } void set_port(uint16_t port) LIBTORRENT_NO_EXPORT; void set_listen_port(uint16_t port) { m_listenPort = port; } char* set_options() { return m_options; } void set_connection(PeerConnectionBase* c) { m_connection = c; } private: PeerInfo(const PeerInfo&); void operator = (const PeerInfo&); // Replace id with a char buffer, or a cheap struct? int m_flags; HashString m_id; char m_id_hex[40]; ClientInfo m_clientInfo; char m_options[8]; uint32_t m_failedCounter; uint32_t m_transferCounter; uint32_t m_lastConnection; uint32_t m_lastHandshake; uint16_t m_listenPort; // Replace this with a union. Since the user never copies PeerInfo // it should be safe to not require sockaddr_in6 to be part of it. sockaddr* m_address; PeerConnectionBase* m_connection; }; inline void PeerInfo::inc_transfer_counter() { if (m_transferCounter == ~uint32_t()) throw internal_error("PeerInfo::inc_transfer_counter() m_transferCounter overflow"); m_transferCounter++; } inline void PeerInfo::dec_transfer_counter() { if (m_transferCounter == 0) throw internal_error("PeerInfo::dec_transfer_counter() m_transferCounter underflow"); m_transferCounter--; } } #endif libtorrent-0.13.6/src/torrent/peer/peer_list.cc000066400000000000000000000321161257211073700215110ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include "download/available_list.h" #include "torrent/peer/client_list.h" #include "torrent/utils/log.h" #include "download_info.h" #include "exceptions.h" #include "globals.h" #include "manager.h" #include "peer_info.h" #include "peer_list.h" #define LT_LOG_EVENTS(log_fmt, ...) \ lt_log_print_info(LOG_PEER_LIST_EVENTS, m_info, "peer_list", log_fmt, __VA_ARGS__); #define LT_LOG_SA_FMT "'%s:%" PRIu16 "'" namespace torrent { ipv4_table PeerList::m_ipv4_table; bool socket_address_less(const sockaddr* s1, const sockaddr* s2) { const rak::socket_address* sa1 = rak::socket_address::cast_from(s1); const rak::socket_address* sa2 = rak::socket_address::cast_from(s2); if (sa1->family() != sa2->family()) return sa1->family() < sa2->family(); else if (sa1->family() == rak::socket_address::af_inet) // Sort by hardware byte order to ensure proper ordering for // humans. return sa1->sa_inet()->address_h() < sa2->sa_inet()->address_h(); else // When we implement INET6 handling, embed the ipv4 address in // the ipv6 address. throw internal_error("socket_address_key(...) tried to compare an invalid family type."); } inline bool socket_address_key::is_comparable(const sockaddr* sa) { return rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet; } struct peer_list_equal_port : public std::binary_function { bool operator () (PeerList::reference p, uint16_t port) { return rak::socket_address::cast_from(p.second->socket_address())->port() == port; } }; // // PeerList: // PeerList::PeerList() : m_available_list(new AvailableList) { } PeerList::~PeerList() { LT_LOG_EVENTS("deleting list total:%" PRIuPTR " available:%" PRIuPTR, size(), m_available_list->size()); std::for_each(begin(), end(), rak::on(rak::mem_ref(&value_type::second), rak::call_delete())); base_type::clear(); m_info = NULL; delete m_available_list; } void PeerList::set_info(DownloadInfo* info) { m_info = info; LT_LOG_EVENTS("creating list", 0); } PeerInfo* PeerList::insert_address(const sockaddr* sa, int flags) { if (!socket_address_key::is_comparable(sa)) { LT_LOG_EVENTS("address not comparable", 0); return NULL; } const rak::socket_address* address = rak::socket_address::cast_from(sa); range_type range = base_type::equal_range(sa); // Do some special handling if we got a new port number but the // address was present. // // What we do depends on the flags, but for now just allow one // PeerInfo per address key and do nothing. if (range.first != range.second) { LT_LOG_EVENTS("address already exists " LT_LOG_SA_FMT, address->address_str().c_str(), address->port()); return NULL; } PeerInfo* peerInfo = new PeerInfo(sa); peerInfo->set_listen_port(address->port()); peerInfo->set_flags(m_ipv4_table.at(address->sa_inet()->address_h()) & PeerInfo::mask_ip_table); manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info()); base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo)); if ((flags & address_available) && peerInfo->listen_port() != 0) { m_available_list->push_back(address); LT_LOG_EVENTS("added available address " LT_LOG_SA_FMT, address->address_str().c_str(), address->port()); } else { LT_LOG_EVENTS("added unavailable address " LT_LOG_SA_FMT, address->address_str().c_str(), address->port()); } return peerInfo; } inline bool socket_address_less_rak(const rak::socket_address& s1, const rak::socket_address& s2) { return socket_address_less(s1.c_sockaddr(), s2.c_sockaddr()); } uint32_t PeerList::insert_available(const void* al) { const AddressList* addressList = static_cast(al); uint32_t inserted = 0; uint32_t invalid = 0; uint32_t unneeded = 0; uint32_t updated = 0; if (m_available_list->size() + addressList->size() > m_available_list->capacity()) m_available_list->reserve(m_available_list->size() + addressList->size() + 128); // Optimize this so that we don't traverse the tree for every // insert, since we know 'al' is sorted. AddressList::const_iterator itr = addressList->begin(); AddressList::const_iterator last = addressList->end(); AvailableList::const_iterator availItr = m_available_list->begin(); AvailableList::const_iterator availLast = m_available_list->end(); for (; itr != last; itr++) { if (!socket_address_key::is_comparable(itr->c_sockaddr()) || itr->port() == 0) { invalid++; continue; } availItr = std::find_if(availItr, availLast, rak::bind2nd(std::ptr_fun(&socket_address_less_rak), *itr)); if (availItr != availLast && !socket_address_less(availItr->c_sockaddr(), itr->c_sockaddr())) { // The address is already in m_available_list, so don't bother // going further. unneeded++; continue; } // Check if the peerinfo exists, if it does, check if we would // ever want to connect. Just update the timer for the last // availability notice if the peer isn't really ideal, but might // be used in an emergency. range_type range = base_type::equal_range(itr->c_sockaddr()); if (range.first != range.second) { // Add some logic here to select the best PeerInfo, but for now // just assume the first one is the only one that exists. PeerInfo* peerInfo = range.first->second; if (peerInfo->listen_port() == 0) peerInfo->set_port(itr->port()); if (peerInfo->connection() != NULL || peerInfo->last_handshake() + 600 > (uint32_t)cachedTime.seconds()) { updated++; continue; } // If the peer has sent us bad chunks or we just connected or // tried to do so a few minutes ago, only update its // availability timer. } // Should we perhaps add to available list even though we don't // want the peer, just to ensure we don't need to search for the // PeerInfo every time it gets reported. Though I'd assume it // won't happen often enough to be worth it. inserted++; m_available_list->push_back(&*itr); } LT_LOG_EVENTS("inserted peers" " inserted:%" PRIu32 " invalid:%" PRIu32 " unneeded:%" PRIu32 " updated:%" PRIu32 " total:%" PRIuPTR " available:%" PRIuPTR, inserted, invalid, unneeded, updated, size(), m_available_list->size()); return inserted; } uint32_t PeerList::available_list_size() const { return m_available_list->size(); } PeerInfo* PeerList::connected(const sockaddr* sa, int flags) { const rak::socket_address* address = rak::socket_address::cast_from(sa); if (!socket_address_key::is_comparable(sa)) return NULL; int filter_value = m_ipv4_table.at(address->sa_inet()->address_h()); // We should also remove any PeerInfo objects already for this // address. if ((filter_value & PeerInfo::flag_unwanted)) return NULL; PeerInfo* peerInfo; range_type range = base_type::equal_range(sa); if (range.first == range.second) { // Create a new entry. peerInfo = new PeerInfo(sa); peerInfo->set_flags(filter_value & PeerInfo::mask_ip_table); base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo)); } else if (!range.first->second->is_connected()) { // Use an old entry. peerInfo = range.first->second; peerInfo->set_port(address->port()); } else { // Make sure we don't end up throwing away the port the host is // actually listening on, when there may be several simultaneous // connection attempts to/from different ports. // // This also ensure we can connect to peers running on the same // host as the tracker. if (flags & connect_keep_handshakes && range.first->second->is_handshake() && rak::socket_address::cast_from(range.first->second->socket_address())->port() != address->port()) m_available_list->buffer()->push_back(*address); return NULL; } if (flags & connect_filter_recent && peerInfo->last_handshake() + 600 > (uint32_t)cachedTime.seconds()) return NULL; if (!(flags & connect_incoming)) peerInfo->set_listen_port(address->port()); if (flags & connect_incoming) peerInfo->set_flags(PeerInfo::flag_incoming); else peerInfo->unset_flags(PeerInfo::flag_incoming); peerInfo->set_flags(PeerInfo::flag_connected); peerInfo->set_last_handshake(cachedTime.seconds()); return peerInfo; } // Make sure we properly clear port when disconnecting. void PeerList::disconnected(PeerInfo* p, int flags) { range_type range = base_type::equal_range(p->socket_address()); iterator itr = std::find_if(range.first, range.second, rak::equal(p, rak::mem_ref(&value_type::second))); if (itr == range.second) { if (std::find_if(base_type::begin(), base_type::end(), rak::equal(p, rak::mem_ref(&value_type::second))) == base_type::end()) throw internal_error("PeerList::disconnected(...) itr == range.second, doesn't exist."); else throw internal_error("PeerList::disconnected(...) itr == range.second, not in the range."); } disconnected(itr, flags); } PeerList::iterator PeerList::disconnected(iterator itr, int flags) { if (itr == base_type::end()) throw internal_error("PeerList::disconnected(...) itr == end()."); if (!itr->second->is_connected()) throw internal_error("PeerList::disconnected(...) !itr->is_connected()."); if (itr->second->transfer_counter() != 0) { // Currently we only log these as it only affects the culling of // peers. LT_LOG_EVENTS("disconnected with non-zero transfer counter (%" PRIu32 ") for peer %40s", itr->second->transfer_counter(), itr->second->id_hex()); } itr->second->unset_flags(PeerInfo::flag_connected); // Replace the socket address port with the listening port so that // future outgoing connections will connect to the right port. itr->second->set_port(0); if (flags & disconnect_set_time) itr->second->set_last_connection(cachedTime.seconds()); if (flags & disconnect_available && itr->second->listen_port() != 0) m_available_list->push_back(rak::socket_address::cast_from(itr->second->socket_address())); // Do magic to get rid of unneeded entries. return ++itr; } uint32_t PeerList::cull_peers(int flags) { uint32_t counter = 0; uint32_t timer; if (flags & cull_old) timer = cachedTime.seconds() - 24 * 60 * 60; else timer = 0; for (iterator itr = base_type::begin(); itr != base_type::end(); ) { if (itr->second->is_connected() || itr->second->transfer_counter() != 0 || // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! itr->second->last_connection() >= timer || (flags & cull_keep_interesting && (itr->second->failed_counter() != 0 || itr->second->is_blocked()))) { itr++; continue; } // ##################### TODO: LOG CULLING OF PEERS ###################### // *** AND STATS OF DISCONNECTING PEERS (the peer info...)... // The key is a pointer to a member in the value, although the key // shouldn't actually be used in erase (I think), just ot be safe // we delete it after erase. iterator tmp = itr++; PeerInfo* peerInfo = tmp->second; base_type::erase(tmp); delete peerInfo; counter++; } return counter; } } libtorrent-0.13.6/src/torrent/peer/peer_list.h000066400000000000000000000116611257211073700213550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_PEER_LIST_H #define LIBTORRENT_PEER_LIST_H #include #include #include namespace torrent { class DownloadInfo; typedef extents ipv4_table; bool socket_address_less(const sockaddr* s1, const sockaddr* s2); // Unique key for the address, excluding port numbers etc. class LIBTORRENT_EXPORT socket_address_key { public: socket_address_key(const sockaddr* sa) : m_sockaddr(sa) {} inline static bool is_comparable(const sockaddr* sa); bool operator < (const socket_address_key& sa) const { return socket_address_less(m_sockaddr, sa.m_sockaddr); } private: const sockaddr* m_sockaddr; }; class LIBTORRENT_EXPORT PeerList : private std::multimap { public: friend class DownloadWrapper; friend class Handshake; friend class HandshakeManager; friend class ConnectionList; typedef std::multimap base_type; typedef std::pair range_type; using base_type::value_type; using base_type::reference; using base_type::difference_type; using base_type::iterator; using base_type::reverse_iterator; using base_type::const_iterator; using base_type::const_reverse_iterator; using base_type::size; using base_type::empty; static const int address_available = (1 << 0); static const int connect_incoming = (1 << 0); static const int connect_keep_handshakes = (1 << 1); static const int connect_filter_recent = (1 << 2); // Make sure any change here match ConnectionList's flags. static const int disconnect_available = (1 << 0); static const int disconnect_quick = (1 << 1); static const int disconnect_unwanted = (1 << 2); static const int disconnect_set_time = (1 << 3); static const int cull_old = (1 << 0); static const int cull_keep_interesting = (1 << 1); PeerList(); ~PeerList(); PeerInfo* insert_address(const sockaddr* address, int flags); // This will be used internally only for the moment. uint32_t insert_available(const void* al) LIBTORRENT_NO_EXPORT; static ipv4_table* ipv4_filter() { return &m_ipv4_table; } AvailableList* available_list() { return m_available_list; } uint32_t available_list_size() const; uint32_t cull_peers(int flags); const_iterator begin() const { return base_type::begin(); } const_iterator end() const { return base_type::end(); } const_reverse_iterator rbegin() const { return base_type::rbegin(); } const_reverse_iterator rend() const { return base_type::rend(); } protected: void set_info(DownloadInfo* info) LIBTORRENT_NO_EXPORT; // Insert, or find a PeerInfo with socket address 'sa'. Returns end // if no more connections are allowed from that host. PeerInfo* connected(const sockaddr* sa, int flags) LIBTORRENT_NO_EXPORT; void disconnected(PeerInfo* p, int flags) LIBTORRENT_NO_EXPORT; iterator disconnected(iterator itr, int flags) LIBTORRENT_NO_EXPORT; private: PeerList(const PeerList&); void operator = (const PeerList&); static ipv4_table m_ipv4_table; DownloadInfo* m_info; AvailableList* m_available_list; }; } #endif libtorrent-0.13.6/src/torrent/poll.h000066400000000000000000000073421257211073700174030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_POLL_H #define LIBTORRENT_TORRENT_POLL_H #include #include namespace torrent { class Event; class LIBTORRENT_EXPORT Poll { public: typedef std::tr1::function slot_poll; static const int poll_worker_thread = 0x1; static const uint32_t flag_waive_global_lock = 0x1; Poll() : m_flags(0) {} virtual ~Poll() {} uint32_t flags() const { return m_flags; } void set_flags(uint32_t flags) { m_flags = flags; } virtual unsigned int do_poll(int64_t timeout_usec, int flags = 0) = 0; // The open max value is used when initializing libtorrent, it // should be less than or equal to sysconf(_SC_OPEN_MAX). virtual uint32_t open_max() const = 0; // Event::get_fd() is guaranteed to be valid and remain constant // from open(...) is called to close(...) returns. The implementor // of this class should not open nor close the file descriptor. virtual void open(Event* event) = 0; virtual void close(Event* event) = 0; // More efficient interface when closing the file descriptor. // Automatically removes the event from all polls. // Event::get_fd() may or may not be closed already. virtual void closed(Event* event) = 0; // Functions for checking whetever the Event is listening to r/w/e? virtual bool in_read(Event* event) = 0; virtual bool in_write(Event* event) = 0; virtual bool in_error(Event* event) = 0; // These functions may be called on 'event's that might, or might // not, already be in the set. virtual void insert_read(Event* event) = 0; virtual void insert_write(Event* event) = 0; virtual void insert_error(Event* event) = 0; virtual void remove_read(Event* event) = 0; virtual void remove_write(Event* event) = 0; virtual void remove_error(Event* event) = 0; // Add one for HUP? Or would that be in event? static slot_poll& slot_create_poll() { return m_slot_create_poll; } private: static slot_poll m_slot_create_poll; uint32_t m_flags; }; } #endif libtorrent-0.13.6/src/torrent/poll_epoll.cc000066400000000000000000000255271257211073700207410ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include #include #include "torrent.h" #include "poll_epoll.h" #include "utils/log.h" #include "utils/thread_base.h" #include "rak/error_number.h" #include "rak/timer.h" #ifdef USE_EPOLL #include #endif #define LT_LOG_EVENT(event, log_level, log_fmt, ...) \ lt_log_print(LOG_SOCKET_##log_level, "epoll->%s(%i): " log_fmt, event->type_name(), event->file_descriptor(), __VA_ARGS__); namespace torrent { #ifdef USE_EPOLL inline uint32_t PollEPoll::event_mask(Event* e) { Table::value_type entry = m_table[e->file_descriptor()]; return entry.second != e ? 0 : entry.first; } inline void PollEPoll::set_event_mask(Event* e, uint32_t m) { m_table[e->file_descriptor()] = Table::value_type(m, e); } inline void PollEPoll::modify(Event* event, int op, uint32_t mask) { if (event_mask(event) == mask) return; LT_LOG_EVENT(event, DEBUG, "Modify event: op:%hx mask:%hx.", op, mask); epoll_event e; e.data.u64 = 0; // Make valgrind happy? Remove please. e.data.fd = event->file_descriptor(); e.events = mask; set_event_mask(event, mask); if (epoll_ctl(m_fd, op, event->file_descriptor(), &e)) { // Socket was probably already closed. Ignore this. if (op == EPOLL_CTL_DEL && errno == ENOENT) return; // Handle some libcurl/c-ares bugs by retrying once. int retry = op; if (op == EPOLL_CTL_ADD && errno == EEXIST) { retry = EPOLL_CTL_MOD; errno = 0; } else if (op == EPOLL_CTL_MOD && errno == ENOENT) { retry = EPOLL_CTL_ADD; errno = 0; } if (errno || epoll_ctl(m_fd, retry, event->file_descriptor(), &e)) { char errmsg[1024]; snprintf(errmsg, sizeof(errmsg), "PollEPoll::modify(...) epoll_ctl(%d, %d -> %d, %d, [%p:%x]) = %d: %s", m_fd, op, retry, event->file_descriptor(), event, mask, errno, strerror(errno)); throw internal_error(errmsg); } } } PollEPoll* PollEPoll::create(int maxOpenSockets) { int fd = epoll_create(maxOpenSockets); if (fd == -1) return NULL; return new PollEPoll(fd, 1024, maxOpenSockets); } PollEPoll::PollEPoll(int fd, int maxEvents, int maxOpenSockets) : m_fd(fd), m_maxEvents(maxEvents), m_waitingEvents(0), m_events(new epoll_event[m_maxEvents]) { m_table.resize(maxOpenSockets); } PollEPoll::~PollEPoll() { m_table.clear(); delete [] m_events; ::close(m_fd); } int PollEPoll::poll(int msec) { int nfds = epoll_wait(m_fd, m_events, m_maxEvents, msec); if (nfds == -1) return -1; return m_waitingEvents = nfds; } // We check m_table to make sure the Event is still listening to the // event, so it is safe to remove Event's while in working. // // TODO: Do we want to guarantee if the Event has been removed from // some event but not closed, it won't call that event? Think so... unsigned int PollEPoll::perform() { unsigned int count = 0; for (epoll_event *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) { if (itr->data.fd < 0 || (size_t)itr->data.fd >= m_table.size()) continue; if ((flags() & flag_waive_global_lock) && thread_base::global_queue_size() != 0) thread_base::waive_global_lock(); Table::iterator evItr = m_table.begin() + itr->data.fd; // Each branch must check for data.ptr != NULL to allow the socket // to remove itself between the calls. // // TODO: Make it so that it checks that read/write is wanted, that // it wasn't removed from one of them but not closed. if (itr->events & EPOLLERR && evItr->second != NULL && evItr->first & EPOLLERR) { count++; evItr->second->event_error(); } if (itr->events & EPOLLIN && evItr->second != NULL && evItr->first & EPOLLIN) { count++; evItr->second->event_read(); } if (itr->events & EPOLLOUT && evItr->second != NULL && evItr->first & EPOLLOUT) { count++; evItr->second->event_write(); } } m_waitingEvents = 0; return count; } unsigned int PollEPoll::do_poll(int64_t timeout_usec, int flags) { rak::timer timeout = rak::timer(timeout_usec); timeout += 10; if (!(flags & poll_worker_thread)) { thread_base::release_global_lock(); thread_base::entering_main_polling(); } int status = poll((timeout.usec() + 999) / 1000); if (!(flags & poll_worker_thread)) { thread_base::leaving_main_polling(); thread_base::acquire_global_lock(); } if (status == -1 && rak::error_number::current().value() != rak::error_number::e_intr) throw std::runtime_error("Poll::work(): " + std::string(rak::error_number::current().c_str())); return perform(); } uint32_t PollEPoll::open_max() const { return m_table.size(); } void PollEPoll::open(Event* event) { LT_LOG_EVENT(event, DEBUG, "Open event.", 0); if (event_mask(event) != 0) throw internal_error("PollEPoll::open(...) called but the file descriptor is active"); } void PollEPoll::close(Event* event) { LT_LOG_EVENT(event, DEBUG, "Close event.", 0); if (event_mask(event) != 0) throw internal_error("PollEPoll::close(...) called but the file descriptor is active"); m_table[event->file_descriptor()] = Table::value_type(); // Clear the event list just in case we open a new socket with the // same fd while in the middle of calling PollEPoll::perform. for (epoll_event *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) if (itr->data.fd == event->file_descriptor()) itr->events = 0; } void PollEPoll::closed(Event* event) { LT_LOG_EVENT(event, DEBUG, "Closed event.", 0); // Kernel removes closed FDs automatically, so just clear the mask and remove it from pending calls. // Don't touch if the FD was re-used before we received the close notification. if (m_table[event->file_descriptor()].second == event) m_table[event->file_descriptor()] = Table::value_type(); // for (epoll_event *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) { // if (itr->data.fd == event->file_descriptor()) // itr->events = 0; // } } // Use custom defines for EPOLL* to make the below code compile with // and with epoll. bool PollEPoll::in_read(Event* event) { return event_mask(event) & EPOLLIN; } bool PollEPoll::in_write(Event* event) { return event_mask(event) & EPOLLOUT; } bool PollEPoll::in_error(Event* event) { return event_mask(event) & EPOLLERR; } void PollEPoll::insert_read(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert read.", 0); modify(event, event_mask(event) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, event_mask(event) | EPOLLIN); } void PollEPoll::insert_write(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert write.", 0); modify(event, event_mask(event) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, event_mask(event) | EPOLLOUT); } void PollEPoll::insert_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert error.", 0); modify(event, event_mask(event) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, event_mask(event) | EPOLLERR); } void PollEPoll::remove_read(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove read.", 0); uint32_t mask = event_mask(event) & ~EPOLLIN; modify(event, mask ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, mask); } void PollEPoll::remove_write(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove write.", 0); uint32_t mask = event_mask(event) & ~EPOLLOUT; modify(event, mask ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, mask); } void PollEPoll::remove_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove error.", 0); uint32_t mask = event_mask(event) & ~EPOLLERR; modify(event, mask ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, mask); } #else // USE_EPOLL PollEPoll* PollEPoll::create(int maxOpenSockets) { return NULL; } PollEPoll::~PollEPoll() {} int PollEPoll::poll(int msec) { throw internal_error("An PollEPoll function was called, but it is disabled."); } unsigned int PollEPoll::perform() { throw internal_error("An PollEPoll function was called, but it is disabled."); } unsigned int PollEPoll::do_poll(int64_t timeout_usec, int flags) { throw internal_error("An PollEPoll function was called, but it is disabled."); } uint32_t PollEPoll::open_max() const { throw internal_error("An PollEPoll function was called, but it is disabled."); } void PollEPoll::open(torrent::Event* event) {} void PollEPoll::close(torrent::Event* event) {} void PollEPoll::closed(torrent::Event* event) {} bool PollEPoll::in_read(torrent::Event* event) { throw internal_error("An PollEPoll function was called, but it is disabled."); } bool PollEPoll::in_write(torrent::Event* event) { throw internal_error("An PollEPoll function was called, but it is disabled."); } bool PollEPoll::in_error(torrent::Event* event) { throw internal_error("An PollEPoll function was called, but it is disabled."); } void PollEPoll::insert_read(torrent::Event* event) {} void PollEPoll::insert_write(torrent::Event* event) {} void PollEPoll::insert_error(torrent::Event* event) {} void PollEPoll::remove_read(torrent::Event* event) {} void PollEPoll::remove_write(torrent::Event* event) {} void PollEPoll::remove_error(torrent::Event* event) {} PollEPoll::PollEPoll(int fd, int maxEvents, int maxOpenSockets) { throw internal_error("An PollEPoll function was called, but it is disabled."); } #endif // USE_EPOLL } libtorrent-0.13.6/src/torrent/poll_epoll.h000066400000000000000000000072371257211073700206010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_POLL_EPOLL_H #define LIBTORRENT_TORRENT_POLL_EPOLL_H #include #include struct epoll_event; namespace torrent { class LIBTORRENT_EXPORT PollEPoll : public torrent::Poll { public: typedef std::vector > Table; static PollEPoll* create(int maxOpenSockets); virtual ~PollEPoll(); int poll(int msec); unsigned int perform(); unsigned int do_poll(int64_t timeout_usec, int flags = 0); int file_descriptor() { return m_fd; } virtual uint32_t open_max() const; // torrent::Event::get_fd() is guaranteed to be valid and remain constant // from open(...) is called to close(...) returns. virtual void open(torrent::Event* event); virtual void close(torrent::Event* event); // torrent::Event::get_fd() was closed outside of our control. virtual void closed(torrent::Event* event); // Functions for checking whetever the torrent::Event is listening to r/w/e? virtual bool in_read(torrent::Event* event); virtual bool in_write(torrent::Event* event); virtual bool in_error(torrent::Event* event); // These functions may be called on 'event's that might, or might // not, already be in the set. virtual void insert_read(torrent::Event* event); virtual void insert_write(torrent::Event* event); virtual void insert_error(torrent::Event* event); virtual void remove_read(torrent::Event* event); virtual void remove_write(torrent::Event* event); virtual void remove_error(torrent::Event* event); private: PollEPoll(int fd, int maxEvents, int maxOpenSockets); inline uint32_t event_mask(Event* e); inline void set_event_mask(Event* e, uint32_t m); inline void modify(torrent::Event* event, int op, uint32_t mask); int m_fd; int m_maxEvents; int m_waitingEvents; Table m_table; epoll_event* m_events; }; } #endif libtorrent-0.13.6/src/torrent/poll_kqueue.cc000066400000000000000000000320441257211073700211150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version.213 // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include #include #include #include "poll_kqueue.h" #include "torrent.h" #include "rak/functional.h" #include "rak/timer.h" #include "rak/error_number.h" #include "utils/log.h" #include "utils/thread_base.h" #ifdef USE_KQUEUE #include #include #include #include #endif #include #define LT_LOG_EVENT(event, log_level, log_fmt, ...) \ lt_log_print(LOG_SOCKET_##log_level, "kqueue->%s(%i): " log_fmt, event->type_name(), event->file_descriptor(), __VA_ARGS__); namespace torrent { #ifdef USE_KQUEUE inline uint32_t PollKQueue::event_mask(Event* e) { assert(e->file_descriptor() != -1); Table::value_type entry = m_table[e->file_descriptor()]; return entry.second != e ? 0 : entry.first; } inline void PollKQueue::set_event_mask(Event* e, uint32_t m) { assert(e->file_descriptor() != -1); m_table[e->file_descriptor()] = Table::value_type(m, e); } void PollKQueue::flush_events() { timespec timeout = { 0, 0 }; int nfds = kevent(m_fd, m_changes, m_changedEvents, m_events + m_waitingEvents, m_maxEvents - m_waitingEvents, &timeout); if (nfds == -1) throw internal_error("PollKQueue::flush_events() error: " + std::string(rak::error_number::current().c_str())); m_changedEvents = 0; m_waitingEvents += nfds; } void PollKQueue::modify(Event* event, unsigned short op, short mask) { LT_LOG_EVENT(event, DEBUG, "Modify event: op:%hx mask:%hx changed:%u.", op, mask, m_changedEvents); // Flush the changed filters to the kernel if the buffer is full. if (m_changedEvents == m_maxEvents) { if (kevent(m_fd, m_changes, m_changedEvents, NULL, 0, NULL) == -1) throw internal_error("PollKQueue::modify() error: " + std::string(rak::error_number::current().c_str())); m_changedEvents = 0; } struct kevent* itr = m_changes + (m_changedEvents++); assert(event == m_table[event->file_descriptor()].second); EV_SET(itr, event->file_descriptor(), mask, op, 0, 0, event); } PollKQueue* PollKQueue::create(int maxOpenSockets) { int fd = kqueue(); if (fd == -1) return NULL; return new PollKQueue(fd, 1024, maxOpenSockets); } PollKQueue::PollKQueue(int fd, int maxEvents, int maxOpenSockets) : m_fd(fd), m_maxEvents(maxEvents), m_waitingEvents(0), m_changedEvents(0), m_stdinEvent(NULL) { m_events = new struct kevent[m_maxEvents]; m_changes = new struct kevent[maxOpenSockets]; m_table.resize(maxOpenSockets); } PollKQueue::~PollKQueue() { m_table.clear(); delete [] m_events; delete [] m_changes; ::close(m_fd); } int PollKQueue::poll(int msec) { #if KQUEUE_SOCKET_ONLY if (m_stdinEvent != NULL) { // Flush all changes to the kqueue poll before we start select // polling, so that they get included. if (m_changedEvents != 0) flush_events(); if (poll_select(msec) == -1) return -1; // The timeout was already handled in select(). msec = 0; } #endif timespec timeout = { msec / 1000, (msec % 1000) * 1000000 }; int nfds = kevent(m_fd, m_changes, m_changedEvents, m_events + m_waitingEvents, m_maxEvents - m_waitingEvents, &timeout); // Clear the changed events even on fail as we might have received a // signal or similar, and the changed events have already been // consumed. // // There's a chance a bad changed event could make kevent return -1, // but it won't as long as there is room enough in m_events. m_changedEvents = 0; if (nfds == -1) return -1; m_waitingEvents += nfds; return nfds; } #if KQUEUE_SOCKET_ONLY int PollKQueue::poll_select(int msec) { if (m_waitingEvents >= m_maxEvents) return 0; timeval selectTimeout = { msec / 1000, (msec % 1000) * 1000 }; // If m_fd isn't the first FD opened by the client and has // a low number, using ::poll() here would perform better. // // This kinda assumes fd_set's internal type is int. int readBuffer[m_fd + 1]; fd_set* readSet = (fd_set*)&readBuffer; std::memset(readBuffer, 0, m_fd + 1); FD_SET(0, readSet); FD_SET(m_fd, readSet); int nfds = select(m_fd + 1, readSet, NULL, NULL, &selectTimeout); if (nfds == -1) return nfds; if (FD_ISSET(0, readSet)) { m_events[m_waitingEvents].ident = 0; m_events[m_waitingEvents].filter = EVFILT_READ; m_events[m_waitingEvents].flags = 0; m_waitingEvents++; } return nfds; } #endif unsigned int PollKQueue::perform() { unsigned int count = 0; for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) { if (itr->ident >= m_table.size()) continue; if ((flags() & flag_waive_global_lock) && thread_base::global_queue_size() != 0) thread_base::waive_global_lock(); Table::iterator evItr = m_table.begin() + itr->ident; if ((itr->flags & EV_ERROR) && evItr->second != NULL) { if (evItr->first & flag_error) evItr->second->event_error(); count++; continue; } // Also check current mask. if (itr->filter == EVFILT_READ && evItr->second != NULL && evItr->first & flag_read) { count++; evItr->second->event_read(); } if (itr->filter == EVFILT_WRITE && evItr->second != NULL && evItr->first & flag_write) { count++; evItr->second->event_write(); } } m_waitingEvents = 0; return count; } unsigned int PollKQueue::do_poll(int64_t timeout_usec, int flags) { rak::timer timeout = rak::timer(timeout_usec); timeout += 10; if (!(flags & poll_worker_thread)) { thread_base::release_global_lock(); thread_base::entering_main_polling(); } int status = poll((timeout.usec() + 999) / 1000); if (!(flags & poll_worker_thread)) { thread_base::leaving_main_polling(); thread_base::acquire_global_lock(); } if (status == -1 && rak::error_number::current().value() != rak::error_number::e_intr) throw std::runtime_error("Poll::work(): " + std::string(rak::error_number::current().c_str())); return perform(); } uint32_t PollKQueue::open_max() const { return m_table.size(); } void PollKQueue::open(Event* event) { LT_LOG_EVENT(event, DEBUG, "Open event.", 0); if (event_mask(event) != 0) throw internal_error("PollKQueue::open(...) called but the file descriptor is active"); } void PollKQueue::close(Event* event) { LT_LOG_EVENT(event, DEBUG, "close event", 0); #if KQUEUE_SOCKET_ONLY if (event->file_descriptor() == 0) { m_stdinEvent = NULL; return; } #endif if (event_mask(event) != 0) throw internal_error("PollKQueue::close(...) called but the file descriptor is active"); m_table[event->file_descriptor()] = Table::value_type(); // Shouldn't be needed anymore. for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) if (itr->udata == event) itr->udata = NULL; m_changedEvents = std::remove_if(m_changes, m_changes + m_changedEvents, rak::equal(event, rak::mem_ref(&kevent::udata))) - m_changes; } void PollKQueue::closed(Event* event) { LT_LOG_EVENT(event, DEBUG, "closed event", 0); #if KQUEUE_SOCKET_ONLY if (event->file_descriptor() == 0) { m_stdinEvent = NULL; return; } #endif // Kernel removes closed FDs automatically, so just clear the mask // and remove it from pending calls. Don't touch if the FD was // re-used before we received the close notification. if (m_table[event->file_descriptor()].second == event) m_table[event->file_descriptor()] = Table::value_type(); // Shouldn't be needed anymore. for (struct kevent *itr = m_events, *last = m_events + m_waitingEvents; itr != last; ++itr) if (itr->udata == event) itr->udata = NULL; m_changedEvents = std::remove_if(m_changes, m_changes + m_changedEvents, rak::equal(event, rak::mem_ref(&kevent::udata))) - m_changes; } // Use custom defines for EPOLL* to make the below code compile with // and with epoll. bool PollKQueue::in_read(Event* event) { return event_mask(event) & flag_read; } bool PollKQueue::in_write(Event* event) { return event_mask(event) & flag_write; } bool PollKQueue::in_error(Event* event) { return event_mask(event) & flag_error; } void PollKQueue::insert_read(Event* event) { if (event_mask(event) & flag_read) return; LT_LOG_EVENT(event, DEBUG, "Insert read.", 0); set_event_mask(event, event_mask(event) | flag_read); #if KQUEUE_SOCKET_ONLY if (event->file_descriptor() == 0) { m_stdinEvent = event; return; } #endif modify(event, EV_ADD, EVFILT_READ); } void PollKQueue::insert_write(Event* event) { if (event_mask(event) & flag_write) return; LT_LOG_EVENT(event, DEBUG, "Insert write.", 0); set_event_mask(event, event_mask(event) | flag_write); modify(event, EV_ADD, EVFILT_WRITE); } void PollKQueue::insert_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert error.", 0); } void PollKQueue::remove_read(Event* event) { if (!(event_mask(event) & flag_read)) return; LT_LOG_EVENT(event, DEBUG, "Remove read.", 0); set_event_mask(event, event_mask(event) & ~flag_read); #if KQUEUE_SOCKET_ONLY if (event->file_descriptor() == 0) { m_stdinEvent = NULL; return; } #endif modify(event, EV_DELETE, EVFILT_READ); } void PollKQueue::remove_write(Event* event) { if (!(event_mask(event) & flag_write)) return; LT_LOG_EVENT(event, DEBUG, "Remove write.", 0); set_event_mask(event, event_mask(event) & ~flag_write); modify(event, EV_DELETE, EVFILT_WRITE); } void PollKQueue::remove_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove error.", 0); } #else // USE_QUEUE PollKQueue* PollKQueue::create(__UNUSED int maxOpenSockets) { return NULL; } PollKQueue::~PollKQueue() { } int PollKQueue::poll(__UNUSED int msec) { throw internal_error("An PollKQueue function was called, but it is disabled."); } unsigned int PollKQueue::perform() { throw internal_error("An PollKQueue function was called, but it is disabled."); } unsigned int PollKQueue::do_poll(int64_t timeout_usec, int flags) { throw internal_error("An PollKQueue function was called, but it is disabled."); } uint32_t PollKQueue::open_max() const { throw internal_error("An PollKQueue function was called, but it is disabled."); } void PollKQueue::open(__UNUSED torrent::Event* event) { } void PollKQueue::close(__UNUSED torrent::Event* event) { } void PollKQueue::closed(__UNUSED torrent::Event* event) { } bool PollKQueue::in_read(__UNUSED torrent::Event* event) { throw internal_error("An PollKQueue function was called, but it is disabled."); } bool PollKQueue::in_write(__UNUSED torrent::Event* event) { throw internal_error("An PollKQueue function was called, but it is disabled."); } bool PollKQueue::in_error(__UNUSED torrent::Event* event) { throw internal_error("An PollKQueue function was called, but it is disabled."); } void PollKQueue::insert_read(__UNUSED torrent::Event* event) { } void PollKQueue::insert_write(__UNUSED torrent::Event* event) { } void PollKQueue::insert_error(__UNUSED torrent::Event* event) { } void PollKQueue::remove_read(__UNUSED torrent::Event* event) { } void PollKQueue::remove_write(__UNUSED torrent::Event* event) { } void PollKQueue::remove_error(__UNUSED torrent::Event* event) { } PollKQueue::PollKQueue(__UNUSED int fd, __UNUSED int maxEvents, __UNUSED int maxOpenSockets) { throw internal_error("An PollKQueue function was called, but it is disabled."); } #endif // USE_KQUEUE } libtorrent-0.13.6/src/torrent/poll_kqueue.h000066400000000000000000000102671257211073700207620ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_POLL_KQUEUE_H #define LIBTORRENT_TORRENT_POLL_KQUEUE_H #include #include struct kevent; namespace torrent { class LIBTORRENT_EXPORT PollKQueue : public torrent::Poll { public: typedef std::vector > Table; static const uint32_t flag_read = (1 << 0); static const uint32_t flag_write = (1 << 1); static const uint32_t flag_error = (1 << 2); static PollKQueue* create(int maxOpenSockets); virtual ~PollKQueue(); int poll(int msec); unsigned int perform(); unsigned int do_poll(int64_t timeout_usec, int flags = 0); int file_descriptor() { return m_fd; } virtual uint32_t open_max() const; // torrent::Event::get_fd() is guaranteed to be valid and remain constant // from open(...) is called to close(...) returns. virtual void open(torrent::Event* event); virtual void close(torrent::Event* event); // torrent::Event::get_fd() was closed outside of our control. virtual void closed(torrent::Event* event); // Functions for checking whetever the torrent::Event is listening to r/w/e? virtual bool in_read(torrent::Event* event); virtual bool in_write(torrent::Event* event); virtual bool in_error(torrent::Event* event); // These functions may be called on 'event's that might, or might // not, already be in the set. virtual void insert_read(torrent::Event* event); virtual void insert_write(torrent::Event* event); virtual void insert_error(torrent::Event* event); virtual void remove_read(torrent::Event* event); virtual void remove_write(torrent::Event* event); virtual void remove_error(torrent::Event* event); private: PollKQueue(int fd, int maxEvents, int maxOpenSockets) LIBTORRENT_NO_EXPORT; inline uint32_t event_mask(Event* e) LIBTORRENT_NO_EXPORT; inline void set_event_mask(Event* e, uint32_t m) LIBTORRENT_NO_EXPORT; int poll_select(int msec) LIBTORRENT_NO_EXPORT; void flush_events() LIBTORRENT_NO_EXPORT; void modify(torrent::Event* event, unsigned short op, short mask) LIBTORRENT_NO_EXPORT; int m_fd; unsigned int m_maxEvents; unsigned int m_waitingEvents; unsigned int m_changedEvents; Table m_table; struct kevent* m_events; struct kevent* m_changes; // Work-around the stdin bug in MacOSX's kqueue implementation. Event* m_stdinEvent; }; } #endif libtorrent-0.13.6/src/torrent/poll_select.cc000066400000000000000000000251721257211073700211010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include #include "net/socket_set.h" #include "rak/allocators.h" #include "event.h" #include "exceptions.h" #include "poll_select.h" #include "torrent.h" #include "rak/timer.h" #include "rak/error_number.h" #include "utils/log.h" #include "utils/thread_base.h" #define LT_LOG_EVENT(event, log_level, log_fmt, ...) \ lt_log_print(LOG_SOCKET_##log_level, "select->%s(%i): " log_fmt, event->type_name(), event->file_descriptor(), __VA_ARGS__); namespace torrent { Poll::slot_poll Poll::m_slot_create_poll; template struct poll_check_t { poll_check_t(Poll* p, fd_set* s, _Operation op) : m_poll(p), m_set(s), m_op(op) {} bool operator () (Event* s) { // This check is nessesary as other events may remove a socket // from the set. if (s == NULL) return false; // This check is not nessesary, just for debugging. if (s->file_descriptor() < 0) throw internal_error("poll_check: s->fd < 0"); if (FD_ISSET(s->file_descriptor(), m_set)) { m_op(s); // We waive the global lock after an event has been processed in // order to ensure that 's' doesn't get removed before the op is // called. if ((m_poll->flags() & Poll::flag_waive_global_lock) && thread_base::global_queue_size() != 0) thread_base::waive_global_lock(); return true; } else { return false; } } Poll* m_poll; fd_set* m_set; _Operation m_op; }; template inline poll_check_t<_Operation> poll_check(Poll* p, fd_set* s, _Operation op) { return poll_check_t<_Operation>(p, s, op); } struct poll_mark { poll_mark(fd_set* s, unsigned int* m) : m_max(m), m_set(s) {} void operator () (Event* s) { // Neither of these checks are nessesary, just for debugging. if (s == NULL) throw internal_error("poll_mark: s == NULL"); if (s->file_descriptor() < 0) throw internal_error("poll_mark: s->fd < 0"); *m_max = std::max(*m_max, (unsigned int)s->file_descriptor()); FD_SET(s->file_descriptor(), m_set); } unsigned int* m_max; fd_set* m_set; }; PollSelect* PollSelect::create(int maxOpenSockets) { if (maxOpenSockets <= 0) throw internal_error("PollSelect::set_open_max(...) received an invalid value"); // Just a temp hack, make some special template function for this... // // Also consider how portable this is for specialized C++ // allocators. struct block_type { PollSelect t1; SocketSet t2; SocketSet t3; SocketSet t4; }; rak::cacheline_allocator cl_alloc; block_type* block = new (cl_alloc) block_type; PollSelect* p = new (&block->t1) PollSelect; p->m_readSet = new (&block->t2) SocketSet; p->m_writeSet = new (&block->t3) SocketSet; p->m_exceptSet = new (&block->t4) SocketSet; p->m_readSet->reserve(maxOpenSockets); p->m_writeSet->reserve(maxOpenSockets); p->m_exceptSet->reserve(maxOpenSockets); return p; } PollSelect::~PollSelect() { m_readSet->prepare(); m_writeSet->prepare(); m_exceptSet->prepare(); // Re-add this check when you've cleaned up the client shutdown procedure. if (!m_readSet->empty() || !m_writeSet->empty() || !m_exceptSet->empty()) { throw internal_error("PollSelect::~PollSelect() called but the sets are not empty"); // for (SocketSet::const_iterator itr = m_readSet->begin(); itr != m_readSet->end(); itr++) // std::cout << "R" << (*itr)->file_descriptor() << std::endl; // for (SocketSet::const_iterator itr = m_writeSet->begin(); itr != m_writeSet->end(); itr++) // std::cout << "W" << (*itr)->file_descriptor() << std::endl; // for (SocketSet::const_iterator itr = m_exceptSet->begin(); itr != m_exceptSet->end(); itr++) // std::cout << "E" << (*itr)->file_descriptor() << std::endl; } // delete m_readSet; // delete m_writeSet; // delete m_exceptSet; m_readSet = m_writeSet = m_exceptSet = NULL; } uint32_t PollSelect::open_max() const { return m_readSet->max_size(); } unsigned int PollSelect::fdset(fd_set* readSet, fd_set* writeSet, fd_set* exceptSet) { unsigned int maxFd = 0; m_readSet->prepare(); std::for_each(m_readSet->begin(), m_readSet->end(), poll_mark(readSet, &maxFd)); m_writeSet->prepare(); std::for_each(m_writeSet->begin(), m_writeSet->end(), poll_mark(writeSet, &maxFd)); m_exceptSet->prepare(); std::for_each(m_exceptSet->begin(), m_exceptSet->end(), poll_mark(exceptSet, &maxFd)); return maxFd; } unsigned int PollSelect::perform(fd_set* readSet, fd_set* writeSet, fd_set* exceptSet) { unsigned int count = 0; // Make sure we don't do read/write on fd's that are in except. This should // not be a problem as any except call should remove it from the m_*Set's. m_exceptSet->prepare(); count += std::count_if(m_exceptSet->begin(), m_exceptSet->end(), poll_check(this, exceptSet, std::mem_fun(&Event::event_error))); m_readSet->prepare(); count += std::count_if(m_readSet->begin(), m_readSet->end(), poll_check(this, readSet, std::mem_fun(&Event::event_read))); m_writeSet->prepare(); count += std::count_if(m_writeSet->begin(), m_writeSet->end(), poll_check(this, writeSet, std::mem_fun(&Event::event_write))); return count; } unsigned int PollSelect::do_poll(int64_t timeout_usec, int flags) { rak::timer timeout = rak::timer(timeout_usec); timeout += 10; uint32_t set_size = open_max(); char read_set_buffer[set_size]; char write_set_buffer[set_size]; char error_set_buffer[set_size]; fd_set* read_set = (fd_set*)read_set_buffer; fd_set* write_set = (fd_set*)write_set_buffer; fd_set* error_set = (fd_set*)error_set_buffer; std::memset(read_set_buffer, 0, set_size); std::memset(write_set_buffer, 0, set_size); std::memset(error_set_buffer, 0, set_size); unsigned int maxFd = fdset(read_set, write_set, error_set); timeval t = timeout.tval(); if (!(flags & poll_worker_thread)) { thread_base::entering_main_polling(); thread_base::release_global_lock(); } int status = select(maxFd + 1, read_set, write_set, error_set, &t); if (!(flags & poll_worker_thread)) { thread_base::leaving_main_polling(); thread_base::acquire_global_lock(); } if (status == -1 && rak::error_number::current().value() != rak::error_number::e_intr) throw std::runtime_error("Poll::work(): " + std::string(rak::error_number::current().c_str())); return perform(read_set, write_set, error_set); } #ifdef LT_LOG_POLL_OPEN inline static void log_poll_open(Event* event) { static int log_fd = -1; char buffer[256]; if (log_fd == -1) { snprintf(buffer, 256, LT_LOG_POLL_OPEN, getpid()); if ((log_fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC)) == -1) throw internal_error("Could not open poll open log file."); } unsigned int buf_lenght = snprintf(buffer, 256, "open %i\n", event->fd()); } #endif void PollSelect::open(Event* event) { LT_LOG_EVENT(event, DEBUG, "Open event.", 0); if ((uint32_t)event->file_descriptor() >= m_readSet->max_size()) throw internal_error("Tried to add a socket to PollSelect that is larger than PollSelect::get_open_max()"); if (in_read(event) || in_write(event) || in_error(event)) throw internal_error("PollSelect::open(...) called on an inserted event"); } void PollSelect::close(Event* event) { LT_LOG_EVENT(event, DEBUG, "Close event.", 0); if ((uint32_t)event->file_descriptor() >= m_readSet->max_size()) throw internal_error("PollSelect::close(...) called with an invalid file descriptor"); if (in_read(event) || in_write(event) || in_error(event)) throw internal_error("PollSelect::close(...) called on an inserted event"); } void PollSelect::closed(Event* event) { LT_LOG_EVENT(event, DEBUG, "Closed event.", 0); // event->get_fd() was closed, remove it from the sets. m_readSet->erase(event); m_writeSet->erase(event); m_exceptSet->erase(event); } bool PollSelect::in_read(Event* event) { return m_readSet->find(event) != m_readSet->end(); } bool PollSelect::in_write(Event* event) { return m_writeSet->find(event) != m_writeSet->end(); } bool PollSelect::in_error(Event* event) { return m_exceptSet->find(event) != m_exceptSet->end(); } void PollSelect::insert_read(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert read.", 0); m_readSet->insert(event); } void PollSelect::insert_write(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert write.", 0); m_writeSet->insert(event); } void PollSelect::insert_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Insert error.", 0); m_exceptSet->insert(event); } void PollSelect::remove_read(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove read.", 0); m_readSet->erase(event); } void PollSelect::remove_write(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove write.", 0); m_writeSet->erase(event); } void PollSelect::remove_error(Event* event) { LT_LOG_EVENT(event, DEBUG, "Remove error.", 0); m_exceptSet->erase(event); } } libtorrent-0.13.6/src/torrent/poll_select.h000066400000000000000000000064011257211073700207350ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_NET_POLL_SELECT_H #define LIBTORRENT_NET_POLL_SELECT_H #include #include #include namespace torrent { // The default Poll implementation using fd_set's. // // You should call torrent::perform() (or whatever the function will // be called) immidiately before and after the call to work(...). This // ensures we dealt with scheduled tasks and updated the cache'ed time. class LIBTORRENT_EXPORT PollSelect : public Poll { public: static PollSelect* create(int maxOpenSockets); virtual ~PollSelect(); virtual uint32_t open_max() const; // Returns the largest fd marked. unsigned int fdset(fd_set* readSet, fd_set* writeSet, fd_set* exceptSet); unsigned int perform(fd_set* readSet, fd_set* writeSet, fd_set* exceptSet); unsigned int do_poll(int64_t timeout_usec, int flags = 0); virtual void open(Event* event); virtual void close(Event* event); virtual void closed(Event* event); virtual bool in_read(Event* event); virtual bool in_write(Event* event); virtual bool in_error(Event* event); virtual void insert_read(Event* event); virtual void insert_write(Event* event); virtual void insert_error(Event* event); virtual void remove_read(Event* event); virtual void remove_write(Event* event); virtual void remove_error(Event* event); private: PollSelect() {} PollSelect(const PollSelect&); void operator = (const PollSelect&); SocketSet* m_readSet; SocketSet* m_writeSet; SocketSet* m_exceptSet; }; } #endif libtorrent-0.13.6/src/torrent/rate.cc000066400000000000000000000047201257211073700175230ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "globals.h" #include "rate.h" #include "exceptions.h" namespace torrent { inline void Rate::discard_old() const { while (!m_container.empty() && m_container.back().first < cachedTime.seconds() - m_span) { m_current -= m_container.back().second; m_container.pop_back(); } } Rate::rate_type Rate::rate() const { discard_old(); return m_current / m_span; } void Rate::insert(rate_type bytes) { discard_old(); if (m_current > ((rate_type)1 << 40) || bytes > ((rate_type)1 << 28)) throw internal_error("Rate::insert(bytes) received out-of-bounds values.."); if (m_container.empty() || m_container.front().first != cachedTime.seconds()) m_container.push_front(value_type(cachedTime.seconds(), bytes)); else m_container.front().second += bytes; m_total += bytes; m_current += bytes; } } libtorrent-0.13.6/src/torrent/rate.h000066400000000000000000000075021257211073700173660ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_RATE_H #define LIBTORRENT_UTILS_RATE_H #include #include namespace torrent { // Keep the current rate count up to date for each call to rate() and // insert(...). This requires a mutable since rate() can be const, but // is justified as we avoid iterating the deque for each call. class LIBTORRENT_EXPORT Rate { public: typedef int32_t timer_type; typedef uint64_t rate_type; typedef uint64_t total_type; typedef std::pair value_type; typedef std::deque queue_type; Rate(timer_type span) : m_current(0), m_total(0), m_span(span) {} // Bytes per second. rate_type rate() const; // Total bytes transfered. total_type total() const { return m_total; } void set_total(total_type bytes) { m_total = bytes; } // Interval in seconds used to calculate the rate. timer_type span() const { return m_span; } void set_span(timer_type s) { m_span = s; } void insert(rate_type bytes); void reset_rate() { m_current = 0; m_container.clear(); } bool operator < (Rate& r) const { return rate() < r.rate(); } bool operator > (Rate& r) const { return rate() > r.rate(); } bool operator == (Rate& r) const { return rate() == r.rate(); } bool operator != (Rate& r) const { return rate() != r.rate(); } bool operator < (rate_type r) const { return rate() < r; } bool operator > (rate_type r) const { return rate() > r; } bool operator == (rate_type r) const { return rate() == r; } bool operator != (rate_type r) const { return rate() != r; } private: inline void discard_old() const; mutable queue_type m_container; mutable rate_type m_current; total_type m_total; timer_type m_span; }; } #endif libtorrent-0.13.6/src/torrent/throttle.cc000066400000000000000000000102401257211073700204270ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "net/throttle_internal.h" #include "net/throttle_list.h" #include "globals.h" #include "exceptions.h" #include "throttle.h" namespace torrent { // Plans: // // Make ThrottleList do a callback when it needs more? This would // allow us to remove us from the task scheduler when we're full. Also // this would let us be abit more flexible with the interval. Throttle* Throttle::create_throttle() { ThrottleInternal* throttle = new ThrottleInternal(ThrottleInternal::flag_root); throttle->m_maxRate = 0; throttle->m_throttleList = new ThrottleList(); return throttle; } void Throttle::destroy_throttle(Throttle* throttle) { delete throttle->m_ptr()->m_throttleList; delete throttle->m_ptr(); } Throttle* Throttle::create_slave() { return m_ptr()->create_slave(); } bool Throttle::is_throttled() { return m_maxRate != 0 && m_maxRate != std::numeric_limits::max(); } void Throttle::set_max_rate(uint32_t v) { if (v == m_maxRate) return; if (v > (1 << 30)) throw input_error("Throttle rate must be between 0 and 2^30."); uint32_t oldRate = m_maxRate; m_maxRate = v; m_throttleList->set_min_chunk_size(calculate_min_chunk_size()); m_throttleList->set_max_chunk_size(calculate_max_chunk_size()); if (!m_ptr()->is_root()) return; if (oldRate == 0) m_ptr()->enable(); else if (m_maxRate == 0) m_ptr()->disable(); } const Rate* Throttle::rate() const { return m_throttleList->rate_slow(); } uint32_t Throttle::calculate_min_chunk_size() const { // Just for each modification, make this into a function, rather // than if-else chain. if (m_maxRate <= (8 << 10)) return (1 << 9); else if (m_maxRate <= (32 << 10)) return (2 << 9); else if (m_maxRate <= (64 << 10)) return (3 << 9); else if (m_maxRate <= (128 << 10)) return (4 << 9); else if (m_maxRate <= (512 << 10)) return (8 << 9); else if (m_maxRate <= (2048 << 10)) return (16 << 9); else return (32 << 9); } uint32_t Throttle::calculate_max_chunk_size() const { // Make this return a lower value for very low throttle settings. return calculate_min_chunk_size() * 4; } uint32_t Throttle::calculate_interval() const { uint32_t rate = m_throttleList->rate_slow()->rate(); if (rate < 1024) return 10 * 100000; // At least two max chunks per tick. uint32_t interval = (5 * m_throttleList->max_chunk_size()) / rate; if (interval == 0) return 1 * 100000; else if (interval > 10) return 10 * 100000; else return interval * 100000; } } libtorrent-0.13.6/src/torrent/throttle.h000066400000000000000000000054231257211073700203000ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_THROTTLE_H #define LIBTORRENT_TORRENT_THROTTLE_H #include namespace torrent { class ThrottleList; class ThrottleInternal; class LIBTORRENT_EXPORT Throttle { public: static Throttle* create_throttle(); static void destroy_throttle(Throttle* throttle); Throttle* create_slave(); bool is_throttled(); // 0 == UNLIMITED. uint32_t max_rate() const { return m_maxRate; } void set_max_rate(uint32_t v); const Rate* rate() const; ThrottleList* throttle_list() { return m_throttleList; } protected: Throttle() {} ~Throttle() {} ThrottleInternal* m_ptr() { return reinterpret_cast(this); } const ThrottleInternal* c_ptr() const { return reinterpret_cast(this); } uint32_t calculate_min_chunk_size() const LIBTORRENT_NO_EXPORT; uint32_t calculate_max_chunk_size() const LIBTORRENT_NO_EXPORT; uint32_t calculate_interval() const LIBTORRENT_NO_EXPORT; uint32_t m_maxRate; ThrottleList* m_throttleList; }; } #endif libtorrent-0.13.6/src/torrent/torrent.cc000066400000000000000000000177161257211073700202760ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "exceptions.h" #include "torrent.h" #include "object.h" #include "object_stream.h" #include "throttle.h" #include "connection_manager.h" #include "poll.h" #include "manager.h" #include "protocol/handshake_manager.h" #include "protocol/peer_factory.h" #include "data/file_manager.h" #include "data/hash_queue.h" #include "data/hash_torrent.h" #include "download/download_constructor.h" #include "download/download_manager.h" #include "download/download_wrapper.h" #include "utils/instrumentation.h" #include "torrent/peer/connection_list.h" #include "torrent/download/resource_manager.h" namespace torrent { uint32_t calculate_max_open_files(uint32_t openMax) { if (openMax >= 8096) return 256; else if (openMax >= 1024) return 128; else if (openMax >= 512) return 64; else if (openMax >= 128) return 16; else // Assumes we don't try less than 64. return 4; } uint32_t calculate_reserved(uint32_t openMax) { if (openMax >= 8096) return 256; else if (openMax >= 1024) return 128; else if (openMax >= 512) return 64; else if (openMax >= 128) return 32; else // Assumes we don't try less than 64. return 16; } void initialize() { if (manager != NULL) throw internal_error("torrent::initialize(...) called but the library has already been initialized"); cachedTime = rak::timer::current(); instrumentation_initialize(); manager = new Manager; manager->main_thread_main()->init_thread(); uint32_t maxFiles = calculate_max_open_files(manager->poll()->open_max()); manager->connection_manager()->set_max_size(manager->poll()->open_max() - maxFiles - calculate_reserved(manager->poll()->open_max())); manager->file_manager()->set_max_open_files(maxFiles); manager->main_thread_disk()->init_thread(); manager->main_thread_disk()->start_thread(); } // Clean up and close stuff. Stopping all torrents and waiting for // them to finish is not required, but recommended. void cleanup() { if (manager == NULL) throw internal_error("torrent::cleanup() called but the library is not initialized."); manager->main_thread_disk()->stop_thread_wait(); delete manager; manager = NULL; } bool is_inactive() { return manager == NULL || std::find_if(manager->download_manager()->begin(), manager->download_manager()->end(), std::not1(std::mem_fun(&DownloadWrapper::is_stopped))) == manager->download_manager()->end(); } thread_base* main_thread() { return manager->main_thread_main(); } ChunkManager* chunk_manager() { return manager->chunk_manager(); } ClientList* client_list() { return manager->client_list(); } FileManager* file_manager() { return manager->file_manager(); } ConnectionManager* connection_manager() { return manager->connection_manager(); } DhtManager* dht_manager() { return manager->dht_manager(); } ResourceManager* resource_manager() { return manager->resource_manager(); } uint32_t total_handshakes() { return manager->handshake_manager()->size(); } Throttle* down_throttle_global() { return manager->download_throttle(); } Throttle* up_throttle_global() { return manager->upload_throttle(); } const Rate* down_rate() { return manager->download_throttle()->rate(); } const Rate* up_rate() { return manager->upload_throttle()->rate(); } const char* version() { return VERSION; } uint32_t hash_queue_size() { return manager->hash_queue()->size(); } EncodingList* encoding_list() { return manager->encoding_list(); } Download download_add(Object* object) { std::auto_ptr download(new DownloadWrapper); DownloadConstructor ctor; ctor.set_download(download.get()); ctor.set_encoding_list(manager->encoding_list()); ctor.initialize(*object); std::string infoHash; if (download->info()->is_meta_download()) infoHash = object->get_key("info").get_key("pieces").as_string(); else infoHash = object_sha1(&object->get_key("info")); if (manager->download_manager()->find(infoHash) != manager->download_manager()->end()) throw input_error("Info hash already used by another torrent."); if (!download->info()->is_meta_download()) { char buffer[1024]; uint64_t metadata_size = 0; object_write_bencode_c(&object_write_to_size, &metadata_size, object_buffer_t(buffer, buffer + sizeof(buffer)), &object->get_key("info")); download->main()->set_metadata_size(metadata_size); } download->set_hash_queue(manager->hash_queue()); download->initialize(infoHash, PEER_NAME + rak::generate_random(20 - std::string(PEER_NAME).size())); // Add trackers, etc, after setting the info hash so that log // entries look sane. ctor.parse_tracker(*object); // Default PeerConnection factory functions. download->main()->connection_list()->slot_new_connection(&createPeerConnectionDefault); // Consider move as much as possible into this function // call. Anything that won't cause possible torrent creation errors // go in there. manager->initialize_download(download.get()); download->set_bencode(object); return Download(download.release()); } void download_remove(Download d) { manager->cleanup_download(d.ptr()); } // Add all downloads to dlist. Make sure it's cleared. void download_list(DList& dlist) { for (DownloadManager::const_iterator itr = manager->download_manager()->begin(); itr != manager->download_manager()->end(); ++itr) dlist.push_back(Download(*itr)); } // Make sure you check that it's valid. Download download_find(const std::string& infohash) { return *manager->download_manager()->find(infohash); } uint32_t download_priority(Download d) { ResourceManager::iterator itr = manager->resource_manager()->find(d.ptr()->main()); if (itr == manager->resource_manager()->end()) throw internal_error("torrent::download_priority(...) could not find the download in the resource manager."); return itr->priority(); } void download_set_priority(Download d, uint32_t pri) { ResourceManager::iterator itr = manager->resource_manager()->find(d.ptr()->main()); if (itr == manager->resource_manager()->end()) throw internal_error("torrent::download_set_priority(...) could not find the download in the resource manager."); if (pri > 1024) throw internal_error("torrent::download_set_priority(...) received an invalid priority."); manager->resource_manager()->set_priority(itr, pri); } } libtorrent-0.13.6/src/torrent/torrent.h000066400000000000000000000100151257211073700201210ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TORRENT_H #define LIBTORRENT_TORRENT_H #include #include #include #include namespace torrent { // Make sure you seed srandom and srand48 if available. void initialize() LIBTORRENT_EXPORT; // Clean up and close stuff. Stopping all torrents and waiting for // them to finish is not required, but recommended. void cleanup() LIBTORRENT_EXPORT; bool is_inactive() LIBTORRENT_EXPORT; class FileManager; class ResourceManager; class thread_base; thread_base* main_thread() LIBTORRENT_EXPORT; ChunkManager* chunk_manager() LIBTORRENT_EXPORT; ClientList* client_list() LIBTORRENT_EXPORT; FileManager* file_manager() LIBTORRENT_EXPORT; ConnectionManager* connection_manager() LIBTORRENT_EXPORT; DhtManager* dht_manager() LIBTORRENT_EXPORT; ResourceManager* resource_manager() LIBTORRENT_EXPORT; uint32_t total_handshakes() LIBTORRENT_EXPORT; Throttle* down_throttle_global() LIBTORRENT_EXPORT; Throttle* up_throttle_global() LIBTORRENT_EXPORT; const Rate* down_rate() LIBTORRENT_EXPORT; const Rate* up_rate() LIBTORRENT_EXPORT; const char* version() LIBTORRENT_EXPORT; // Disk access tuning. uint32_t hash_queue_size() LIBTORRENT_EXPORT; typedef std::list DList; typedef std::list EncodingList; EncodingList* encoding_list() LIBTORRENT_EXPORT; // Will always return a valid Download. On errors it // throws. 'encodingList' contains a list of prefered encodings to use // for file names. // // The Object must be on the heap allocated with 'new'. If // 'download_add' throws the client must handle the deletion, else it // is done by 'download_remove'. // // Might consider redesigning that... Download download_add(Object* s) LIBTORRENT_EXPORT; void download_remove(Download d) LIBTORRENT_EXPORT; // Add all downloads to dlist. The client is responsible for clearing // it before the call. void download_list(DList& dlist) LIBTORRENT_EXPORT; // Make sure you check the returned Download's is_valid(). Download download_find(const std::string& infohash) LIBTORRENT_EXPORT; uint32_t download_priority(Download d) LIBTORRENT_EXPORT; void download_set_priority(Download d, uint32_t pri) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/tracker.cc000066400000000000000000000076431257211073700202320ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include "exceptions.h" #include "globals.h" #include "tracker.h" #include "tracker_list.h" namespace torrent { Tracker::Tracker(TrackerList* parent, const std::string& url, int flags) : m_flags(flags), m_parent(parent), m_group(0), m_url(url), m_normal_interval(1800), m_min_interval(600), m_latest_event(EVENT_NONE), m_latest_new_peers(0), m_latest_sum_peers(0), m_success_time_last(0), m_success_counter(0), m_failed_time_last(0), m_failed_counter(0), m_scrape_time_last(0), m_scrape_counter(0), m_scrape_complete(0), m_scrape_incomplete(0), m_scrape_downloaded(0), m_request_time_last(torrent::cachedTime.seconds()), m_request_counter(0) { } void Tracker::enable() { if (is_enabled()) return; m_flags |= flag_enabled; if (m_parent->slot_tracker_enabled()) m_parent->slot_tracker_enabled()(this); } void Tracker::disable() { if (!is_enabled()) return; close(); m_flags &= ~flag_enabled; if (m_parent->slot_tracker_disabled()) m_parent->slot_tracker_disabled()(this); } uint32_t Tracker::success_time_next() const { if (m_success_counter == 0) return 0; return m_success_time_last + m_normal_interval; } uint32_t Tracker::failed_time_next() const { if (m_failed_counter == 0) return 0; return m_failed_time_last + (5 << std::min(m_failed_counter - 1, (uint32_t)6)); } std::string Tracker::scrape_url_from(std::string url) { size_t delim_slash = url.rfind('/'); if (delim_slash == std::string::npos || url.find("/announce", delim_slash) != delim_slash) throw internal_error("Tried to make scrape url from invalid url."); return url.replace(delim_slash, sizeof("/announce") - 1, "/scrape"); } void Tracker::send_scrape() { throw internal_error("Tracker type does not support scrape."); } void Tracker::inc_request_counter() { m_request_counter -= std::min(m_request_counter, (uint32_t)cachedTime.seconds() - m_request_time_last); m_request_counter++; m_request_time_last = cachedTime.seconds(); if (m_request_counter >= 10) throw internal_error("Tracker request had more than 10 requests in 10 seconds."); } void Tracker::clear_stats() { m_latest_new_peers = 0; m_latest_sum_peers = 0; m_success_counter = 0; m_failed_counter = 0; m_scrape_counter = 0; } } libtorrent-0.13.6/src/torrent/tracker.h000066400000000000000000000161511257211073700200660ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_H #define LIBTORRENT_TRACKER_H #include #include #include namespace torrent { class AddressList; class TrackerList; class LIBTORRENT_EXPORT Tracker { public: friend class TrackerList; typedef enum { TRACKER_NONE, TRACKER_HTTP, TRACKER_UDP, TRACKER_DHT, } Type; enum tracker_event { EVENT_NONE, EVENT_COMPLETED, EVENT_STARTED, EVENT_STOPPED, EVENT_SCRAPE }; static const int flag_enabled = 0x1; static const int flag_extra_tracker = 0x2; static const int flag_can_scrape = 0x4; static const int max_flag_size = 0x10; static const int mask_base_flags = 0x10 - 1; virtual ~Tracker() {} int flags() const { return m_flags; } bool is_enabled() const { return (m_flags & flag_enabled); } bool is_extra_tracker() const { return (m_flags & flag_extra_tracker); } bool is_in_use() const { return is_enabled() && m_success_counter != 0; } bool can_scrape() const { return (m_flags & flag_can_scrape); } virtual bool is_busy() const = 0; bool is_busy_not_scrape() const { return m_latest_event != EVENT_SCRAPE && is_busy(); } virtual bool is_usable() const { return is_enabled(); } bool can_request_state() const; void enable(); void disable(); TrackerList* parent() { return m_parent; } uint32_t group() const { return m_group; } virtual Type type() const = 0; const std::string& url() const { return m_url; } void set_url(const std::string& url) { m_url = url; } const std::string& tracker_id() const { return m_tracker_id; } void set_tracker_id(const std::string& id) { m_tracker_id = id; } uint32_t normal_interval() const { return m_normal_interval; } uint32_t min_interval() const { return m_min_interval; } int latest_event() const { return m_latest_event; } uint32_t latest_new_peers() const { return m_latest_new_peers; } uint32_t latest_sum_peers() const { return m_latest_sum_peers; } uint32_t success_time_next() const; uint32_t success_time_last() const { return m_success_time_last; } uint32_t success_counter() const { return m_success_counter; } uint32_t failed_time_next() const; uint32_t failed_time_last() const { return m_failed_time_last; } uint32_t failed_counter() const { return m_failed_counter; } uint32_t activity_time_last() const { return failed_counter() ? m_failed_time_last : m_success_time_last; } uint32_t activity_time_next() const { return failed_counter() ? failed_time_next() : success_time_next(); } uint32_t scrape_time_last() const { return m_scrape_time_last; } uint32_t scrape_counter() const { return m_scrape_counter; } uint32_t scrape_complete() const { return m_scrape_complete; } uint32_t scrape_incomplete() const { return m_scrape_incomplete; } uint32_t scrape_downloaded() const { return m_scrape_downloaded; } virtual void get_status(char* buffer, int length) { buffer[0] = 0; } static std::string scrape_url_from(std::string url); protected: Tracker(TrackerList* parent, const std::string& url, int flags = 0); Tracker(const Tracker& t); void operator = (const Tracker& t); virtual void send_state(int state) = 0; virtual void send_scrape(); virtual void close() = 0; virtual void disown() = 0; // Safeguard to catch bugs that lead to hammering of trackers. void inc_request_counter(); void clear_stats(); void set_group(uint32_t v) { m_group = v; } void set_normal_interval(int v) { m_normal_interval = std::min(std::max(600, v), 3600); } void set_min_interval(int v) { m_min_interval = std::min(std::max(300, v), 1800); } int m_flags; TrackerList* m_parent; uint32_t m_group; std::string m_url; std::string m_tracker_id; uint32_t m_normal_interval; uint32_t m_min_interval; int m_latest_event; uint32_t m_latest_new_peers; uint32_t m_latest_sum_peers; uint32_t m_success_time_last; uint32_t m_success_counter; uint32_t m_failed_time_last; uint32_t m_failed_counter; uint32_t m_scrape_time_last; uint32_t m_scrape_counter; uint32_t m_scrape_complete; uint32_t m_scrape_incomplete; uint32_t m_scrape_downloaded; // Timing of the last request, and a counter for how many requests // there's been in the recent past. uint32_t m_request_time_last; uint32_t m_request_counter; }; inline bool Tracker::can_request_state() const { return !(is_busy() && latest_event() != EVENT_SCRAPE) && is_usable(); } } #endif libtorrent-0.13.6/src/torrent/tracker_controller.cc000066400000000000000000000422661257211073700224750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "exceptions.h" #include "download_info.h" #include "tracker.h" #include "tracker_controller.h" #include "tracker_list.h" #include "rak/priority_queue_default.h" #include "utils/log.h" #include "globals.h" #define LT_LOG_TRACKER(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TRACKER_##log_level, m_tracker_list->info(), "tracker_controller", log_fmt, __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { struct tracker_controller_private { rak::priority_item task_timeout; rak::priority_item task_scrape; }; // End temp hacks... void TrackerController::update_timeout(uint32_t seconds_to_next) { if (!(m_flags & flag_active)) throw internal_error("TrackerController cannot set timeout when inactive."); rak::timer next_timeout = cachedTime; if (seconds_to_next != 0) next_timeout = (cachedTime + rak::timer::from_seconds(seconds_to_next)).round_seconds(); priority_queue_erase(&taskScheduler, &m_private->task_timeout); priority_queue_insert(&taskScheduler, &m_private->task_timeout, next_timeout); } inline int TrackerController::current_send_state() const { switch ((m_flags & mask_send)) { case flag_send_start: return Tracker::EVENT_STARTED; case flag_send_stop: return Tracker::EVENT_STOPPED; case flag_send_completed: return Tracker::EVENT_COMPLETED; case flag_send_update: default: return Tracker::EVENT_NONE; } } TrackerController::TrackerController(TrackerList* trackers) : m_flags(0), m_tracker_list(trackers), m_private(new tracker_controller_private) { m_private->task_timeout.slot() = std::tr1::bind(&TrackerController::do_timeout, this); m_private->task_scrape.slot() = std::tr1::bind(&TrackerController::do_scrape, this); } TrackerController::~TrackerController() { priority_queue_erase(&taskScheduler, &m_private->task_timeout); priority_queue_erase(&taskScheduler, &m_private->task_scrape); delete m_private; } rak::priority_item* TrackerController::task_timeout() { return &m_private->task_timeout; } rak::priority_item* TrackerController::task_scrape() { return &m_private->task_scrape; } int64_t TrackerController::next_timeout() const { return m_private->task_timeout.time().usec(); } int64_t TrackerController::next_scrape() const { return m_private->task_scrape.time().usec(); } uint32_t TrackerController::seconds_to_next_timeout() const { return std::max(m_private->task_timeout.time() - cachedTime, rak::timer()).seconds_ceiling(); } uint32_t TrackerController::seconds_to_next_scrape() const { return std::max(m_private->task_scrape.time() - cachedTime, rak::timer()).seconds_ceiling(); } void TrackerController::manual_request(bool request_now) { if (!m_private->task_timeout.is_queued()) return; // Add functions to get the lowest timeout, etc... send_update_event(); } void TrackerController::scrape_request(uint32_t seconds_to_request) { rak::timer next_timeout = cachedTime; if (seconds_to_request != 0) next_timeout = (cachedTime + rak::timer::from_seconds(seconds_to_request)).round_seconds(); priority_queue_erase(&taskScheduler, &m_private->task_scrape); priority_queue_insert(&taskScheduler, &m_private->task_scrape, next_timeout); } // The send_*_event() functions tries to ensure the relevant trackers // receive the event. // // When we just want more peers the start_requesting() function is // used. This is all independent of the regular updates sent to the // trackers. void TrackerController::send_start_event() { // This will now be 'lazy', rather than a definite event. We tell // the controller that a 'start' event should be sent, and it will // send it when the tracker controller get's enabled. // If the controller is already running, we insert this new event. // Return, or something, if already active and sending? if (m_flags & flag_send_start) { // Do we just return, or bork? At least we need to check to see // that there's something requesting 'start' event or fail hard. } m_flags &= ~mask_send; m_flags |= flag_send_start; if (!(m_flags & flag_active) || !m_tracker_list->has_usable()) { LT_LOG_TRACKER(INFO, "Queueing started event.", 0); return; } // Start with requesting from the first tracker. Add timer to // catch when we don't get any response within the first few // seconds, at which point we go promiscious. // Do we use the old 'focus' thing?... Rather react on no reply, // go into promiscious. LT_LOG_TRACKER(INFO, "Sending started event.", 0); close(); m_tracker_list->send_state_itr(m_tracker_list->find_usable(m_tracker_list->begin()), Tracker::EVENT_STARTED); if (m_tracker_list->count_usable() > 1) { m_flags |= flag_promiscuous_mode; update_timeout(3); } } void TrackerController::send_stop_event() { if (m_flags & flag_send_stop) { // Do we just return, or bork? At least we need to check to see // that there's something requesting 'start' event or fail hard. } m_flags &= ~mask_send; if (!(m_flags & flag_active) || !m_tracker_list->has_usable()) { LT_LOG_TRACKER(INFO, "Skipping stopped event as no tracker need it.", 0); return; } m_flags |= flag_send_stop; LT_LOG_TRACKER(INFO, "Sending stopped event.", 0); close(); for (TrackerList::iterator itr = m_tracker_list->begin(); itr != m_tracker_list->end(); itr++) { if (!(*itr)->is_in_use()) continue; m_tracker_list->send_state(*itr, Tracker::EVENT_STOPPED); } // Timer... } void TrackerController::send_completed_event() { if (m_flags & flag_send_completed) { // Do we just return, or bork? At least we need to check to see // that there's something requesting 'start' event or fail hard. } m_flags &= ~mask_send; m_flags |= flag_send_completed; if (!(m_flags & flag_active) || !m_tracker_list->has_usable()) { LT_LOG_TRACKER(INFO, "Queueing completed event.", 0); return; } LT_LOG_TRACKER(INFO, "Sending completed event.", 0); // Send to all trackers that would want to know. close(); for (TrackerList::iterator itr = m_tracker_list->begin(); itr != m_tracker_list->end(); itr++) { if (!(*itr)->is_in_use()) continue; m_tracker_list->send_state(*itr, Tracker::EVENT_COMPLETED); } // Timer... } void TrackerController::send_update_event() { if (!(m_flags & flag_active) || !m_tracker_list->has_usable()) return; if ((m_flags & mask_send) && m_tracker_list->has_active()) return; // We can lose a state here... if (!(m_flags & mask_send)) m_flags |= flag_send_update; LT_LOG_TRACKER(INFO, "Sending update event.", 0); m_tracker_list->send_state_itr(m_tracker_list->find_usable(m_tracker_list->begin()), Tracker::EVENT_NONE); // if (m_tracker_list->has_active()) // priority_queue_erase(&taskScheduler, &m_private->task_timeout); } // Currently being used by send_state, fixme. void TrackerController::close(int flags) { m_flags &= ~(flag_requesting | flag_promiscuous_mode); if ((flags & (close_disown_stop | close_disown_completed))) m_tracker_list->disown_all_including(close_disown_stop | close_disown_completed); m_tracker_list->close_all(); priority_queue_erase(&taskScheduler, &m_private->task_timeout); } void TrackerController::enable(int enable_flags) { if ((m_flags & flag_active)) return; // Clearing send stop here in case we cycle disable/enable too // fast. In the future do this based on flags passed. m_flags |= flag_active; m_flags &= ~flag_send_stop; m_tracker_list->close_all_excluding((1 << Tracker::EVENT_COMPLETED)); if (!(enable_flags & enable_dont_reset_stats)) m_tracker_list->clear_stats(); LT_LOG_TRACKER(INFO, "Called enable with %u trackers.", m_tracker_list->size()); // Adding of the tracker requests gets done after the caller has had // a chance to override the default behavior. update_timeout(0); } void TrackerController::disable() { if (!(m_flags & flag_active)) return; // Disable other flags?... m_flags &= ~(flag_active | flag_requesting | flag_promiscuous_mode); m_tracker_list->close_all_excluding((1 << Tracker::EVENT_STOPPED) | (1 << Tracker::EVENT_COMPLETED)); priority_queue_erase(&taskScheduler, &m_private->task_timeout); LT_LOG_TRACKER(INFO, "Called disable with %u trackers.", m_tracker_list->size()); } void TrackerController::start_requesting() { if ((m_flags & flag_requesting)) return; m_flags |= flag_requesting; if ((m_flags & flag_active)) update_timeout(0); LT_LOG_TRACKER(INFO, "Start requesting.", 0); } void TrackerController::stop_requesting() { if (!(m_flags & flag_requesting)) return; m_flags &= ~flag_requesting; LT_LOG_TRACKER(INFO, "Stop requesting.", 0); } uint32_t tracker_next_timeout(Tracker* tracker, int controller_flags) { if ((controller_flags & TrackerController::flag_requesting)) return tracker_next_timeout_promiscuous(tracker); if ((tracker->is_busy() && tracker->latest_event() != Tracker::EVENT_SCRAPE) || !tracker->is_usable()) return ~uint32_t(); if ((controller_flags & TrackerController::flag_promiscuous_mode)) return 0; if ((controller_flags & TrackerController::flag_send_update)) return tracker_next_timeout_update(tracker); // if (tracker->success_counter() == 0 && tracker->failed_counter() == 0) // return 0; int32_t last_activity = cachedTime.seconds() - tracker->activity_time_last(); // TODO: Use min interval if we're requesting manual update. return tracker->normal_interval() - std::min(last_activity, (int32_t)tracker->normal_interval()); } uint32_t tracker_next_timeout_update(Tracker* tracker) { if ((tracker->is_busy() && tracker->latest_event() != Tracker::EVENT_SCRAPE) || !tracker->is_usable()) return ~uint32_t(); // Make sure we don't request _too_ often, check last activity. // int32_t last_activity = cachedTime.seconds() - tracker->activity_time_last(); return 0; } uint32_t tracker_next_timeout_promiscuous(Tracker* tracker) { if ((tracker->is_busy() && tracker->latest_event() != Tracker::EVENT_SCRAPE) || !tracker->is_usable()) return ~uint32_t(); int32_t interval; if (tracker->failed_counter()) interval = 5 << std::min(tracker->failed_counter() - 1, 6); else interval = tracker->normal_interval(); int32_t min_interval = std::max(tracker->min_interval(), (uint32_t)300); int32_t use_interval = std::min(interval, min_interval); int32_t since_last = cachedTime.seconds() - (int32_t)tracker->activity_time_last(); return std::max(use_interval - since_last, 0); } TrackerList::iterator tracker_find_preferred(TrackerList::iterator first, TrackerList::iterator last, uint32_t* next_timeout) { TrackerList::iterator preferred = last; uint32_t preferred_time_last = ~uint32_t(); for (; first != last; first++) { uint32_t tracker_timeout = tracker_next_timeout_promiscuous(*first); if (tracker_timeout != 0) { *next_timeout = std::min(tracker_timeout, *next_timeout); continue; } if ((*first)->activity_time_last() < preferred_time_last) { preferred = first; preferred_time_last = (*first)->activity_time_last(); } } return preferred; } void TrackerController::do_timeout() { if (!(m_flags & flag_active) || !m_tracker_list->has_usable()) return; priority_queue_erase(&taskScheduler, &m_private->task_timeout); int send_state = current_send_state(); if ((m_flags & (flag_promiscuous_mode | flag_requesting))) { uint32_t next_timeout = ~uint32_t(); TrackerList::iterator itr = m_tracker_list->begin(); while (itr != m_tracker_list->end()) { uint32_t group = (*itr)->group(); if (m_tracker_list->has_active_not_scrape_in_group(group)) { itr = m_tracker_list->end_group(group); continue; } TrackerList::iterator group_end = m_tracker_list->end_group((*itr)->group()); TrackerList::iterator preferred = itr; if (!(*itr)->is_usable() || (*itr)->failed_counter()) { // The selected tracker in the group is either disabled or not // reachable, try the others to find a new one to use. preferred = tracker_find_preferred(preferred, group_end, &next_timeout); } else { uint32_t tracker_timeout = tracker_next_timeout_promiscuous(*preferred); if (tracker_timeout != 0) { next_timeout = std::min(tracker_timeout, next_timeout); preferred = group_end; } } if (preferred != group_end) m_tracker_list->send_state_itr(preferred, send_state); itr = group_end; } if (next_timeout != ~uint32_t()) update_timeout(next_timeout); // TODO: Send for start/completed also? } else { TrackerList::iterator itr = m_tracker_list->find_next_to_request(m_tracker_list->begin()); if (itr == m_tracker_list->end()) return; int32_t next_timeout = (*itr)->activity_time_next(); if (next_timeout <= cachedTime.seconds()) m_tracker_list->send_state_itr(itr, send_state); else update_timeout(next_timeout - cachedTime.seconds()); } if (m_slot_timeout) m_slot_timeout(); } void TrackerController::do_scrape() { TrackerList::iterator itr = m_tracker_list->begin(); while (itr != m_tracker_list->end()) { uint32_t group = (*itr)->group(); if (m_tracker_list->has_active_in_group(group)) { itr = m_tracker_list->end_group(group); continue; } TrackerList::iterator group_end = m_tracker_list->end_group((*itr)->group()); while (itr != group_end) { if ((*itr)->can_scrape() && (*itr)->is_usable()) { m_tracker_list->send_scrape(*itr); break; } itr++; } itr = group_end; } } uint32_t TrackerController::receive_success(Tracker* tb, TrackerController::address_list* l) { if (!(m_flags & flag_active)) return m_slot_success(l); // if () { m_flags &= ~(mask_send | flag_promiscuous_mode | flag_failure_mode); // } // If we still have active trackers, skip the timeout. // Calculate the next timeout according to a list of in-use // trackers, with the first timeout as the interval. if ((m_flags & flag_requesting)) update_timeout(30); else if (!m_tracker_list->has_active()) // TODO: Instead find the lowest timeout, correct timeout? update_timeout(tb->normal_interval()); return m_slot_success(l); } void TrackerController::receive_failure(Tracker* tb, const std::string& msg) { if (!(m_flags & flag_active)) { m_slot_failure(msg); return; } if (tb == NULL) { LT_LOG_TRACKER(INFO, "Received failure msg:'%s'.", msg.c_str()); m_slot_failure(msg); return; } if (tb->failed_counter() == 1 && tb->success_counter() > 0) m_flags |= flag_failure_mode; do_timeout(); m_slot_failure(msg); } void TrackerController::receive_scrape(Tracker* tb) { if (!(m_flags & flag_active)) { return; } } void TrackerController::receive_tracker_enabled(Tracker* tb) { // TODO: This won't be needed if we rely only on Tracker::m_enable, // rather than a virtual function. if (!m_tracker_list->has_usable()) return; if ((m_flags & flag_active)) { if (!m_private->task_timeout.is_queued() && !m_tracker_list->has_active()) { // TODO: Figure out the proper timeout to use here based on when the // tracker last connected, etc. update_timeout(0); } } if (m_slot_tracker_enabled) m_slot_tracker_enabled(tb); } void TrackerController::receive_tracker_disabled(Tracker* tb) { if ((m_flags & flag_active) && !m_private->task_timeout.is_queued()) update_timeout(0); if (m_slot_tracker_disabled) m_slot_tracker_disabled(tb); } } libtorrent-0.13.6/src/torrent/tracker_controller.h000066400000000000000000000136551257211073700223370ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_CONTROLLER_H #define LIBTORRENT_TRACKER_CONTROLLER_H #include #include #include #include // Refactor: namespace rak { class priority_item; } namespace torrent { class AddressList; class TrackerList; struct tracker_controller_private; class LIBTORRENT_EXPORT TrackerController { public: typedef AddressList address_list; typedef std::tr1::function slot_void; typedef std::tr1::function slot_string; typedef std::tr1::function slot_address_list; typedef std::tr1::function slot_tracker; static const int flag_send_update = 0x1; static const int flag_send_completed = 0x2; static const int flag_send_start = 0x4; static const int flag_send_stop = 0x8; static const int flag_active = 0x10; static const int flag_requesting = 0x20; static const int flag_failure_mode = 0x40; static const int flag_promiscuous_mode = 0x80; static const int mask_send = flag_send_update | flag_send_start | flag_send_stop | flag_send_completed; static const int enable_dont_reset_stats = 0x1; static const int close_disown_stop = 0x1 << Tracker::EVENT_STOPPED; static const int close_disown_completed = 0x1 << Tracker::EVENT_COMPLETED; TrackerController(TrackerList* trackers); ~TrackerController(); int flags() const { return m_flags; } bool is_active() const { return m_flags & flag_active; } bool is_requesting() const { return m_flags & flag_requesting; } bool is_failure_mode() const { return m_flags & flag_failure_mode; } bool is_promiscuous_mode() const { return m_flags & flag_promiscuous_mode; } TrackerList* tracker_list() { return m_tracker_list; } TrackerList* tracker_list() const { return m_tracker_list; } int64_t next_timeout() const; int64_t next_scrape() const; uint32_t seconds_to_next_timeout() const; uint32_t seconds_to_next_scrape() const; void manual_request(bool request_now); void scrape_request(uint32_t seconds_to_request); //protected: void send_start_event(); void send_stop_event(); void send_completed_event(); void send_update_event(); void close(int flags = close_disown_stop | close_disown_completed); void enable(int enable_flags = 0); void disable(); void start_requesting(); void stop_requesting(); uint32_t receive_success(Tracker* tb, address_list* l); void receive_failure(Tracker* tb, const std::string& msg); void receive_scrape(Tracker* tb); void receive_tracker_enabled(Tracker* tb); void receive_tracker_disabled(Tracker* tb); slot_void& slot_timeout() { return m_slot_timeout; } slot_address_list& slot_success() { return m_slot_success; } slot_string& slot_failure() { return m_slot_failure; } slot_tracker& slot_tracker_enabled() { return m_slot_tracker_enabled; } slot_tracker& slot_tracker_disabled() { return m_slot_tracker_disabled; } // TEMP: rak::priority_item* task_timeout(); rak::priority_item* task_scrape(); private: void do_timeout(); void do_scrape(); void update_timeout(uint32_t seconds_to_next); inline int current_send_state() const; TrackerController(); void operator = (const TrackerController&); int m_flags; TrackerList* m_tracker_list; slot_void m_slot_timeout; slot_address_list m_slot_success; slot_string m_slot_failure; slot_tracker m_slot_tracker_enabled; slot_tracker m_slot_tracker_disabled; // Refactor this out. tracker_controller_private* m_private; }; uint32_t tracker_next_timeout(Tracker* tracker, int controller_flags); uint32_t tracker_next_timeout_update(Tracker* tracker); uint32_t tracker_next_timeout_promiscuous(Tracker* tracker); } #endif libtorrent-0.13.6/src/torrent/tracker_list.cc000066400000000000000000000254751257211073700212700ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "net/address_list.h" #include "torrent/utils/log.h" #include "torrent/utils/option_strings.h" #include "torrent/download_info.h" #include "tracker/tracker_dht.h" #include "tracker/tracker_http.h" #include "tracker/tracker_udp.h" #include "globals.h" #include "exceptions.h" #include "tracker.h" #include "tracker_list.h" #define LT_LOG_TRACKER(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TRACKER_##log_level, info(), "tracker_list", log_fmt, __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { TrackerList::TrackerList() : m_info(NULL), m_state(DownloadInfo::STOPPED), m_key(0), m_numwant(-1) { } bool TrackerList::has_active() const { return std::find_if(begin(), end(), std::mem_fun(&Tracker::is_busy)) != end(); } bool TrackerList::has_active_not_scrape() const { return std::find_if(begin(), end(), std::mem_fun(&Tracker::is_busy_not_scrape)) != end(); } bool TrackerList::has_active_in_group(uint32_t group) const { return std::find_if(begin_group(group), end_group(group), std::mem_fun(&Tracker::is_busy)) != end_group(group); } bool TrackerList::has_active_not_scrape_in_group(uint32_t group) const { return std::find_if(begin_group(group), end_group(group), std::mem_fun(&Tracker::is_busy_not_scrape)) != end_group(group); } // Need a custom predicate because the is_usable function is virtual. struct tracker_usable_t : public std::unary_function { bool operator () (const TrackerList::value_type& value) const { return value->is_usable(); } }; bool TrackerList::has_usable() const { return std::find_if(begin(), end(), tracker_usable_t()) != end(); } unsigned int TrackerList::count_active() const { return std::count_if(begin(), end(), std::mem_fun(&Tracker::is_busy)); } unsigned int TrackerList::count_usable() const { return std::count_if(begin(), end(), tracker_usable_t()); } void TrackerList::close_all_excluding(int event_bitmap) { for (iterator itr = begin(); itr != end(); itr++) { if ((event_bitmap & (1 << (*itr)->latest_event()))) continue; (*itr)->close(); } } void TrackerList::disown_all_including(int event_bitmap) { for (iterator itr = begin(); itr != end(); itr++) { if ((event_bitmap & (1 << (*itr)->latest_event()))) (*itr)->disown(); } } void TrackerList::clear() { std::for_each(begin(), end(), rak::call_delete()); base_type::clear(); } void TrackerList::clear_stats() { std::for_each(begin(), end(), std::mem_fun(&Tracker::clear_stats)); } void TrackerList::send_state(Tracker* tracker, int new_event) { if (!tracker->is_usable() || new_event == Tracker::EVENT_SCRAPE) return; if (tracker->is_busy()) { if (tracker->latest_event() != Tracker::EVENT_SCRAPE) return; tracker->close(); } tracker->send_state(new_event); tracker->inc_request_counter(); LT_LOG_TRACKER(INFO, "Sending '%s' to group:%u url:'%s'.", option_as_string(OPTION_TRACKER_EVENT, new_event), tracker->group(), tracker->url().c_str()); } void TrackerList::send_scrape(Tracker* tracker) { if (tracker->is_busy() || !tracker->is_usable()) return; if (!(tracker->flags() & Tracker::flag_can_scrape)) return; if (rak::timer::from_seconds(tracker->scrape_time_last()) + rak::timer::from_seconds(10 * 60) > cachedTime ) return; tracker->send_scrape(); tracker->inc_request_counter(); LT_LOG_TRACKER(INFO, "Sending 'scrape' to group:%u url:'%s'.", tracker->group(), tracker->url().c_str()); } TrackerList::iterator TrackerList::insert(unsigned int group, Tracker* tracker) { tracker->set_group(group); iterator itr = base_type::insert(end_group(group), tracker); if (m_slot_tracker_enabled) m_slot_tracker_enabled(tracker); return itr; } // TODO: Use proper flags for insert options. void TrackerList::insert_url(unsigned int group, const std::string& url, bool extra_tracker) { Tracker* tracker; int flags = Tracker::flag_enabled; if (extra_tracker) flags |= Tracker::flag_extra_tracker; if (std::strncmp("http://", url.c_str(), 7) == 0 || std::strncmp("https://", url.c_str(), 8) == 0) { tracker = new TrackerHttp(this, url, flags); } else if (std::strncmp("udp://", url.c_str(), 6) == 0) { tracker = new TrackerUdp(this, url, flags); } else if (std::strncmp("dht://", url.c_str(), 6) == 0 && TrackerDht::is_allowed()) { tracker = new TrackerDht(this, url, flags); } else { LT_LOG_TRACKER(WARN, "Could find matching tracker protocol for url: '%s'.", url.c_str()); if (extra_tracker) throw torrent::input_error("Could find matching tracker protocol for url: '" + url + "'."); return; } LT_LOG_TRACKER(INFO, "Added tracker group:%i url:'%s'.", group, url.c_str()); insert(group, tracker); } TrackerList::iterator TrackerList::find_url(const std::string& url) { return std::find_if(begin(), end(), tr1::bind(std::equal_to(), url, tr1::bind(&Tracker::url, tr1::placeholders::_1))); } TrackerList::iterator TrackerList::find_usable(iterator itr) { while (itr != end() && !tracker_usable_t()(*itr)) ++itr; return itr; } TrackerList::const_iterator TrackerList::find_usable(const_iterator itr) const { while (itr != end() && !tracker_usable_t()(*itr)) ++itr; return itr; } TrackerList::iterator TrackerList::find_next_to_request(iterator itr) { TrackerList::iterator preferred = itr = std::find_if(itr, end(), std::mem_fun(&Tracker::can_request_state)); if (preferred == end() || (*preferred)->failed_counter() == 0) return preferred; while (++itr != end()) { if (!(*itr)->can_request_state()) continue; if ((*itr)->failed_counter() != 0) { if ((*itr)->failed_time_next() < (*preferred)->failed_time_next()) preferred = itr; } else { if ((*itr)->success_time_next() < (*preferred)->failed_time_next()) preferred = itr; break; } } return preferred; } TrackerList::iterator TrackerList::begin_group(unsigned int group) { return std::find_if(begin(), end(), rak::less_equal(group, std::mem_fun(&Tracker::group))); } TrackerList::const_iterator TrackerList::begin_group(unsigned int group) const { return std::find_if(begin(), end(), rak::less_equal(group, std::mem_fun(&Tracker::group))); } TrackerList::size_type TrackerList::size_group() const { return !empty() ? back()->group() + 1 : 0; } void TrackerList::cycle_group(unsigned int group) { iterator itr = begin_group(group); iterator prev = itr; if (itr == end() || (*itr)->group() != group) return; while (++itr != end() && (*itr)->group() == group) { std::iter_swap(itr, prev); prev = itr; } } TrackerList::iterator TrackerList::promote(iterator itr) { iterator first = begin_group((*itr)->group()); if (first == end()) throw internal_error("torrent::TrackerList::promote(...) Could not find beginning of group."); std::swap(*first, *itr); return first; } void TrackerList::randomize_group_entries() { // Random random random. iterator itr = begin(); while (itr != end()) { iterator tmp = end_group((*itr)->group()); std::random_shuffle(itr, tmp); itr = tmp; } } void TrackerList::receive_success(Tracker* tb, AddressList* l) { iterator itr = find(tb); if (itr == end() || tb->is_busy()) throw internal_error("TrackerList::receive_success(...) called but the iterator is invalid."); // Promote the tracker to the front of the group since it was // successfull. itr = promote(itr); l->sort(); l->erase(std::unique(l->begin(), l->end()), l->end()); LT_LOG_TRACKER(INFO, "Received %u peers from tracker url:'%s'.", l->size(), tb->url().c_str()); tb->m_success_time_last = cachedTime.seconds(); tb->m_success_counter++; tb->m_failed_counter = 0; tb->m_latest_sum_peers = l->size(); tb->m_latest_new_peers = m_slot_success(tb, l); } void TrackerList::receive_failed(Tracker* tb, const std::string& msg) { iterator itr = find(tb); if (itr == end() || tb->is_busy()) throw internal_error("TrackerList::receive_failed(...) called but the iterator is invalid."); LT_LOG_TRACKER(INFO, "Failed to connect to tracker url:'%s' msg:'%s'.", tb->url().c_str(), msg.c_str()); tb->m_failed_time_last = cachedTime.seconds(); tb->m_failed_counter++; m_slot_failed(tb, msg); } void TrackerList::receive_scrape_success(Tracker* tb) { iterator itr = find(tb); if (itr == end() || tb->is_busy()) throw internal_error("TrackerList::receive_success(...) called but the iterator is invalid."); LT_LOG_TRACKER(INFO, "Received scrape from tracker url:'%s'.", tb->url().c_str()); tb->m_scrape_time_last = cachedTime.seconds(); tb->m_scrape_counter++; if (m_slot_scrape_success) m_slot_scrape_success(tb); } void TrackerList::receive_scrape_failed(Tracker* tb, const std::string& msg) { iterator itr = find(tb); if (itr == end() || tb->is_busy()) throw internal_error("TrackerList::receive_failed(...) called but the iterator is invalid."); LT_LOG_TRACKER(INFO, "Failed to scrape tracker url:'%s' msg:'%s'.", tb->url().c_str(), msg.c_str()); if (m_slot_scrape_failed) m_slot_scrape_failed(tb, msg); } } libtorrent-0.13.6/src/torrent/tracker_list.h000066400000000000000000000163621257211073700211250ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_LIST_H #define LIBTORRENT_TRACKER_LIST_H #include #include #include #include #include namespace torrent { class AddressList; class DownloadInfo; class DownloadWrapper; class Tracker; // The tracker list will contain a list of tracker, divided into // subgroups. Each group must be randomized before we start. When // starting the tracker request, always start from the beginning and // iterate if the request failed. Upon request success move the // tracker to the beginning of the subgroup and start from the // beginning of the whole list. class LIBTORRENT_EXPORT TrackerList : private std::vector { public: friend class DownloadWrapper; typedef std::vector base_type; typedef AddressList address_list; typedef std::tr1::function slot_tracker; typedef std::tr1::function slot_string; typedef std::tr1::function slot_address_list; using base_type::value_type; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::const_reverse_iterator; using base_type::size; using base_type::empty; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::front; using base_type::back; using base_type::at; using base_type::operator[]; TrackerList(); bool has_active() const; bool has_active_not_scrape() const; bool has_active_in_group(uint32_t group) const; bool has_active_not_scrape_in_group(uint32_t group) const; bool has_usable() const; unsigned int count_active() const; unsigned int count_usable() const; void close_all() { close_all_excluding(0); } void close_all_excluding(int event_bitmap); void disown_all_including(int event_bitmap); void clear(); void clear_stats(); iterator insert(unsigned int group, Tracker* tracker); void insert_url(unsigned int group, const std::string& url, bool extra_tracker = false); void send_state(Tracker* tracker, int new_event); void send_state_idx(unsigned idx, int new_event); void send_state_itr(iterator itr, int new_event); void send_scrape(Tracker* tracker); DownloadInfo* info() { return m_info; } int state() { return m_state; } uint32_t key() const { return m_key; } void set_key(uint32_t key) { m_key = key; } int32_t numwant() const { return m_numwant; } void set_numwant(int32_t n) { m_numwant = n; } iterator find(Tracker* tb) { return std::find(begin(), end(), tb); } iterator find_url(const std::string& url); iterator find_usable(iterator itr); const_iterator find_usable(const_iterator itr) const; iterator find_next_to_request(iterator itr); iterator begin_group(unsigned int group); const_iterator begin_group(unsigned int group) const; iterator end_group(unsigned int group) { return begin_group(group + 1); } const_iterator end_group(unsigned int group) const { return begin_group(group + 1); } size_type size_group() const; void cycle_group(unsigned int group); iterator promote(iterator itr); void randomize_group_entries(); void receive_success(Tracker* tb, AddressList* l); void receive_failed(Tracker* tb, const std::string& msg); void receive_scrape_success(Tracker* tb); void receive_scrape_failed(Tracker* tb, const std::string& msg); // Used by libtorrent internally. slot_address_list& slot_success() { return m_slot_success; } slot_string& slot_failure() { return m_slot_failed; } slot_tracker& slot_scrape_success() { return m_slot_scrape_success; } slot_string& slot_scrape_failure() { return m_slot_scrape_failed; } slot_tracker& slot_tracker_enabled() { return m_slot_tracker_enabled; } slot_tracker& slot_tracker_disabled() { return m_slot_tracker_disabled; } protected: void set_info(DownloadInfo* info) { m_info = info; } void set_state(int s) { m_state = s; } private: TrackerList(const TrackerList&) LIBTORRENT_NO_EXPORT; void operator = (const TrackerList&) LIBTORRENT_NO_EXPORT; DownloadInfo* m_info; int m_state; uint32_t m_key; int32_t m_numwant; slot_address_list m_slot_success; slot_string m_slot_failed; slot_tracker m_slot_scrape_success; slot_string m_slot_scrape_failed; slot_tracker m_slot_tracker_enabled; slot_tracker m_slot_tracker_disabled; }; inline void TrackerList::send_state_idx(unsigned idx, int new_event) { send_state(at(idx), new_event); } inline void TrackerList::send_state_itr(iterator itr, int new_event) { if (itr == end()) return; send_state(*itr, new_event); } } #endif libtorrent-0.13.6/src/torrent/utils/000077500000000000000000000000001257211073700174165ustar00rootroot00000000000000libtorrent-0.13.6/src/torrent/utils/Makefile.am000066400000000000000000000012361257211073700214540ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_torrentutils.la libsub_torrentutils_la_SOURCES = \ extents.h \ log.cc \ log.h \ log_buffer.cc \ log_buffer.h \ net.cc \ net.h \ option_strings.cc \ option_strings.h \ ranges.h \ resume.cc \ resume.h \ signal_bitfield.cc \ signal_bitfield.h \ thread_base.cc \ thread_base.h \ thread_interrupt.cc \ thread_interrupt.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir) libtorrentincludedir = $(includedir)/torrent/utils libtorrentinclude_HEADERS = \ extents.h \ log.h \ log_buffer.h \ net.h \ option_strings.h \ ranges.h \ resume.h \ signal_bitfield.h \ thread_base.h \ thread_interrupt.h libtorrent-0.13.6/src/torrent/utils/extents.h000066400000000000000000000202301257211073700212560ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_EXTENTS_H #define LIBTORRENT_UTILS_EXTENTS_H #include namespace torrent { template struct extents_base { typedef Key key_type; typedef std::pair range_type; typedef std::pair mapped_type; typedef Tp mapped_value_type; typedef std::tr1::array table_type; extents_base(key_type pos, unsigned int mb, mapped_value_type val) : mask_bits(mb), position(pos) { table.assign(mapped_type(NULL, mapped_value_type())); } extents_base(extents_base* parent, typename table_type::const_iterator itr) : mask_bits(parent->mask_bits - TableBits), position(parent->partition_pos(itr)) { table.assign(mapped_type(NULL, itr->second)); } ~extents_base(); bool is_divisible(key_type key) const { return key % mask_bits == 0; } bool is_leaf_branch() const { return mask_bits == 0; } bool is_equal_range(key_type first, key_type last, const mapped_value_type& val) const; unsigned int sizeof_data() const; typename table_type::iterator partition_at(key_type key) { return table.begin() + ((key >> mask_bits) & (TableSize - 1)); } typename table_type::const_iterator partition_at(key_type key) const { return table.begin() + ((key >> mask_bits) & (TableSize - 1)); } unsigned int mask_distance(unsigned int mb) { return (~(~key_type() << mb) >> mask_bits); } key_type partition_pos(typename table_type::const_iterator part) const { return position + (std::distance(table.begin(), part) << mask_bits); } void insert(key_type pos, unsigned int mb, const mapped_value_type& val); const mapped_value_type& at(key_type key) const; unsigned int mask_bits; key_type position; table_type table; }; template class extents : private extents_base { public: typedef extents_base base_type; typedef typename base_type::key_type key_type; typedef base_type value_type; typedef typename base_type::range_type range_type; typedef typename base_type::mapped_type mapped_type; typedef typename base_type::mapped_value_type mapped_value_type; typedef typename base_type::table_type table_type; static const key_type mask_bits = MaskBits; static const key_type table_bits = TableBits; static const key_type table_size = TableSize; using base_type::at; using base_type::sizeof_data; extents(); bool is_equal_range(key_type first, key_type last, const mapped_value_type& val) const; void insert(key_type pos, unsigned int mb, const mapped_value_type& val); base_type* data() { return this; } }; template extents::extents() : base_type(key_type(), mask_bits - table_bits, mapped_value_type()) { } template extents_base::~extents_base() { for (typename table_type::const_iterator itr = table.begin(), last = table.end(); itr != last; itr++) delete itr->first; } template unsigned int extents_base::sizeof_data() const { unsigned int sum = sizeof(*this); for (typename table_type::const_iterator itr = table.begin(), last = table.end(); itr != last; itr++) if (itr->first != NULL) sum += itr->first->sizeof_data(); return sum; } template void extents::insert(key_type pos, unsigned int mb, const mapped_value_type& val) { key_type mask = ~key_type() << mb; base_type::insert(pos & mask, mb, val); } template void extents_base::insert(key_type pos, unsigned int mb, const mapped_value_type& val) { // RESTRICTED typename table_type::iterator first = partition_at(pos); typename table_type::iterator last = partition_at(pos) + mask_distance(mb) + 1; if (mb < mask_bits) { if (first->first == NULL) first->first = new extents_base(this, first); first->first->insert(pos, mb, val); return; } while (first != last) { if (first->first != NULL) { delete first->first; first->first = NULL; } (first++)->second = val; } } template bool extents::is_equal_range(key_type first, key_type last, const mapped_value_type& val) const { // RESTRICTED first = std::max(first, key_type()); last = std::min(last, key_type() + (~key_type() >> (sizeof(key_type) * 8 - MaskBits))); if (first <= last) return base_type::is_equal_range(first, last, val); else return true; } template bool extents_base::is_equal_range(key_type key_first, key_type key_last, const mapped_value_type& val) const { // RESTRICTED typename table_type::const_iterator first = partition_at(key_first); typename table_type::const_iterator last = partition_at(key_last) + 1; do { // std::cout << "shift_amount " << key_first << ' ' << key_last << std::endl; if (first->first == NULL && val != first->second) return false; if (first->first != NULL && !first->first->is_equal_range(std::max(key_first, partition_pos(first)), std::min(key_last, partition_pos(first + 1) - 1), val)) return false; } while (++first != last); return true; } // Assumes 'key' is within the range of the range. template const typename extents_base::mapped_value_type& extents_base::at(key_type key) const { typename table_type::const_iterator itr = partition_at(key); while (itr->first != NULL) itr = itr->first->partition_at(key); return itr->second; } } #endif libtorrent-0.13.6/src/torrent/utils/log.cc000066400000000000000000000346031257211073700205140ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include "log.h" #include "log_buffer.h" #include "globals.h" #include "torrent/exceptions.h" #include "torrent/hash_string.h" #include #include #include #include #include #include #include #include #include #include namespace torrent { struct log_cache_entry { typedef log_group::outputs_type outputs_type; bool equal_outputs(const outputs_type& out) const { return out == outputs; } void allocate(unsigned int count) { cache_first = new log_slot[count]; cache_last = cache_first + count; } void clear() { delete [] cache_first; cache_first = NULL; cache_last = NULL; } outputs_type outputs; log_slot* cache_first; log_slot* cache_last; }; struct log_gz_output { log_gz_output(const char* filename) { gz_file = gzopen(filename, "w"); } ~log_gz_output() { if (gz_file != NULL) gzclose(gz_file); } bool is_valid() { return gz_file != Z_NULL; } // bool set_buffer(unsigned size) { return gzbuffer(gz_file, size) == 0; } gzFile gz_file; }; typedef std::vector log_cache_list; typedef std::vector > log_child_list; typedef std::vector log_slot_list; typedef std::vector > log_output_list; log_output_list log_outputs; log_child_list log_children; log_cache_list log_cache; log_group_list log_groups; pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; const char log_level_char[] = { 'C', 'E', 'W', 'N', 'I', 'D' }; // Removing logs always triggers a check if we got any un-used // log_output objects. void log_update_child_cache(int index) { log_child_list::const_iterator first = std::find_if(log_children.begin(), log_children.end(), std::bind2nd(std::greater_equal >(), std::make_pair(index, 0))); if (first == log_children.end()) return; const log_group::outputs_type& outputs = log_groups[index].cached_outputs(); while (first != log_children.end() && first->first == index) { if ((outputs & log_groups[first->second].cached_outputs()) != outputs) { log_groups[first->second].set_cached_outputs(outputs | log_groups[first->second].cached_outputs()); log_update_child_cache(first->second); } first++; } // If we got any circular connections re-do the update to ensure all // children have our new outputs. if (outputs != log_groups[index].cached_outputs()) log_update_child_cache(index); } void log_rebuild_cache() { std::for_each(log_groups.begin(), log_groups.end(), std::mem_fun_ref(&log_group::clear_cached_outputs)); for (int i = 0; i < LOG_GROUP_MAX_SIZE; i++) log_update_child_cache(i); // Clear the cache... std::for_each(log_cache.begin(), log_cache.end(), std::mem_fun_ref(&log_cache_entry::clear)); log_cache.clear(); for (int idx = 0, last = log_groups.size(); idx != last; idx++) { const log_group::outputs_type& use_outputs = log_groups[idx].cached_outputs(); if (use_outputs == 0) { log_groups[idx].set_cached(NULL, NULL); continue; } log_cache_list::iterator cache_itr = std::find_if(log_cache.begin(), log_cache.end(), std::tr1::bind(&log_cache_entry::equal_outputs, std::tr1::placeholders::_1, use_outputs)); if (cache_itr == log_cache.end()) { cache_itr = log_cache.insert(log_cache.end(), log_cache_entry()); cache_itr->outputs = use_outputs; cache_itr->allocate(use_outputs.count()); log_slot* dest_itr = cache_itr->cache_first; for (size_t index = 0; index < log_outputs.size(); index++) { if (use_outputs[index]) *dest_itr++ = log_outputs[index].second; } } log_groups[idx].set_cached(cache_itr->cache_first, cache_itr->cache_last); } } void log_group::internal_print(const HashString* hash, const char* subsystem, const void* dump_data, size_t dump_size, const char* fmt, ...) { va_list ap; unsigned int buffer_size = 4096; char buffer[buffer_size]; char* first = buffer; if (hash != NULL && subsystem != NULL) { first = hash_string_to_hex(*hash, first); first += snprintf(first, 4096 - (first - buffer), "->%s: ", subsystem); } va_start(ap, fmt); int count = vsnprintf(first, 4096 - (first - buffer), fmt, ap); first += std::min(count, buffer_size - 1); va_end(ap); if (count <= 0) return; pthread_mutex_lock(&log_mutex); std::for_each(m_first, m_last, std::tr1::bind(&log_slot::operator(), std::tr1::placeholders::_1, buffer, std::distance(buffer, first), std::distance(log_groups.begin(), this))); if (dump_data != NULL) std::for_each(m_first, m_last, std::tr1::bind(&log_slot::operator(), std::tr1::placeholders::_1, (const char*)dump_data, dump_size, -1)); pthread_mutex_unlock(&log_mutex); } #define LOG_CASCADE(parent) LOG_CHILDREN_CASCADE(parent, parent) #define LOG_CHILDREN_CASCADE(parent, subgroup) \ log_children.push_back(std::make_pair(parent + LOG_ERROR, subgroup + LOG_CRITICAL)); \ log_children.push_back(std::make_pair(parent + LOG_WARN, subgroup + LOG_ERROR)); \ log_children.push_back(std::make_pair(parent + LOG_NOTICE, subgroup + LOG_WARN)); \ log_children.push_back(std::make_pair(parent + LOG_INFO, subgroup + LOG_NOTICE)); \ log_children.push_back(std::make_pair(parent + LOG_DEBUG, subgroup + LOG_INFO)); #define LOG_CHILDREN_SUBGROUP(parent, subgroup) \ log_children.push_back(std::make_pair(parent + LOG_CRITICAL, subgroup + LOG_CRITICAL)); \ log_children.push_back(std::make_pair(parent + LOG_ERROR, subgroup + LOG_ERROR)); \ log_children.push_back(std::make_pair(parent + LOG_WARN, subgroup + LOG_WARN)); \ log_children.push_back(std::make_pair(parent + LOG_NOTICE, subgroup + LOG_NOTICE)); \ log_children.push_back(std::make_pair(parent + LOG_INFO, subgroup + LOG_INFO)); \ log_children.push_back(std::make_pair(parent + LOG_DEBUG, subgroup + LOG_DEBUG)); void log_initialize() { pthread_mutex_lock(&log_mutex); LOG_CASCADE(LOG_CRITICAL); LOG_CASCADE(LOG_CONNECTION_CRITICAL); LOG_CASCADE(LOG_PEER_CRITICAL); LOG_CASCADE(LOG_SOCKET_CRITICAL); LOG_CASCADE(LOG_STORAGE_CRITICAL); LOG_CASCADE(LOG_THREAD_CRITICAL); LOG_CASCADE(LOG_TRACKER_CRITICAL); LOG_CASCADE(LOG_TORRENT_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_CONNECTION_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_PEER_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_SOCKET_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_STORAGE_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_THREAD_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_TRACKER_CRITICAL); LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_TORRENT_CRITICAL); std::sort(log_children.begin(), log_children.end()); log_rebuild_cache(); pthread_mutex_unlock(&log_mutex); } void log_cleanup() { pthread_mutex_lock(&log_mutex); log_groups.assign(log_group()); log_outputs.clear(); log_children.clear(); std::for_each(log_cache.begin(), log_cache.end(), std::mem_fun_ref(&log_cache_entry::clear)); log_cache.clear(); pthread_mutex_unlock(&log_mutex); } log_output_list::iterator log_find_output_name(const char* name) { log_output_list::iterator itr = log_outputs.begin(); log_output_list::iterator last = log_outputs.end(); while (itr != last && itr->first != name) itr++; return itr; } void log_open_output(const char* name, log_slot slot) { pthread_mutex_lock(&log_mutex); if (log_outputs.size() >= log_group::max_size_outputs()) { pthread_mutex_unlock(&log_mutex); throw input_error("Cannot open more than 64 log output handlers."); } if (log_find_output_name(name) != log_outputs.end()) { pthread_mutex_unlock(&log_mutex); throw input_error("Log name already used."); } log_outputs.push_back(std::make_pair(name, slot)); log_rebuild_cache(); pthread_mutex_unlock(&log_mutex); } void log_close_output(const char* name) { } void log_add_group_output(int group, const char* name) { pthread_mutex_lock(&log_mutex); log_output_list::iterator itr = log_find_output_name(name); size_t index = std::distance(log_outputs.begin(), itr); if (itr == log_outputs.end()) { pthread_mutex_unlock(&log_mutex); throw input_error("Log name not found."); } if (index >= log_group::max_size_outputs()) { pthread_mutex_unlock(&log_mutex); throw input_error("Cannot add more log group outputs."); } log_groups[group].set_output_at(index, true); log_rebuild_cache(); pthread_mutex_unlock(&log_mutex); } void log_remove_group_output(int group, const char* name) { } // The log_children list is since we build the output // cache by crawling from child to parent. void log_add_child(int group, int child) { pthread_mutex_lock(&log_mutex); if (std::find(log_children.begin(), log_children.end(), std::make_pair(group, child)) != log_children.end()) return; log_children.push_back(std::make_pair(group, child)); std::sort(log_children.begin(), log_children.end()); log_rebuild_cache(); pthread_mutex_unlock(&log_mutex); } void log_remove_child(int group, int child) { // Remove from all groups, then modify all outputs. } void log_file_write(std::tr1::shared_ptr& outfile, const char* data, size_t length, int group) { // Add group name, data, etc as flags. // Normal groups are nul-terminated strings. if (group >= LOG_NON_CASCADING) { *outfile << cachedTime.seconds() << ' ' << data << std::endl; } else if (group >= 0) { *outfile << cachedTime.seconds() << ' ' << log_level_char[group % 6] << ' ' << data << std::endl; } else if (group == -1) { *outfile << "---DUMP---" << std::endl; if (length != 0) { outfile->rdbuf()->sputn(data, length); *outfile << std::endl; } *outfile << "---END---" << std::endl; } } void log_gz_file_write(std::tr1::shared_ptr& outfile, const char* data, size_t length, int group) { char buffer[64]; // Normal groups are nul-terminated strings. if (group >= 0) { const char* fmt = (group >= LOG_NON_CASCADING) ? ("%" PRIi32 " ") : ("%" PRIi32 " %c"); int buffer_length = snprintf(buffer, 64, fmt, cachedTime.seconds(), log_level_char[group % 6]); if (buffer_length > 0) gzwrite(outfile->gz_file, buffer, buffer_length); gzwrite(outfile->gz_file, data, length); gzwrite(outfile->gz_file, "\n", 1); } else if (group == -1) { gzwrite(outfile->gz_file, "---DUMP---\n", sizeof("---DUMP---\n") - 1); if (length != 0) gzwrite(outfile->gz_file, data, length); gzwrite(outfile->gz_file, "---END---\n", sizeof("---END---\n") - 1); } } void log_open_file_output(const char* name, const char* filename) { std::tr1::shared_ptr outfile(new std::ofstream(filename)); if (!outfile->good()) throw input_error("Could not open log file '" + std::string(filename) + "'."); log_open_output(name, std::tr1::bind(&log_file_write, outfile, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3)); } void log_open_gz_file_output(const char* name, const char* filename) { std::tr1::shared_ptr outfile(new log_gz_output(filename)); if (!outfile->is_valid()) throw input_error("Could not open log gzip file '" + std::string(filename) + "'."); // if (!outfile->set_buffer(1 << 14)) // throw input_error("Could not set gzip log file buffer size."); log_open_output(name, std::tr1::bind(&log_gz_file_write, outfile, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3)); } log_buffer* log_open_log_buffer(const char* name) { log_buffer* buffer = new log_buffer; try { log_open_output(name, std::tr1::bind(&log_buffer::lock_and_push_log, buffer, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3)); return buffer; } catch (torrent::input_error& e) { delete buffer; throw; } } } libtorrent-0.13.6/src/torrent/utils/log.h000066400000000000000000000161221257211073700203520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_LOG_H #define LIBTORRENT_UTILS_LOG_H #include #include #include #include #include #include namespace torrent { // TODO: Add option_strings support. enum { LOG_CRITICAL, LOG_ERROR, LOG_WARN, LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_CONNECTION_CRITICAL, LOG_CONNECTION_ERROR, LOG_CONNECTION_WARN, LOG_CONNECTION_NOTICE, LOG_CONNECTION_INFO, LOG_CONNECTION_DEBUG, LOG_DHT_CRITICAL, LOG_DHT_ERROR, LOG_DHT_WARN, LOG_DHT_NOTICE, LOG_DHT_INFO, LOG_DHT_DEBUG, LOG_PEER_CRITICAL, LOG_PEER_ERROR, LOG_PEER_WARN, LOG_PEER_NOTICE, LOG_PEER_INFO, LOG_PEER_DEBUG, LOG_SOCKET_CRITICAL, LOG_SOCKET_ERROR, LOG_SOCKET_WARN, LOG_SOCKET_NOTICE, LOG_SOCKET_INFO, LOG_SOCKET_DEBUG, LOG_STORAGE_CRITICAL, LOG_STORAGE_ERROR, LOG_STORAGE_WARN, LOG_STORAGE_NOTICE, LOG_STORAGE_INFO, LOG_STORAGE_DEBUG, LOG_THREAD_CRITICAL, LOG_THREAD_ERROR, LOG_THREAD_WARN, LOG_THREAD_NOTICE, LOG_THREAD_INFO, LOG_THREAD_DEBUG, LOG_TRACKER_CRITICAL, LOG_TRACKER_ERROR, LOG_TRACKER_WARN, LOG_TRACKER_NOTICE, LOG_TRACKER_INFO, LOG_TRACKER_DEBUG, LOG_TORRENT_CRITICAL, LOG_TORRENT_ERROR, LOG_TORRENT_WARN, LOG_TORRENT_NOTICE, LOG_TORRENT_INFO, LOG_TORRENT_DEBUG, LOG_NON_CASCADING, LOG_INSTRUMENTATION_MEMORY, LOG_INSTRUMENTATION_MINCORE, LOG_INSTRUMENTATION_CHOKE, LOG_INSTRUMENTATION_POLLING, LOG_INSTRUMENTATION_TRANSFERS, LOG_PEER_LIST_EVENTS, LOG_PROTOCOL_PIECE_EVENTS, LOG_PROTOCOL_METADATA_EVENTS, LOG_PROTOCOL_NETWORK_ERRORS, LOG_PROTOCOL_STORAGE_ERRORS, LOG_RPC_EVENTS, LOG_RPC_DUMP, LOG_UI_EVENTS, LOG_GROUP_MAX_SIZE }; #define lt_log_is_valid(log_group) (torrent::log_groups[log_group].valid()) #define lt_log_print(log_group, ...) \ if (torrent::log_groups[log_group].valid()) \ torrent::log_groups[log_group].internal_print(NULL, NULL, NULL, 0, __VA_ARGS__); #define lt_log_print_info(log_group, log_info, log_subsystem, ...) \ if (torrent::log_groups[log_group].valid()) \ torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, NULL, 0, __VA_ARGS__); #define lt_log_print_data(log_group, log_data, log_subsystem, ...) \ if (torrent::log_groups[log_group].valid()) \ torrent::log_groups[log_group].internal_print(&log_data->hash(), log_subsystem, NULL, 0, __VA_ARGS__); #define lt_log_print_dump(log_group, log_dump_data, log_dump_size, ...) \ if (torrent::log_groups[log_group].valid()) \ torrent::log_groups[log_group].internal_print(NULL, NULL, log_dump_data, log_dump_size, __VA_ARGS__); \ #define lt_log_print_info_dump(log_group, log_dump_data, log_dump_size, log_info, log_subsystem, ...) \ if (torrent::log_groups[log_group].valid()) \ torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, log_dump_data, log_dump_size, __VA_ARGS__); \ class log_buffer; typedef std::tr1::function log_slot; class LIBTORRENT_EXPORT log_group { public: typedef std::bitset<64> outputs_type; log_group() : m_first(NULL), m_last(NULL) { m_outputs.reset(); m_cached_outputs.reset(); } bool valid() const { return m_first != NULL; } bool empty() const { return m_first == NULL; } size_t size_outputs() const { return std::distance(m_first, m_last); } static size_t max_size_outputs() { return 64; } // // Internal: // void internal_print(const HashString* hash, const char* subsystem, const void* dump_data, size_t dump_size, const char* fmt, ...); const outputs_type& outputs() const { return m_outputs; } const outputs_type& cached_outputs() const { return m_cached_outputs; } void clear_cached_outputs() { m_cached_outputs = m_outputs; } void set_outputs(const outputs_type& val) { m_outputs = val; } void set_cached_outputs(const outputs_type& val) { m_cached_outputs = val; } void set_output_at(size_t index, bool val) { m_outputs[index] = val; } void set_cached(log_slot* f, log_slot* l) { m_first = f; m_last = l; } private: outputs_type m_outputs; outputs_type m_cached_outputs; log_slot* m_first; log_slot* m_last; }; typedef std::tr1::array log_group_list; extern log_group_list log_groups LIBTORRENT_EXPORT; void log_initialize() LIBTORRENT_EXPORT; void log_cleanup() LIBTORRENT_EXPORT; void log_open_output(const char* name, log_slot slot) LIBTORRENT_EXPORT; void log_close_output(const char* name) LIBTORRENT_EXPORT; void log_add_group_output(int group, const char* name) LIBTORRENT_EXPORT; void log_remove_group_output(int group, const char* name) LIBTORRENT_EXPORT; void log_add_child(int group, int child) LIBTORRENT_EXPORT; void log_remove_child(int group, int child) LIBTORRENT_EXPORT; void log_open_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT; void log_open_gz_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT; log_buffer* log_open_log_buffer(const char* name) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/utils/log_buffer.cc000066400000000000000000000046351257211073700220470ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "log_buffer.h" #include #include #include "globals.h" namespace tr1 { using namespace std::tr1; } namespace torrent { // Rename function/args? log_buffer::const_iterator log_buffer::find_older(int32_t older_than) { if (empty() || !back().is_younger_than(older_than)) return end(); return std::find_if(begin(), end(), tr1::bind(&log_entry::is_younger_or_same, tr1::placeholders::_1, older_than)); } void log_buffer::lock_and_push_log(const char* data, size_t length, int group) { if (group < 0) return; lock(); if (size() >= max_size()) base_type::pop_front(); base_type::push_back(log_entry(cachedTime.seconds(), group % 6, std::string(data, length))); if (m_slot_update) m_slot_update(); unlock(); } } libtorrent-0.13.6/src/torrent/utils/log_buffer.h000066400000000000000000000066571257211073700217170ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_LOG_BUFFER_H #define LIBTORRENT_UTILS_LOG_BUFFER_H #include #include #include #include #include namespace torrent { struct log_entry { log_entry(int32_t t, int32_t grp, const std::string& msg) : timestamp(t), group(grp), message(msg) {} bool is_older_than(int32_t t) const { return timestamp < t; } bool is_younger_than(int32_t t) const { return timestamp > t; } bool is_younger_or_same(int32_t t) const { return timestamp >= t; } int32_t timestamp; int32_t group; std::string message; }; class LIBTORRENT_EXPORT log_buffer : private std::deque { public: typedef std::deque base_type; typedef std::tr1::function slot_void; using base_type::iterator; using base_type::const_iterator; using base_type::reverse_iterator; using base_type::const_reverse_iterator; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::front; using base_type::back; using base_type::empty; using base_type::size; log_buffer() : m_max_size(200) { pthread_mutex_init(&m_lock, NULL); } unsigned int max_size() const { return m_max_size; } // Always lock before calling any function. void lock() { pthread_mutex_lock(&m_lock); } void unlock() { pthread_mutex_unlock(&m_lock); } const_iterator find_older(int32_t older_than); void lock_and_set_update_slot(const slot_void& slot) { lock(); m_slot_update = slot; unlock(); } void lock_and_push_log(const char* data, size_t length, int group); private: pthread_mutex_t m_lock; unsigned int m_max_size; slot_void m_slot_update; }; } #endif libtorrent-0.13.6/src/torrent/utils/net.cc000066400000000000000000000043751257211073700205240ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "net.h" #include "exceptions.h" #include namespace torrent { addrinfo* address_info_lookup(const char* hostname, int family, int socktype) { addrinfo hints; std::memset(&hints, 0, sizeof(addrinfo)); hints.ai_family = family; hints.ai_socktype = socktype; addrinfo* res = NULL; int err = ::getaddrinfo(hostname, NULL, &hints, &res); if (err) throw address_info_error(err); return res; } bool address_info_call(addrinfo* ai, int flags, slot_ai_success slot_success) { while (ai != NULL) { slot_success(ai->ai_addr, ai->ai_addrlen); return true; } return false; } } libtorrent-0.13.6/src/torrent/utils/net.h000066400000000000000000000042631257211073700203620ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_NET_H #define LIBTORRENT_UTILS_NET_H #include #include namespace torrent { typedef std::tr1::function slot_ai_success; //typedef std::tr1::function slot_ai_failure; // Throws address_info_error on lookup failure. addrinfo* address_info_lookup(const char* hostname, int family, int socktype); inline void address_info_free(addrinfo* ai) { ::freeaddrinfo(ai); } bool address_info_call(addrinfo* ai, int flags, slot_ai_success slot_success); } #endif libtorrent-0.13.6/src/torrent/utils/option_strings.cc000066400000000000000000000203111257211073700230030ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "torrent/connection_manager.h" #include "torrent/object.h" #include "torrent/download/choke_group.h" #include "torrent/download/choke_queue.h" #include "exceptions.h" #include "download.h" #include "log.h" #include "option_strings.h" namespace torrent { struct option_single { unsigned int size; const char** name; }; struct option_pair { const char* name; unsigned int value; }; option_pair option_list_connection[] = { { "leech", Download::CONNECTION_LEECH }, { "seed", Download::CONNECTION_SEED }, { "initial_seed", Download::CONNECTION_INITIAL_SEED }, { "metadata", Download::CONNECTION_METADATA }, { NULL, 0 } }; option_pair option_list_heuristics[] = { { "upload_leech", choke_queue::HEURISTICS_UPLOAD_LEECH }, { "upload_leech_dummy", choke_queue::HEURISTICS_UPLOAD_LEECH_DUMMY }, { "download_leech", choke_queue::HEURISTICS_DOWNLOAD_LEECH }, { "download_leech_dummy", choke_queue::HEURISTICS_DOWNLOAD_LEECH_DUMMY }, { "invalid", choke_queue::HEURISTICS_MAX_SIZE }, { NULL, 0 } }; option_pair option_list_heuristics_download[] = { { "download_leech", choke_queue::HEURISTICS_DOWNLOAD_LEECH }, { "download_leech_dummy", choke_queue::HEURISTICS_DOWNLOAD_LEECH_DUMMY }, { NULL, 0 } }; option_pair option_list_heuristics_upload[] = { { "upload_leech", choke_queue::HEURISTICS_UPLOAD_LEECH }, { "upload_leech_dummy", choke_queue::HEURISTICS_UPLOAD_LEECH_DUMMY }, { NULL, 0 } }; option_pair option_list_encryption[] = { { "none", torrent::ConnectionManager::encryption_none }, { "allow_incoming", torrent::ConnectionManager::encryption_allow_incoming }, { "try_outgoing", torrent::ConnectionManager::encryption_try_outgoing }, { "require", torrent::ConnectionManager::encryption_require }, { "require_RC4", torrent::ConnectionManager::encryption_require_RC4 }, { "require_rc4", torrent::ConnectionManager::encryption_require_RC4 }, { "enable_retry", torrent::ConnectionManager::encryption_enable_retry }, { "prefer_plaintext", torrent::ConnectionManager::encryption_prefer_plaintext }, { NULL, 0 } }; option_pair option_list_ip_filter[] = { { "unwanted", PeerInfo::flag_unwanted }, { "preferred", PeerInfo::flag_preferred }, { NULL, 0 } }; option_pair option_list_ip_tos[] = { { "default", torrent::ConnectionManager::iptos_default }, { "lowdelay", torrent::ConnectionManager::iptos_lowdelay }, { "throughput", torrent::ConnectionManager::iptos_throughput }, { "reliability", torrent::ConnectionManager::iptos_reliability }, { "mincost", torrent::ConnectionManager::iptos_mincost }, { NULL, 0 } }; option_pair option_list_tracker_mode[] = { { "normal", choke_group::TRACKER_MODE_NORMAL }, { "aggressive", choke_group::TRACKER_MODE_AGGRESSIVE }, { NULL, 0 } }; const char* option_list_log_group[] = { "critical", "error", "warn", "notice", "info", "debug", "connection_critical", "connection_error", "connection_warn", "connection_notice", "connection_info", "connection_debug", "dht_critical", "dht_error", "dht_warn", "dht_notice", "dht_info", "dht_debug", "peer_critical", "peer_error", "peer_warn", "peer_notice", "peer_info", "peer_debug", "socket_critical", "socket_error", "socket_warn", "socket_notice", "socket_info", "socket_debug", "storage_critical", "storage_error", "storage_warn", "storage_notice", "storage_info", "storage_debug", "thread_critical", "thread_error", "thread_warn", "thread_notice", "thread_info", "thread_debug", "tracker_critical", "tracker_error", "tracker_warn", "tracker_notice", "tracker_info", "tracker_debug", "torrent_critical", "torrent_error", "torrent_warn", "torrent_notice", "torrent_info", "torrent_debug", "__non_cascading__", "instrumentation_memory", "instrumentation_mincore", "instrumentation_choke", "instrumentation_polling", "instrumentation_transfers", "peer_list_events", "protocol_piece_events", "protocol_metadata_events", "protocol_network_errors", "protocol_storage_errors", "rpc_events", "rpc_dump", "ui_events", NULL }; const char* option_list_tracker_event[] = { "updated", "completed", "started", "stopped", "scrape", NULL }; option_pair* option_pair_lists[OPTION_START_COMPACT] = { option_list_connection, option_list_heuristics, option_list_heuristics_download, option_list_heuristics_upload, option_list_encryption, option_list_ip_filter, option_list_ip_tos, option_list_tracker_mode, }; #define OPTION_SINGLE_ENTRY(single_name) \ { sizeof(single_name) / sizeof(const char*) - 1, single_name } option_single option_single_lists[OPTION_SINGLE_SIZE] = { OPTION_SINGLE_ENTRY(option_list_log_group), OPTION_SINGLE_ENTRY(option_list_tracker_event), }; int option_find_string(option_enum opt_enum, const char* name) { if (opt_enum < OPTION_START_COMPACT) { option_pair* itr = option_pair_lists[opt_enum]; do { if (std::strcmp(itr->name, name) == 0) return itr->value; } while ((++itr)->name != NULL); } else if (opt_enum < OPTION_MAX_SIZE) { const char** itr = option_single_lists[opt_enum - OPTION_START_COMPACT].name; do { if (std::strcmp(*itr, name) == 0) return std::distance(option_single_lists[opt_enum - OPTION_START_COMPACT].name, itr); } while (*++itr != NULL); } throw input_error("Invalid option name."); } const char* option_as_string(option_enum opt_enum, unsigned int value) { if (opt_enum < OPTION_START_COMPACT) { option_pair* itr = option_pair_lists[opt_enum]; do { if (itr->value == value) return itr->name; } while ((++itr)->name != NULL); } else if (opt_enum < OPTION_MAX_SIZE) { if (value < option_single_lists[opt_enum - OPTION_START_COMPACT].size) return option_single_lists[opt_enum - OPTION_START_COMPACT].name[value]; } throw input_error("Invalid option value."); } torrent::Object option_list_strings(option_enum opt_enum) { Object::list_type result; if (opt_enum < OPTION_START_COMPACT) { option_pair* itr = option_pair_lists[opt_enum]; while (itr->name != NULL) result.push_back(std::string(itr++->name)); } else if (opt_enum < OPTION_MAX_SIZE) { const char** itr = option_single_lists[opt_enum - OPTION_START_COMPACT].name; while (*itr != NULL) result.push_back(std::string(*itr++)); } return Object::from_list(result); } } libtorrent-0.13.6/src/torrent/utils/option_strings.h000066400000000000000000000051151257211073700226520ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_OPTION_STRINGS_H #define LIBTORRENT_UTILS_OPTION_STRINGS_H #include #include namespace torrent { class Object; enum option_enum { OPTION_CONNECTION_TYPE, OPTION_CHOKE_HEURISTICS, OPTION_CHOKE_HEURISTICS_DOWNLOAD, OPTION_CHOKE_HEURISTICS_UPLOAD, OPTION_ENCRYPTION, OPTION_IP_FILTER, OPTION_IP_TOS, OPTION_TRACKER_MODE, OPTION_LOG_GROUP, OPTION_TRACKER_EVENT, OPTION_MAX_SIZE, OPTION_START_COMPACT = OPTION_LOG_GROUP, OPTION_SINGLE_SIZE = OPTION_MAX_SIZE - OPTION_START_COMPACT }; int option_find_string(option_enum opt_enum, const char* name) LIBTORRENT_EXPORT; inline int option_find_string_str(option_enum opt_enum, const std::string& name) { return option_find_string(opt_enum, name.c_str()); } const char* option_as_string(option_enum opt_enum, unsigned int value) LIBTORRENT_EXPORT; torrent::Object option_list_strings(option_enum opt_enum) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/utils/ranges.h000066400000000000000000000174361257211073700210610ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_RANGES_H #define LIBTORRENT_UTILS_RANGES_H #include #include // TODO: Use tr1 functional instead. #include namespace torrent { template class ranges : private std::vector > { public: typedef std::vector > base_type; typedef RangesType bound_type; typedef typename base_type::value_type value_type; typedef typename base_type::reference reference; typedef typename base_type::iterator iterator; typedef typename base_type::const_iterator const_iterator; typedef typename base_type::reverse_iterator reverse_iterator; using base_type::clear; using base_type::empty; using base_type::size; using base_type::begin; using base_type::end; using base_type::rbegin; using base_type::rend; using base_type::front; using base_type::back; void insert(bound_type first, bound_type last) { insert(std::make_pair(first, last)); } void erase(bound_type first, bound_type last) { erase(std::make_pair(first, last)); } void insert(value_type r); void erase(value_type r); // Find the first ranges that has an end greater than index. iterator find(bound_type index); const_iterator find(bound_type index) const; // Use find with no closest match. bool has(bound_type index) const; size_t intersect_distance(bound_type first, bound_type last) const; size_t intersect_distance(value_type range) const; static ranges create_union(const ranges& left, const ranges& right); }; template void ranges::insert(value_type r) { if (r.first >= r.second) return; iterator first = std::find_if(begin(), end(), rak::less_equal(r.first, rak::const_mem_ref(&value_type::second))); if (first == end() || r.second < first->first) { // The new range is before the first, after the last or between // two ranges. base_type::insert(first, r); } else { first->first = std::min(r.first, first->first); first->second = std::max(r.second, first->second); iterator last = std::find_if(first, end(), rak::less(first->second, rak::const_mem_ref(&value_type::second))); if (last != end() && first->second >= last->first) first->second = (last++)->second; base_type::erase(first + 1, last); } } template void ranges::erase(value_type r) { if (r.first >= r.second) return; iterator first = std::find_if(begin(), end(), rak::less(r.first, rak::const_mem_ref(&value_type::second))); iterator last = std::find_if(first, end(), rak::less(r.second, rak::const_mem_ref(&value_type::second))); if (first == end()) return; if (first == last) { if (r.first > first->first) { std::swap(first->first, r.second); base_type::insert(first, value_type(r.second, r.first)); } else if (r.second > first->first) { first->first = r.second; } } else { if (r.first > first->first) (first++)->second = r.first; if (last != end() && r.second > last->first) last->first = r.second; base_type::erase(first, last); } } // Find the first ranges that has an end greater than index. template inline typename ranges::iterator ranges::find(bound_type index) { return std::find_if(begin(), end(), rak::less(index, rak::const_mem_ref(&value_type::second))); } template inline typename ranges::const_iterator ranges::find(bound_type index) const { return std::find_if(begin(), end(), rak::less(index, rak::const_mem_ref(&value_type::second))); } // Use find with no closest match. template bool ranges::has(bound_type index) const { const_iterator itr = find(index); return itr != end() && index >= itr->first; } template size_t ranges::intersect_distance(bound_type first, bound_type last) const { return intersect_distance(std::make_pair(first, last)); } // The total length of all the extents within the bounds of 'range'. template size_t ranges::intersect_distance(value_type range) const { const_iterator first = find(range.first); if (first == end() || range.second <= first->first) return 0; size_t dist = std::min(range.second, first->second) - std::max(range.first, first->first); while (++first != end() && range.second > first->first) dist += std::min(range.second, first->second) - first->first; return dist; } template ranges ranges::create_union(const ranges& left, const ranges& right) { if (left.empty()) return right; if (right.empty()) return left; ranges result; typename ranges::const_iterator left_itr = left.begin(); typename ranges::const_iterator left_last = left.end(); typename ranges::const_iterator right_itr = right.begin(); typename ranges::const_iterator right_last = right.end(); if (left_itr->first < right_itr->first) result.base_type::push_back(*left_itr++); else result.base_type::push_back(*right_itr++); while (left_itr != left_last && right_itr != right_last) { value_type next; if (left_itr->first < right_itr->first) next = *left_itr++; else next = *right_itr++; if (next.first <= result.back().second) result.back().second = std::max(next.second, result.back().second); else result.base_type::push_back(next); } // Only one of these while loops will be triggered. for (; left_itr != left_last; left_itr++) { if (left_itr->first <= result.back().second) result.back().second = std::max(left_itr->second, result.back().second); else result.base_type::push_back(*left_itr); } for (; right_itr != right_last; right_itr++) { if (right_itr->first <= result.back().second) result.back().second = std::max(right_itr->second, result.back().second); else result.base_type::push_back(*right_itr); } return result; } } #endif libtorrent-0.13.6/src/torrent/utils/resume.cc000066400000000000000000000434601257211073700212340ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "peer/peer_info.h" #include "peer/peer_list.h" #include "data/file.h" #include "data/file_list.h" #include "data/transfer_list.h" #include "net/address_list.h" #include "common.h" #include "bitfield.h" #include "download.h" #include "download_info.h" #include "object.h" #include "tracker.h" #include "tracker_list.h" #include "globals.h" #include "resume.h" namespace torrent { void resume_load_progress(Download download, const Object& object) { if (!object.has_key_list("files")) return; const Object::list_type& files = object.get_key_list("files"); if (files.size() != download.file_list()->size_files()) return; if (object.has_key_string("bitfield")) { const Object::string_type& bitfield = object.get_key_string("bitfield"); if (bitfield.size() != download.file_list()->bitfield()->size_bytes()) return; download.set_bitfield((uint8_t*)bitfield.c_str(), (uint8_t*)(bitfield.c_str() + bitfield.size())); } else if (object.has_key_value("bitfield")) { Object::value_type chunksDone = object.get_key_value("bitfield"); if (chunksDone == download.file_list()->bitfield()->size_bits()) download.set_bitfield(true); else if (chunksDone == 0) download.set_bitfield(false); else return; } else { return; } Object::list_const_iterator filesItr = files.begin(); FileList* fileList = download.file_list(); for (FileList::iterator listItr = fileList->begin(), listLast = fileList->end(); listItr != listLast; ++listItr, ++filesItr) { rak::file_stat fs; if (!filesItr->has_key_value("mtime")) { // If 'mtime' is erased, it means we should start hashing and // downloading the file as if it was a new torrent. (*listItr)->set_flags(File::flag_create_queued | File::flag_resize_queued); download.update_range(Download::update_range_recheck | Download::update_range_clear, (*listItr)->range().first, (*listItr)->range().second); continue; } int64_t mtimeValue = filesItr->get_key_value("mtime"); bool fileExists = fs.update(fileList->root_dir() + (*listItr)->path()->as_string()); // The default action when we have 'mtime' is not to create nor // resize the file. (*listItr)->unset_flags(File::flag_create_queued | File::flag_resize_queued); if (mtimeValue == ~int64_t(0) || mtimeValue == ~int64_t(1)) { // If 'mtime' is ~0 it means we haven't gotten around to // creating the file. // // Else if it is ~1 it means the file doesn't exist nor do we // want to create it. // // When 'mtime' is ~2 we need to recheck the hash without // creating the file. It will just fail on the mtime check // later, so we don't need to handle it explicitly. if (mtimeValue == ~int64_t(0)) (*listItr)->set_flags(File::flag_create_queued | File::flag_resize_queued); // Ensure the bitfield range is cleared so that stray resume // data doesn't get counted. download.update_range(Download::update_range_clear | (fileExists ? Download::update_range_recheck : 0), (*listItr)->range().first, (*listItr)->range().second); continue; } // If the file is the wrong size, queue resize and clear resume // data for that file. if ((uint64_t)fs.size() != (*listItr)->size_bytes()) { (*listItr)->set_flags(File::flag_resize_queued); download.update_range(Download::update_range_clear | Download::update_range_recheck, (*listItr)->range().first, (*listItr)->range().second); continue; } // An 'mtime' of ~3 means the resume data was written while the // torrent was actively downloading, and thus we need to recheck // chunks that might not have been completely written to disk. // // This gets handled below, so just skip to the next file. if (mtimeValue == ~int64_t(3)) continue; // An 'mtime' of ~2 indicates that the resume data was made by an // old rtorrent version which does not include 'uncertain_pieces' // field, and thus can't be relied upon. // // If the 'mtime' is an actual mtime we check to see if it matches // the file, else clear the range. This should be set only for // files that have completed and got no indices in // TransferList::completed_list(). if (mtimeValue == ~int64_t(2) || mtimeValue != fs.modified_time()) { download.update_range(Download::update_range_clear | Download::update_range_recheck, (*listItr)->range().first, (*listItr)->range().second); continue; } } resume_load_uncertain_pieces(download, object); } void resume_save_progress(Download download, Object& object) { // We don't remove the old hash data since it might still be valid, // just that the client didn't finish the check this time. if (!download.is_hash_checked()) return; download.sync_chunks(); // If syncing failed, invalidate all resume data and return. if (!download.is_hash_checked()) { if (!object.has_key_list("files")) return; Object::list_type& files = object.get_key_list("files"); for (Object::list_iterator itr = files.begin(), last = files.end(); itr != last; itr++) itr->insert_key("mtime", ~int64_t(2)); return; } const Bitfield* bitfield = download.file_list()->bitfield(); if (bitfield->is_all_set() || bitfield->is_all_unset()) object.insert_key("bitfield", bitfield->size_set()); else object.insert_key("bitfield", std::string((char*)bitfield->begin(), bitfield->size_bytes())); Object::list_type& files = object.insert_preserve_copy("files", Object::create_list()).first->second.as_list(); Object::list_iterator filesItr = files.begin(); FileList* fileList = download.file_list(); for (FileList::iterator listItr = fileList->begin(), listLast = fileList->end(); listItr != listLast; ++listItr, ++filesItr) { if (filesItr == files.end()) filesItr = files.insert(filesItr, Object::create_map()); else if (!filesItr->is_map()) *filesItr = Object::create_map(); filesItr->insert_key("completed", (int64_t)(*listItr)->completed_chunks()); rak::file_stat fs; bool fileExists = fs.update(fileList->root_dir() + (*listItr)->path()->as_string()); if (!fileExists) { if ((*listItr)->is_create_queued()) // ~0 means the file still needs to be created. filesItr->insert_key("mtime", ~int64_t(0)); else // ~1 means the file shouldn't be created. filesItr->insert_key("mtime", ~int64_t(1)); // } else if ((*listItr)->completed_chunks() == (*listItr)->size_chunks()) { } else if (bitfield->is_all_set()) { // Currently only checking if we're finished. This needs to be // smarter when it comes to downloading partial torrents, etc. // This assumes the syncs are properly called before // resume_save_progress gets called after finishing a torrent. filesItr->insert_key("mtime", (int64_t)fs.modified_time()); } else if (!download.info()->is_active()) { // When stopped, all chunks should have received sync, thus the // file's mtime will be correct. (We hope) filesItr->insert_key("mtime", (int64_t)fs.modified_time()); } else { // If the torrent isn't done and we've not shut down, then set // 'mtime' to ~3 so as to indicate that the 'mtime' is not to be // trusted, yet we have a partial bitfield for the file. filesItr->insert_key("mtime", ~int64_t(3)); } } } void resume_clear_progress(Download download, Object& object) { object.erase_key("bitfield"); } void resume_load_uncertain_pieces(Download download, const Object& object) { // Don't rehash when loading resume data within the same session. if (!object.has_key_string("uncertain_pieces") || !object.has_key_value("uncertain_pieces.timestamp") || object.get_key_value("uncertain_pieces.timestamp") >= (int64_t)download.info()->load_date()) return; const Object::string_type& uncertain = object.get_key_string("uncertain_pieces"); for (const char* itr = uncertain.c_str(), *last = uncertain.c_str() + uncertain.size(); itr + sizeof(uint32_t) <= last; itr += sizeof(uint32_t)) { // Fix this so it does full ranges. download.update_range(Download::update_range_recheck | Download::update_range_clear, ntohl(*(uint32_t*)itr), ntohl(*(uint32_t*)itr) + 1); } } void resume_save_uncertain_pieces(Download download, Object& object) { // Add information on what chunks might still not have been properly // written to disk. object.erase_key("uncertain_pieces"); object.insert_key("uncertain_pieces.timestamp", rak::timer::current_seconds()); const TransferList::completed_list_type& completedList = download.transfer_list()->completed_list(); TransferList::completed_list_type::const_iterator itr = std::find_if(completedList.begin(), completedList.end(), rak::less_equal((rak::timer::current() - rak::timer::from_minutes(15)).usec(), rak::const_mem_ref(&TransferList::completed_list_type::value_type::first))); if (itr == completedList.end()) return; std::vector buffer; buffer.reserve(std::distance(itr, completedList.end())); while (itr != completedList.end()) buffer.push_back((itr++)->second); std::sort(buffer.begin(), buffer.end()); for (std::vector::iterator itr2 = buffer.begin(), last = buffer.end(); itr2 != last; itr2++) *itr2 = htonl(*itr2); Object::string_type& completed = object.insert_key("uncertain_pieces", std::string()).as_string(); completed.append((const char*)&buffer.front(), buffer.size() * sizeof(uint32_t)); } bool resume_check_target_files(Download download, __UNUSED const Object& object) { FileList* fileList = download.file_list(); if (!fileList->is_open()) return false; if (!fileList->is_root_dir_created()) return false; if (fileList->is_multi_file()) { // Here we should probably check all/most of the files within the // torrent. But for now just return true, as the root dir is // usually created for each (multi) torrent. // int failed = 0; // int exists = 0; // for (FileList::const_iterator itr = fileList->begin(), last = fileList->end(); itr != last; itr++) { // if (!(*itr)->is_previously_created()) // continue; // if ((*itr)->is_created()) // exists++; // else // failed++; // } // return failed >= exists; return true; } else { // We consider empty file lists as being valid. return fileList->empty() || fileList->front()->is_created(); } } void resume_load_file_priorities(Download download, const Object& object) { if (!object.has_key_list("files")) return; const Object::list_type& files = object.get_key_list("files"); Object::list_const_iterator filesItr = files.begin(); Object::list_const_iterator filesLast = files.end(); FileList* fileList = download.file_list(); for (FileList::iterator listItr = fileList->begin(), listLast = fileList->end(); listItr != listLast; ++listItr, ++filesItr) { if (filesItr == filesLast) return; // Update the priority from the fast resume data. if (filesItr->has_key_value("priority") && filesItr->get_key_value("priority") >= 0 && filesItr->get_key_value("priority") <= PRIORITY_HIGH) (*listItr)->set_priority((priority_t)filesItr->get_key_value("priority")); if (filesItr->has_key_value("completed")) (*listItr)->set_completed_chunks(filesItr->get_key_value("completed")); } } void resume_save_file_priorities(Download download, Object& object) { Object::list_type& files = object.insert_preserve_copy("files", Object::create_list()).first->second.as_list(); Object::list_iterator filesItr = files.begin(); FileList* fileList = download.file_list(); for (FileList::iterator listItr = fileList->begin(), listLast = fileList->end(); listItr != listLast; ++listItr, ++filesItr) { if (filesItr == files.end()) filesItr = files.insert(filesItr, Object::create_map()); else if (!filesItr->is_map()) *filesItr = Object::create_map(); filesItr->insert_key("priority", (int64_t)(*listItr)->priority()); } } void resume_load_addresses(Download download, const Object& object) { if (!object.has_key_list("peers")) return; PeerList* peerList = download.peer_list(); const Object::list_type& src = object.get_key_list("peers"); for (Object::list_const_iterator itr = src.begin(), last = src.end(); itr != last; ++itr) { if (!itr->is_map() || !itr->has_key_string("inet") || itr->get_key_string("inet").size() != sizeof(SocketAddressCompact) || !itr->has_key_value("failed") || !itr->has_key_value("last") || itr->get_key_value("last") > cachedTime.seconds()) continue; int flags = 0; rak::socket_address socketAddress = *reinterpret_cast(itr->get_key_string("inet").c_str()); if (socketAddress.port() != 0) flags |= PeerList::address_available; PeerInfo* peerInfo = peerList->insert_address(socketAddress.c_sockaddr(), flags); if (peerInfo == NULL) continue; peerInfo->set_failed_counter(itr->get_key_value("failed")); peerInfo->set_last_connection(itr->get_key_value("last")); } // Tell rTorrent to harvest addresses. } void resume_save_addresses(Download download, Object& object) { const PeerList* peerList = download.peer_list(); Object& dest = object.insert_key("peers", Object::create_list()); for (PeerList::const_iterator itr = peerList->begin(), last = peerList->end(); itr != last; ++itr) { // Add some checks, like see if there's anything interesting to // save, etc. Or if we can reconnect to it at some later time. // // This should really ensure that if called on a torrent that has // been closed for a while, it won't throw out perfectly good // entries. Object& peer = dest.insert_back(Object::create_map()); const rak::socket_address* sa = rak::socket_address::cast_from(itr->second->socket_address()); if (sa->family() == rak::socket_address::af_inet) peer.insert_key("inet", std::string(SocketAddressCompact(sa->sa_inet()->address_n(), htons(itr->second->listen_port())).c_str(), sizeof(SocketAddressCompact))); peer.insert_key("failed", itr->second->failed_counter()); peer.insert_key("last", itr->second->is_connected() ? cachedTime.seconds() : itr->second->last_connection()); } } void resume_load_tracker_settings(Download download, const Object& object) { if (!object.has_key_map("trackers")) return; const Object& src = object.get_key("trackers"); TrackerList* tracker_list = download.tracker_list(); for (Object::map_const_iterator itr = src.as_map().begin(), last = src.as_map().end(); itr != last; ++itr) { if (!itr->second.has_key("extra_tracker") || itr->second.get_key_value("extra_tracker") == 0 || !itr->second.has_key("group")) continue; if (tracker_list->find_url(itr->first) != tracker_list->end()) continue; download.tracker_list()->insert_url(itr->second.get_key_value("group"), itr->first); } for (TrackerList::iterator itr = tracker_list->begin(), last = tracker_list->end(); itr != last; ++itr) { if (!src.has_key_map((*itr)->url())) continue; const Object& trackerObject = src.get_key((*itr)->url()); if (trackerObject.has_key_value("enabled") && trackerObject.get_key_value("enabled") == 0) (*itr)->disable(); else (*itr)->enable(); } } void resume_save_tracker_settings(Download download, Object& object) { Object& dest = object.insert_preserve_copy("trackers", Object::create_map()).first->second; TrackerList* tracker_list = download.tracker_list(); for (TrackerList::iterator itr = tracker_list->begin(), last = tracker_list->end(); itr != last; ++itr) { Object& trackerObject = dest.insert_key((*itr)->url(), Object::create_map()); trackerObject.insert_key("enabled", Object((int64_t)(*itr)->is_enabled())); if ((*itr)->is_extra_tracker()) { trackerObject.insert_key("extra_tracker", Object((int64_t)1)); trackerObject.insert_key("group", (*itr)->group()); } } } } libtorrent-0.13.6/src/torrent/utils/resume.h000066400000000000000000000064061257211073700210750ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY // Various functions for loading and saving various states of a // download to an Object. // // These functions use only the public interface, and thus the client // may choose to replace these with their own resume code. // Should propably move this into a sub-directory. #ifndef LIBTORRENT_UTILS_RESUME_H #define LIBTORRENT_UTILS_RESUME_H #include namespace torrent { // When saving resume data for a torrent that is currently active, set // 'onlyCompleted' to ensure that a crash, etc, will cause incomplete // files to be hashed. void resume_load_progress(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_save_progress(Download download, Object& object) LIBTORRENT_EXPORT; void resume_clear_progress(Download download, Object& object) LIBTORRENT_EXPORT; // Do not call 'resume_load_uncertain_pieces' directly. void resume_load_uncertain_pieces(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_save_uncertain_pieces(Download download, Object& object) LIBTORRENT_EXPORT; bool resume_check_target_files(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_load_file_priorities(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_save_file_priorities(Download download, Object& object) LIBTORRENT_EXPORT; void resume_load_addresses(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_save_addresses(Download download, Object& object) LIBTORRENT_EXPORT; void resume_load_tracker_settings(Download download, const Object& object) LIBTORRENT_EXPORT; void resume_save_tracker_settings(Download download, Object& object) LIBTORRENT_EXPORT; } #endif libtorrent-0.13.6/src/torrent/utils/signal_bitfield.cc000066400000000000000000000047451257211073700230560ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "torrent/exceptions.h" #include "signal_bitfield.h" namespace torrent { // Only the thread owning this signal bitfield should add signals. unsigned int signal_bitfield::add_signal(slot_type slot) { if (m_size >= max_size) throw internal_error("signal_bitfield::add_signal(...): No more available slots."); if (!slot) throw internal_error("signal_bitfield::add_signal(...): Cannot add empty slot."); unsigned int index = m_size; __sync_add_and_fetch(&m_size, 1); m_slots[index] = slot; return index; } void signal_bitfield::work() { bitfield_type bitfield; while (!__sync_bool_compare_and_swap(&m_bitfield, (bitfield = m_bitfield), 0)) ; // Do nothing. unsigned int i = 0; while (bitfield) { if ((bitfield & (1 << i))) { m_slots[i](); bitfield = bitfield & ~(1 << i); } i++; } } } libtorrent-0.13.6/src/torrent/utils/signal_bitfield.h000066400000000000000000000045671257211073700227220ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_SIGNAL_BITFIELD_H #define LIBTORRENT_UTILS_SIGNAL_BITFIELD_H #include #include namespace torrent { class LIBTORRENT_EXPORT lt_cacheline_aligned signal_bitfield { public: typedef uint32_t bitfield_type; typedef std::tr1::function slot_type; static const unsigned int max_size = 32; signal_bitfield() : m_bitfield(0), m_size(0) {} // Do the interrupt from the thread? void signal(unsigned int index) { __sync_or_and_fetch(&m_bitfield, 1 << index); } void work(); unsigned int add_signal(slot_type slot); private: bitfield_type m_bitfield; unsigned int m_size; slot_type m_slots[max_size] lt_cacheline_aligned; }; } #endif libtorrent-0.13.6/src/torrent/utils/thread_base.cc000066400000000000000000000132101257211073700221630ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include #include "exceptions.h" #include "poll.h" #include "thread_base.h" #include "thread_interrupt.h" #include "utils/log.h" #include "utils/instrumentation.h" namespace torrent { thread_base::global_lock_type lt_cacheline_aligned thread_base::m_global = { 0, 0, PTHREAD_MUTEX_INITIALIZER }; thread_base::thread_base() : m_state(STATE_UNKNOWN), m_flags(0), m_instrumentation_index(INSTRUMENTATION_POLLING_DO_POLL_OTHERS - INSTRUMENTATION_POLLING_DO_POLL), m_poll(NULL), m_interrupt_sender(NULL), m_interrupt_receiver(NULL) { std::memset(&m_thread, 0, sizeof(pthread_t)); // #ifdef USE_INTERRUPT_SOCKET thread_interrupt::pair_type interrupt_sockets = thread_interrupt::create_pair(); m_interrupt_sender = interrupt_sockets.first; m_interrupt_receiver = interrupt_sockets.second; // #endif } thread_base::~thread_base() { delete m_interrupt_sender; delete m_interrupt_receiver; } void thread_base::start_thread() { if (m_poll == NULL) throw internal_error("No poll object for thread defined."); if (!is_initialized() || pthread_create(&m_thread, NULL, (pthread_func)&thread_base::event_loop, this)) throw internal_error("Failed to create thread."); } void thread_base::stop_thread() { __sync_fetch_and_or(&m_flags, flag_do_shutdown); interrupt(); } void thread_base::stop_thread_wait() { stop_thread(); release_global_lock(); while (!is_inactive()) { usleep(1000); } acquire_global_lock(); } // Fix interrupting when shutting down thread. void thread_base::interrupt() { // Only poke when polling, set no_timeout if (is_polling()) m_interrupt_sender->poke(); } bool thread_base::should_handle_sigusr1() { return false; } void* thread_base::event_loop(thread_base* thread) { __sync_lock_test_and_set(&thread->m_state, STATE_ACTIVE); #if defined(HAS_PTHREAD_SETNAME_NP_DARWIN) pthread_setname_np(thread->name()); #elif defined(HAS_PTHREAD_SETNAME_NP_GENERIC) pthread_setname_np(thread->m_thread, thread->name()); #endif lt_log_print(torrent::LOG_THREAD_NOTICE, "%s: Starting thread.", thread->name()); try { // #ifdef USE_INTERRUPT_SOCKET thread->m_poll->insert_read(thread->m_interrupt_receiver); // #endif while (true) { if (thread->m_slot_do_work) thread->m_slot_do_work(); thread->call_events(); thread->signal_bitfield()->work(); __sync_fetch_and_or(&thread->m_flags, flag_polling); // Call again after setting flag_polling to ensure we process // any events set while it was working. if (thread->m_slot_do_work) thread->m_slot_do_work(); thread->call_events(); thread->signal_bitfield()->work(); uint64_t next_timeout = 0; if (!thread->has_no_timeout()) { next_timeout = thread->next_timeout_usec(); if (thread->m_slot_next_timeout) next_timeout = std::min(next_timeout, thread->m_slot_next_timeout()); } // Add the sleep call when testing interrupts, etc. // usleep(50); int poll_flags = 0; if (!(thread->flags() & flag_main_thread)) poll_flags = torrent::Poll::poll_worker_thread; instrumentation_update(INSTRUMENTATION_POLLING_DO_POLL, 1); instrumentation_update(instrumentation_enum(INSTRUMENTATION_POLLING_DO_POLL + thread->m_instrumentation_index), 1); int event_count = thread->m_poll->do_poll(next_timeout, poll_flags); instrumentation_update(INSTRUMENTATION_POLLING_EVENTS, event_count); instrumentation_update(instrumentation_enum(INSTRUMENTATION_POLLING_EVENTS + thread->m_instrumentation_index), event_count); __sync_fetch_and_and(&thread->m_flags, ~(flag_polling | flag_no_timeout)); } // #ifdef USE_INTERRUPT_SOCKET thread->m_poll->remove_write(thread->m_interrupt_receiver); // #endif } catch (torrent::shutdown_exception& e) { lt_log_print(torrent::LOG_THREAD_NOTICE, "%s: Shutting down thread.", thread->name()); } __sync_lock_test_and_set(&thread->m_state, STATE_INACTIVE); return NULL; } } libtorrent-0.13.6/src/torrent/utils/thread_base.h000066400000000000000000000153721257211073700220400ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_THREAD_BASE_H #define LIBTORRENT_UTILS_THREAD_BASE_H #include #include #include #include #include namespace torrent { class Poll; class thread_interrupt; class LIBTORRENT_EXPORT lt_cacheline_aligned thread_base { public: typedef void* (*pthread_func)(void*); typedef std::tr1::function slot_void; typedef std::tr1::function slot_timer; typedef class signal_bitfield signal_type; enum state_type { STATE_UNKNOWN, STATE_INITIALIZED, STATE_ACTIVE, STATE_INACTIVE }; static const int flag_do_shutdown = 0x1; static const int flag_did_shutdown = 0x2; static const int flag_no_timeout = 0x4; static const int flag_polling = 0x8; static const int flag_main_thread = 0x10; thread_base(); virtual ~thread_base(); bool is_initialized() const { return state() == STATE_INITIALIZED; } bool is_active() const { return state() == STATE_ACTIVE; } bool is_inactive() const { return state() == STATE_INACTIVE; } bool is_polling() const; bool is_current() const; bool has_no_timeout() const { return (flags() & flag_no_timeout); } bool has_do_shutdown() const { return (flags() & flag_do_shutdown); } bool has_did_shutdown() const { return (flags() & flag_did_shutdown); } state_type state() const; int flags() const; virtual const char* name() const = 0; Poll* poll() { return m_poll; } signal_type* signal_bitfield() { return &m_signal_bitfield; } pthread_t pthread() { return m_thread; } virtual void init_thread() = 0; virtual void start_thread(); virtual void stop_thread(); void stop_thread_wait(); void interrupt(); void send_event_signal(unsigned int index, bool interrupt = true); slot_void& slot_do_work() { return m_slot_do_work; } slot_timer& slot_next_timeout() { return m_slot_next_timeout; } static inline int global_queue_size() { return m_global.waiting; } static inline void acquire_global_lock(); static inline bool trylock_global_lock(); static inline void release_global_lock(); static inline void waive_global_lock(); static inline bool is_main_polling() { return m_global.main_polling; } static inline void entering_main_polling(); static inline void leaving_main_polling(); static bool should_handle_sigusr1(); static void* event_loop(thread_base* thread); protected: struct lt_cacheline_aligned global_lock_type { int waiting; int main_polling; pthread_mutex_t lock; }; virtual void call_events() = 0; virtual int64_t next_timeout_usec() = 0; static global_lock_type m_global; pthread_t m_thread; state_type m_state lt_cacheline_aligned; int m_flags lt_cacheline_aligned; int m_instrumentation_index; Poll* m_poll; signal_type m_signal_bitfield; slot_void m_slot_do_work; slot_timer m_slot_next_timeout; thread_interrupt* m_interrupt_sender; thread_interrupt* m_interrupt_receiver; }; inline bool thread_base::is_polling() const { return (flags() & flag_polling); } inline bool thread_base::is_current() const { return m_thread == pthread_self(); } inline int thread_base::flags() const { __sync_synchronize(); return m_flags; } inline thread_base::state_type thread_base::state() const { __sync_synchronize(); return m_state; } inline void thread_base::send_event_signal(unsigned int index, bool do_interrupt) { m_signal_bitfield.signal(index); if (do_interrupt) interrupt(); } inline void thread_base::acquire_global_lock() { __sync_add_and_fetch(&thread_base::m_global.waiting, 1); pthread_mutex_lock(&thread_base::m_global.lock); __sync_sub_and_fetch(&thread_base::m_global.waiting, 1); } inline bool thread_base::trylock_global_lock() { return pthread_mutex_trylock(&thread_base::m_global.lock) == 0; } inline void thread_base::release_global_lock() { pthread_mutex_unlock(&thread_base::m_global.lock); } inline void thread_base::waive_global_lock() { pthread_mutex_unlock(&thread_base::m_global.lock); // Do we need to sleep here? Make a CppUnit test for this. acquire_global_lock(); } // 'entering/leaving_main_polling' is used by the main polling thread // to indicate to other threads when it is safe to change the main // thread's event entries. // // A thread should first aquire global lock, then if it needs to // change poll'ed sockets on the main thread it should call // 'interrupt_main_polling' unless 'is_main_polling() == false'. inline void thread_base::entering_main_polling() { __sync_lock_test_and_set(&thread_base::m_global.main_polling, 1); } inline void thread_base::leaving_main_polling() { __sync_lock_test_and_set(&thread_base::m_global.main_polling, 0); } } #endif libtorrent-0.13.6/src/torrent/utils/thread_interrupt.cc000066400000000000000000000065311257211073700233150ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include "thread_interrupt.h" #include #include "net/socket_fd.h" #include "rak/error_number.h" #include "torrent/exceptions.h" #include "utils/instrumentation.h" namespace torrent { thread_interrupt::thread_interrupt(int fd) : m_poking(false) { m_fileDesc = fd; get_fd().set_nonblock(); } thread_interrupt::~thread_interrupt() { if (m_fileDesc == -1) return; ::close(m_fileDesc); m_fileDesc = -1; } bool thread_interrupt::poke() { if (!__sync_bool_compare_and_swap(&m_other->m_poking, false, true)) return true; int result = ::send(m_fileDesc, "a", 1, 0); if (result == 0 || (result == -1 && !rak::error_number::current().is_blocked_momentary())) throw internal_error("Invalid result writing to thread_interrupt socket."); instrumentation_update(INSTRUMENTATION_POLLING_INTERRUPT_POKE, 1); return true; } thread_interrupt::pair_type thread_interrupt::create_pair() { int fd1, fd2; if (!SocketFd::open_socket_pair(fd1, fd2)) throw internal_error("Could not create socket pair for thread_interrupt: " + std::string(rak::error_number::current().c_str()) + "."); thread_interrupt* t1 = new thread_interrupt(fd1); thread_interrupt* t2 = new thread_interrupt(fd2); t1->m_other = t2; t2->m_other = t1; return pair_type(t1, t2); } void thread_interrupt::event_read() { char buffer[256]; int result = ::recv(m_fileDesc, buffer, 256, 0); if (result == 0 || (result == -1 && !rak::error_number::current().is_blocked_momentary())) throw internal_error("Invalid result reading from thread_interrupt socket."); instrumentation_update(INSTRUMENTATION_POLLING_INTERRUPT_READ_EVENT, 1); __sync_bool_compare_and_swap(&m_poking, true, false); } } libtorrent-0.13.6/src/torrent/utils/thread_interrupt.h000066400000000000000000000047401257211073700231570ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_THREAD_INTERRUPT_H #define LIBTORRENT_UTILS_THREAD_INTERRUPT_H #include #include namespace torrent { class SocketFd; class LIBTORRENT_EXPORT lt_cacheline_aligned thread_interrupt : public Event { public: typedef std::pair pair_type; ~thread_interrupt(); static pair_type create_pair(); bool is_poking() const; bool poke(); void event_read(); void event_write() {} void event_error() {} private: thread_interrupt(int fd); SocketFd& get_fd() { return *reinterpret_cast(&m_fileDesc); } thread_interrupt* m_other; bool m_poking lt_cacheline_aligned; }; inline bool thread_interrupt::is_poking() const { return m_poking; } } #endif libtorrent-0.13.6/src/tracker/000077500000000000000000000000001257211073700162145ustar00rootroot00000000000000libtorrent-0.13.6/src/tracker/Makefile.am000066400000000000000000000003511257211073700202470ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_tracker.la libsub_tracker_la_SOURCES = \ tracker_dht.cc \ tracker_dht.h \ tracker_http.cc \ tracker_http.h \ tracker_udp.cc \ tracker_udp.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/tracker/tracker_dht.cc000066400000000000000000000113001257211073700210100ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #include "dht/dht_router.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/dht_manager.h" #include "torrent/exceptions.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "tracker_dht.h" #include "globals.h" #include "manager.h" namespace torrent { const char* TrackerDht::states[] = { "Idle", "Searching", "Announcing" }; bool TrackerDht::is_allowed() { return manager->dht_manager()->is_valid(); } TrackerDht::TrackerDht(TrackerList* parent, const std::string& url, int flags) : Tracker(parent, url, flags), m_state(state_idle) { if (!manager->dht_manager()->is_valid()) throw internal_error("Trying to add DHT tracker with no DHT manager."); } TrackerDht::~TrackerDht() { if (is_busy()) manager->dht_manager()->router()->cancel_announce(NULL, this); } bool TrackerDht::is_busy() const { return m_state != state_idle; } bool TrackerDht::is_usable() const { return is_enabled() && manager->dht_manager()->is_active(); } void TrackerDht::send_state(int state) { if (m_parent == NULL) throw internal_error("TrackerDht::send_state(...) does not have a valid m_parent."); if (is_busy()) { manager->dht_manager()->router()->cancel_announce(m_parent->info(), this); if (is_busy()) throw internal_error("TrackerDht::send_state cancel_announce did not cancel announce."); } m_latest_event = state; if (state == DownloadInfo::STOPPED) return; m_state = state_searching; if (!manager->dht_manager()->is_active()) return receive_failed("DHT server not active."); manager->dht_manager()->router()->announce(m_parent->info(), this); set_normal_interval(20 * 60); set_min_interval(0); } void TrackerDht::close() { if (is_busy()) manager->dht_manager()->router()->cancel_announce(m_parent->info(), this); } void TrackerDht::disown() { close(); } TrackerDht::Type TrackerDht::type() const { return TRACKER_DHT; } void TrackerDht::receive_peers(raw_list peers) { if (!is_busy()) throw internal_error("TrackerDht::receive_peers called while not busy."); m_peers.parse_address_bencode(peers); } void TrackerDht::receive_success() { if (!is_busy()) throw internal_error("TrackerDht::receive_success called while not busy."); m_state = state_idle; m_parent->receive_success(this, &m_peers); m_peers.clear(); } void TrackerDht::receive_failed(const char* msg) { if (!is_busy()) throw internal_error("TrackerDht::receive_failed called while not busy."); m_state = state_idle; m_parent->receive_failed(this, msg); m_peers.clear(); } void TrackerDht::receive_progress(int replied, int contacted) { if (!is_busy()) throw internal_error("TrackerDht::receive_status called while not busy."); m_replied = replied; m_contacted = contacted; } void TrackerDht::get_status(char* buffer, int length) { if (!is_busy()) throw internal_error("TrackerDht::get_status called while not busy."); snprintf(buffer, length, "[%s: %d/%d nodes replied]", states[m_state], m_replied, m_contacted); } } libtorrent-0.13.6/src/tracker/tracker_dht.h000066400000000000000000000057021257211073700206630ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_TRACKER_DHT_H #define LIBTORRENT_TRACKER_TRACKER_DHT_H #include "net/address_list.h" #include "torrent/object.h" #include "torrent/tracker.h" namespace torrent { class TrackerDht : public Tracker { public: TrackerDht(TrackerList* parent, const std::string& url, int flags); ~TrackerDht(); typedef enum { state_idle, state_searching, state_announcing, } state_type; static const char* states[]; static bool is_allowed(); virtual bool is_busy() const; virtual bool is_usable() const; virtual void send_state(int state); virtual void close(); virtual void disown(); virtual Type type() const; virtual void get_status(char* buffer, int length); void set_state(state_type state) { m_state = state; } state_type get_state() const { return m_state; } bool has_peers() const { return !m_peers.empty(); } void receive_peers(raw_list peers); void receive_success(); void receive_failed(const char* msg); void receive_progress(int replied, int contacted); private: AddressList m_peers; state_type m_state; int m_replied; int m_contacted; }; } #endif libtorrent-0.13.6/src/tracker/tracker_http.cc000066400000000000000000000261571257211073700212300ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include #include "net/address_list.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/exceptions.h" #include "torrent/http.h" #include "torrent/object_stream.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "torrent/utils/option_strings.h" #include "tracker_http.h" #include "globals.h" #include "manager.h" #define LT_LOG_TRACKER(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TRACKER_##log_level, m_parent->info(), "tracker", "[%u] " log_fmt, group(), __VA_ARGS__); #define LT_LOG_TRACKER_DUMP(log_level, log_dump_data, log_dump_size, log_fmt, ...) \ lt_log_print_info_dump(LOG_TRACKER_##log_level, log_dump_data, log_dump_size, m_parent->info(), "tracker", "[%u] " log_fmt, group(), __VA_ARGS__); namespace tr1 { using namespace std::tr1; } namespace torrent { TrackerHttp::TrackerHttp(TrackerList* parent, const std::string& url, int flags) : Tracker(parent, url, flags), m_get(Http::slot_factory()()), m_data(NULL) { m_get->signal_done().push_back(tr1::bind(&TrackerHttp::receive_done, this)); m_get->signal_failed().push_back(tr1::bind(&TrackerHttp::receive_failed, this, tr1::placeholders::_1)); // Haven't considered if this needs any stronger error detection, // can dropping the '?' be used for malicious purposes? size_t delim_options = url.rfind('?'); m_dropDeliminator = delim_options != std::string::npos && url.find('/', delim_options) == std::string::npos; // Check the url if we can use scrape. size_t delim_slash = url.rfind('/'); if (delim_slash != std::string::npos && url.find("/announce", delim_slash) == delim_slash) m_flags |= flag_can_scrape; } TrackerHttp::~TrackerHttp() { delete m_get; delete m_data; } bool TrackerHttp::is_busy() const { return m_data != NULL; } void TrackerHttp::request_prefix(std::stringstream* stream, const std::string& url) { char hash[61]; *rak::copy_escape_html(m_parent->info()->hash().begin(), m_parent->info()->hash().end(), hash) = '\0'; *stream << url << (m_dropDeliminator ? '&' : '?') << "info_hash=" << hash; } void TrackerHttp::send_state(int state) { close_directly(); if (m_parent == NULL) throw internal_error("TrackerHttp::send_state(...) does not have a valid m_parent."); m_latest_event = state; std::stringstream s; s.imbue(std::locale::classic()); char localId[61]; DownloadInfo* info = m_parent->info(); request_prefix(&s, m_url); *rak::copy_escape_html(info->local_id().begin(), info->local_id().end(), localId) = '\0'; s << "&peer_id=" << localId; if (m_parent->key()) s << "&key=" << std::hex << std::setw(8) << std::setfill('0') << m_parent->key() << std::dec; if (!m_tracker_id.empty()) s << "&trackerid=" << rak::copy_escape_html(m_tracker_id); const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address()); if (localAddress->family() == rak::socket_address::af_inet && !localAddress->sa_inet()->is_address_any()) s << "&ip=" << localAddress->address_str(); if (info->is_compact()) s << "&compact=1"; if (m_parent->numwant() >= 0 && state != DownloadInfo::STOPPED) s << "&numwant=" << m_parent->numwant(); if (manager->connection_manager()->listen_port()) s << "&port=" << manager->connection_manager()->listen_port(); uint64_t uploaded_adjusted = info->uploaded_adjusted(); uint64_t completed_adjusted = info->completed_adjusted(); uint64_t download_left = info->slot_left()(); s << "&uploaded=" << uploaded_adjusted << "&downloaded=" << completed_adjusted << "&left=" << download_left; switch(state) { case DownloadInfo::STARTED: s << "&event=started"; break; case DownloadInfo::STOPPED: s << "&event=stopped"; break; case DownloadInfo::COMPLETED: s << "&event=completed"; break; default: break; } m_data = new std::stringstream(); std::string request_url = s.str(); LT_LOG_TRACKER_DUMP(DEBUG, request_url.c_str(), request_url.size(), "Tracker HTTP request: state:%s up_adj:%" PRIu64 " completed_adj:%" PRIu64 " left_adj:%" PRIu64 ".", option_as_string(OPTION_TRACKER_EVENT, state), uploaded_adjusted, completed_adjusted, download_left); m_get->set_url(request_url); m_get->set_stream(m_data); m_get->set_timeout(2 * 60); m_get->start(); } void TrackerHttp::send_scrape() { if (m_data != NULL) return; m_latest_event = EVENT_SCRAPE; std::stringstream s; s.imbue(std::locale::classic()); request_prefix(&s, scrape_url_from(m_url)); m_data = new std::stringstream(); std::string request_url = s.str(); LT_LOG_TRACKER_DUMP(DEBUG, request_url.c_str(), request_url.size(), "Tracker HTTP scrape.", 0); m_get->set_url(request_url); m_get->set_stream(m_data); m_get->set_timeout(2 * 60); m_get->start(); } void TrackerHttp::close() { if (m_data == NULL) return; LT_LOG_TRACKER(DEBUG, "Tracker HTTP request cancelled: state:%s url:%s.", option_as_string(OPTION_TRACKER_EVENT, m_latest_event), m_url.c_str()); close_directly(); } void TrackerHttp::disown() { if (m_data == NULL) return; LT_LOG_TRACKER(DEBUG, "Tracker HTTP request disowned: state:%s url:%s.", option_as_string(OPTION_TRACKER_EVENT, m_latest_event), m_url.c_str()); m_get->set_delete_self(); m_get->set_delete_stream(); m_get->signal_done().clear(); m_get->signal_failed().clear(); // Allocate this dynamically, so that we don't need to do this here. m_get = Http::slot_factory()(); m_data = NULL; } TrackerHttp::Type TrackerHttp::type() const { return TRACKER_HTTP; } void TrackerHttp::close_directly() { if (m_data == NULL) return; m_get->close(); m_get->set_stream(NULL); delete m_data; m_data = NULL; } void TrackerHttp::receive_done() { if (m_data == NULL) throw internal_error("TrackerHttp::receive_done() called on an invalid object"); if (lt_log_is_valid(LOG_TRACKER_DEBUG)) { std::string dump = m_data->str(); LT_LOG_TRACKER_DUMP(DEBUG, dump.c_str(), dump.size(), "Tracker HTTP reply.", 0); } Object b; *m_data >> b; if (m_data->fail()) return receive_failed("Could not parse bencoded data"); if (!b.is_map()) return receive_failed("Root not a bencoded map"); if (b.has_key("failure reason")) return receive_failed("Failure reason \"" + (b.get_key("failure reason").is_string() ? b.get_key_string("failure reason") : std::string("failure reason not a string")) + "\""); if (m_latest_event == EVENT_SCRAPE) process_scrape(b); else process_success(b); } void TrackerHttp::receive_failed(std::string msg) { if (lt_log_is_valid(LOG_TRACKER_DEBUG)) { std::string dump = m_data->str(); LT_LOG_TRACKER_DUMP(DEBUG, dump.c_str(), dump.size(), "Tracker HTTP failed.", 0); } close_directly(); if (m_latest_event == EVENT_SCRAPE) m_parent->receive_scrape_failed(this, msg); else m_parent->receive_failed(this, msg); } void TrackerHttp::process_success(const Object& object) { if (object.has_key_value("interval")) set_normal_interval(object.get_key_value("interval")); if (object.has_key_value("min interval")) set_min_interval(object.get_key_value("min interval")); if (object.has_key_string("tracker id")) m_tracker_id = object.get_key_string("tracker id"); if (object.has_key_value("complete") && object.has_key_value("incomplete")) { m_scrape_complete = std::max(object.get_key_value("complete"), 0); m_scrape_incomplete = std::max(object.get_key_value("incomplete"), 0); m_scrape_time_last = cachedTime.seconds(); } if (object.has_key_value("downloaded")) m_scrape_downloaded = std::max(object.get_key_value("downloaded"), 0); AddressList l; try { // Due to some trackers sending the wrong type when no peers are // available, don't bork on it. if (object.get_key("peers").is_string()) l.parse_address_compact(object.get_key_string("peers")); else if (object.get_key("peers").is_list()) l.parse_address_normal(object.get_key_list("peers")); } catch (bencode_error& e) { return receive_failed(e.what()); } close_directly(); m_parent->receive_success(this, &l); } void TrackerHttp::process_scrape(const Object& object) { if (!object.has_key_map("files")) return receive_failed("Tracker scrape does not have files entry."); // Add better validation here... const Object& files = object.get_key("files"); if (!files.has_key_map(m_parent->info()->hash().str())) return receive_failed("Tracker scrape replay did not contain infohash."); const Object& stats = files.get_key(m_parent->info()->hash().str()); if (stats.has_key_value("complete")) m_scrape_complete = std::max(stats.get_key_value("complete"), 0); if (stats.has_key_value("incomplete")) m_scrape_incomplete = std::max(stats.get_key_value("incomplete"), 0); if (stats.has_key_value("downloaded")) m_scrape_downloaded = std::max(stats.get_key_value("downloaded"), 0); LT_LOG_TRACKER(INFO, "Tracker scrape for %u torrents: complete:%u incomplete:%u downloaded:%u.", files.as_map().size(), m_scrape_complete, m_scrape_incomplete, m_scrape_downloaded); close_directly(); m_parent->receive_scrape_success(this); } } libtorrent-0.13.6/src/tracker/tracker_http.h000066400000000000000000000051701257211073700210620ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_TRACKER_HTTP_H #define LIBTORRENT_TRACKER_TRACKER_HTTP_H #include #include "torrent/object.h" #include "torrent/tracker.h" namespace torrent { class Http; class TrackerHttp : public Tracker { public: TrackerHttp(TrackerList* parent, const std::string& url, int flags); ~TrackerHttp(); virtual bool is_busy() const; virtual void send_state(int state); virtual void send_scrape(); virtual void close(); virtual void disown(); virtual Type type() const; private: void close_directly(); void request_prefix(std::stringstream* stream, const std::string& url); void receive_done(); void receive_failed(std::string msg); void process_success(const Object& object); void process_scrape(const Object& object); Http* m_get; std::stringstream* m_data; bool m_dropDeliminator; }; } #endif libtorrent-0.13.6/src/tracker/tracker_udp.cc000066400000000000000000000273331257211073700210360ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include #include #include #include "net/address_list.h" #include "torrent/exceptions.h" #include "torrent/connection_manager.h" #include "torrent/download_info.h" #include "torrent/poll.h" #include "torrent/tracker_list.h" #include "torrent/utils/log.h" #include "torrent/utils/option_strings.h" #include "tracker_udp.h" #include "manager.h" namespace tr1 { using namespace std::tr1; } #define LT_LOG_TRACKER(log_level, log_fmt, ...) \ lt_log_print_info(LOG_TRACKER_##log_level, m_parent->info(), "tracker", "[%u] " log_fmt, group(), __VA_ARGS__); #define LT_LOG_TRACKER_DUMP(log_level, log_dump_data, log_dump_size, log_fmt, ...) \ lt_log_print_info_dump(LOG_TRACKER_##log_level, log_dump_data, log_dump_size, m_parent->info(), "tracker", "[%u] " log_fmt, group(), __VA_ARGS__); namespace torrent { TrackerUdp::TrackerUdp(TrackerList* parent, const std::string& url, int flags) : Tracker(parent, url, flags), m_slot_resolver(NULL), m_readBuffer(NULL), m_writeBuffer(NULL) { m_taskTimeout.slot() = std::tr1::bind(&TrackerUdp::receive_timeout, this); } TrackerUdp::~TrackerUdp() { if (m_slot_resolver != NULL) { *m_slot_resolver = resolver_type(); m_slot_resolver = NULL; } close_directly(); } bool TrackerUdp::is_busy() const { return get_fd().is_valid(); } void TrackerUdp::send_state(int state) { close_directly(); m_latest_event = state; char hostname[1024]; if (std::sscanf(m_url.c_str(), "udp://%1023[^:]:%i", hostname, &m_port) != 2 || hostname[0] == '\0' || m_port <= 0 || m_port >= (1 << 16)) return receive_failed("Could not parse UDP hostname or port."); // Because we can only remember one slot, set any pending resolves blocked // so that if this tracker is deleted, the member function won't be called. if (m_slot_resolver != NULL) { *m_slot_resolver = resolver_type(); m_slot_resolver = NULL; } m_sendState = state; m_slot_resolver = manager->connection_manager()->resolver()(hostname, PF_INET, SOCK_DGRAM, tr1::bind(&TrackerUdp::start_announce, this, tr1::placeholders::_1, tr1::placeholders::_2)); } void TrackerUdp::start_announce(const sockaddr* sa, int err) { if (m_slot_resolver != NULL) { *m_slot_resolver = resolver_type(); m_slot_resolver = NULL; } if (sa == NULL) return receive_failed("Could not resolve hostname."); m_connectAddress = *rak::socket_address::cast_from(sa); m_connectAddress.set_port(m_port); if (!m_connectAddress.is_valid()) return receive_failed("Invalid tracker address."); if (!get_fd().open_datagram() || !get_fd().set_nonblock() || !get_fd().bind(*rak::socket_address::cast_from(manager->connection_manager()->bind_address()))) return receive_failed("Could not open UDP socket."); m_readBuffer = new ReadBuffer; m_writeBuffer = new WriteBuffer; prepare_connect_input(); manager->poll()->open(this); manager->poll()->insert_read(this); manager->poll()->insert_write(this); manager->poll()->insert_error(this); m_tries = m_parent->info()->udp_tries(); priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_parent->info()->udp_timeout())).round_seconds()); } void TrackerUdp::close() { if (!get_fd().is_valid()) return; LT_LOG_TRACKER(DEBUG, "Tracker UDP request cancelled: state:%s url:%s.", option_as_string(OPTION_TRACKER_EVENT, m_latest_event), m_url.c_str()); close_directly(); } void TrackerUdp::disown() { if (!get_fd().is_valid()) return; LT_LOG_TRACKER(DEBUG, "Tracker UDP request disowned: state:%s url:%s.", option_as_string(OPTION_TRACKER_EVENT, m_latest_event), m_url.c_str()); close_directly(); } void TrackerUdp::close_directly() { if (!get_fd().is_valid()) return; delete m_readBuffer; delete m_writeBuffer; m_readBuffer = NULL; m_writeBuffer = NULL; priority_queue_erase(&taskScheduler, &m_taskTimeout); manager->poll()->remove_read(this); manager->poll()->remove_write(this); manager->poll()->remove_error(this); manager->poll()->close(this); get_fd().close(); get_fd().clear(); } TrackerUdp::Type TrackerUdp::type() const { return TRACKER_UDP; } void TrackerUdp::receive_failed(const std::string& msg) { close_directly(); m_parent->receive_failed(this, msg); } void TrackerUdp::receive_timeout() { if (m_taskTimeout.is_queued()) throw internal_error("TrackerUdp::receive_timeout() called but m_taskTimeout is still scheduled."); if (--m_tries == 0) { receive_failed("Unable to connect to UDP tracker."); } else { priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_parent->info()->udp_timeout())).round_seconds()); manager->poll()->insert_write(this); } } void TrackerUdp::event_read() { rak::socket_address sa; int s = read_datagram(m_readBuffer->begin(), m_readBuffer->reserved(), &sa); if (s < 0) return; m_readBuffer->reset_position(); m_readBuffer->set_end(s); LT_LOG_TRACKER_DUMP(DEBUG, (const char*)m_readBuffer->begin(), s, "Tracker UDP reply.", 0); if (s < 4) return; // Make sure sa is from the source we expected? // Do something with the content here. switch (m_readBuffer->read_32()) { case 0: if (m_action != 0 || !process_connect_output()) return; prepare_announce_input(); priority_queue_erase(&taskScheduler, &m_taskTimeout); priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(m_parent->info()->udp_timeout())).round_seconds()); m_tries = m_parent->info()->udp_tries(); manager->poll()->insert_write(this); return; case 1: if (m_action != 1 || !process_announce_output()) return; return; case 3: if (!process_error_output()) return; return; default: return; }; } void TrackerUdp::event_write() { if (m_writeBuffer->size_end() == 0) throw internal_error("TrackerUdp::write() called but the write buffer is empty."); int __UNUSED s = write_datagram(m_writeBuffer->begin(), m_writeBuffer->size_end(), &m_connectAddress); // TODO: If send failed, retry shortly or do i call receive_failed? // if (s != m_writeBuffer->size_end()) // ; manager->poll()->remove_write(this); } void TrackerUdp::event_error() { } void TrackerUdp::prepare_connect_input() { m_writeBuffer->reset(); m_writeBuffer->write_64(m_connectionId = magic_connection_id); m_writeBuffer->write_32(m_action = 0); m_writeBuffer->write_32(m_transactionId = random()); LT_LOG_TRACKER_DUMP(DEBUG, m_writeBuffer->begin(), m_writeBuffer->size_end(), "Tracker UDP connect: id:%" PRIx32 ".", m_transactionId); } void TrackerUdp::prepare_announce_input() { DownloadInfo* info = m_parent->info(); m_writeBuffer->reset(); m_writeBuffer->write_64(m_connectionId); m_writeBuffer->write_32(m_action = 1); m_writeBuffer->write_32(m_transactionId = random()); m_writeBuffer->write_range(info->hash().begin(), info->hash().end()); m_writeBuffer->write_range(info->local_id().begin(), info->local_id().end()); uint64_t uploaded_adjusted = info->uploaded_adjusted(); uint64_t completed_adjusted = info->completed_adjusted(); uint64_t download_left = info->slot_left()(); m_writeBuffer->write_64(completed_adjusted); m_writeBuffer->write_64(download_left); m_writeBuffer->write_64(uploaded_adjusted); m_writeBuffer->write_32(m_sendState); const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address()); // This code assumes we're have a inet address. if (localAddress->family() != rak::socket_address::af_inet) throw internal_error("TrackerUdp::prepare_announce_input() info->local_address() not of family AF_INET."); m_writeBuffer->write_32_n(localAddress->sa_inet()->address_n()); m_writeBuffer->write_32(m_parent->key()); m_writeBuffer->write_32(m_parent->numwant()); m_writeBuffer->write_16(manager->connection_manager()->listen_port()); if (m_writeBuffer->size_end() != 98) throw internal_error("TrackerUdp::prepare_announce_input() ended up with the wrong size"); LT_LOG_TRACKER_DUMP(DEBUG, m_writeBuffer->begin(), m_writeBuffer->size_end(), "Tracker UDP announce: state:%s id:%" PRIx32 " up_adj:%" PRIu64 " completed_adj:%" PRIu64 " left_adj:%" PRIu64 ".", option_as_string(OPTION_TRACKER_EVENT, m_sendState), m_transactionId, uploaded_adjusted, completed_adjusted, download_left); } bool TrackerUdp::process_connect_output() { if (m_readBuffer->size_end() < 16 || m_readBuffer->read_32() != m_transactionId) return false; m_connectionId = m_readBuffer->read_64(); return true; } bool TrackerUdp::process_announce_output() { if (m_readBuffer->size_end() < 20 || m_readBuffer->read_32() != m_transactionId) return false; set_normal_interval(m_readBuffer->read_32()); m_scrape_incomplete = m_readBuffer->read_32(); // leechers m_scrape_complete = m_readBuffer->read_32(); // seeders m_scrape_time_last = rak::timer::current().seconds(); AddressList l; std::copy(reinterpret_cast(m_readBuffer->position()), reinterpret_cast(m_readBuffer->end() - m_readBuffer->remaining() % sizeof(SocketAddressCompact)), std::back_inserter(l)); // Some logic here to decided on whetever we're going to close the // connection or not? close_directly(); m_parent->receive_success(this, &l); return true; } bool TrackerUdp::process_error_output() { if (m_readBuffer->size_end() < 8 || m_readBuffer->read_32() != m_transactionId) return false; receive_failed("Received error message: " + std::string(m_readBuffer->position(), m_readBuffer->end())); return true; } } libtorrent-0.13.6/src/tracker/tracker_udp.h000066400000000000000000000067641257211073700207050ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_TRACKER_TRACKER_UDP_H #define LIBTORRENT_TRACKER_TRACKER_UDP_H #include #include "net/protocol_buffer.h" #include "net/socket_datagram.h" #include "torrent/connection_manager.h" #include "torrent/tracker.h" #include "globals.h" namespace torrent { class TrackerUdp : public SocketDatagram, public Tracker { public: typedef ProtocolBuffer<512> ReadBuffer; typedef ProtocolBuffer<512> WriteBuffer; typedef ConnectionManager::slot_resolver_result_type resolver_type; static const uint64_t magic_connection_id = 0x0000041727101980ll; TrackerUdp(TrackerList* parent, const std::string& url, int flags); ~TrackerUdp(); const char* type_name() const { return "tracker_udp"; } virtual bool is_busy() const; virtual void send_state(int state); virtual void close(); virtual void disown(); virtual Type type() const; virtual void event_read(); virtual void event_write(); virtual void event_error(); private: void close_directly(); void receive_failed(const std::string& msg); void receive_timeout(); void start_announce(const sockaddr* sa, int err); void prepare_connect_input(); void prepare_announce_input(); bool process_connect_output(); bool process_announce_output(); bool process_error_output(); rak::socket_address m_connectAddress; int m_port; int m_sendState; resolver_type* m_slot_resolver; uint32_t m_action; uint64_t m_connectionId; uint32_t m_transactionId; ReadBuffer* m_readBuffer; WriteBuffer* m_writeBuffer; uint32_t m_tries; rak::priority_item m_taskTimeout; }; } #endif libtorrent-0.13.6/src/utils/000077500000000000000000000000001257211073700157215ustar00rootroot00000000000000libtorrent-0.13.6/src/utils/Makefile.am000066400000000000000000000004211257211073700177520ustar00rootroot00000000000000noinst_LTLIBRARIES = libsub_utils.la libsub_utils_la_SOURCES = \ diffie_hellman.cc \ diffie_hellman.h \ instrumentation.cc \ instrumentation.h \ rc4.h \ sha1.h \ sha_fast.cc \ sha_fast.h \ queue_buckets.h AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) libtorrent-0.13.6/src/utils/diffie_hellman.cc000066400000000000000000000061111257211073700211550ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #include #include #ifdef USE_OPENSSL #include #endif #include "diffie_hellman.h" #include "torrent/exceptions.h" namespace torrent { DiffieHellman::DiffieHellman(const unsigned char *prime, int primeLength, const unsigned char *generator, int generatorLength) : m_secret(NULL), m_size(0) { #ifdef USE_OPENSSL m_dh = DH_new(); m_dh->p = BN_bin2bn(prime, primeLength, NULL); m_dh->g = BN_bin2bn(generator, generatorLength, NULL); DH_generate_key(m_dh); #else throw internal_error("Compiled without encryption support."); #endif }; DiffieHellman::~DiffieHellman() { delete [] m_secret; #ifdef USE_OPENSSL DH_free(m_dh); #endif }; bool DiffieHellman::is_valid() const { #ifdef USE_OPENSSL return m_dh != NULL && m_dh->pub_key != NULL; #else return false; #endif } bool DiffieHellman::compute_secret(const unsigned char *pubkey, unsigned int length) { #ifdef USE_OPENSSL BIGNUM* k = BN_bin2bn(pubkey, length, NULL); delete [] m_secret; m_secret = new char[DH_size(m_dh)]; m_size = DH_compute_key((unsigned char*)m_secret, k, m_dh); BN_free(k); return m_size != -1; #else return false; #endif }; void DiffieHellman::store_pub_key(unsigned char* dest, unsigned int length) { #ifdef USE_OPENSSL std::memset(dest, 0, length); if ((int)length >= BN_num_bytes(m_dh->pub_key)) BN_bn2bin(m_dh->pub_key, dest + length - BN_num_bytes(m_dh->pub_key)); #endif } }; libtorrent-0.13.6/src/utils/diffie_hellman.h000066400000000000000000000052371257211073700210270ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_DIFFIE_HELLMAN_H #define LIBTORRENT_DIFFIE_HELLMAN_H #include "config.h" #include #ifdef USE_OPENSSL #include #endif namespace torrent { class DiffieHellman { public: DiffieHellman(const unsigned char prime[], int primeLength, const unsigned char generator[], int generatorLength); ~DiffieHellman(); bool compute_secret(const unsigned char pubkey[], unsigned int length); void store_pub_key(unsigned char* dest, unsigned int length); bool is_valid() const; unsigned int size() const { return m_size; } const char* c_str() const { return m_secret; } std::string secret_str() const { return std::string(m_secret, m_size); } private: DiffieHellman(const DiffieHellman& dh); DiffieHellman& operator = (const DiffieHellman& dh); #ifdef USE_OPENSSL DH* m_dh; #else void* m_void; #endif char* m_secret; unsigned int m_size; }; }; #endif libtorrent-0.13.6/src/utils/instrumentation.cc000066400000000000000000000251351257211073700215010ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #include "config.h" #define __STDC_FORMAT_MACROS #include "instrumentation.h" namespace torrent { std::tr1::array instrumentation_values lt_cacheline_aligned; inline int64_t instrumentation_fetch_and_clear(instrumentation_enum type) { #ifdef LT_INSTRUMENTATION return __sync_fetch_and_and(&instrumentation_values[type], int64_t()); #else return instrumentation_values[type] = 0; #endif } void instrumentation_tick() { // Since the values are updated with __sync_add, they can be read // without any memory barriers. lt_log_print(LOG_INSTRUMENTATION_MEMORY, "%" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64, instrumentation_values[INSTRUMENTATION_MEMORY_CHUNK_USAGE], instrumentation_values[INSTRUMENTATION_MEMORY_CHUNK_COUNT], instrumentation_values[INSTRUMENTATION_MEMORY_HASHING_CHUNK_USAGE], instrumentation_values[INSTRUMENTATION_MEMORY_HASHING_CHUNK_COUNT], instrumentation_values[INSTRUMENTATION_MEMORY_BITFIELDS]); lt_log_print(LOG_INSTRUMENTATION_MINCORE, "%" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64, instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_TOUCHED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_NEW), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_NOT_INCORE_TOUCHED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_NOT_INCORE_NEW), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_BREAK), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_SUCCESS), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_FAILED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_NOT_SYNCED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_NOT_DEALLOCATED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_ALLOC_FAILED), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_ALLOCATIONS), instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_DEALLOCATIONS)); lt_log_print(LOG_INSTRUMENTATION_POLLING, "%" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64, instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_INTERRUPT_POKE), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_INTERRUPT_READ_EVENT), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_MAIN), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_DISK), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_OTHERS), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_MAIN), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_DISK), instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_OTHERS)); lt_log_print(LOG_INSTRUMENTATION_TRANSFERS, "%" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64 " %" PRIi64, instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_DELEGATED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_DOWNLOADING), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_FINISHED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_SKIPPED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNKNOWN), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_ADDED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_MOVED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_REMOVED), instrumentation_values[INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_TOTAL], instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_ADDED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_MOVED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_REMOVED), instrumentation_values[INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_TOTAL], instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_ADDED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_MOVED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_REMOVED), instrumentation_values[INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_TOTAL], instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_ADDED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_MOVED), instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_REMOVED), instrumentation_values[INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_TOTAL], instrumentation_values[INSTRUMENTATION_TRANSFER_PEER_INFO_UNACCOUNTED]); } void instrumentation_reset() { instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_TOUCHED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_NEW); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_NOT_INCORE_TOUCHED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_NOT_INCORE_NEW); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_INCORE_BREAK); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_SUCCESS); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_FAILED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_NOT_SYNCED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_SYNC_NOT_DEALLOCATED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_ALLOC_FAILED); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_ALLOCATIONS); instrumentation_fetch_and_clear(INSTRUMENTATION_MINCORE_DEALLOCATIONS); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_INTERRUPT_POKE); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_INTERRUPT_READ_EVENT); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_MAIN); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_DISK); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_DO_POLL_OTHERS); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_MAIN); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_DISK); instrumentation_fetch_and_clear(INSTRUMENTATION_POLLING_EVENTS_OTHERS); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_DELEGATED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_DOWNLOADING); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_FINISHED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_SKIPPED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNKNOWN); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_ADDED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_MOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_REMOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_ADDED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_MOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_REMOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_ADDED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_MOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_REMOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_ADDED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_MOVED); instrumentation_fetch_and_clear(INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_REMOVED); } } libtorrent-0.13.6/src/utils/instrumentation.h000066400000000000000000000112041257211073700213330ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_UTILS_INSTRUMENTATION_H #define LIBTORRENT_UTILS_INSTRUMENTATION_H #include #include "torrent/common.h" #include "torrent/utils/log.h" namespace torrent { enum instrumentation_enum { INSTRUMENTATION_MEMORY_BITFIELDS, INSTRUMENTATION_MEMORY_CHUNK_USAGE, INSTRUMENTATION_MEMORY_CHUNK_COUNT, INSTRUMENTATION_MEMORY_HASHING_CHUNK_USAGE, INSTRUMENTATION_MEMORY_HASHING_CHUNK_COUNT, INSTRUMENTATION_MINCORE_INCORE_TOUCHED, INSTRUMENTATION_MINCORE_INCORE_NEW, INSTRUMENTATION_MINCORE_NOT_INCORE_TOUCHED, INSTRUMENTATION_MINCORE_NOT_INCORE_NEW, INSTRUMENTATION_MINCORE_INCORE_BREAK, INSTRUMENTATION_MINCORE_SYNC_SUCCESS, INSTRUMENTATION_MINCORE_SYNC_FAILED, INSTRUMENTATION_MINCORE_SYNC_NOT_SYNCED, INSTRUMENTATION_MINCORE_SYNC_NOT_DEALLOCATED, INSTRUMENTATION_MINCORE_ALLOC_FAILED, INSTRUMENTATION_MINCORE_ALLOCATIONS, INSTRUMENTATION_MINCORE_DEALLOCATIONS, INSTRUMENTATION_POLLING_INTERRUPT_POKE, INSTRUMENTATION_POLLING_INTERRUPT_READ_EVENT, INSTRUMENTATION_POLLING_DO_POLL, INSTRUMENTATION_POLLING_DO_POLL_MAIN, INSTRUMENTATION_POLLING_DO_POLL_DISK, INSTRUMENTATION_POLLING_DO_POLL_OTHERS, INSTRUMENTATION_POLLING_EVENTS, INSTRUMENTATION_POLLING_EVENTS_MAIN, INSTRUMENTATION_POLLING_EVENTS_DISK, INSTRUMENTATION_POLLING_EVENTS_OTHERS, INSTRUMENTATION_TRANSFER_REQUESTS_DELEGATED, INSTRUMENTATION_TRANSFER_REQUESTS_DOWNLOADING, INSTRUMENTATION_TRANSFER_REQUESTS_FINISHED, INSTRUMENTATION_TRANSFER_REQUESTS_SKIPPED, INSTRUMENTATION_TRANSFER_REQUESTS_UNKNOWN, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED, INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_STALLED_TOTAL, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_ADDED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_MOVED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_REMOVED, INSTRUMENTATION_TRANSFER_REQUESTS_CHOKED_TOTAL, INSTRUMENTATION_TRANSFER_PEER_INFO_UNACCOUNTED, INSTRUMENTATION_MAX_SIZE }; extern std::tr1::array instrumentation_values lt_cacheline_aligned; void instrumentation_initialize(); void instrumentation_update(instrumentation_enum type, int64_t change); void instrumentation_tick(); void instrumentation_reset(); // // Implementation: // inline void instrumentation_initialize() { instrumentation_values.assign(int64_t()); } inline void instrumentation_update(instrumentation_enum type, int64_t change) { #ifdef LT_INSTRUMENTATION __sync_add_and_fetch(&instrumentation_values[type], change); #endif } } #endif libtorrent-0.13.6/src/utils/queue_buckets.h000066400000000000000000000240011257211073700207330ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_QUEUE_BUCKETS_H #define LIBTORRENT_QUEUE_BUCKETS_H #include #include #include #include namespace torrent { template class queue_buckets : private std::tr1::array, Constants::bucket_count> { public: typedef std::deque queue_type; typedef std::tr1::array base_type; typedef Constants constants; typedef typename queue_type::value_type value_type; typedef typename queue_type::size_type size_type; typedef typename queue_type::difference_type difference_type; typedef typename queue_type::iterator iterator; typedef typename queue_type::const_iterator const_iterator; size_type queue_size(int idx) const { return queue_at(idx).size(); } bool queue_empty(int idx) const { return queue_at(idx).empty(); } bool empty() const; iterator begin(int idx) { return queue_at(idx).begin(); } iterator end(int idx) { return queue_at(idx).end(); } const_iterator begin(int idx) const { return queue_at(idx).begin(); } const_iterator end(int idx) const { return queue_at(idx).end(); } value_type front(int idx) { return queue_at(idx).front(); } value_type back(int idx) { return queue_at(idx).back(); } const value_type front(int idx) const { return queue_at(idx).front(); } const value_type back(int idx) const { return queue_at(idx).back(); } void pop_front(int idx); void pop_back(int idx); value_type pop_and_front(int idx); value_type pop_and_back(int idx); void push_front(int idx, const value_type& value_type); void push_back(int idx, const value_type& value_type); value_type take(int idx, iterator itr); void clear(int idx); void destroy(int idx, iterator begin, iterator end); void move_to(int src_idx, iterator src_begin, iterator src_end, int dst_idx); void move_all_to(int src_idx, int dst_idx); private: queue_type& queue_at(int idx) { return base_type::operator[](idx); } const queue_type& queue_at(int idx) const { return base_type::operator[](idx); } }; // // Helper methods: // template void queue_bucket_for_all_in_queue(QueueBucket& queues, int idx, Ftor ftor) { std::for_each(queues.begin(idx), queues.end(idx), ftor); } template void queue_bucket_for_all_in_queue(const QueueBucket& queues, int idx, Ftor ftor) { std::for_each(queues.begin(idx), queues.end(idx), ftor); } template inline typename QueueBucket::iterator queue_bucket_find_if_in_queue(QueueBucket& queues, int idx, Ftor ftor) { return std::find_if(queues.begin(idx), queues.end(idx), ftor); } template inline typename QueueBucket::const_iterator queue_bucket_find_if_in_queue(const QueueBucket& queues, int idx, Ftor ftor) { return std::find_if(queues.begin(idx), queues.end(idx), ftor); } template inline std::pair queue_bucket_find_if_in_any(QueueBucket& queues, Ftor ftor) { for (int i = 0; i < QueueBucket::constants::bucket_count; i++) { typename QueueBucket::iterator itr = std::find_if(queues.begin(i), queues.end(i), ftor); if (itr != queues.end(i)) return std::make_pair(i, itr); } return std::make_pair(QueueBucket::constants::bucket_count, queues.end(QueueBucket::constants::bucket_count - 1)); } template inline std::pair queue_bucket_find_if_in_any(const QueueBucket& queues, Ftor ftor) { for (int i = 0; i < QueueBucket::constants::bucket_count; i++) { typename QueueBucket::const_iterator itr = std::find_if(queues.begin(i), queues.end(i), ftor); if (itr != queues.end(i)) return std::make_pair(i, itr); } return std::make_pair(QueueBucket::constants::bucket_count, queues.end(QueueBucket::constants::bucket_count - 1)); } // // Implementation: // // TODO: Consider renaming bucket to queue. // TODO: when adding removal/etc of element or ranges do logging on if // it hit the first element or had to search. template inline bool queue_buckets::empty() const { for (int i = 0; i < constants::bucket_count; i++) if (!queue_empty(i)) return false; return true; } template inline void queue_buckets::pop_front(int idx) { queue_at(idx).pop_front(); instrumentation_update(constants::instrumentation_removed[idx], 1); instrumentation_update(constants::instrumentation_total[idx], -1); } template inline void queue_buckets::pop_back(int idx) { queue_at(idx).pop_back(); instrumentation_update(constants::instrumentation_removed[idx], 1); instrumentation_update(constants::instrumentation_total[idx], -1); } template inline typename queue_buckets::value_type queue_buckets::pop_and_front(int idx) { value_type v = queue_at(idx).front(); pop_front(idx); return v; } template inline typename queue_buckets::value_type queue_buckets::pop_and_back(int idx) { value_type v = queue_at(idx).back(); pop_back(idx); return v; } template inline void queue_buckets::push_front(int idx, const value_type& value) { queue_at(idx).push_front(value); instrumentation_update(constants::instrumentation_added[idx], 1); instrumentation_update(constants::instrumentation_total[idx], 1); } template inline void queue_buckets::push_back(int idx, const value_type& value) { queue_at(idx).push_back(value); instrumentation_update(constants::instrumentation_added[idx], 1); instrumentation_update(constants::instrumentation_total[idx], 1); } template inline typename queue_buckets::value_type queue_buckets::take(int idx, iterator itr) { value_type v = *itr; queue_at(idx).erase(itr); instrumentation_update(constants::instrumentation_removed[idx], 1); instrumentation_update(constants::instrumentation_total[idx], -1); // TODO: Add 'taken' instrumentation. return v; } template inline void queue_buckets::clear(int idx) { destroy(idx, begin(idx), end(idx)); } template inline void queue_buckets::destroy(int idx, iterator begin, iterator end) { difference_type difference = std::distance(begin, end); instrumentation_update(constants::instrumentation_removed[idx], difference); instrumentation_update(constants::instrumentation_total[idx], -difference); // Consider moving these to a temporary dequeue before releasing: std::for_each(begin, end, std::tr1::function(&constants::template destroy)); queue_at(idx).erase(begin, end); } template inline void queue_buckets::move_to(int src_idx, iterator src_begin, iterator src_end, int dst_idx) { difference_type difference = std::distance(src_begin, src_end); instrumentation_update(constants::instrumentation_moved[src_idx], difference); instrumentation_update(constants::instrumentation_total[src_idx], -difference); instrumentation_update(constants::instrumentation_added[dst_idx], difference); instrumentation_update(constants::instrumentation_total[dst_idx], difference); // TODO: Check for better move operations: if (queue_at(dst_idx).empty() && src_begin == queue_at(src_idx).begin() && src_end == queue_at(src_idx).end()) { queue_at(dst_idx).swap(queue_at(src_idx)); } else { queue_at(dst_idx).insert(queue_at(dst_idx).end(), src_begin, src_end); queue_at(src_idx).erase(src_begin, src_end); } } template inline void queue_buckets::move_all_to(int src_idx, int dst_idx) { move_to(src_idx, queue_at(src_idx).begin(), queue_at(src_idx).end(), dst_idx); } } #endif // LIBTORRENT_QUEUE_BUCKETS_H libtorrent-0.13.6/src/utils/rc4.h000066400000000000000000000057501257211073700165710ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_RC4_H #define LIBTORRENT_RC4_H #include "config.h" #ifdef USE_CYRUS_RC4 extern "C" { #include } #else #ifdef USE_OPENSSL #include #endif #endif namespace torrent { class RC4 { public: RC4() { } #ifdef USE_CYRUS_RC4 RC4(const unsigned char key[], int len) { rc4_init(&m_key, key, len); } void crypt(const void* indata, void* outdata, unsigned int length) { rc4_encrypt(&m_key, (const char*)indata, (char*)outdata, length); } void crypt(void* data, unsigned int length) { rc4_encrypt(&m_key, (const char*)data, (char*)data, length); } private: rc4_context_t m_key; #else #ifdef USE_OPENSSL RC4(const unsigned char key[], int len) { RC4_set_key(&m_key, len, key); } void crypt(const void* indata, void* outdata, unsigned int length) { ::RC4(&m_key, length, (const unsigned char*)indata, (unsigned char*)outdata); } void crypt(void* data, unsigned int length) { ::RC4(&m_key, length, (unsigned char*)data, (unsigned char*)data); } private: RC4_KEY m_key; #else RC4(const unsigned char key[], int len) { } void crypt(const void* indata, void* outdata, unsigned int length) { } void crypt(void* data, unsigned int length) {} #endif #endif }; }; #endif libtorrent-0.13.6/src/utils/sha1.h000066400000000000000000000065711257211073700167370ustar00rootroot00000000000000// libTorrent - BitTorrent library // Copyright (C) 2005-2011, Jari Sundell // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations // including the two. // // You must obey the GNU General Public License in all respects for // all of the code used other than OpenSSL. If you modify file(s) // with this exception, you may extend this exception to your version // of the file(s), but you are not obligated to do so. If you do not // wish to do so, delete this exception statement from your version. // If you delete this exception statement from all source files in the // program, then also delete it here. // // Contact: Jari Sundell // // Skomakerveien 33 // 3185 Skoppum, NORWAY #ifndef LIBTORRENT_HASH_COMPUTE_H #define LIBTORRENT_HASH_COMPUTE_H #include #if defined USE_NSS_SHA #include "sha_fast.h" #elif defined USE_OPENSSL_SHA #include #else #error "No SHA1 implementation selected, choose between Mozilla's NSS and OpenSSL." #endif namespace torrent { class Sha1 { public: void init(); void update(const void* data, unsigned int length); void final_c(char* buffer); #if defined USE_NSS_SHA private: SHA1Context m_ctx; }; inline void Sha1::init() { SHA1_Begin(&m_ctx); } inline void Sha1::update(const void* data, unsigned int length) { SHA1_Update(&m_ctx, (unsigned char*)data, length); } inline void Sha1::final_c(char* buffer) { unsigned int len; SHA1_End(&m_ctx, (unsigned char*)buffer, &len, 20); } #elif defined USE_OPENSSL_SHA private: SHA_CTX m_ctx; }; inline void Sha1::init() { SHA1_Init(&m_ctx); } inline void Sha1::update(const void* data, unsigned int length) { SHA1_Update(&m_ctx, (const void*)data, length); } inline void Sha1::final_c(char* buffer) { SHA1_Final((unsigned char*)buffer, &m_ctx); } #else }; #endif inline void sha1_salt(const char* salt, unsigned int saltLength, const char* key, unsigned int keyLength, void* out) { Sha1 sha1; sha1.init(); sha1.update(salt, saltLength); sha1.update(key, keyLength); sha1.final_c((char*)out); } inline void sha1_salt(const char* salt, unsigned int saltLength, const char* key1, unsigned int key1Length, const char* key2, unsigned int key2Length, void* out) { Sha1 sha1; sha1.init(); sha1.update(salt, saltLength); sha1.update(key1, key1Length); sha1.update(key2, key2Length); sha1.final_c((char*)out); } } #endif libtorrent-0.13.6/src/utils/sha_fast.cc000066400000000000000000000316351257211073700200300ustar00rootroot00000000000000// The OpenSSL library exception does not cover the program if you // compile the code below. #include "config.h" #ifdef USE_NSS_SHA /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is SHA 180-1 Reference Implementation (Optimized) * * The Initial Developer of the Original Code is Paul Kocher of * Cryptography Research. Portions created by Paul Kocher are * Copyright (C) 1995-9 by Cryptography Research, Inc. All * Rights Reserved. * * Contributor(s): * * Paul Kocher * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #include #include "sha_fast.h" namespace torrent { #define SHA1_INPUT_LEN 64 #define SHA1_LENGTH 20 #define SHA_MASK 0x00FF00FF #if defined(IS_LITTLE_ENDIAN) #define SHA_HTONL(x) (A = (x), A = (A << 16) | (A >> 16), \ ((A & SHA_MASK) << 8) | ((A >> 8) & SHA_MASK)) #else #define SHA_HTONL(x) (x) #endif #define SHA_BYTESWAP(x) x = SHA_HTONL(x) static void shaCompress(SHA1Context *ctx); #define W u.w #define B u.b #if defined(_MSC_VER) && defined(_X86_) #pragma intrinsic (_lrotr, _lrotl) #define SHA_ROTL(x,n) _lrotl(x,n) #else #define SHA_ROTL(X,n) (((X) << (n)) | ((X) >> (32-(n)))) #endif #define SHA_F1(X,Y,Z) ((((Y)^(Z))&(X))^(Z)) #define SHA_F2(X,Y,Z) ((X)^(Y)^(Z)) #define SHA_F3(X,Y,Z) (((X)&(Y))|((Z)&((X)|(Y)))) #define SHA_F4(X,Y,Z) ((X)^(Y)^(Z)) #define SHA_MIX(t) ctx->W[t] = \ (A = ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], SHA_ROTL(A, 1)) #define PORT_Assert(x) /* * SHA: Zeroize and initialize context */ void SHA1_Begin(SHA1Context *ctx) { memset(ctx, 0, sizeof(SHA1Context)); /* * Initialize H with constants from FIPS180-1. */ ctx->H[0] = 0x67452301L; ctx->H[1] = 0xefcdab89L; ctx->H[2] = 0x98badcfeL; ctx->H[3] = 0x10325476L; ctx->H[4] = 0xc3d2e1f0L; } /* * SHA: Add data to context. */ void SHA1_Update(SHA1Context *ctx, const unsigned char *dataIn, unsigned int len) { register unsigned int lenB = ctx->sizeLo & 63; register unsigned int togo; if (!len) return; /* accumulate the byte count. */ ctx->sizeLo += len; ctx->sizeHi += (ctx->sizeLo < len); /* * Read the data into W and process blocks as they get full */ if (lenB > 0) { togo = 64 - lenB; if (len < togo) togo = len; memcpy(ctx->B + lenB, dataIn, togo); len -= togo; dataIn += togo; lenB = (lenB + togo) & 63; if (!lenB) { shaCompress(ctx); } } while (len >= 64) { memcpy(ctx->B, dataIn, 64); dataIn += 64; len -= 64; shaCompress(ctx); } if (len) { memcpy(ctx->B, dataIn, len); } } /* * SHA: Generate hash value from context */ void SHA1_End(SHA1Context *ctx, unsigned char *hashout, unsigned int *pDigestLen, unsigned int maxDigestLen) { register uint32_t sizeHi, sizeLo, lenB; static const unsigned char bulk_pad[64] = { 0x80,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; #define A lenB PORT_Assert (maxDigestLen >= SHA1_LENGTH); /* * Pad with a binary 1 (e.g. 0x80), then zeroes, then length in bits */ sizeHi = ctx->sizeHi; sizeLo = ctx->sizeLo; lenB = sizeLo & 63; SHA1_Update(ctx, bulk_pad, (((55+64) - lenB) & 63) + 1); PORT_Assert((ctx->sizeLo & 63) == 56); /* Convert size{Hi,Lo} from bytes to bits. */ sizeHi = (sizeHi << 3) | (sizeLo >> 29); sizeLo <<= 3; ctx->W[14] = SHA_HTONL(sizeHi); ctx->W[15] = SHA_HTONL(sizeLo); shaCompress(ctx); /* * Output hash */ #if defined(IS_LITTLE_ENDIAN) SHA_BYTESWAP(ctx->H[0]); SHA_BYTESWAP(ctx->H[1]); SHA_BYTESWAP(ctx->H[2]); SHA_BYTESWAP(ctx->H[3]); SHA_BYTESWAP(ctx->H[4]); #endif memcpy(hashout, ctx->H, SHA1_LENGTH); *pDigestLen = SHA1_LENGTH; /* * Re-initialize the context (also zeroizes contents) */ SHA1_Begin(ctx); } #undef A #undef B /* * SHA: Compression function, unrolled. */ static void shaCompress(SHA1Context *ctx) { register uint32_t A, B, C, D, E; #if defined(IS_LITTLE_ENDIAN) SHA_BYTESWAP(ctx->W[0]); SHA_BYTESWAP(ctx->W[1]); SHA_BYTESWAP(ctx->W[2]); SHA_BYTESWAP(ctx->W[3]); SHA_BYTESWAP(ctx->W[4]); SHA_BYTESWAP(ctx->W[5]); SHA_BYTESWAP(ctx->W[6]); SHA_BYTESWAP(ctx->W[7]); SHA_BYTESWAP(ctx->W[8]); SHA_BYTESWAP(ctx->W[9]); SHA_BYTESWAP(ctx->W[10]); SHA_BYTESWAP(ctx->W[11]); SHA_BYTESWAP(ctx->W[12]); SHA_BYTESWAP(ctx->W[13]); SHA_BYTESWAP(ctx->W[14]); SHA_BYTESWAP(ctx->W[15]); #endif /* * This can be moved into the main code block below, but doing * so can cause some compilers to run out of registers and resort * to storing intermediates in RAM. */ SHA_MIX(16); SHA_MIX(17); SHA_MIX(18); SHA_MIX(19); SHA_MIX(20); SHA_MIX(21); SHA_MIX(22); SHA_MIX(23); SHA_MIX(24); SHA_MIX(25); SHA_MIX(26); SHA_MIX(27); SHA_MIX(28); SHA_MIX(29); SHA_MIX(30); SHA_MIX(31); SHA_MIX(32); SHA_MIX(33); SHA_MIX(34); SHA_MIX(35); SHA_MIX(36); SHA_MIX(37); SHA_MIX(38); SHA_MIX(39); SHA_MIX(40); SHA_MIX(41); SHA_MIX(42); SHA_MIX(43); SHA_MIX(44); SHA_MIX(45); SHA_MIX(46); SHA_MIX(47); SHA_MIX(48); SHA_MIX(49); SHA_MIX(50); SHA_MIX(51); SHA_MIX(52); SHA_MIX(53); SHA_MIX(54); SHA_MIX(55); SHA_MIX(56); SHA_MIX(57); SHA_MIX(58); SHA_MIX(59); SHA_MIX(60); SHA_MIX(61); SHA_MIX(62); SHA_MIX(63); SHA_MIX(64); SHA_MIX(65); SHA_MIX(66); SHA_MIX(67); SHA_MIX(68); SHA_MIX(69); SHA_MIX(70); SHA_MIX(71); SHA_MIX(72); SHA_MIX(73); SHA_MIX(74); SHA_MIX(75); SHA_MIX(76); SHA_MIX(77); SHA_MIX(78); SHA_MIX(79); A = ctx->H[0]; B = ctx->H[1]; C = ctx->H[2]; D = ctx->H[3]; E = ctx->H[4]; E = SHA_ROTL(A,5)+SHA_F1(B,C,D)+E+ctx->W[ 0]+0x5a827999L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F1(A,B,C)+D+ctx->W[ 1]+0x5a827999L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F1(E,A,B)+C+ctx->W[ 2]+0x5a827999L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F1(D,E,A)+B+ctx->W[ 3]+0x5a827999L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F1(C,D,E)+A+ctx->W[ 4]+0x5a827999L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F1(B,C,D)+E+ctx->W[ 5]+0x5a827999L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F1(A,B,C)+D+ctx->W[ 6]+0x5a827999L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F1(E,A,B)+C+ctx->W[ 7]+0x5a827999L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F1(D,E,A)+B+ctx->W[ 8]+0x5a827999L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F1(C,D,E)+A+ctx->W[ 9]+0x5a827999L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F1(B,C,D)+E+ctx->W[10]+0x5a827999L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F1(A,B,C)+D+ctx->W[11]+0x5a827999L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F1(E,A,B)+C+ctx->W[12]+0x5a827999L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F1(D,E,A)+B+ctx->W[13]+0x5a827999L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F1(C,D,E)+A+ctx->W[14]+0x5a827999L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F1(B,C,D)+E+ctx->W[15]+0x5a827999L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F1(A,B,C)+D+ctx->W[16]+0x5a827999L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F1(E,A,B)+C+ctx->W[17]+0x5a827999L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F1(D,E,A)+B+ctx->W[18]+0x5a827999L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F1(C,D,E)+A+ctx->W[19]+0x5a827999L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F2(B,C,D)+E+ctx->W[20]+0x6ed9eba1L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F2(A,B,C)+D+ctx->W[21]+0x6ed9eba1L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F2(E,A,B)+C+ctx->W[22]+0x6ed9eba1L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F2(D,E,A)+B+ctx->W[23]+0x6ed9eba1L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F2(C,D,E)+A+ctx->W[24]+0x6ed9eba1L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F2(B,C,D)+E+ctx->W[25]+0x6ed9eba1L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F2(A,B,C)+D+ctx->W[26]+0x6ed9eba1L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F2(E,A,B)+C+ctx->W[27]+0x6ed9eba1L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F2(D,E,A)+B+ctx->W[28]+0x6ed9eba1L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F2(C,D,E)+A+ctx->W[29]+0x6ed9eba1L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F2(B,C,D)+E+ctx->W[30]+0x6ed9eba1L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F2(A,B,C)+D+ctx->W[31]+0x6ed9eba1L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F2(E,A,B)+C+ctx->W[32]+0x6ed9eba1L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F2(D,E,A)+B+ctx->W[33]+0x6ed9eba1L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F2(C,D,E)+A+ctx->W[34]+0x6ed9eba1L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F2(B,C,D)+E+ctx->W[35]+0x6ed9eba1L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F2(A,B,C)+D+ctx->W[36]+0x6ed9eba1L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F2(E,A,B)+C+ctx->W[37]+0x6ed9eba1L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F2(D,E,A)+B+ctx->W[38]+0x6ed9eba1L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F2(C,D,E)+A+ctx->W[39]+0x6ed9eba1L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F3(B,C,D)+E+ctx->W[40]+0x8f1bbcdcL; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F3(A,B,C)+D+ctx->W[41]+0x8f1bbcdcL; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F3(E,A,B)+C+ctx->W[42]+0x8f1bbcdcL; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F3(D,E,A)+B+ctx->W[43]+0x8f1bbcdcL; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F3(C,D,E)+A+ctx->W[44]+0x8f1bbcdcL; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F3(B,C,D)+E+ctx->W[45]+0x8f1bbcdcL; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F3(A,B,C)+D+ctx->W[46]+0x8f1bbcdcL; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F3(E,A,B)+C+ctx->W[47]+0x8f1bbcdcL; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F3(D,E,A)+B+ctx->W[48]+0x8f1bbcdcL; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F3(C,D,E)+A+ctx->W[49]+0x8f1bbcdcL; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F3(B,C,D)+E+ctx->W[50]+0x8f1bbcdcL; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F3(A,B,C)+D+ctx->W[51]+0x8f1bbcdcL; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F3(E,A,B)+C+ctx->W[52]+0x8f1bbcdcL; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F3(D,E,A)+B+ctx->W[53]+0x8f1bbcdcL; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F3(C,D,E)+A+ctx->W[54]+0x8f1bbcdcL; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F3(B,C,D)+E+ctx->W[55]+0x8f1bbcdcL; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F3(A,B,C)+D+ctx->W[56]+0x8f1bbcdcL; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F3(E,A,B)+C+ctx->W[57]+0x8f1bbcdcL; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F3(D,E,A)+B+ctx->W[58]+0x8f1bbcdcL; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F3(C,D,E)+A+ctx->W[59]+0x8f1bbcdcL; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F4(B,C,D)+E+ctx->W[60]+0xca62c1d6L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F4(A,B,C)+D+ctx->W[61]+0xca62c1d6L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F4(E,A,B)+C+ctx->W[62]+0xca62c1d6L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F4(D,E,A)+B+ctx->W[63]+0xca62c1d6L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F4(C,D,E)+A+ctx->W[64]+0xca62c1d6L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F4(B,C,D)+E+ctx->W[65]+0xca62c1d6L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F4(A,B,C)+D+ctx->W[66]+0xca62c1d6L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F4(E,A,B)+C+ctx->W[67]+0xca62c1d6L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F4(D,E,A)+B+ctx->W[68]+0xca62c1d6L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F4(C,D,E)+A+ctx->W[69]+0xca62c1d6L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F4(B,C,D)+E+ctx->W[70]+0xca62c1d6L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F4(A,B,C)+D+ctx->W[71]+0xca62c1d6L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F4(E,A,B)+C+ctx->W[72]+0xca62c1d6L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F4(D,E,A)+B+ctx->W[73]+0xca62c1d6L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F4(C,D,E)+A+ctx->W[74]+0xca62c1d6L; C=SHA_ROTL(C,30); E = SHA_ROTL(A,5)+SHA_F4(B,C,D)+E+ctx->W[75]+0xca62c1d6L; B=SHA_ROTL(B,30); D = SHA_ROTL(E,5)+SHA_F4(A,B,C)+D+ctx->W[76]+0xca62c1d6L; A=SHA_ROTL(A,30); C = SHA_ROTL(D,5)+SHA_F4(E,A,B)+C+ctx->W[77]+0xca62c1d6L; E=SHA_ROTL(E,30); B = SHA_ROTL(C,5)+SHA_F4(D,E,A)+B+ctx->W[78]+0xca62c1d6L; D=SHA_ROTL(D,30); A = SHA_ROTL(B,5)+SHA_F4(C,D,E)+A+ctx->W[79]+0xca62c1d6L; C=SHA_ROTL(C,30); ctx->H[0] += A; ctx->H[1] += B; ctx->H[2] += C; ctx->H[3] += D; ctx->H[4] += E; } } #endif // USE_NSS_SHA libtorrent-0.13.6/src/utils/sha_fast.h000066400000000000000000000054651257211073700176740ustar00rootroot00000000000000// The OpenSSL library exception does not cover the program if you // compile the code below. #ifdef USE_NSS_SHA /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is SHA 180-1 Reference Implementation (Optimized) * * The Initial Developer of the Original Code is Paul Kocher of * Cryptography Research. Portions created by Paul Kocher are * Copyright (C) 1995-9 by Cryptography Research, Inc. All * Rights Reserved. * * Contributor(s): * * Paul Kocher * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #ifndef _SHA_FAST_H_ #define _SHA_FAST_H_ #include namespace torrent { struct SHA1ContextStr { union { uint32_t w[80]; /* input buffer, plus 64 words */ uint8_t b[320]; } u; uint32_t H[5]; /* 5 state variables */ uint32_t sizeHi,sizeLo; /* 64-bit count of hashed bytes. */ }; typedef SHA1ContextStr SHA1Context; /******************************************/ /* ** SHA-1 secure hash function */ /* ** Reset a SHA-1 context, preparing it for a fresh round of hashing */ extern void SHA1_Begin(SHA1Context *cx); /* ** Update the SHA-1 hash function with more data. ** "cx" the context ** "input" the data to hash ** "inputLen" the amount of data to hash */ extern void SHA1_Update(SHA1Context *cx, const unsigned char *input, unsigned int inputLen); /* ** Finish the SHA-1 hash function. Produce the digested results in "digest" ** "cx" the context ** "digest" where the 16 bytes of digest data are stored ** "digestLen" where the digest length (20) is stored ** "maxDigestLen" the maximum amount of data that can ever be ** stored in "digest" */ extern void SHA1_End(SHA1Context *cx, unsigned char *digest, unsigned int *digestLen, unsigned int maxDigestLen); } #endif /* _SHA_FAST_H_ */ #endif // USE_NSS_SHA libtorrent-0.13.6/test/000077500000000000000000000000001257211073700147515ustar00rootroot00000000000000libtorrent-0.13.6/test/Makefile.am000066400000000000000000000047551257211073700170200ustar00rootroot00000000000000TESTS = LibTorrentTest AUTOMAKE_OPTIONS = subdir-objects check_PROGRAMS = $(TESTS) LibTorrentTest_LDADD = \ ../src/libtorrent.la \ ../src/torrent/libsub_torrent.la \ ../src/torrent/data/libsub_torrentdata.la \ ../src/torrent/download/libsub_torrentdownload.la \ ../src/torrent/peer/libsub_torrentpeer.la \ ../src/data/libsub_data.la \ ../src/dht/libsub_dht.la \ ../src/net/libsub_net.la \ ../src/protocol/libsub_protocol.la \ ../src/download/libsub_download.la \ ../src/tracker/libsub_tracker.la \ ../src/utils/libsub_utils.la \ ../src/torrent/utils/libsub_torrentutils.la LibTorrentTest_SOURCES = \ ../src/thread_disk.cc \ ../src/thread_disk.h \ \ rak/allocators_test.cc \ rak/allocators_test.h \ rak/ranges_test.cc \ rak/ranges_test.h \ data/chunk_list_test.cc \ data/chunk_list_test.h \ data/hash_check_queue_test.cc \ data/hash_check_queue_test.h \ data/hash_queue_test.cc \ data/hash_queue_test.h \ protocol/test_request_list.cc \ protocol/test_request_list.h \ torrent/http_test.cc \ torrent/http_test.h \ torrent/object_test.cc \ torrent/object_test.h \ torrent/object_test_utils.cc \ torrent/object_test_utils.h \ torrent/object_static_map_test.cc \ torrent/object_static_map_test.h \ torrent/object_stream_test.cc \ torrent/object_stream_test.h \ torrent/tracker_controller_test.cc \ torrent/tracker_controller_test.h \ torrent/tracker_controller_features.cc \ torrent/tracker_controller_features.h \ torrent/tracker_controller_requesting.cc \ torrent/tracker_controller_requesting.h \ torrent/tracker_list_test.cc \ torrent/tracker_list_test.h \ torrent/tracker_list_features_test.cc \ torrent/tracker_list_features_test.h \ torrent/tracker_timeout_test.cc \ torrent/tracker_timeout_test.h \ torrent/utils/log_test.cc \ torrent/utils/log_test.h \ torrent/utils/log_buffer_test.cc \ torrent/utils/log_buffer_test.h \ torrent/utils/net_test.cc \ torrent/utils/net_test.h \ torrent/utils/option_strings_test.cc \ torrent/utils/option_strings_test.h \ torrent/utils/test_extents.cc \ torrent/utils/test_extents.h \ torrent/utils/test_queue_buckets.cc \ torrent/utils/test_queue_buckets.h \ torrent/utils/signal_bitfield_test.cc \ torrent/utils/signal_bitfield_test.h \ torrent/utils/thread_base_test.cc \ torrent/utils/thread_base_test.h \ tracker/tracker_http_test.cc \ tracker/tracker_http_test.h \ main.cc LibTorrentTest_CXXFLAGS = $(CPPUNIT_CFLAGS) LibTorrentTest_LDFLAGS = $(CPPUNIT_LIBS) -ldl AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src libtorrent-0.13.6/test/configure.ac000066400000000000000000000010701257211073700172350ustar00rootroot00000000000000AC_INIT(libtorrent_test, 0.12.5, jaris@ifi.uio.no) AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) AM_PATH_CPPUNIT(1.9.6) AC_PROG_CXX AC_PROG_CC AC_PROG_INSTALL TORRENT_CHECK_CXXFLAGS TORRENT_ENABLE_DEBUG TORRENT_ENABLE_EXTRA_DEBUG TORRENT_ENABLE_WERROR TORRENT_ENABLE_TR1 TORRENT_CHECK_MADVISE() TORRENT_CHECK_CACHELINE() TORRENT_MINCORE() TORRENT_OTFD() CC_ATTRIBUTE_UNUSED( AC_DEFINE([__UNUSED], [__attribute__((unused))], [Wrapper around unused attribute]), AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported]) ) AC_OUTPUT([ Makefile ])libtorrent-0.13.6/test/data/000077500000000000000000000000001257211073700156625ustar00rootroot00000000000000libtorrent-0.13.6/test/data/chunk_list_test.cc000066400000000000000000000102741257211073700213770ustar00rootroot00000000000000#include "config.h" #include "chunk_list_test.h" #include "torrent/chunk_manager.h" #include "torrent/exceptions.h" CPPUNIT_TEST_SUITE_REGISTRATION(ChunkListTest); namespace tr1 { using namespace std::tr1; } torrent::Chunk* func_create_chunk(uint32_t index, int prot_flags) { // Do proper handling of prot_flags... char* memory_part1 = (char*)mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (memory_part1 == MAP_FAILED) throw torrent::internal_error("func_create_chunk() failed: " + std::string(strerror(errno))); std::memset(memory_part1, index, 10); torrent::Chunk* chunk = new torrent::Chunk(); chunk->push_back(torrent::ChunkPart::MAPPED_MMAP, torrent::MemoryChunk(memory_part1, memory_part1, memory_part1 + 10, torrent::MemoryChunk::prot_read, 0)); if (chunk == NULL) throw torrent::internal_error("func_create_chunk() failed: chunk == NULL."); return chunk; } uint64_t func_free_diskspace(torrent::ChunkList* chunk_list) { return 0; } void func_storage_error(torrent::ChunkList* chunk_list, const std::string& message) { } void ChunkListTest::test_basic() { torrent::ChunkManager chunk_manager; torrent::ChunkList chunk_list; CPPUNIT_ASSERT(chunk_list.flags() == 0); CPPUNIT_ASSERT(chunk_list.chunk_size() == 0); chunk_list.set_chunk_size(1 << 16); chunk_list.set_manager(&chunk_manager); chunk_list.resize(32); CPPUNIT_ASSERT(chunk_list.size() == 32); CPPUNIT_ASSERT(chunk_list.chunk_size() == (1 << 16)); for (unsigned int i = 0; i < 32; i++) CPPUNIT_ASSERT(chunk_list[i].index() == i); } void ChunkListTest::test_get_release() { SETUP_CHUNK_LIST(); CPPUNIT_ASSERT(!(*chunk_list)[0].is_valid()); torrent::ChunkHandle handle_0 = chunk_list->get(0); CPPUNIT_ASSERT(handle_0.object() != NULL); CPPUNIT_ASSERT(handle_0.object()->index() == 0); CPPUNIT_ASSERT(handle_0.index() == 0); CPPUNIT_ASSERT(!handle_0.is_writable()); CPPUNIT_ASSERT(!handle_0.is_blocking()); CPPUNIT_ASSERT((*chunk_list)[0].is_valid()); CPPUNIT_ASSERT((*chunk_list)[0].references() == 1); CPPUNIT_ASSERT((*chunk_list)[0].writable() == 0); CPPUNIT_ASSERT((*chunk_list)[0].blocking() == 0); chunk_list->release(&handle_0); torrent::ChunkHandle handle_1 = chunk_list->get(1, torrent::ChunkList::get_writable); CPPUNIT_ASSERT(handle_1.object() != NULL); CPPUNIT_ASSERT(handle_1.object()->index() == 1); CPPUNIT_ASSERT(handle_1.index() == 1); CPPUNIT_ASSERT(handle_1.is_writable()); CPPUNIT_ASSERT(!handle_1.is_blocking()); CPPUNIT_ASSERT((*chunk_list)[1].is_valid()); CPPUNIT_ASSERT((*chunk_list)[1].references() == 1); CPPUNIT_ASSERT((*chunk_list)[1].writable() == 1); CPPUNIT_ASSERT((*chunk_list)[1].blocking() == 0); chunk_list->release(&handle_1); torrent::ChunkHandle handle_2 = chunk_list->get(2, torrent::ChunkList::get_blocking); CPPUNIT_ASSERT(handle_2.object() != NULL); CPPUNIT_ASSERT(handle_2.object()->index() == 2); CPPUNIT_ASSERT(handle_2.index() == 2); CPPUNIT_ASSERT(!handle_2.is_writable()); CPPUNIT_ASSERT(handle_2.is_blocking()); CPPUNIT_ASSERT((*chunk_list)[2].is_valid()); CPPUNIT_ASSERT((*chunk_list)[2].references() == 1); CPPUNIT_ASSERT((*chunk_list)[2].writable() == 0); CPPUNIT_ASSERT((*chunk_list)[2].blocking() == 1); chunk_list->release(&handle_2); // Test ro->wr, etc. CLEANUP_CHUNK_LIST(); } // Make sure we can't go into writable when blocking, etc. void ChunkListTest::test_blocking() { SETUP_CHUNK_LIST(); torrent::ChunkHandle handle_0_ro = chunk_list->get(0, torrent::ChunkList::get_blocking); CPPUNIT_ASSERT(handle_0_ro.is_valid()); // Test writable, etc, on blocking without get_nonblock using a // timer on other thread. // torrent::ChunkHandle handle_1 = chunk_list->get(0, torrent::ChunkList::get_writable); torrent::ChunkHandle handle_0_rw = chunk_list->get(0, torrent::ChunkList::get_writable | torrent::ChunkList::get_nonblock); CPPUNIT_ASSERT(!handle_0_rw.is_valid()); CPPUNIT_ASSERT(handle_0_rw.error_number() == rak::error_number::e_again); chunk_list->release(&handle_0_ro); handle_0_rw = chunk_list->get(0, torrent::ChunkList::get_writable); CPPUNIT_ASSERT(handle_0_rw.is_valid()); chunk_list->release(&handle_0_rw); CLEANUP_CHUNK_LIST(); } libtorrent-0.13.6/test/data/chunk_list_test.h000066400000000000000000000026771257211073700212510ustar00rootroot00000000000000#include #include "data/chunk_list.h" class ChunkListTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChunkListTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_get_release); CPPUNIT_TEST(test_blocking); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_get_release(); void test_blocking(); }; torrent::Chunk* func_create_chunk(uint32_t index, int prot_flags); uint64_t func_free_diskspace(torrent::ChunkList* chunk_list); void func_storage_error(torrent::ChunkList* chunk_list, const std::string& message); #define SETUP_CHUNK_LIST() \ torrent::ChunkManager* chunk_manager = new torrent::ChunkManager; \ torrent::ChunkList* chunk_list = new torrent::ChunkList; \ chunk_list->set_manager(chunk_manager); \ chunk_list->slot_create_chunk() = tr1::bind(&func_create_chunk, tr1::placeholders::_1, tr1::placeholders::_2); \ chunk_list->slot_free_diskspace() = tr1::bind(&func_free_diskspace, chunk_list); \ chunk_list->slot_storage_error() = tr1::bind(&func_storage_error, chunk_list, tr1::placeholders::_1); \ chunk_list->set_chunk_size(1 << 16); \ chunk_list->resize(32); #define CLEANUP_CHUNK_LIST() \ delete chunk_list; \ delete chunk_manager; libtorrent-0.13.6/test/data/hash_check_queue_test.cc000066400000000000000000000133641257211073700225230ustar00rootroot00000000000000#include "config.h" #include #include #include "data/hash_queue_node.h" #include "utils/sha1.h" #include "torrent/chunk_manager.h" #include "torrent/exceptions.h" #include "torrent/poll_select.h" #include "torrent/utils/thread_base_test.h" #include "thread_disk.h" #include "chunk_list_test.h" #include "hash_check_queue_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(HashCheckQueueTest); namespace tr1 { using namespace std::tr1; } pthread_mutex_t done_chunks_lock = PTHREAD_MUTEX_INITIALIZER; static void chunk_done(done_chunks_type* done_chunks, torrent::HashChunk* hash_chunk, const torrent::HashString& hash_value) { pthread_mutex_lock(&done_chunks_lock); (*done_chunks)[hash_chunk->handle().index()] = hash_value; pthread_mutex_unlock(&done_chunks_lock); } torrent::HashString hash_for_index(uint32_t index) { char buffer[10]; std::memset(buffer, index, 10); torrent::Sha1 sha1; torrent::HashString hash; sha1.init(); sha1.update(buffer, 10); sha1.final_c(hash.data()); return hash; } bool verify_hash(const done_chunks_type* done_chunks, int index, const torrent::HashString& hash) { pthread_mutex_lock(&done_chunks_lock); done_chunks_type::const_iterator itr = done_chunks->find(index); if (itr == done_chunks->end()) { pthread_mutex_unlock(&done_chunks_lock); return false; } bool matches = itr->second == hash; pthread_mutex_unlock(&done_chunks_lock); if (!matches) { // std::cout << "chunk compare: " << index << " " // << torrent::hash_string_to_hex_str(itr->second) << ' ' << torrent::hash_string_to_hex_str(hash) << ' ' // << (itr != done_chunks->end() && itr->second == hash) // << std::endl; throw torrent::internal_error("Could not verify hash..."); } return true; } static torrent::Poll* create_select_poll() { return torrent::PollSelect::create(256); } static void do_nothing() {} void HashCheckQueueTest::setUp() { torrent::Poll::slot_create_poll() = tr1::bind(&create_select_poll); signal(SIGUSR1, (sig_t)&do_nothing); } void HashCheckQueueTest::tearDown() { } void HashCheckQueueTest::test_basic() { } void HashCheckQueueTest::test_single() { SETUP_CHUNK_LIST(); torrent::HashCheckQueue hash_queue; done_chunks_type done_chunks; hash_queue.slot_chunk_done() = tr1::bind(&chunk_done, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2); torrent::ChunkHandle handle_0 = chunk_list->get(0, torrent::ChunkList::get_blocking); hash_queue.push_back(new torrent::HashChunk(handle_0)); CPPUNIT_ASSERT(hash_queue.size() == 1); CPPUNIT_ASSERT(hash_queue.front()->handle().is_blocking()); CPPUNIT_ASSERT(hash_queue.front()->handle().object() == &((*chunk_list)[0])); hash_queue.perform(); CPPUNIT_ASSERT(done_chunks.find(0) != done_chunks.end()); CPPUNIT_ASSERT(done_chunks[0] == hash_for_index(0)); // Should not be needed... Also verify that HashChunk gets deleted. chunk_list->release(&handle_0); CLEANUP_CHUNK_LIST(); } void HashCheckQueueTest::test_multiple() { SETUP_CHUNK_LIST(); torrent::HashCheckQueue hash_queue; done_chunks_type done_chunks; hash_queue.slot_chunk_done() = tr1::bind(&chunk_done, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2); handle_list handles; for (unsigned int i = 0; i < 20; i++) { handles.push_back(chunk_list->get(i, torrent::ChunkList::get_blocking)); hash_queue.push_back(new torrent::HashChunk(handles.back())); CPPUNIT_ASSERT(hash_queue.size() == i + 1); CPPUNIT_ASSERT(hash_queue.back()->handle().is_blocking()); CPPUNIT_ASSERT(hash_queue.back()->handle().object() == &((*chunk_list)[i])); } hash_queue.perform(); for (unsigned int i = 0; i < 20; i++) { CPPUNIT_ASSERT(done_chunks.find(i) != done_chunks.end()); CPPUNIT_ASSERT(done_chunks[i] == hash_for_index(i)); // Should not be needed... chunk_list->release(&handles[i]); } CLEANUP_CHUNK_LIST(); } void HashCheckQueueTest::test_erase() { // SETUP_CHUNK_LIST(); // torrent::HashCheckQueue hash_queue; // done_chunks_type done_chunks; // hash_queue.slot_chunk_done() = tr1::bind(&chunk_done, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2); // handle_list handles; // for (unsigned int i = 0; i < 20; i++) { // handles.push_back(chunk_list->get(i, torrent::ChunkList::get_blocking)); // hash_queue.push_back(new torrent::HashChunk(handles.back())); // CPPUNIT_ASSERT(hash_queue.size() == i + 1); // CPPUNIT_ASSERT(hash_queue.back()->handle().is_blocking()); // CPPUNIT_ASSERT(hash_queue.back()->handle().object() == &((*chunk_list)[i])); // } // hash_queue.perform(); // for (unsigned int i = 0; i < 20; i++) { // CPPUNIT_ASSERT(done_chunks.find(i) != done_chunks.end()); // CPPUNIT_ASSERT(done_chunks[i] == hash_for_index(i)); // // Should not be needed... // chunk_list->release(&handles[i]); // } // CLEANUP_CHUNK_LIST(); } void HashCheckQueueTest::test_thread() { SETUP_CHUNK_LIST(); SETUP_THREAD(); thread_disk->start_thread(); torrent::HashCheckQueue* hash_queue = thread_disk->hash_queue(); done_chunks_type done_chunks; hash_queue->slot_chunk_done() = tr1::bind(&chunk_done, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2); for (int i = 0; i < 1000; i++) { pthread_mutex_lock(&done_chunks_lock); done_chunks.erase(0); pthread_mutex_unlock(&done_chunks_lock); torrent::ChunkHandle handle_0 = chunk_list->get(0, torrent::ChunkList::get_blocking); hash_queue->push_back(new torrent::HashChunk(handle_0)); thread_disk->interrupt(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&verify_hash, &done_chunks, 0, hash_for_index(0)))); chunk_list->release(&handle_0); } thread_disk->stop_thread(); CLEANUP_THREAD(); CLEANUP_CHUNK_LIST(); } libtorrent-0.13.6/test/data/hash_check_queue_test.h000066400000000000000000000015231257211073700223570ustar00rootroot00000000000000#include #include #include #include "data/hash_check_queue.h" #include "torrent/hash_string.h" class HashCheckQueueTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HashCheckQueueTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_single); CPPUNIT_TEST(test_multiple); CPPUNIT_TEST(test_erase); CPPUNIT_TEST(test_thread); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_single(); void test_multiple(); void test_erase(); void test_thread(); }; typedef std::map done_chunks_type; typedef std::vector handle_list; torrent::HashString hash_for_index(uint32_t index); bool verify_hash(const done_chunks_type* done_chunks, int index, const torrent::HashString& hash); libtorrent-0.13.6/test/data/hash_queue_test.cc000066400000000000000000000127421257211073700213650ustar00rootroot00000000000000#include "config.h" #include #include #include "data/hash_queue_node.h" #include "torrent/chunk_manager.h" #include "torrent/exceptions.h" #include "torrent/hash_string.h" #include "torrent/poll_select.h" #include "torrent/utils/thread_base_test.h" #include "globals.h" #include "thread_disk.h" #include "chunk_list_test.h" #include "hash_queue_test.h" #include "hash_check_queue_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(HashQueueTest); namespace tr1 { using namespace std::tr1; } typedef std::map done_chunks_type; static void chunk_done(torrent::ChunkList* chunk_list, done_chunks_type* done_chunks, torrent::ChunkHandle handle, const char* hash_value) { if (hash_value != NULL) (*done_chunks)[handle.index()] = *torrent::HashString::cast_from(hash_value); chunk_list->release(&handle); } bool check_for_chunk_done(torrent::HashQueue* hash_queue, done_chunks_type* done_chunks, int index) { hash_queue->work(); return done_chunks->find(index) != done_chunks->end(); } static torrent::Poll* create_select_poll() { return torrent::PollSelect::create(256); } static void do_nothing() {} void HashQueueTest::setUp() { CPPUNIT_ASSERT(torrent::taskScheduler.empty()); torrent::Poll::slot_create_poll() = tr1::bind(&create_select_poll); signal(SIGUSR1, (sig_t)&do_nothing); } void HashQueueTest::tearDown() { torrent::taskScheduler.clear(); } void HashQueueTest::test_basic() { // SETUP_CHUNK_LIST(); // SETUP_THREAD(); // thread_disk->start_thread(); // torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk); // // Do stuff? // delete hash_queue; // thread_disk->stop_thread(); // CLEANUP_THREAD(); // CLEANUP_CHUNK_LIST(); } static void fill_queue() { } void HashQueueTest::test_single() { SETUP_CHUNK_LIST(); SETUP_THREAD(); thread_disk->start_thread(); done_chunks_type done_chunks; torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk); hash_queue->slot_has_work() = tr1::bind(&fill_queue); torrent::ChunkHandle handle_0 = chunk_list->get(0, torrent::ChunkList::get_blocking); hash_queue->push_back(handle_0, NULL, tr1::bind(&chunk_done, chunk_list, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2)); CPPUNIT_ASSERT(hash_queue->size() == 1); CPPUNIT_ASSERT(hash_queue->front().handle().is_blocking()); CPPUNIT_ASSERT(hash_queue->front().handle().object() == &((*chunk_list)[0])); hash_queue->work(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&check_for_chunk_done, hash_queue, &done_chunks, 0))); CPPUNIT_ASSERT(done_chunks[0] == hash_for_index(0)); // chunk_list->release(&handle_0); CPPUNIT_ASSERT(thread_disk->hash_queue()->empty()); delete hash_queue; thread_disk->stop_thread(); CLEANUP_THREAD(); CLEANUP_CHUNK_LIST(); } void HashQueueTest::test_multiple() { SETUP_CHUNK_LIST(); SETUP_THREAD(); thread_disk->start_thread(); done_chunks_type done_chunks; torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk); hash_queue->slot_has_work() = tr1::bind(&fill_queue); for (unsigned int i = 0; i < 20; i++) { hash_queue->push_back(chunk_list->get(i, torrent::ChunkList::get_blocking), NULL, tr1::bind(&chunk_done, chunk_list, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2)); CPPUNIT_ASSERT(hash_queue->size() == i + 1); CPPUNIT_ASSERT(hash_queue->back().handle().is_blocking()); CPPUNIT_ASSERT(hash_queue->back().handle().object() == &((*chunk_list)[i])); } for (unsigned int i = 0; i < 20; i++) { CPPUNIT_ASSERT(wait_for_true(tr1::bind(&check_for_chunk_done, hash_queue, &done_chunks, i))); CPPUNIT_ASSERT(done_chunks[i] == hash_for_index(i)); } CPPUNIT_ASSERT(thread_disk->hash_queue()->empty()); delete hash_queue; thread_disk->stop_thread(); CLEANUP_THREAD(); CLEANUP_CHUNK_LIST(); } void HashQueueTest::test_erase() { SETUP_CHUNK_LIST(); SETUP_THREAD(); torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk); hash_queue->slot_has_work() = tr1::bind(&fill_queue); done_chunks_type done_chunks; for (unsigned int i = 0; i < 20; i++) { hash_queue->push_back(chunk_list->get(i, torrent::ChunkList::get_blocking), NULL, tr1::bind(&chunk_done, chunk_list, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2)); CPPUNIT_ASSERT(hash_queue->size() == i + 1); } hash_queue->remove(NULL); CPPUNIT_ASSERT(hash_queue->empty()); CPPUNIT_ASSERT(thread_disk->hash_queue()->empty()); delete hash_queue; delete thread_disk; CLEANUP_CHUNK_LIST(); } void HashQueueTest::test_erase_stress() { SETUP_CHUNK_LIST(); SETUP_THREAD(); thread_disk->start_thread(); torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk); hash_queue->slot_has_work() = tr1::bind(&fill_queue); done_chunks_type done_chunks; for (unsigned int i = 0; i < 1000; i++) { for (unsigned int i = 0; i < 20; i++) { hash_queue->push_back(chunk_list->get(i, torrent::ChunkList::get_blocking), NULL, tr1::bind(&chunk_done, chunk_list, &done_chunks, tr1::placeholders::_1, tr1::placeholders::_2)); CPPUNIT_ASSERT(hash_queue->size() == i + 1); } hash_queue->remove(NULL); CPPUNIT_ASSERT(hash_queue->empty()); } CPPUNIT_ASSERT(thread_disk->hash_queue()->empty()); delete hash_queue; thread_disk->stop_thread(); CLEANUP_THREAD(); CLEANUP_CHUNK_LIST(); } // Test erase of different id's. // Current code doesn't work well if we remove a hash... libtorrent-0.13.6/test/data/hash_queue_test.h000066400000000000000000000010001257211073700212100ustar00rootroot00000000000000#include #include "data/hash_queue.h" class HashQueueTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HashQueueTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_single); CPPUNIT_TEST(test_multiple); CPPUNIT_TEST(test_erase); CPPUNIT_TEST(test_erase_stress); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_single(); void test_multiple(); void test_erase(); void test_erase_stress(); }; libtorrent-0.13.6/test/main.cc000066400000000000000000000013751257211073700162120ustar00rootroot00000000000000#include #include #include int main(int argc, char* argv[]) { // Get the top level suite from the registry CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); // Adds the test to the list of test to run CppUnit::TextUi::TestRunner runner; runner.addTest( suite ); // Change the default outputter to a compiler error format outputter runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), std::cerr ) ); // Run the tests. bool wasSucessful = runner.run(); // Return error code 1 if the one of test failed. return wasSucessful ? 0 : 1; } libtorrent-0.13.6/test/protocol/000077500000000000000000000000001257211073700166125ustar00rootroot00000000000000libtorrent-0.13.6/test/protocol/test_request_list.cc000066400000000000000000000176021257211073700227110ustar00rootroot00000000000000#include "config.h" #include "test_request_list.h" #include "torrent/exceptions.h" #include "torrent/peer/peer_info.h" #include "download/delegator.h" #include "protocol/peer_chunks.h" #include "protocol/request_list.h" #include "rak/socket_address.h" CPPUNIT_TEST_SUITE_REGISTRATION(TestRequestList); static uint32_t chunk_index_size(uint32_t index) { return 1 << 10; } static void transfer_list_void() { // std::cout << "list_void" << std::endl; } static void transfer_list_completed(torrent::TransferList* transfer_list, uint32_t index) { torrent::TransferList::iterator itr = transfer_list->find(index); // std::cout << "list_completed:" << index << " found: " << (itr != transfer_list->end()) << std::endl; CPPUNIT_ASSERT(itr != transfer_list->end()); transfer_list->erase(itr); } // Move to support header: #define SETUP_DELEGATOR(fpc_prefix) \ torrent::Delegator* delegator = new torrent::Delegator; \ delegator->slot_chunk_find() = std::tr1::bind(&fpc_prefix ## _find_peer_chunk, std::tr1::placeholders::_1, std::tr1::placeholders::_2); \ delegator->slot_chunk_size() = std::tr1::bind(&chunk_index_size, std::tr1::placeholders::_1); \ delegator->transfer_list()->slot_canceled() = std::tr1::bind(&transfer_list_void); \ delegator->transfer_list()->slot_queued() = std::tr1::bind(&transfer_list_void); \ delegator->transfer_list()->slot_completed() = std::tr1::bind(&transfer_list_completed, delegator->transfer_list(), std::tr1::placeholders::_1); \ delegator->transfer_list()->slot_corrupt() = std::tr1::bind(&transfer_list_void); // Set bitfield size... #define SETUP_PEER_CHUNKS() \ rak::socket_address peer_info_address; \ torrent::PeerInfo* peer_info = new torrent::PeerInfo(peer_info_address.c_sockaddr()); \ torrent::PeerChunks* peer_chunks = new torrent::PeerChunks; \ peer_chunks->set_peer_info(peer_info); #define CLEANUP_PEER_CHUNKS() \ delete peer_chunks; \ delete peer_info; #define SETUP_REQUEST_LIST() \ torrent::RequestList* request_list = new torrent::RequestList; \ request_list->set_delegator(delegator); \ request_list->set_peer_chunks(peer_chunks); #define SETUP_ALL(fpc_prefix) \ SET_CACHED_TIME(0); \ SETUP_DELEGATOR(basic); \ SETUP_PEER_CHUNKS(); \ SETUP_REQUEST_LIST(); #define SETUP_ALL_WITH_3(fpc_prefix) \ SETUP_ALL(fpc_prefix); \ const torrent::Piece* piece_1 = request_list->delegate(); \ const torrent::Piece* piece_2 = request_list->delegate(); \ const torrent::Piece* piece_3 = request_list->delegate(); \ CPPUNIT_ASSERT(piece_1 && piece_2 && piece_3); #define CLEANUP_ALL(fpc_prefix) \ CLEANUP_PEER_CHUNKS(); \ delete delegator; \ delete request_list; #define CLEAR_TRANSFERS(fpc_prefix) \ delegator->transfer_list()->clear(); // // // #define VERIFY_QUEUE_SIZES(s_0, s_1, s_2, s_3) \ CPPUNIT_ASSERT(request_list->queued_size() == s_0); \ CPPUNIT_ASSERT(request_list->unordered_size() == s_1); \ CPPUNIT_ASSERT(request_list->stalled_size() == s_2); \ CPPUNIT_ASSERT(request_list->choked_size() == s_3); #define VERIFY_PIECE_IS_LEADER(piece) \ CPPUNIT_ASSERT(request_list->transfer() != NULL); \ CPPUNIT_ASSERT(request_list->transfer()->is_leader()); \ CPPUNIT_ASSERT(request_list->transfer()->peer_info() != NULL); \ CPPUNIT_ASSERT(request_list->transfer()->peer_info() == peer_info); #define VERIFY_TRANSFER_COUNTER(transfer, count) \ CPPUNIT_ASSERT(transfer != NULL); \ CPPUNIT_ASSERT(transfer->peer_info() != NULL); \ CPPUNIT_ASSERT(transfer->peer_info()->transfer_counter() == count); #define SET_CACHED_TIME(seconds) \ torrent::cachedTime = rak::timer::from_seconds(1000 + seconds); \ rak::priority_queue_perform(&torrent::taskScheduler, torrent::cachedTime); // // Basic tests: // static uint32_t basic_find_peer_chunk(torrent::PeerChunks* peerChunk, bool highPriority) { static int next_index = 0; return next_index++; } void TestRequestList::test_basic() { SETUP_ALL(basic); CPPUNIT_ASSERT(!request_list->is_downloading()); CPPUNIT_ASSERT(!request_list->is_interested_in_active()); VERIFY_QUEUE_SIZES(0, 0, 0, 0); CPPUNIT_ASSERT(request_list->calculate_pipe_size(1024 * 0) == 2); CPPUNIT_ASSERT(request_list->calculate_pipe_size(1024 * 10) == 12); CPPUNIT_ASSERT(request_list->transfer() == NULL); CLEANUP_ALL(); } void TestRequestList::test_single_request() { SETUP_ALL(basic); const torrent::Piece* piece = request_list->delegate(); // std::cout << piece->index() << ' ' << piece->offset() << ' ' << piece->length() << std::endl; // std::cout << peer_info->transfer_counter() << std::endl; CPPUNIT_ASSERT(request_list->downloading(*piece)); VERIFY_PIECE_IS_LEADER(*piece); VERIFY_TRANSFER_COUNTER(request_list->transfer(), 1); request_list->transfer()->adjust_position(piece->length()); CPPUNIT_ASSERT(request_list->transfer()->is_finished()); request_list->finished(); CPPUNIT_ASSERT(peer_info->transfer_counter() == 0); CLEANUP_ALL(); } void TestRequestList::test_single_canceled() { SETUP_ALL(basic); const torrent::Piece* piece = request_list->delegate(); // std::cout << piece->index() << ' ' << piece->offset() << ' ' << piece->length() << std::endl; // std::cout << peer_info->transfer_counter() << std::endl; CPPUNIT_ASSERT(request_list->downloading(*piece)); VERIFY_PIECE_IS_LEADER(*piece); // REMOVE VERIFY_TRANSFER_COUNTER(request_list->transfer(), 1); // REMOVE request_list->transfer()->adjust_position(piece->length() / 2); CPPUNIT_ASSERT(!request_list->transfer()->is_finished()); request_list->skipped(); // The transfer remains in Block until it gets the block has a // transfer trigger finished. // TODO: We need to have a way of clearing disowned transfers, then // make transfer list delete Block's(?) with those before dtor... CPPUNIT_ASSERT(peer_info->transfer_counter() == 0); CLEAR_TRANSFERS(); CLEANUP_ALL(); } void TestRequestList::test_choke_normal() { SETUP_ALL_WITH_3(basic); VERIFY_QUEUE_SIZES(3, 0, 0, 0); request_list->choked(); SET_CACHED_TIME(1); VERIFY_QUEUE_SIZES(0, 0, 0, 3); SET_CACHED_TIME(6); VERIFY_QUEUE_SIZES(0, 0, 0, 0); CLEAR_TRANSFERS(); CLEANUP_ALL(); } void TestRequestList::test_choke_unchoke_discard() { SETUP_ALL_WITH_3(basic); request_list->choked(); SET_CACHED_TIME(5); request_list->unchoked(); SET_CACHED_TIME(10); VERIFY_QUEUE_SIZES(0, 0, 0, 3); SET_CACHED_TIME(65); VERIFY_QUEUE_SIZES(0, 0, 0, 0); CLEAR_TRANSFERS(); CLEANUP_ALL(); } void TestRequestList::test_choke_unchoke_transfer() { SETUP_ALL_WITH_3(basic); request_list->choked(); SET_CACHED_TIME(5); request_list->unchoked(); SET_CACHED_TIME(10); CPPUNIT_ASSERT(request_list->downloading(*piece_1)); request_list->transfer()->adjust_position(piece_1->length()); request_list->finished(); SET_CACHED_TIME(60); CPPUNIT_ASSERT(request_list->downloading(*piece_2)); request_list->transfer()->adjust_position(piece_2->length()); request_list->finished(); SET_CACHED_TIME(110); CPPUNIT_ASSERT(request_list->downloading(*piece_3)); request_list->transfer()->adjust_position(piece_3->length()); request_list->finished(); VERIFY_QUEUE_SIZES(0, 0, 0, 0); CLEAR_TRANSFERS(); CLEANUP_ALL(); } libtorrent-0.13.6/test/protocol/test_request_list.h000066400000000000000000000012371257211073700225500ustar00rootroot00000000000000#include #include "protocol/request_list.h" class TestRequestList : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestRequestList); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_single_request); CPPUNIT_TEST(test_single_canceled); CPPUNIT_TEST(test_choke_normal); CPPUNIT_TEST(test_choke_unchoke_discard); CPPUNIT_TEST(test_choke_unchoke_transfer); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_single_request(); void test_single_canceled(); void test_choke_normal(); void test_choke_unchoke_discard(); void test_choke_unchoke_transfer(); }; libtorrent-0.13.6/test/rak/000077500000000000000000000000001257211073700155265ustar00rootroot00000000000000libtorrent-0.13.6/test/rak/allocators_test.cc000066400000000000000000000012301257211073700212330ustar00rootroot00000000000000#include "config.h" #include #include "allocators_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(AllocatorsTest); template bool is_aligned(const T& t) { return t.empty() || (reinterpret_cast(&t[0]) & (LT_SMP_CACHE_BYTES - 1)) == 0x0; } void AllocatorsTest::testAlignment() { aligned_vector_type v1; aligned_vector_type v2(1, 'a'); aligned_vector_type v3(16, 'a'); aligned_vector_type v4(LT_SMP_CACHE_BYTES, 'b'); aligned_vector_type v5(1, 'a'); CPPUNIT_ASSERT(is_aligned(v1)); CPPUNIT_ASSERT(is_aligned(v2)); CPPUNIT_ASSERT(is_aligned(v3)); CPPUNIT_ASSERT(is_aligned(v4)); CPPUNIT_ASSERT(is_aligned(v5)); } libtorrent-0.13.6/test/rak/allocators_test.h000066400000000000000000000006231257211073700211020ustar00rootroot00000000000000#include #include #include "rak/allocators.h" class AllocatorsTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AllocatorsTest); CPPUNIT_TEST(testAlignment); CPPUNIT_TEST_SUITE_END(); public: typedef std::vector > aligned_vector_type; void setUp() {} void tearDown() {} void testAlignment(); }; libtorrent-0.13.6/test/rak/ranges_test.cc000066400000000000000000000077641257211073700203710ustar00rootroot00000000000000#include "config.h" #include #include "ranges_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(RangesTest); template bool verify_ranges(const torrent::ranges& ranges) { typename torrent::ranges::const_iterator first = ranges.begin(); typename torrent::ranges::const_iterator last = ranges.begin(); if (first == last) return true; if (first->second <= first->first) return false; Type boundary = first++->second; do { if (first->first <= boundary) return false; if (first->second <= first->first) return false; } while (++first != last); return true; } void RangesTest::test_basic() { } void RangesTest::test_intersect() { torrent::ranges range; CPPUNIT_ASSERT(verify_ranges(range)); CPPUNIT_ASSERT(range.intersect_distance(0, 0) == 0); CPPUNIT_ASSERT(range.intersect_distance(0, 10) == 0); range.insert(0, 5); CPPUNIT_ASSERT(verify_ranges(range)); CPPUNIT_ASSERT(range.intersect_distance(0, 5) == 5); CPPUNIT_ASSERT(range.intersect_distance(0, 10) == 5); CPPUNIT_ASSERT(range.intersect_distance(-5, 5) == 5); CPPUNIT_ASSERT(range.intersect_distance(-5, 10) == 5); CPPUNIT_ASSERT(range.intersect_distance(2, 2) == 0); CPPUNIT_ASSERT(range.intersect_distance(1, 4) == 3); CPPUNIT_ASSERT(range.intersect_distance(1, 10) == 4); CPPUNIT_ASSERT(range.intersect_distance(-5, 4) == 4); range.insert(10, 15); CPPUNIT_ASSERT(verify_ranges(range)); CPPUNIT_ASSERT(range.intersect_distance(0, 15) == 10); CPPUNIT_ASSERT(range.intersect_distance(0, 20) == 10); CPPUNIT_ASSERT(range.intersect_distance(-5, 15) == 10); CPPUNIT_ASSERT(range.intersect_distance(-5, 20) == 10); CPPUNIT_ASSERT(range.intersect_distance(2, 12) == 5); CPPUNIT_ASSERT(range.intersect_distance(1, 14) == 8); CPPUNIT_ASSERT(range.intersect_distance(1, 20) == 9); CPPUNIT_ASSERT(range.intersect_distance(-5, 14) == 9); } void RangesTest::test_create_union() { torrent::ranges range_1l; torrent::ranges range_1r; torrent::ranges range_1u; // Empty: range_1u = torrent::ranges::create_union(range_1l, range_1r); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 0); range_1l.insert(0, 5); // Left one entry: range_1u = torrent::ranges::create_union(range_1l, range_1r); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 5); // Right one entry: range_1u = torrent::ranges::create_union(range_1r, range_1l); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 5); range_1r.insert(10, 15); // Both one entry: range_1u = torrent::ranges::create_union(range_1l, range_1r); // std::cout << "range_1u.intersect_distance(-5, 20) = " << range_1u.intersect_distance(-5, 20) << std::endl; CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 10); range_1r.insert(4, 6); // Overlap: range_1u = torrent::ranges::create_union(range_1l, range_1r); // std::cout << "range_1u.intersect_distance(-5, 20) = " << range_1u.intersect_distance(-5, 20) << std::endl; CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 11); range_1r.insert(9, 10); // Boundary: range_1u = torrent::ranges::create_union(range_1l, range_1r); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 20) == 12); // Trailing ranges left. range_1l.insert(25, 30); range_1l.insert(35, 40); range_1u = torrent::ranges::create_union(range_1l, range_1r); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 50) == 22); // Trailing ranges right. range_1r.insert(37, 45); range_1r.insert(50, 55); range_1u = torrent::ranges::create_union(range_1l, range_1r); CPPUNIT_ASSERT(verify_ranges(range_1u)); CPPUNIT_ASSERT(range_1u.intersect_distance(-5, 60) == 32); } libtorrent-0.13.6/test/rak/ranges_test.h000066400000000000000000000006611257211073700202200ustar00rootroot00000000000000#include #include #include "torrent/utils/ranges.h" class RangesTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RangesTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_intersect); CPPUNIT_TEST(test_create_union); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_intersect(); void test_create_union(); }; libtorrent-0.13.6/test/torrent/000077500000000000000000000000001257211073700164465ustar00rootroot00000000000000libtorrent-0.13.6/test/torrent/http_test.cc000066400000000000000000000110671257211073700210000ustar00rootroot00000000000000#include "config.h" #include #include "http_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(HttpTest); #define HTTP_SETUP() \ bool http_destroyed = false; \ bool stream_destroyed = false; \ \ TestHttp* test_http = new TestHttp(&http_destroyed); \ torrent::Http* http = test_http; \ std::stringstream* http_stream = new StringStream(&stream_destroyed); \ \ int done_counter = 0; \ int failed_counter = 0; \ \ http->set_stream(http_stream); \ http->signal_done().push_back(std::tr1::bind(&increment_value, &done_counter)); \ http->signal_failed().push_back(std::tr1::bind(&increment_value, &failed_counter)); class StringStream : public std::stringstream { public: StringStream(bool *destroyed) : m_destroyed(destroyed) {} ~StringStream() { *m_destroyed = true; } private: bool* m_destroyed; }; class TestHttp : public torrent::Http { public: static const int flag_active = 0x1; TestHttp(bool *destroyed = NULL) : m_flags(0), m_destroyed(destroyed) {} virtual ~TestHttp() { if (m_destroyed) *m_destroyed = true; } virtual void start() { m_flags |= flag_active; } virtual void close() { m_flags &= ~flag_active; } bool trigger_signal_done(); bool trigger_signal_failed(); private: int m_flags; bool* m_destroyed; }; bool TestHttp::trigger_signal_done() { if (!(m_flags & flag_active)) return false; m_flags &= ~flag_active; trigger_done(); return true; } bool TestHttp::trigger_signal_failed() { if (!(m_flags & flag_active)) return false; m_flags &= ~flag_active; trigger_failed("We Fail."); return true; } TestHttp* create_test_http() { return new TestHttp; } static void increment_value(int* value) { (*value)++; } void HttpTest::test_basic() { torrent::Http::slot_factory() = std::tr1::bind(&create_test_http); torrent::Http* http = torrent::Http::slot_factory()(); std::stringstream* http_stream = new std::stringstream; http->set_url("http://example.com"); CPPUNIT_ASSERT(http->url() == "http://example.com"); CPPUNIT_ASSERT(http->stream() == NULL); http->set_stream(http_stream); CPPUNIT_ASSERT(http->stream() == http_stream); CPPUNIT_ASSERT(http->timeout() == 0); http->set_timeout(666); CPPUNIT_ASSERT(http->timeout() == 666); delete http; delete http_stream; } void HttpTest::test_done() { HTTP_SETUP(); http->start(); CPPUNIT_ASSERT(test_http->trigger_signal_done()); // Check that we didn't delete... CPPUNIT_ASSERT(done_counter == 1 && failed_counter == 0); } void HttpTest::test_failure() { HTTP_SETUP(); http->start(); CPPUNIT_ASSERT(test_http->trigger_signal_failed()); // Check that we didn't delete... CPPUNIT_ASSERT(done_counter == 0 && failed_counter == 1); } void HttpTest::test_delete_on_done() { HTTP_SETUP(); http->start(); http->set_delete_stream(); CPPUNIT_ASSERT(!stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(test_http->trigger_signal_done()); CPPUNIT_ASSERT(stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(http->stream() == NULL); stream_destroyed = false; http_stream = new StringStream(&stream_destroyed); http->set_stream(http_stream); http->start(); http->set_delete_self(); CPPUNIT_ASSERT(!stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(test_http->trigger_signal_done()); CPPUNIT_ASSERT(stream_destroyed); CPPUNIT_ASSERT(http_destroyed); } void HttpTest::test_delete_on_failure() { HTTP_SETUP(); http->start(); http->set_delete_stream(); CPPUNIT_ASSERT(!stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(test_http->trigger_signal_failed()); CPPUNIT_ASSERT(stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(http->stream() == NULL); stream_destroyed = false; http_stream = new StringStream(&stream_destroyed); http->set_stream(http_stream); http->start(); http->set_delete_self(); CPPUNIT_ASSERT(!stream_destroyed); CPPUNIT_ASSERT(!http_destroyed); CPPUNIT_ASSERT(test_http->trigger_signal_failed()); CPPUNIT_ASSERT(stream_destroyed); CPPUNIT_ASSERT(http_destroyed); } libtorrent-0.13.6/test/torrent/http_test.h000066400000000000000000000010141257211073700206310ustar00rootroot00000000000000#include #include "torrent/http.h" class HttpTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HttpTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_done); CPPUNIT_TEST(test_failure); CPPUNIT_TEST(test_delete_on_done); CPPUNIT_TEST(test_delete_on_failure); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_done(); void test_failure(); void test_delete_on_done(); void test_delete_on_failure(); }; libtorrent-0.13.6/test/torrent/object_static_map_test.cc000066400000000000000000000330151257211073700234700ustar00rootroot00000000000000#include "config.h" #include #include #include "torrent/object_static_map.h" #include "protocol/extensions.h" #include "object_test_utils.h" #include "object_static_map_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStaticMapTest); // Possible bencode keys in a DHT message. enum keys { key_d_a, key_d_d_a, key_d_b, key_e_0, key_e_1, key_e_2, key_s_a, key_s_b, key_v_a, key_v_b, key_LAST, }; enum keys_2 { key_2_d_x_y, key_2_d_x_z, key_2_l_x_1, key_2_l_x_2, key_2_s_a, key_2_v_a, key_2_LAST }; typedef torrent::static_map_type test_map_type; typedef torrent::static_map_type test_map_2_type; // List of all possible keys we need/support in a DHT message. // Unsupported keys we receive are dropped (ignored) while decoding. // See torrent/object_static_map.h for how this works. template <> const test_map_type::key_list_type test_map_type::keys = { { key_d_a, "d_a::b" }, { key_d_d_a, "d_a::c::a" }, { key_d_b, "d_a::d" }, { key_e_0, "e[]" }, { key_e_1, "e[]" }, { key_e_2, "e[]" }, { key_s_a, "s_a" }, { key_s_b, "s_b" }, { key_v_a, "v_a" }, { key_v_b, "v_b" }, }; template <> const test_map_2_type::key_list_type test_map_2_type::keys = { { key_2_d_x_y, "d_x::f" }, { key_2_d_x_z, "d_x::g" }, { key_2_l_x_1, "l_x[]" }, { key_2_l_x_2, "l_x[]" }, { key_2_s_a, "s_a" }, { key_2_v_a, "v_a" }, }; void ObjectStaticMapTest::test_basics() { test_map_type test_map; test_map[key_v_a] = int64_t(1); test_map[key_v_b] = int64_t(2); test_map[key_s_a] = std::string("a"); test_map[key_s_b] = std::string("b"); CPPUNIT_ASSERT(test_map[key_v_a].as_value() == 1); CPPUNIT_ASSERT(test_map[key_v_b].as_value() == 2); CPPUNIT_ASSERT(test_map[key_s_a].as_string() == "a"); CPPUNIT_ASSERT(test_map[key_s_b].as_string() == "b"); } void ObjectStaticMapTest::test_write() { test_map_type test_map; test_map[key_v_a] = int64_t(1); test_map[key_v_b] = int64_t(2); test_map[key_s_a] = std::string("a"); // test_map[key_s_b] = std::string("b"); test_map[key_d_a] = std::string("xx"); test_map[key_d_d_a] = std::string("a"); test_map[key_d_b] = std::string("yy"); test_map[key_e_0] = std::string("f"); test_map[key_e_1] = torrent::object_create_raw_bencode_c_str("1:g"); test_map[key_e_2] = std::string("h"); char buffer[1024]; torrent::object_buffer_t result = torrent::static_map_write_bencode_c(torrent::object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), test_map); // std::cout << "static map write: '" << std::string(buffer, std::distance(buffer, result.first)) << "'" << std::endl; CPPUNIT_ASSERT(validate_bencode(buffer, result.first)); } static const char* test_read_bencode = "d3:d_xd1:fi3e1:gli4ei5eee3:l_xli1ei2ei3ee3:s_a1:a3:v_ai2ee"; static const char* test_read_skip_bencode = "d3:d_xd1:fi3e1:gli4ei5eee1:fi1e3:l_xli1ei2ei3ee3:s_a1:a3:v_ai2ee"; template bool static_map_read_bencode(map_type& map, const char* str) { try { return torrent::static_map_read_bencode(str, str + strlen(str), map) == str + strlen(str); } catch (torrent::bencode_error&) { return false; } } template bool static_map_read_bencode_exception(map_type& map, const char* str) { try { torrent::static_map_read_bencode(str, str + strlen(str), map); return false; } catch (torrent::bencode_error&) { return true; } } void ObjectStaticMapTest::test_read() { test_map_2_type test_map; CPPUNIT_ASSERT(static_map_read_bencode(test_map, test_read_bencode)); CPPUNIT_ASSERT(test_map[key_2_d_x_y].as_value() == 3); CPPUNIT_ASSERT(test_map[key_2_d_x_z].as_list().size() == 2); CPPUNIT_ASSERT(test_map[key_2_l_x_1].as_value() == 1); CPPUNIT_ASSERT(test_map[key_2_l_x_2].as_value() == 2); CPPUNIT_ASSERT(test_map[key_2_s_a].as_string() == "a"); CPPUNIT_ASSERT(test_map[key_2_v_a].as_value() == 2); test_map_2_type test_skip_map; CPPUNIT_ASSERT(static_map_read_bencode(test_skip_map, test_read_skip_bencode)); } enum ext_test_keys { key_e, // key_m_utMetadata, key_m_utPex, // key_metadataSize, key_p, key_reqq, key_v, key_test_LAST }; typedef torrent::static_map_type ext_test_message; template <> const ext_test_message::key_list_type ext_test_message::keys = { { key_e, "e" }, { key_m_utPex, "m::ut_pex" }, { key_p, "p" }, { key_reqq, "reqq" }, { key_v, "v" }, }; void ObjectStaticMapTest::test_read_extensions() { ext_test_message test_ext; CPPUNIT_ASSERT(static_map_read_bencode(test_ext, "d1:ai1ee")); CPPUNIT_ASSERT(static_map_read_bencode(test_ext, "d1:mi1ee")); CPPUNIT_ASSERT(static_map_read_bencode(test_ext, "d1:mdee")); CPPUNIT_ASSERT(static_map_read_bencode(test_ext, "d6:ut_pexi0ee")); CPPUNIT_ASSERT(static_map_read_bencode(test_ext, "d1:md6:ut_pexi0eee")); } template bool static_map_write_bencode(map_type map, const char* original) { try { char buffer[1023]; char* last = torrent::static_map_write_bencode_c(&torrent::object_write_to_buffer, NULL, torrent::object_buffer_t(buffer, buffer + 1024), map).first; return std::strncmp(buffer, original, std::distance(buffer, last)) == 0; } catch (torrent::bencode_error& e) { return false; } } // // Proper unit tests: // enum keys_empty { key_empty_LAST }; enum keys_single { key_single_a, key_single_LAST }; enum keys_raw { key_raw_a, key_raw_LAST }; enum keys_raw_types { key_raw_types_empty, key_raw_types_list, key_raw_types_map, key_raw_types_str, key_raw_types_LAST}; enum keys_multiple { key_multiple_a, key_multiple_b, key_multiple_c, key_multiple_LAST }; enum keys_dict { key_dict_a_b, key_dict_LAST }; typedef torrent::static_map_type test_empty_type; typedef torrent::static_map_type test_single_type; typedef torrent::static_map_type test_raw_type; typedef torrent::static_map_type test_raw_types_type; typedef torrent::static_map_type test_multiple_type; typedef torrent::static_map_type test_dict_type; template <> const test_empty_type::key_list_type test_empty_type::keys = { }; template <> const test_single_type::key_list_type test_single_type::keys = { { key_single_a, "b" } }; template <> const test_raw_type::key_list_type test_raw_type::keys = { { key_raw_a, "b*" } }; template <> const test_raw_types_type::key_list_type test_raw_types_type::keys = { { key_raw_types_empty, "e*"}, { key_raw_types_list, "l*L"}, { key_raw_types_map, "m*M"}, { key_raw_types_str, "s*S"} }; template <> const test_multiple_type::key_list_type test_multiple_type::keys = { { key_multiple_a, "a" }, { key_multiple_b, "b*" }, { key_multiple_c, "c" } }; template <> const test_dict_type::key_list_type test_dict_type::keys = { { key_dict_a_b, "a::b" } }; void ObjectStaticMapTest::test_read_empty() { test_empty_type map_normal; test_empty_type map_skip; test_empty_type map_incomplete; CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "de")); CPPUNIT_ASSERT(static_map_read_bencode(map_skip, "d1:ai1ee")); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "")); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d")); } void ObjectStaticMapTest::test_read_single() { test_single_type map_normal; test_single_type map_skip; test_single_type map_incomplete; CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:bi1ee")); CPPUNIT_ASSERT(map_normal[key_single_a].as_value() == 1); CPPUNIT_ASSERT(static_map_read_bencode(map_skip, "d1:ai0e1:bi1e1:ci2ee")); CPPUNIT_ASSERT(map_skip[key_single_a].as_value() == 1); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d")); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:b")); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:bi1")); CPPUNIT_ASSERT(static_map_read_bencode_exception(map_incomplete, "d1:bi1e")); } void ObjectStaticMapTest::test_read_single_raw() { test_raw_type map_raw; CPPUNIT_ASSERT(static_map_read_bencode(map_raw, "d1:bi1ee")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode(), "i1e")); CPPUNIT_ASSERT(static_map_read_bencode(map_raw, "d1:b2:abe")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode(), "2:ab")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode().as_raw_string(), "ab")); CPPUNIT_ASSERT(static_map_read_bencode(map_raw, "d1:bli1ei2eee")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode(), "li1ei2ee")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode().as_raw_list(), "i1ei2e")); CPPUNIT_ASSERT(static_map_read_bencode(map_raw, "d1:bd1:ai1e1:bi2eee")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode(), "d1:ai1e1:bi2ee")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_a].as_raw_bencode().as_raw_map(), "1:ai1e1:bi2e")); } void ObjectStaticMapTest::test_read_raw_types() { test_raw_types_type map_raw; CPPUNIT_ASSERT(static_map_read_bencode(map_raw, "d" "1:ei1e" "1:lli1ei2ee" "1:md1:ai1e1:bi2ee" "1:s2:ab" "e")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_types_empty].as_raw_bencode(), "i1e")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_types_str].as_raw_string(), "ab")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_types_list].as_raw_list(), "i1ei2e")); CPPUNIT_ASSERT(torrent::raw_bencode_equal_c_str(map_raw[key_raw_types_map].as_raw_map(), "1:ai1e1:bi2e")); } void ObjectStaticMapTest::test_read_multiple() { test_multiple_type map_normal; CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ai1ee")); CPPUNIT_ASSERT(map_normal[key_multiple_a].as_value() == 1); CPPUNIT_ASSERT(map_normal[key_multiple_b].is_empty()); CPPUNIT_ASSERT(map_normal[key_multiple_c].is_empty()); map_normal = test_multiple_type(); CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ci3ee")); CPPUNIT_ASSERT(map_normal[key_multiple_a].is_empty()); CPPUNIT_ASSERT(map_normal[key_multiple_b].is_empty()); CPPUNIT_ASSERT(map_normal[key_multiple_c].as_value() == 3); map_normal = test_multiple_type(); CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ai1e1:ci3ee")); CPPUNIT_ASSERT(map_normal[key_multiple_a].as_value() == 1); CPPUNIT_ASSERT(map_normal[key_multiple_b].is_empty()); CPPUNIT_ASSERT(map_normal[key_multiple_c].as_value() == 3); map_normal = test_multiple_type(); CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ai1e1:bi2e1:ci3ee")); CPPUNIT_ASSERT(map_normal[key_multiple_a].as_value() == 1); // CPPUNIT_ASSERT(map_normal[key_multiple_b].as_raw_bencode().as_raw_value().size() == 1); // CPPUNIT_ASSERT(map_normal[key_multiple_b].as_raw_bencode().as_raw_value().data()[0] == '2'); CPPUNIT_ASSERT(map_normal[key_multiple_c].as_value() == 3); } void ObjectStaticMapTest::test_read_dict() { test_dict_type map_normal; CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ai1ee")); CPPUNIT_ASSERT(map_normal[key_dict_a_b].is_empty()); CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:adee")); CPPUNIT_ASSERT(map_normal[key_dict_a_b].is_empty()); CPPUNIT_ASSERT(static_map_read_bencode(map_normal, "d1:ad1:bi1eee")); CPPUNIT_ASSERT(map_normal[key_dict_a_b].as_value() == 1); } void ObjectStaticMapTest::test_write_empty() { test_empty_type map_normal; CPPUNIT_ASSERT(static_map_write_bencode(map_normal, "de")); } void ObjectStaticMapTest::test_write_single() { test_single_type map_value; CPPUNIT_ASSERT(static_map_write_bencode(map_value, "de")); map_value[key_single_a] = (int64_t)1; CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bi1ee")); map_value[key_single_a] = "test"; CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:b4:teste")); map_value[key_single_a] = torrent::Object::create_list(); map_value[key_single_a].as_list().push_back("test"); CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bl4:testee")); map_value[key_single_a] = torrent::raw_bencode("i1e", 3); CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bi1ee")); // map_value[key_single_a] = torrent::raw_value("1", 1); // CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bi1ee")); map_value[key_single_a] = torrent::raw_string("test", 4); CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:b4:teste")); map_value[key_single_a] = torrent::raw_list("1:a2:bb", strlen("1:a2:bb")); CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bl1:a2:bbee")); map_value[key_single_a] = torrent::raw_map("1:a2:bb", strlen("1:a2:bb")); CPPUNIT_ASSERT(static_map_write_bencode(map_value, "d1:bd1:a2:bbee")); } void ObjectStaticMapTest::test_write_multiple() { // test_multiple_type map_value; // CPPUNIT_ASSERT(static_map_write_bencode(map_value, "de")); } libtorrent-0.13.6/test/torrent/object_static_map_test.h000066400000000000000000000020601257211073700233260ustar00rootroot00000000000000#include #include "torrent/object.h" class ObjectStaticMapTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ObjectStaticMapTest); CPPUNIT_TEST(test_basics); CPPUNIT_TEST(test_write); CPPUNIT_TEST(test_read); CPPUNIT_TEST(test_read_extensions); CPPUNIT_TEST(test_read_empty); CPPUNIT_TEST(test_read_single); CPPUNIT_TEST(test_read_single_raw); CPPUNIT_TEST(test_read_raw_types); CPPUNIT_TEST(test_read_multiple); CPPUNIT_TEST(test_read_dict); CPPUNIT_TEST(test_write_empty); CPPUNIT_TEST(test_write_single); CPPUNIT_TEST(test_write_multiple); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basics(); void test_write(); void test_read(); void test_read_extensions(); // Proper unit tests: void test_read_empty(); void test_read_single(); void test_read_single_raw(); void test_read_raw_types(); void test_read_multiple(); void test_read_dict(); void test_write_empty(); void test_write_single(); void test_write_multiple(); }; libtorrent-0.13.6/test/torrent/object_stream_test.cc000066400000000000000000000160601257211073700226400ustar00rootroot00000000000000#include "config.h" #define __STDC_CONSTANT_MACROS #include #include #include #include #include "object_stream_test.h" #include "object_test_utils.h" CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStreamTest); static const char* ordered_bencode = "d1:ei0e4:ipv44:XXXX4:ipv616:XXXXXXXXXXXXXXXX1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1ee13:metadata_sizei15408e1:pi16033e4:reqqi255e1:v15:uuTorrent 1.8.46:yourip4:XXXXe"; static const char* unordered_bencode = "d1:ei0e1:md11:upload_onlyi3e12:ut_holepunchi4e11:ut_metadatai2e6:ut_pexi1ee4:ipv44:XXXX4:ipv616:XXXXXXXXXXXXXXXX13:metadata_sizei15408e1:pi16033e4:reqqi255e1:v15:uuTorrent 1.8.46:yourip4:XXXXe"; static const char* string_bencode = "32:aaaaaaaabbbbbbbbccccccccdddddddd"; void ObjectStreamTest::testInputOrdered() { torrent::Object orderedObj = create_bencode(ordered_bencode); torrent::Object unorderedObj = create_bencode(unordered_bencode); CPPUNIT_ASSERT(!(orderedObj.flags() & torrent::Object::flag_unordered)); CPPUNIT_ASSERT(unorderedObj.flags() & torrent::Object::flag_unordered); } void ObjectStreamTest::testInputNullKey() { torrent::Object obj = create_bencode("d0:i1e5:filesi2ee"); CPPUNIT_ASSERT(!(obj.flags() & torrent::Object::flag_unordered)); } void ObjectStreamTest::testOutputMask() { torrent::Object normalObj = create_bencode("d1:ai1e1:bi2e1:ci3ee"); CPPUNIT_ASSERT(compare_bencode(normalObj, "d1:ai1e1:bi2e1:ci3ee")); normalObj.get_key("b").set_flags(torrent::Object::flag_session_data); normalObj.get_key("c").set_flags(torrent::Object::flag_static_data); CPPUNIT_ASSERT(compare_bencode(normalObj, "d1:ai1e1:ci3ee", torrent::Object::flag_session_data)); } // // Testing for bugs in bencode write. // // Dummy function that invalidates the buffer once called. torrent::object_buffer_t object_write_to_invalidate(void* data, torrent::object_buffer_t buffer) { return torrent::object_buffer_t(buffer.second, buffer.second); } void ObjectStreamTest::testBuffer() { char raw_buffer[16]; torrent::object_buffer_t buffer(raw_buffer, raw_buffer + 16); torrent::Object obj = create_bencode(string_bencode); object_write_bencode_c(&object_write_to_invalidate, NULL, buffer, &obj); } static const char* single_level_bencode = "d1:ai1e1:bi2e1:cl1:ai1e1:bi2eee"; void ObjectStreamTest::testReadBencodeC() { torrent::Object orderedObj = create_bencode_c(ordered_bencode); torrent::Object unorderedObj = create_bencode_c(unordered_bencode); CPPUNIT_ASSERT(!(orderedObj.flags() & torrent::Object::flag_unordered)); CPPUNIT_ASSERT(unorderedObj.flags() & torrent::Object::flag_unordered); CPPUNIT_ASSERT(compare_bencode(orderedObj, ordered_bencode)); // torrent::Object single_level = create_bencode_c(single_level_bencode); torrent::Object single_level = create_bencode_c(single_level_bencode); CPPUNIT_ASSERT(compare_bencode(single_level, single_level_bencode)); } bool object_write_bencode(const torrent::Object& obj, const char* original) { try { char buffer[1025]; char* last = torrent::object_write_bencode(buffer, buffer + 1024, &obj).first; return std::strncmp(buffer, original, std::distance(buffer, last)) == 0; } catch (torrent::bencode_error& e) { return false; } } bool object_stream_read_skip(const char* input) { try { torrent::Object tmp; return torrent::object_read_bencode_c(input, input + strlen(input), &tmp) == input + strlen(input) && torrent::object_read_bencode_skip_c(input, input + strlen(input)) == input + strlen(input); } catch (torrent::bencode_error& e) { return false; } } bool object_stream_read_skip_catch(const char* input) { try { torrent::Object tmp; torrent::object_read_bencode_c(input, input + strlen(input), &tmp); std::cout << "(N)"; return false; } catch (torrent::bencode_error& e) { } try { torrent::object_read_bencode_skip_c(input, input + strlen(input)); std::cout << "(S)"; return false; } catch (torrent::bencode_error& e) { return true; } } void ObjectStreamTest::test_read_skip() { CPPUNIT_ASSERT(object_stream_read_skip("i0e")); CPPUNIT_ASSERT(object_stream_read_skip("i9999e")); CPPUNIT_ASSERT(object_stream_read_skip("i-1e")); CPPUNIT_ASSERT(object_stream_read_skip("i-9999e")); CPPUNIT_ASSERT(object_stream_read_skip("0:")); CPPUNIT_ASSERT(object_stream_read_skip("4:test")); CPPUNIT_ASSERT(object_stream_read_skip("le")); CPPUNIT_ASSERT(object_stream_read_skip("li1ee")); CPPUNIT_ASSERT(object_stream_read_skip("llee")); CPPUNIT_ASSERT(object_stream_read_skip("ll1:a1:bel1:c1:dee")); CPPUNIT_ASSERT(object_stream_read_skip("de")); CPPUNIT_ASSERT(object_stream_read_skip("d1:ai1e1:b1:xe")); CPPUNIT_ASSERT(object_stream_read_skip("d1:ali1eee")); CPPUNIT_ASSERT(object_stream_read_skip("d1:ad1:bi1eee")); CPPUNIT_ASSERT(object_stream_read_skip("d1:md6:ut_pexi0eee")); } void ObjectStreamTest::test_read_skip_invalid() { CPPUNIT_ASSERT(object_stream_read_skip_catch("")); CPPUNIT_ASSERT(object_stream_read_skip_catch("i")); CPPUNIT_ASSERT(object_stream_read_skip_catch("1")); CPPUNIT_ASSERT(object_stream_read_skip_catch("d")); CPPUNIT_ASSERT(object_stream_read_skip_catch("i-0e")); CPPUNIT_ASSERT(object_stream_read_skip_catch("i--1e")); CPPUNIT_ASSERT(object_stream_read_skip_catch("-1")); CPPUNIT_ASSERT(object_stream_read_skip_catch("-1a")); CPPUNIT_ASSERT(object_stream_read_skip_catch("llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll" "llllllll")); } void ObjectStreamTest::test_write() { torrent::Object obj; CPPUNIT_ASSERT(object_write_bencode(torrent::Object(), "")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object((int64_t)0), "i0e")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object((int64_t)1), "i1e")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object((int64_t)-1), "i-1e")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object(INT64_C(123456789012345)), "i123456789012345e")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object(INT64_C(-123456789012345)), "i-123456789012345e")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object("test"), "4:test")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object::create_list(), "le")); CPPUNIT_ASSERT(object_write_bencode(torrent::Object::create_map(), "de")); obj = torrent::Object::create_map(); obj.as_map()["a"] = (int64_t)1; CPPUNIT_ASSERT(object_write_bencode(obj, "d1:ai1ee")); obj.as_map()["b"] = "test"; CPPUNIT_ASSERT(object_write_bencode(obj, "d1:ai1e1:b4:teste")); obj.as_map()["c"] = torrent::Object::create_list(); obj.as_map()["c"].as_list().push_back("foo"); CPPUNIT_ASSERT(object_write_bencode(obj, "d1:ai1e1:b4:test1:cl3:fooee")); obj.as_map()["c"].as_list().push_back(torrent::Object()); obj.as_map()["d"] = torrent::Object(); CPPUNIT_ASSERT(object_write_bencode(obj, "d1:ai1e1:b4:test1:cl3:fooee")); } libtorrent-0.13.6/test/torrent/object_stream_test.h000066400000000000000000000013321257211073700224760ustar00rootroot00000000000000#include #include "torrent/object_stream.h" class ObjectStreamTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ObjectStreamTest); CPPUNIT_TEST(testInputOrdered); CPPUNIT_TEST(testInputNullKey); CPPUNIT_TEST(testOutputMask); CPPUNIT_TEST(testBuffer); CPPUNIT_TEST(testReadBencodeC); CPPUNIT_TEST(test_read_skip); CPPUNIT_TEST(test_read_skip_invalid); CPPUNIT_TEST(test_write); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void testInputOrdered(); void testInputNullKey(); void testOutputMask(); void testBuffer(); void testReadBencodeC(); void test_read_skip(); void test_read_skip_invalid(); void test_write(); }; libtorrent-0.13.6/test/torrent/object_test.cc000066400000000000000000000116231257211073700212650ustar00rootroot00000000000000#include "config.h" #include #include #include "object_test.h" #include "object_test_utils.h" CPPUNIT_TEST_SUITE_REGISTRATION(ObjectTest); void object_test_return_void() { } // template // struct object_void_wrapper { // typedef // object_void_wrapper(Slot s) : m_slot(s) {} // torrent::Object operator () () { m_slot( // Slot m_slot; // } void ObjectTest::test_basic() { // std::cout << "sizeof(torrent::Object) = " << sizeof(torrent::Object) << std::endl; // std::cout << "sizeof(torrent::Object::list_type) = " << sizeof(torrent::Object::list_type) << std::endl; // std::cout << "sizeof(torrent::Object::map_type) = " << sizeof(torrent::Object::map_type) << std::endl; // torrent::Object obj_void(object_test_return_void()); } void ObjectTest::test_flags() { torrent::Object objectFlagsValue = torrent::Object(int64_t()); torrent::Object objectNoFlagsEmpty = torrent::Object(); torrent::Object objectNoFlagsValue = torrent::Object(int64_t()); objectFlagsValue.set_flags(torrent::Object::flag_static_data | torrent::Object::flag_session_data); CPPUNIT_ASSERT(objectNoFlagsEmpty.flags() == 0); CPPUNIT_ASSERT(objectNoFlagsValue.flags() == 0); CPPUNIT_ASSERT(objectFlagsValue.flags() & torrent::Object::flag_session_data && objectFlagsValue.flags() & torrent::Object::flag_static_data); objectFlagsValue.unset_flags(torrent::Object::flag_session_data); CPPUNIT_ASSERT(!(objectFlagsValue.flags() & torrent::Object::flag_session_data) && objectFlagsValue.flags() & torrent::Object::flag_static_data); } void ObjectTest::test_merge() { } #define TEST_VALUE_A "i10e" #define TEST_VALUE_B "i20e" #define TEST_STRING_A "1:g" #define TEST_STRING_B "1:h" #define TEST_MAP_A "d1:ai1e1:bi2ee" #define TEST_MAP_B "d1:ci4e1:di5ee" #define TEST_LIST_A "l1:e1:fe" #define TEST_LIST_B "li1ei2ee" static bool swap_compare(const char* left, const char* right) { torrent::Object obj_left = create_bencode(left); torrent::Object obj_right = create_bencode(right); obj_left.swap(obj_right); if (!compare_bencode(obj_left, right) || !compare_bencode(obj_right, left)) return false; obj_left.swap(obj_right); if (!compare_bencode(obj_left, left) || !compare_bencode(obj_right, right)) return false; return true; } static bool swap_compare_dict_key(const char* left_key, const char* left_obj, const char* right_key, const char* right_obj) { torrent::Object obj_left = torrent::Object::create_dict_key(); torrent::Object obj_right = torrent::Object::create_dict_key(); obj_left.as_dict_key() = left_key; obj_left.as_dict_obj() = create_bencode(left_obj); obj_right.as_dict_key() = right_key; obj_right.as_dict_obj() = create_bencode(right_obj); obj_left.swap(obj_right); if (obj_left.as_dict_key() != right_key || !compare_bencode(obj_left.as_dict_obj(), right_obj) || obj_right.as_dict_key() != left_key || !compare_bencode(obj_right.as_dict_obj(), left_obj)) return false; obj_left.swap(obj_right); if (obj_left.as_dict_key() != left_key || !compare_bencode(obj_left.as_dict_obj(), left_obj) || obj_right.as_dict_key() != right_key || !compare_bencode(obj_right.as_dict_obj(), right_obj)) return false; return true; } void ObjectTest::test_swap_and_move() { CPPUNIT_ASSERT(swap_compare(TEST_VALUE_A, TEST_VALUE_B)); CPPUNIT_ASSERT(swap_compare(TEST_STRING_A, TEST_STRING_B)); CPPUNIT_ASSERT(swap_compare(TEST_MAP_A, TEST_MAP_B)); CPPUNIT_ASSERT(swap_compare(TEST_LIST_A, TEST_LIST_B)); CPPUNIT_ASSERT(swap_compare(TEST_VALUE_A, TEST_STRING_B)); CPPUNIT_ASSERT(swap_compare(TEST_STRING_A, TEST_MAP_B)); CPPUNIT_ASSERT(swap_compare(TEST_MAP_A, TEST_LIST_B)); CPPUNIT_ASSERT(swap_compare(TEST_LIST_A, TEST_VALUE_B)); CPPUNIT_ASSERT(swap_compare("i1e", TEST_VALUE_A)); CPPUNIT_ASSERT(swap_compare("i1e", TEST_MAP_A)); CPPUNIT_ASSERT(swap_compare("i1e", TEST_LIST_A)); CPPUNIT_ASSERT(swap_compare_dict_key("a", TEST_VALUE_A, "b", TEST_STRING_B)); CPPUNIT_ASSERT(swap_compare_dict_key("a", TEST_STRING_A, "b", TEST_STRING_B)); } void ObjectTest::test_create_normal() { torrent::Object obj; CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_bencode_c("i45e")).as_value() == 45); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_bencode_c("4:test")).as_string() == "test"); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_bencode_c("li5ee")).as_list().front().as_value() == 5); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_bencode_c("d1:ai6ee")).as_map()["a"].as_value() == 6); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_string_c("test")).as_string() == "test"); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_list_c("i5ei6e")).as_list().back().as_value() == 6); CPPUNIT_ASSERT(torrent::object_create_normal(create_bencode_raw_map_c("1:ai2e1:bi3e")).as_map()["b"].as_value() == 3); } libtorrent-0.13.6/test/torrent/object_test.h000066400000000000000000000007531257211073700211310ustar00rootroot00000000000000#include #include "torrent/object.h" class ObjectTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ObjectTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_flags); CPPUNIT_TEST(test_swap_and_move); CPPUNIT_TEST(test_create_normal); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_flags(); void test_merge(); void test_swap_and_move(); void test_create_normal(); }; libtorrent-0.13.6/test/torrent/object_test_utils.cc000066400000000000000000000027061257211073700225070ustar00rootroot00000000000000#include "config.h" #include #include #include "torrent/object_stream.h" #include "object_test_utils.h" torrent::Object create_bencode(const char* str) { torrent::Object obj; std::stringstream stream(str); stream >> obj; CPPUNIT_ASSERT(!stream.fail()); return obj; } torrent::Object create_bencode_c(const char* str) { torrent::Object obj; const char* last = str + strlen(str); CPPUNIT_ASSERT(object_read_bencode_c(str, last, &obj) == last); return obj; } // torrent::Object // create_bencode_raw_bencode_c(const char* str) { // torrent::Object obj; // const char* last = str + strlen(str); // CPPUNIT_ASSERT(object_read_bencode_skip_c(str, last) == last); // return torrent::raw_bencode(str, std::distance(str, last)); // } // torrent::Object // create_bencode_raw_list_c(const char* str) { // torrent::Object obj; // const char* last = str + strlen(str); // CPPUNIT_ASSERT(object_read_bencode_skip_c(str, last) == last); // return torrent::raw_bencode(str, std::distance(str, last)); // } bool validate_bencode(const char* first, const char* last) { torrent::Object obj; return object_read_bencode_c(first, last, &obj) == last; } bool compare_bencode(const torrent::Object& obj, const char* str, uint32_t skip_mask) { char buffer[256]; std::memset(buffer, 0, 256); torrent::object_write_bencode(buffer, buffer + 256, &obj, skip_mask); return strcmp(buffer, str) == 0; } libtorrent-0.13.6/test/torrent/object_test_utils.h000066400000000000000000000014221257211073700223430ustar00rootroot00000000000000#include #include "torrent/object.h" torrent::Object create_bencode(const char* str); torrent::Object create_bencode_c(const char* str); inline torrent::Object create_bencode_raw_bencode_c(const char* str) { return torrent::raw_bencode(str, std::strlen(str)); } inline torrent::Object create_bencode_raw_string_c(const char* str) { return torrent::raw_string(str, std::strlen(str)); } inline torrent::Object create_bencode_raw_list_c(const char* str) { return torrent::raw_list(str, std::strlen(str)); } inline torrent::Object create_bencode_raw_map_c(const char* str) { return torrent::raw_map(str, std::strlen(str)); } bool validate_bencode(const char* first, const char* last); bool compare_bencode(const torrent::Object& obj, const char* str, uint32_t skip_mask = 0); libtorrent-0.13.6/test/torrent/task_manager_test.cc000066400000000000000000000052501257211073700224520ustar00rootroot00000000000000#include "config.h" #include "task_manager_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(TaskManagerTest); struct TMT_Data { TMT_Data(TaskManagerTest* m) : manager(m), counter(0) {} TaskManagerTest *manager; unsigned int counter; }; void TaskManagerTest::setUp() { } void TaskManagerTest::tearDown() { } void* TMT_Func(TMT_Data* data) { __sync_add_and_fetch(&data->counter, 1); return NULL; } void* TMT_lock_func(TMT_Data* data) { __sync_add_and_fetch(&data->counter, 1); data->manager->m_manager.acquire_global_lock(); usleep(10000); data->manager->m_manager.release_global_lock(); __sync_add_and_fetch(&data->counter, 1); return NULL; } void* TMT_waive_lock_func(TMT_Data* data) { __sync_add_and_fetch(&data->counter, 1); data->manager->m_manager.acquire_global_lock(); usleep(20000); data->manager->m_manager.waive_global_lock(); __sync_add_and_fetch(&data->counter, 1); return NULL; } inline TaskManagerTest::task_pair TaskManagerTest::create_task(const char* name, torrent::Task::pt_func func) { TMT_Data* data = new TMT_Data(this); return task_pair(m_manager.insert(name, func, data), data); } void TaskManagerTest::testInsert() { task_pair task1 = create_task("Test 1", (torrent::Task::pt_func)&TMT_Func); task_pair task2 = create_task("Test 2", (torrent::Task::pt_func)&TMT_Func); usleep(10000); CPPUNIT_ASSERT(task1.first != m_manager.end() && task2.first != m_manager.end()); CPPUNIT_ASSERT(task1.second->counter == 1 && task2.second->counter == 1); // Clean up test, check that we've taken down the threads properly. // delete data1; // delete data2; } void TaskManagerTest::testLock() { task_pair task1 = create_task("Test Lock 1", (torrent::Task::pt_func)&TMT_lock_func); task_pair task2 = create_task("Test Lock 2", (torrent::Task::pt_func)&TMT_lock_func); task_pair task3 = create_task("Test Lock 3", (torrent::Task::pt_func)&TMT_lock_func); usleep(100000); CPPUNIT_ASSERT(task1.second->counter == 2 && task2.second->counter == 2 && task3.second->counter == 2); } void TaskManagerTest::testWaiveLock() { task_pair task1 = create_task("Test Waive Lock 1", (torrent::Task::pt_func)&TMT_waive_lock_func); usleep(5000); task_pair task2 = create_task("Test Waive Lock 2", (torrent::Task::pt_func)&TMT_lock_func); task_pair task3 = create_task("Test Waive Lock 3", (torrent::Task::pt_func)&TMT_lock_func); usleep(100000); CPPUNIT_ASSERT(task1.second->counter == 2 && task2.second->counter == 2 && task3.second->counter == 2); // The global lock needs to be released as 'Test Lock 1' doesn't // release it. m_manager.release_global_lock(); } libtorrent-0.13.6/test/torrent/task_manager_test.h000066400000000000000000000011411257211073700223070ustar00rootroot00000000000000#include #include "torrent/task_manager.h" struct TMT_Data; class TaskManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TaskManagerTest); CPPUNIT_TEST(testInsert); CPPUNIT_TEST(testLock); CPPUNIT_TEST(testWaiveLock); CPPUNIT_TEST_SUITE_END(); public: typedef std::pair task_pair; void setUp(); void tearDown(); void testInsert(); void testLock(); void testWaiveLock(); inline task_pair create_task(const char* name, torrent::Task::pt_func func); torrent::TaskManager m_manager; }; libtorrent-0.13.6/test/torrent/tracker_controller_features.cc000066400000000000000000000251221257211073700245530ustar00rootroot00000000000000#include "config.h" #include #include #include "rak/priority_queue_default.h" #include "globals.h" #include "tracker_list_test.h" #include "tracker_controller_features.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_controller_features); void tracker_controller_features::setUp() { CPPUNIT_ASSERT(torrent::taskScheduler.empty()); torrent::cachedTime = rak::timer::current(); } void tracker_controller_features::tearDown() { torrent::taskScheduler.clear(); } void tracker_controller_features::test_requesting_basic() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_0_0->trigger_success(8, 9)); tracker_controller.start_requesting(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("00111", "00111"); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); CPPUNIT_ASSERT(tracker_3_0->trigger_success()); // TODO: Change this so that requesting state results in tracker // requests from many peers. Also, add a limit so we don't keep // requesting from spent trackers. // Next timeout should be soon... CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); TEST_MULTI3_IS_BUSY("00000", "00000"); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->min_interval() - 30)); TEST_MULTI3_IS_BUSY("10111", "10111"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); CPPUNIT_ASSERT(tracker_3_0->trigger_success()); tracker_controller.stop_requesting(); TEST_MULTIPLE_END(8, 0); } void tracker_controller_features::test_requesting_timeout() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); tracker_controller.start_requesting(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("10111", "10111"); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 5); // CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); CPPUNIT_ASSERT(tracker_1_0->trigger_failure()); CPPUNIT_ASSERT(tracker_2_0->trigger_failure()); CPPUNIT_ASSERT(tracker_3_0->trigger_failure()); // CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("01000", "01000"); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 5)); TEST_MULTI3_IS_BUSY("01111", "01111"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 30); TEST_MULTIPLE_END(1, 4); } void tracker_controller_features::test_promiscious_timeout() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 3)); TEST_MULTI3_IS_BUSY("10111", "10111"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::flag_promiscuous_mode)); // CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_3_0->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->normal_interval())); TEST_MULTIPLE_END(4, 0); } // Fix failure event handler so that it properly handles multi-request // situations. This includes fixing old tests. void tracker_controller_features::test_promiscious_failed() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::flag_promiscuous_mode)); TEST_MULTI3_IS_BUSY("01111", "01111"); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_3_0->trigger_failure()); torrent::cachedTime += rak::timer::from_seconds(2); CPPUNIT_ASSERT(tracker_2_0->trigger_failure()); TEST_MULTI3_IS_BUSY("01100", "01100"); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 3)); TEST_MULTI3_IS_BUSY("01101", "01101"); CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); CPPUNIT_ASSERT(tracker_1_0->trigger_failure()); CPPUNIT_ASSERT(tracker_3_0->trigger_failure()); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); TEST_MULTIPLE_END(0, 7); } void tracker_controller_features::test_scrape_basic() { TEST_GROUP_BEGIN(); tracker_controller.disable(); CPPUNIT_ASSERT(!tracker_controller.task_scrape()->is_queued()); tracker_0_1->set_can_scrape(); tracker_0_2->set_can_scrape(); tracker_2_0->set_can_scrape(); tracker_controller.scrape_request(0); TEST_GROUP_IS_BUSY("000000", "000000"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_controller.task_scrape()->is_queued()); CPPUNIT_ASSERT(tracker_0_1->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0_2->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_2_0->latest_event() == torrent::Tracker::EVENT_NONE); TEST_GOTO_NEXT_SCRAPE(0); TEST_GROUP_IS_BUSY("010001", "010001"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(!tracker_controller.task_scrape()->is_queued()); CPPUNIT_ASSERT(tracker_0_1->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_0_2->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_2_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_0_1->trigger_scrape()); CPPUNIT_ASSERT(tracker_2_0->trigger_scrape()); TEST_GROUP_IS_BUSY("000000", "000000"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(!tracker_controller.task_scrape()->is_queued()); CPPUNIT_ASSERT(tracker_0_1->scrape_time_last() != 0); CPPUNIT_ASSERT(tracker_0_2->scrape_time_last() == 0); CPPUNIT_ASSERT(tracker_2_0->scrape_time_last() != 0); TEST_MULTIPLE_END(0, 0); } void tracker_controller_features::test_scrape_priority() { TEST_SINGLE_BEGIN(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); tracker_0_0->trigger_success(); tracker_0_0->set_can_scrape(); tracker_controller.scrape_request(0); TEST_GOTO_NEXT_SCRAPE(0); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); // Check the other event types too? tracker_controller.send_update_event(); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(!tracker_controller.task_scrape()->is_queued()); tracker_0_0->trigger_success(); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() > 1); torrent::cachedTime += rak::timer::from_seconds(tracker_controller.seconds_to_next_timeout() - 1); rak::priority_queue_perform(&torrent::taskScheduler, torrent::cachedTime); tracker_controller.scrape_request(0); TEST_GOTO_NEXT_SCRAPE(0); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 1)); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_NONE); TEST_SINGLE_END(2, 0); } void tracker_controller_features::test_groups_requesting() { TEST_GROUP_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); // CPPUNIT_ASSERT(tracker_0_0->trigger_success(10, 20)); tracker_controller.start_requesting(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_GROUP_IS_BUSY("100101", "100101"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); // TODO: Change this so that requesting state results in tracker // requests from many peers. Also, add a limit so we don't keep // requesting from spent trackers. // Next timeout should be soon... CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); TEST_GROUP_IS_BUSY("000000", "000000"); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->min_interval() - 30)); TEST_GROUP_IS_BUSY("100101", "100101"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); // Once we've requested twice, it should stop requesting from that tier. CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); TEST_GROUP_IS_BUSY("000000", "000000"); tracker_controller.stop_requesting(); TEST_MULTIPLE_END(6, 0); } void tracker_controller_features::test_groups_scrape() { TEST_GROUP_BEGIN(); tracker_controller.disable(); tracker_0_0->set_can_scrape(); tracker_0_1->set_can_scrape(); tracker_0_2->set_can_scrape(); tracker_1_0->set_can_scrape(); tracker_1_1->set_can_scrape(); tracker_2_0->set_can_scrape(); CPPUNIT_ASSERT(!tracker_controller.task_scrape()->is_queued()); tracker_controller.scrape_request(0); TEST_GROUP_IS_BUSY("000000", "000000"); TEST_GOTO_NEXT_SCRAPE(0); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_0_1->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0_2->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_1_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_1_1->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_2_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); TEST_GROUP_IS_BUSY("100101", "100101"); CPPUNIT_ASSERT(tracker_0_0->trigger_scrape()); CPPUNIT_ASSERT(tracker_1_0->trigger_scrape()); CPPUNIT_ASSERT(tracker_2_0->trigger_scrape()); // Test with a non-can_scrape !busy tracker? // TEST_GROUP_IS_BUSY("100101", "100101"); // CPPUNIT_ASSERT(tracker_0_0->trigger_scrape()); // CPPUNIT_ASSERT(tracker_0_1->trigger_scrape()); // CPPUNIT_ASSERT(tracker_0_2->trigger_scrape()); // CPPUNIT_ASSERT(tracker_1_0->trigger_scrape()); // CPPUNIT_ASSERT(tracker_1_1->trigger_scrape()); // CPPUNIT_ASSERT(tracker_2_0->trigger_scrape()); TEST_GROUP_IS_BUSY("000000", "000000"); TEST_MULTIPLE_END(0, 0); } libtorrent-0.13.6/test/torrent/tracker_controller_features.h000066400000000000000000000015231257211073700244140ustar00rootroot00000000000000#include #include "tracker_controller_test.h" class tracker_controller_features : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_controller_features); CPPUNIT_TEST(test_requesting_basic); CPPUNIT_TEST(test_requesting_timeout); CPPUNIT_TEST(test_promiscious_timeout); CPPUNIT_TEST(test_promiscious_failed); CPPUNIT_TEST(test_scrape_basic); CPPUNIT_TEST(test_scrape_priority); CPPUNIT_TEST(test_groups_requesting); CPPUNIT_TEST(test_groups_scrape); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_requesting_basic(); void test_requesting_timeout(); void test_promiscious_timeout(); void test_promiscious_failed(); void test_scrape_basic(); void test_scrape_priority(); void test_groups_requesting(); void test_groups_scrape(); }; libtorrent-0.13.6/test/torrent/tracker_controller_requesting.cc000066400000000000000000000140501257211073700251210ustar00rootroot00000000000000#include "config.h" #include #include #include "rak/priority_queue_default.h" #include "globals.h" #include "tracker_list_test.h" #include "tracker_controller_requesting.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_controller_requesting); void tracker_controller_requesting::setUp() { CPPUNIT_ASSERT(torrent::taskScheduler.empty()); torrent::cachedTime = rak::timer::current(); } void tracker_controller_requesting::tearDown() { torrent::taskScheduler.clear(); } void do_test_hammering_basic(bool success1, bool success2, bool success3, uint32_t min_interval = 0) { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); if (min_interval != 0) tracker_0_0->set_new_min_interval(min_interval); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(success1 ? tracker_0_0->trigger_success() : tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == tracker_0_0->normal_interval()); CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::flag_promiscuous_mode)); tracker_controller.start_requesting(); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::flag_requesting)); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->min_interval())); CPPUNIT_ASSERT(tracker_0_0->is_busy()); if (success2) { CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->min_interval() - 30)); } else { CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 5)); CPPUNIT_ASSERT(tracker_0_0->is_busy()); } tracker_controller.stop_requesting(); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(success3 ? tracker_0_0->trigger_success() : tracker_0_0->trigger_failure()); TEST_SINGLE_END(success1 + success2 + success3, !success1 + !success2 + !success3); } void tracker_controller_requesting::test_hammering_basic_success() { do_test_hammering_basic(true, true, true); } void tracker_controller_requesting::test_hammering_basic_success_long_timeout() { do_test_hammering_basic(true, true, true, 1000); } void tracker_controller_requesting::test_hammering_basic_success_short_timeout() { do_test_hammering_basic(true, true, true, 300); } void tracker_controller_requesting::test_hammering_basic_failure() { do_test_hammering_basic(true, false, false); } void tracker_controller_requesting::test_hammering_basic_failure_long_timeout() { do_test_hammering_basic(true, false, false, 1000); } void tracker_controller_requesting::test_hammering_basic_failure_short_timeout() { do_test_hammering_basic(true, false, false, 300); } // Differentiate between failure connection / http error and tracker returned error. void do_test_hammering_multi3(bool success1, bool success2, bool success3, uint32_t min_interval = 0) { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); if (min_interval != 0) tracker_0_0->set_new_min_interval(min_interval); TEST_MULTI3_IS_BUSY("10000", "10000"); CPPUNIT_ASSERT(success1 ? tracker_0_0->trigger_success() : tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == tracker_0_0->normal_interval()); CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::flag_promiscuous_mode)); tracker_controller.start_requesting(); TEST_MULTI3_IS_BUSY("00000", "00000"); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("00111", "00111"); if (success2) CPPUNIT_ASSERT(tracker_2_0->trigger_success()); else CPPUNIT_ASSERT(tracker_2_0->trigger_failure()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); TEST_MULTI3_IS_BUSY("00101", "00101"); TrackerTest* next_tracker = tracker_0_0; unsigned int next_timeout = next_tracker->min_interval(); const char* next_is_busy = "10111"; if (tracker_0_0->min_interval() < tracker_2_0->min_interval()) { CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, next_tracker->min_interval() - 30)); TEST_MULTI3_IS_BUSY("10101", "10101"); } else if (tracker_0_0->min_interval() > tracker_2_0->min_interval()) { next_tracker = tracker_2_0; next_timeout = tracker_0_0->min_interval() - tracker_2_0->min_interval(); next_is_busy = "10101"; CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, next_tracker->min_interval() - 30)); TEST_MULTI3_IS_BUSY("00111", "00111"); } else { CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, next_tracker->min_interval() - 30)); TEST_MULTI3_IS_BUSY("10111", "10111"); } if (success2) { CPPUNIT_ASSERT(next_tracker->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 30)); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, next_timeout - 30)); TEST_MULTI3_IS_BUSY(next_is_busy, next_is_busy); } else { CPPUNIT_ASSERT(next_tracker->trigger_failure()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 5)); TEST_MULTI3_IS_BUSY("10000", "10000"); } tracker_controller.stop_requesting(); TEST_MULTI3_IS_BUSY(next_is_busy, next_is_busy); CPPUNIT_ASSERT(success3 ? tracker_0_0->trigger_success() : tracker_0_0->trigger_failure()); TEST_MULTIPLE_END(success1 + 2*success2 + success3, !success1 + 2*!success2 + !success3); } void tracker_controller_requesting::test_hammering_multi_success() { do_test_hammering_multi3(true, true, true); } void tracker_controller_requesting::test_hammering_multi_success_long_timeout() { do_test_hammering_multi3(true, true, true, 1000); } void tracker_controller_requesting::test_hammering_multi_success_short_timeout() { do_test_hammering_multi3(true, true, true, 300); } void tracker_controller_requesting::test_hammering_multi_failure() { } libtorrent-0.13.6/test/torrent/tracker_controller_requesting.h000066400000000000000000000027671257211073700247770ustar00rootroot00000000000000#include #include "tracker_controller_test.h" class tracker_controller_requesting : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_controller_requesting); CPPUNIT_TEST(test_hammering_basic_success); CPPUNIT_TEST(test_hammering_basic_success_long_timeout); CPPUNIT_TEST(test_hammering_basic_success_short_timeout); CPPUNIT_TEST(test_hammering_basic_failure); CPPUNIT_TEST(test_hammering_basic_failure_long_timeout); CPPUNIT_TEST(test_hammering_basic_failure_short_timeout); CPPUNIT_TEST(test_hammering_multi_success); CPPUNIT_TEST(test_hammering_multi_success_long_timeout); CPPUNIT_TEST(test_hammering_multi_success_short_timeout); CPPUNIT_TEST(test_hammering_multi_failure); // CPPUNIT_TEST(test_hammering_multi_failure_long_timeout); // CPPUNIT_TEST(test_hammering_multi_failure_short_timeout); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_hammering_basic_success(); void test_hammering_basic_success_long_timeout(); void test_hammering_basic_success_short_timeout(); void test_hammering_basic_failure(); void test_hammering_basic_failure_long_timeout(); void test_hammering_basic_failure_short_timeout(); void test_hammering_multi_success(); void test_hammering_multi_success_long_timeout(); void test_hammering_multi_success_short_timeout(); void test_hammering_multi_failure(); void test_hammering_multi_failure_long_timeout(); void test_hammering_multi_failure_short_timeout(); }; libtorrent-0.13.6/test/torrent/tracker_controller_test.cc000066400000000000000000000442371257211073700237240ustar00rootroot00000000000000#include "config.h" #include #include #include "rak/priority_queue_default.h" #include "globals.h" #include "tracker_list_test.h" #include "tracker_controller_test.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_controller_test); bool test_goto_next_timeout(torrent::TrackerController* tracker_controller, uint32_t assumed_timeout, bool is_scrape) { uint32_t next_timeout = tracker_controller->task_timeout()->is_queued() ? tracker_controller->seconds_to_next_timeout() : ~uint32_t(); uint32_t next_scrape = tracker_controller->task_scrape()->is_queued() ? tracker_controller->seconds_to_next_scrape() : ~uint32_t(); if (next_timeout == next_scrape && next_timeout == ~uint32_t()) { std::cout << "(nq)"; return false; } if (next_timeout < next_scrape) { if (is_scrape) { std::cout << "(t" << next_timeout << "is_busy()); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); tracker_controller.enable(); rak::priority_queue_perform(&torrent::taskScheduler, torrent::cachedTime); CPPUNIT_ASSERT(timeout_counter == 1); TEST_SINGLE_END(0, 0); } void tracker_controller_test::test_single_success() { TEST_SINGLE_BEGIN(); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(success_counter == 1 && failure_counter == 0); tracker_list.send_state_idx(0, 2); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); TEST_SINGLE_END(1, 1); } #define TEST_SINGLE_FAILURE_TIMEOUT(test_interval) \ rak::priority_queue_perform(&torrent::taskScheduler, torrent::cachedTime); \ CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); \ CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == test_interval); \ torrent::cachedTime += rak::timer::from_seconds(test_interval); void tracker_controller_test::test_single_failure() { torrent::cachedTime = rak::timer::from_seconds(1 << 20); TEST_SINGLE_BEGIN(); TEST_SINGLE_FAILURE_TIMEOUT(5); TEST_SINGLE_FAILURE_TIMEOUT(10); TEST_SINGLE_FAILURE_TIMEOUT(20); TEST_SINGLE_FAILURE_TIMEOUT(40); TEST_SINGLE_FAILURE_TIMEOUT(80); TEST_SINGLE_FAILURE_TIMEOUT(160); TEST_SINGLE_FAILURE_TIMEOUT(320); TEST_SINGLE_FAILURE_TIMEOUT(320); // TODO: Test with cachedTime not rounded to second. // Test also with a low timer value... // torrent::cachedTime = rak::timer::from_seconds(1000); TEST_SINGLE_END(0, 4); } void tracker_controller_test::test_single_disable() { TEST_SINGLE_BEGIN(); tracker_list.send_state_idx(0, 0); TEST_SINGLE_END(0, 0); } void tracker_controller_test::test_send_start() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(start); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_0->requesting_state() == torrent::Tracker::EVENT_STARTED); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::mask_send)); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() != 0); tracker_controller.send_start_event(); tracker_controller.disable(); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); TEST_SEND_SINGLE_END(1, 0); } void tracker_controller_test::test_send_stop_normal() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); tracker_controller.send_stop_event(); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == torrent::TrackerController::flag_send_stop); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 0); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == 0); tracker_controller.send_stop_event(); tracker_controller.disable(); CPPUNIT_ASSERT(tracker_0_0->is_busy()); tracker_0_0->trigger_success(); TEST_SEND_SINGLE_END(3, 0); } // send_stop during request and right after start, send stop failed. void tracker_controller_test::test_send_completed_normal() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); tracker_controller.send_completed_event(); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == torrent::TrackerController::flag_send_completed); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 0); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == 0); tracker_controller.send_completed_event(); tracker_controller.disable(); CPPUNIT_ASSERT(tracker_0_0->is_busy()); tracker_0_0->trigger_success(); TEST_SEND_SINGLE_END(3, 0); } void tracker_controller_test::test_send_update_normal() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == torrent::TrackerController::flag_send_update); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->latest_event() == torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); TEST_SEND_SINGLE_END(1, 0); } void tracker_controller_test::test_send_update_failure() { torrent::cachedTime = rak::timer::from_seconds(1 << 20); TEST_SINGLE_BEGIN(); tracker_controller.send_update_event(); TEST_SINGLE_FAILURE_TIMEOUT(5); TEST_SINGLE_FAILURE_TIMEOUT(10); TEST_SINGLE_END(0, 2); } void tracker_controller_test::test_send_task_timeout() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); TEST_SEND_SINGLE_END(0, 0); } void tracker_controller_test::test_send_close_on_enable() { TRACKER_CONTROLLER_SETUP(); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(0, tracker_2); TRACKER_INSERT(0, tracker_3); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(1, torrent::Tracker::EVENT_STARTED); tracker_list.send_state_idx(2, torrent::Tracker::EVENT_STOPPED); tracker_list.send_state_idx(3, torrent::Tracker::EVENT_COMPLETED); tracker_controller.enable(); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_1->is_busy()); CPPUNIT_ASSERT(!tracker_2->is_busy()); CPPUNIT_ASSERT(tracker_3->is_busy()); } void tracker_controller_test::test_multiple_success() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->normal_interval())); TEST_MULTI3_IS_BUSY("10000", "10000"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->normal_interval())); TEST_MULTI3_IS_BUSY("10000", "10000"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(tracker_0_0->success_counter() == 3); TEST_MULTIPLE_END(3, 0); } void tracker_controller_test::test_multiple_failure() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(!tracker_controller.is_failure_mode()); TEST_MULTI3_IS_BUSY("01000", "01000"); CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); TEST_MULTI3_IS_BUSY("00100", "00100"); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_0->normal_interval())); TEST_MULTI3_IS_BUSY("10000", "10000"); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); TEST_MULTI3_IS_BUSY("01000", "01000"); CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); CPPUNIT_ASSERT(!tracker_controller.is_failure_mode()); TEST_MULTI3_IS_BUSY("00100", "00100"); CPPUNIT_ASSERT(tracker_1_0->trigger_failure()); CPPUNIT_ASSERT(tracker_controller.is_failure_mode()); TEST_MULTI3_IS_BUSY("00010", "00010"); CPPUNIT_ASSERT(tracker_2_0->trigger_failure()); TEST_MULTI3_IS_BUSY("00001", "00001"); CPPUNIT_ASSERT(tracker_3_0->trigger_failure()); // Properly tests that we're going into timeouts... Disable remaining trackers. // for (int i = 0; i < 5; i++) // std::cout << std::endl << i << ": " // << tracker_list[i]->activity_time_last() << ' ' // << tracker_list[i]->success_time_last() << ' ' // << tracker_list[i]->failed_time_last() << std::endl; // Try inserting some delays in order to test the timers. CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 5)); TEST_MULTI3_IS_BUSY("00100", "00100"); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(!tracker_controller.is_failure_mode()); // CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_list[0]->normal_interval())); // TEST_MULTI3_IS_BUSY("01000", "10000"); // CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_list.count_active() == 0); TEST_MULTIPLE_END(2, 7); } void tracker_controller_test::test_multiple_cycle() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_list.front() == tracker_0_1); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, tracker_0_1->normal_interval())); TEST_MULTI3_IS_BUSY("01000", "10000"); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_list.count_active() == 0); TEST_MULTIPLE_END(2, 1); } void tracker_controller_test::test_multiple_cycle_second_group() { TEST_MULTI3_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_0_0->trigger_failure()); CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); CPPUNIT_ASSERT(tracker_1_0->trigger_success()); CPPUNIT_ASSERT(tracker_list[0] == tracker_0_0); CPPUNIT_ASSERT(tracker_list[1] == tracker_0_1); CPPUNIT_ASSERT(tracker_list[2] == tracker_1_0); CPPUNIT_ASSERT(tracker_list[3] == tracker_2_0); CPPUNIT_ASSERT(tracker_list[4] == tracker_3_0); TEST_MULTIPLE_END(1, 2); } void tracker_controller_test::test_multiple_send_stop() { TEST_MULTI3_BEGIN(); tracker_list.send_state_idx(1, torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(3, torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(4, torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); CPPUNIT_ASSERT(tracker_3_0->trigger_success()); CPPUNIT_ASSERT(tracker_list.count_active() == 0); tracker_controller.send_stop_event(); CPPUNIT_ASSERT(tracker_list.count_active() == 3); TEST_MULTI3_IS_BUSY("01011", "10011"); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) != torrent::TrackerController::flag_send_stop); CPPUNIT_ASSERT(tracker_2_0->trigger_success()); CPPUNIT_ASSERT(tracker_3_0->trigger_success()); CPPUNIT_ASSERT(tracker_list.count_active() == 0); tracker_controller.send_stop_event(); tracker_controller.disable(); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == torrent::TrackerController::flag_send_stop); TEST_MULTI3_IS_BUSY("01011", "10011"); tracker_controller.enable(torrent::TrackerController::enable_dont_reset_stats); TEST_MULTI3_IS_BUSY("00000", "00000"); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == 0); tracker_controller.send_stop_event(); TEST_MULTI3_IS_BUSY("01011", "10011"); tracker_controller.disable(); tracker_controller.enable(); TEST_MULTI3_IS_BUSY("00000", "00000"); CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == 0); tracker_controller.send_stop_event(); TEST_MULTI3_IS_BUSY("00000", "00000"); // Test also that after closing the tracker controller, and // reopening it a 'send stop' event causes no tracker to be busy. TEST_MULTIPLE_END(6, 0); } void tracker_controller_test::test_multiple_send_update() { TEST_MULTI3_BEGIN(); tracker_controller.send_update_event(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("10000", "10000"); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); tracker_0_0->set_failed(1, torrent::cachedTime.seconds()); tracker_controller.send_update_event(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("01000", "01000"); CPPUNIT_ASSERT(tracker_0_1->trigger_failure()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("00100", "00100"); TEST_MULTIPLE_END(2, 0); } // Test timeout called, no usable trackers at all which leads to // disabling the timeout. // // The enable/disable tracker functions will poke the tracker // controller, ensuring the tast timeout gets re-started. void tracker_controller_test::test_timeout_lacking_usable() { TEST_MULTI3_BEGIN(); std::for_each(tracker_list.begin(), tracker_list.end(), std::mem_fun(&torrent::Tracker::disable)); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("00000", "00000"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); tracker_1_0->enable(); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 0); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); TEST_MULTI3_IS_BUSY("00100", "00100"); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); tracker_3_0->enable(); CPPUNIT_ASSERT(!tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(enabled_counter == 5 + 2 && disabled_counter == 5); TEST_MULTIPLE_END(0, 0); } void tracker_controller_test::test_disable_tracker() { TEST_SINGLE_BEGIN(); TEST_SEND_SINGLE_BEGIN(update); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); CPPUNIT_ASSERT(tracker_0_0->is_busy()); tracker_0_0->disable(); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_controller.task_timeout()->is_queued()); TEST_SINGLE_END(0, 0); } void tracker_controller_test::test_new_peers() { TRACKER_CONTROLLER_SETUP(); TRACKER_INSERT(0, tracker_0); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0->trigger_success(10)); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 10); tracker_controller.enable(); CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, 0)); CPPUNIT_ASSERT(tracker_0->trigger_success(20)); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 20); } // Add new function for finding the first tracker that will time out, // e.g. both with failure mode and normal rerequesting. // Make sure that adding a new tracker to a tracker list with no // usable tracker restarts the tracker requests. // Test timeout called, usable trackers exist but could not be called // at this moment, thus leading to no active trackers. (Rather, clean // up the 'is_usable()' functions, and make it a new function that is // independent of enabled, or is itself manipulating/dependent on // enabled) // Add checks to ensure we don't prematurely do timeout on a tracker // after disabling the lead one. // Make sure that we always remain with timeout, even if we send a // request that somehow doesn't actually activate any // trackers. e.g. check all send_tracker_itr uses. // Add test for promiscious mode while with a single tracker? // Test send_* with controller not enabled. // Test cleanup after disable. // Test cleanup of timers at disable (and empty timers at enable). // Test trying to send_start twice, etc. // Test send_start promiscious... // - Make sure we check that no timer is inserted while still having active trackers. // - Calculate the next timeout according to a list of in-use trackers, with the first timeout as the interval. // Test clearing of recv/failed counter on trackers. libtorrent-0.13.6/test/torrent/tracker_controller_test.h000066400000000000000000000171251257211073700235620ustar00rootroot00000000000000#include #include "torrent/tracker_controller.h" class tracker_controller_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_controller_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_enable); CPPUNIT_TEST(test_requesting); CPPUNIT_TEST(test_timeout); CPPUNIT_TEST(test_single_success); CPPUNIT_TEST(test_single_failure); CPPUNIT_TEST(test_single_disable); CPPUNIT_TEST(test_send_start); CPPUNIT_TEST(test_send_stop_normal); CPPUNIT_TEST(test_send_completed_normal); CPPUNIT_TEST(test_send_update_normal); CPPUNIT_TEST(test_send_update_failure); CPPUNIT_TEST(test_send_task_timeout); CPPUNIT_TEST(test_send_close_on_enable); CPPUNIT_TEST(test_multiple_success); CPPUNIT_TEST(test_multiple_failure); CPPUNIT_TEST(test_multiple_cycle); CPPUNIT_TEST(test_multiple_cycle_second_group); CPPUNIT_TEST(test_multiple_send_stop); CPPUNIT_TEST(test_timeout_lacking_usable); CPPUNIT_TEST(test_disable_tracker); CPPUNIT_TEST(test_new_peers); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_enable(); void test_disable(); void test_requesting(); void test_timeout(); void test_single_success(); void test_single_failure(); void test_single_disable(); void test_send_start(); void test_send_stop_normal(); void test_send_completed_normal(); void test_send_update_normal(); void test_send_update_failure(); void test_send_task_timeout(); void test_send_close_on_enable(); void test_multiple_success(); void test_multiple_failure(); void test_multiple_cycle(); void test_multiple_cycle_second_group(); void test_multiple_send_stop(); void test_multiple_send_update(); void test_timeout_lacking_usable(); void test_disable_tracker(); void test_new_peers(); }; #define TRACKER_CONTROLLER_SETUP() \ torrent::TrackerList tracker_list; \ torrent::TrackerController tracker_controller(&tracker_list); \ \ int success_counter = 0; \ int failure_counter = 0; \ int timeout_counter = 0; \ int enabled_counter = 0; \ int disabled_counter = 0; \ \ tracker_controller.slot_success() = tr1::bind(&increment_value, &success_counter); \ tracker_controller.slot_failure() = tr1::bind(&increment_value, &failure_counter); \ tracker_controller.slot_timeout() = tr1::bind(&increment_value, &timeout_counter); \ tracker_controller.slot_tracker_enabled() = tr1::bind(&increment_value, &enabled_counter); \ tracker_controller.slot_tracker_disabled() = tr1::bind(&increment_value, &disabled_counter); \ \ tracker_list.slot_success() = tr1::bind(&torrent::TrackerController::receive_success, &tracker_controller, tr1::placeholders::_1, tr1::placeholders::_2); \ tracker_list.slot_failure() = tr1::bind(&torrent::TrackerController::receive_failure, &tracker_controller, tr1::placeholders::_1, tr1::placeholders::_2); \ tracker_list.slot_tracker_enabled() = tr1::bind(&torrent::TrackerController::receive_tracker_enabled, &tracker_controller, tr1::placeholders::_1); \ tracker_list.slot_tracker_disabled() = tr1::bind(&torrent::TrackerController::receive_tracker_disabled, &tracker_controller, tr1::placeholders::_1); #define TEST_SINGLE_BEGIN() \ TRACKER_CONTROLLER_SETUP(); \ TRACKER_INSERT(0, tracker_0_0); \ \ tracker_controller.enable(); \ CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::mask_send)); \ #define TEST_SINGLE_END(succeeded, failed) \ tracker_controller.disable(); \ CPPUNIT_ASSERT(!tracker_list.has_active()); \ CPPUNIT_ASSERT(success_counter == succeeded && \ failure_counter == failure_counter); #define TEST_SEND_SINGLE_BEGIN(event_name) \ tracker_controller.send_##event_name##_event(); \ CPPUNIT_ASSERT((tracker_controller.flags() & torrent::TrackerController::mask_send) == \ torrent::TrackerController::flag_send_##event_name); \ \ CPPUNIT_ASSERT(tracker_controller.is_active()); \ CPPUNIT_ASSERT(tracker_controller.tracker_list()->count_active() == 1); #define TEST_SEND_SINGLE_END(succeeded, failed) \ TEST_SINGLE_END(succeeded, failed) \ CPPUNIT_ASSERT(tracker_controller.seconds_to_next_timeout() == 0); \ //CPPUNIT_ASSERT(tracker_controller.seconds_to_promicious_mode() != 0); #define TEST_MULTI3_BEGIN() \ TRACKER_CONTROLLER_SETUP(); \ TRACKER_INSERT(0, tracker_0_0); \ TRACKER_INSERT(0, tracker_0_1); \ TRACKER_INSERT(1, tracker_1_0); \ TRACKER_INSERT(2, tracker_2_0); \ TRACKER_INSERT(3, tracker_3_0); \ \ tracker_controller.enable(); \ CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::mask_send)); \ #define TEST_GROUP_BEGIN() \ TRACKER_CONTROLLER_SETUP(); \ TRACKER_INSERT(0, tracker_0_0); \ TRACKER_INSERT(0, tracker_0_1); \ TRACKER_INSERT(0, tracker_0_2); \ TRACKER_INSERT(1, tracker_1_0); \ TRACKER_INSERT(1, tracker_1_1); \ TRACKER_INSERT(2, tracker_2_0); \ \ tracker_controller.enable(); \ CPPUNIT_ASSERT(!(tracker_controller.flags() & torrent::TrackerController::mask_send)); \ #define TEST_MULTIPLE_END(succeeded, failed) \ tracker_controller.disable(); \ CPPUNIT_ASSERT(!tracker_list.has_active()); \ CPPUNIT_ASSERT(success_counter == succeeded && \ failure_counter == failed); #define TEST_GOTO_NEXT_SCRAPE(assumed_scrape) \ CPPUNIT_ASSERT(tracker_controller.task_scrape()->is_queued()); \ CPPUNIT_ASSERT(assumed_scrape == tracker_controller.seconds_to_next_scrape()); \ CPPUNIT_ASSERT(test_goto_next_timeout(&tracker_controller, assumed_scrape, true)); bool test_goto_next_timeout(torrent::TrackerController* tracker_controller, uint32_t assumed_timeout, bool is_scrape = false); libtorrent-0.13.6/test/torrent/tracker_list_features_test.cc000066400000000000000000000213211257211073700243770ustar00rootroot00000000000000#include "config.h" #include #include "torrent/http.h" #include "net/address_list.h" #include "globals.h" #include "tracker_list_test.h" #include "tracker_list_features_test.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_list_features_test); void tracker_list_features_test::setUp() { CPPUNIT_ASSERT(torrent::taskScheduler.empty()); torrent::cachedTime = rak::timer::current(); } void tracker_list_features_test::tearDown() { } void tracker_list_features_test::test_new_peers() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 0); CPPUNIT_ASSERT(tracker_0->latest_sum_peers() == 0); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0->trigger_success(10, 20)); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 10); CPPUNIT_ASSERT(tracker_0->latest_sum_peers() == 20); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(tracker_0->trigger_failure()); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 10); CPPUNIT_ASSERT(tracker_0->latest_sum_peers() == 20); tracker_list.clear_stats(); CPPUNIT_ASSERT(tracker_0->latest_new_peers() == 0); CPPUNIT_ASSERT(tracker_0->latest_sum_peers() == 0); } // test last_connect timer. // test has_active, and then clean up TrackerManager. void tracker_list_features_test::test_has_active() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0_0); TRACKER_INSERT(0, tracker_0_1); TRACKER_INSERT(1, tracker_1_0); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_list.has_active()); CPPUNIT_ASSERT(tracker_list.has_active_not_scrape()); tracker_0_0->trigger_success(); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); tracker_list.send_state_idx(2, 1); CPPUNIT_ASSERT(tracker_list.has_active()); tracker_1_0->trigger_success(); CPPUNIT_ASSERT(!tracker_list.has_active()); // Test multiple active trackers. tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_list.has_active()); tracker_list.send_state_idx(1, 1); tracker_0_0->trigger_success(); CPPUNIT_ASSERT(tracker_list.has_active()); tracker_0_1->trigger_success(); CPPUNIT_ASSERT(!tracker_list.has_active()); tracker_1_0->set_can_scrape(); tracker_list.send_scrape(tracker_1_0); CPPUNIT_ASSERT(tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); } void tracker_list_features_test::test_find_next_to_request() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(0, tracker_2); TRACKER_INSERT(0, tracker_3); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin()); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin() + 1) == tracker_list.begin() + 1); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.end()) == tracker_list.end()); tracker_0->disable(); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 1); tracker_0->enable(); tracker_0->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 1); tracker_1->set_failed(1, torrent::cachedTime.seconds() - 0); tracker_2->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 3); tracker_3->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 0); tracker_0->set_failed(1, torrent::cachedTime.seconds() - 3); tracker_1->set_failed(1, torrent::cachedTime.seconds() - 2); tracker_2->set_failed(1, torrent::cachedTime.seconds() - 4); tracker_3->set_failed(1, torrent::cachedTime.seconds() - 2); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 2); tracker_1->set_failed(0, torrent::cachedTime.seconds() - 1); tracker_1->set_success(1, torrent::cachedTime.seconds() - 1); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 0); tracker_1->set_success(1, torrent::cachedTime.seconds() - (tracker_1->normal_interval() - 1)); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 1); } void tracker_list_features_test::test_find_next_to_request_groups() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(1, tracker_2); TRACKER_INSERT(1, tracker_3); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin()); tracker_0->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 1); tracker_1->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 2); tracker_2->set_failed(1, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 3); tracker_1->set_failed(0, torrent::cachedTime.seconds() - 0); CPPUNIT_ASSERT(tracker_list.find_next_to_request(tracker_list.begin()) == tracker_list.begin() + 1); } void tracker_list_features_test::test_count_active() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0_0); TRACKER_INSERT(0, tracker_0_1); TRACKER_INSERT(1, tracker_1_0); TRACKER_INSERT(2, tracker_2_0); CPPUNIT_ASSERT(tracker_list.count_active() == 0); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_list.count_active() == 1); tracker_list.send_state_idx(3, 1); CPPUNIT_ASSERT(tracker_list.count_active() == 2); tracker_list.send_state_idx(1, 1); tracker_list.send_state_idx(2, 1); CPPUNIT_ASSERT(tracker_list.count_active() == 4); tracker_0_0->trigger_success(); CPPUNIT_ASSERT(tracker_list.count_active() == 3); tracker_0_1->trigger_success(); tracker_2_0->trigger_success(); CPPUNIT_ASSERT(tracker_list.count_active() == 1); tracker_1_0->trigger_success(); CPPUNIT_ASSERT(tracker_list.count_active() == 0); } // Add separate functions for sending state to multiple trackers... bool verify_did_internal_error(tr1::function func, bool should_throw) { bool did_throw = false; try { func(); } catch (torrent::internal_error& e) { did_throw = true; } return should_throw == did_throw; } void tracker_list_features_test::test_request_safeguard() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(0, tracker_2); TRACKER_INSERT(0, tracker_3); TRACKER_INSERT(0, tracker_foo); for (unsigned int i = 0; i < 9; i++) { CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_1, 1), false)); CPPUNIT_ASSERT(tracker_1->trigger_success()); CPPUNIT_ASSERT(tracker_1->success_counter() == (i + 1)); } CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_1, 1), true)); CPPUNIT_ASSERT(tracker_1->trigger_success()); torrent::cachedTime += rak::timer::from_seconds(1000); for (unsigned int i = 0; i < 9; i++) { CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_foo, 1), false)); CPPUNIT_ASSERT(tracker_foo->trigger_success()); CPPUNIT_ASSERT(tracker_foo->success_counter() == (i + 1)); CPPUNIT_ASSERT(tracker_foo->is_usable()); } CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_foo, 1), true)); CPPUNIT_ASSERT(tracker_foo->trigger_success()); for (unsigned int i = 0; i < 40; i++) { CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_2, 1), false)); CPPUNIT_ASSERT(tracker_2->trigger_success()); CPPUNIT_ASSERT(tracker_2->success_counter() == (i + 1)); torrent::cachedTime += rak::timer::from_seconds(1); } for (unsigned int i = 0; i < 17; i++) { CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_3, 1), false)); CPPUNIT_ASSERT(tracker_3->trigger_success()); CPPUNIT_ASSERT(tracker_3->success_counter() == (i + 1)); if (i % 2) torrent::cachedTime += rak::timer::from_seconds(1); } CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::TrackerList::send_state, &tracker_list, tracker_3, 1), true)); CPPUNIT_ASSERT(tracker_3->trigger_success()); } libtorrent-0.13.6/test/torrent/tracker_list_features_test.h000066400000000000000000000012271257211073700242440ustar00rootroot00000000000000#include class tracker_list_features_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_list_features_test); CPPUNIT_TEST(test_new_peers); CPPUNIT_TEST(test_has_active); CPPUNIT_TEST(test_find_next_to_request); CPPUNIT_TEST(test_find_next_to_request_groups); CPPUNIT_TEST(test_count_active); CPPUNIT_TEST(test_request_safeguard); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_new_peers(); void test_has_active(); void test_find_next_to_request(); void test_find_next_to_request_groups(); void test_count_active(); void test_request_safeguard(); }; libtorrent-0.13.6/test/torrent/tracker_list_test.cc000066400000000000000000000426021257211073700225060ustar00rootroot00000000000000#include "config.h" #include "torrent/http.h" #include "net/address_list.h" #include "globals.h" #include "tracker_list_test.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_list_test); uint32_t return_new_peers = 0xdeadbeef; class http_get : public torrent::Http { public: ~http_get() { } // Start must never throw on bad input. Calling start/stop on an // object in the wrong state should throw a torrent::internal_error. void start() { } void close() { } }; torrent::Http* http_factory() { return new http_get; } bool TrackerTest::trigger_success(uint32_t new_peers, uint32_t sum_peers) { torrent::TrackerList::address_list address_list; for (unsigned int i = 0; i != sum_peers; i++) { rak::socket_address sa; sa.sa_inet()->clear(); sa.sa_inet()->set_port(0x100 + i); address_list.push_back(sa); } return trigger_success(&address_list, new_peers); } bool TrackerTest::trigger_success(torrent::TrackerList::address_list* address_list, uint32_t new_peers) { if (parent() == NULL || !is_busy() || !is_open()) return false; m_busy = false; m_open = !(m_flags & flag_close_on_done); return_new_peers = new_peers; if (m_latest_event == EVENT_SCRAPE) parent()->receive_scrape_success(this); else parent()->receive_success(this, address_list); m_requesting_state = -1; return true; } bool TrackerTest::trigger_failure() { if (parent() == NULL || !is_busy() || !is_open()) return false; m_busy = false; m_open = !(m_flags & flag_close_on_done); return_new_peers = 0; if (m_latest_event == EVENT_SCRAPE) parent()->receive_scrape_failed(this, "failed"); else parent()->receive_failed(this, "failed"); m_requesting_state = -1; return true; } bool TrackerTest::trigger_scrape() { if (parent() == NULL || !is_busy() || !is_open()) return false; if (m_latest_event != EVENT_SCRAPE) return false; return trigger_success(); } void tracker_list_test::test_basic() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); CPPUNIT_ASSERT(tracker_0 == tracker_list[0]); CPPUNIT_ASSERT(tracker_list[0]->parent() == &tracker_list); CPPUNIT_ASSERT(std::distance(tracker_list.begin_group(0), tracker_list.end_group(0)) == 1); CPPUNIT_ASSERT(tracker_list.find_usable(tracker_list.begin()) != tracker_list.end()); } void tracker_list_test::test_enable() { TRACKER_SETUP(); int enabled_counter = 0; int disabled_counter = 0; tracker_list.slot_tracker_enabled() = tr1::bind(&increment_value, &enabled_counter); tracker_list.slot_tracker_disabled() = tr1::bind(&increment_value, &disabled_counter); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(1, tracker_1); CPPUNIT_ASSERT(enabled_counter == 2 && disabled_counter == 0); tracker_0->enable(); tracker_1->enable(); CPPUNIT_ASSERT(enabled_counter == 2 && disabled_counter == 0); tracker_0->disable(); tracker_1->enable(); CPPUNIT_ASSERT(enabled_counter == 2 && disabled_counter == 1); tracker_1->disable(); tracker_0->disable(); CPPUNIT_ASSERT(enabled_counter == 2 && disabled_counter == 2); tracker_0->enable(); tracker_1->enable(); tracker_0->enable(); tracker_1->enable(); CPPUNIT_ASSERT(enabled_counter == 4 && disabled_counter == 2); } void tracker_list_test::test_close() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(0, tracker_2); TRACKER_INSERT(0, tracker_3); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(1, torrent::Tracker::EVENT_STARTED); tracker_list.send_state_idx(2, torrent::Tracker::EVENT_STOPPED); tracker_list.send_state_idx(3, torrent::Tracker::EVENT_COMPLETED); CPPUNIT_ASSERT(tracker_0->is_busy()); CPPUNIT_ASSERT(tracker_1->is_busy()); CPPUNIT_ASSERT(tracker_2->is_busy()); CPPUNIT_ASSERT(tracker_3->is_busy()); tracker_list.close_all_excluding((1 << torrent::Tracker::EVENT_STARTED) | (1 << torrent::Tracker::EVENT_STOPPED)); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(tracker_1->is_busy()); CPPUNIT_ASSERT(tracker_2->is_busy()); CPPUNIT_ASSERT(!tracker_3->is_busy()); tracker_list.close_all(); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_1->is_busy()); CPPUNIT_ASSERT(!tracker_2->is_busy()); CPPUNIT_ASSERT(!tracker_3->is_busy()); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(1, torrent::Tracker::EVENT_STARTED); tracker_list.send_state_idx(2, torrent::Tracker::EVENT_STOPPED); tracker_list.send_state_idx(3, torrent::Tracker::EVENT_COMPLETED); tracker_list.close_all_excluding((1 << torrent::Tracker::EVENT_NONE) | (1 << torrent::Tracker::EVENT_COMPLETED)); CPPUNIT_ASSERT(tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_1->is_busy()); CPPUNIT_ASSERT(!tracker_2->is_busy()); CPPUNIT_ASSERT(tracker_3->is_busy()); } // Test clear. void tracker_list_test::test_tracker_flags() { TRACKER_SETUP(); tracker_list.insert(0, new TrackerTest(&tracker_list, "")); tracker_list.insert(0, new TrackerTest(&tracker_list, "", 0)); tracker_list.insert(0, new TrackerTest(&tracker_list, "", torrent::Tracker::flag_enabled)); tracker_list.insert(0, new TrackerTest(&tracker_list, "", torrent::Tracker::flag_extra_tracker)); tracker_list.insert(0, new TrackerTest(&tracker_list, "", torrent::Tracker::flag_enabled | torrent::Tracker::flag_extra_tracker)); CPPUNIT_ASSERT((tracker_list[0]->flags() & torrent::Tracker::mask_base_flags) == torrent::Tracker::flag_enabled); CPPUNIT_ASSERT((tracker_list[1]->flags() & torrent::Tracker::mask_base_flags) == 0); CPPUNIT_ASSERT((tracker_list[2]->flags() & torrent::Tracker::mask_base_flags) == torrent::Tracker::flag_enabled); CPPUNIT_ASSERT((tracker_list[3]->flags() & torrent::Tracker::mask_base_flags) == torrent::Tracker::flag_extra_tracker); CPPUNIT_ASSERT((tracker_list[4]->flags() & torrent::Tracker::mask_base_flags) == (torrent::Tracker::flag_enabled | torrent::Tracker::flag_extra_tracker)); } void tracker_list_test::test_find_url() { TRACKER_SETUP(); tracker_list.insert(0, new TrackerTest(&tracker_list, "http://1")); tracker_list.insert(0, new TrackerTest(&tracker_list, "http://2")); tracker_list.insert(1, new TrackerTest(&tracker_list, "http://3")); CPPUNIT_ASSERT(tracker_list.find_url("http://") == tracker_list.end()); CPPUNIT_ASSERT(tracker_list.find_url("http://1") != tracker_list.end()); CPPUNIT_ASSERT(*tracker_list.find_url("http://1") == tracker_list[0]); CPPUNIT_ASSERT(tracker_list.find_url("http://2") != tracker_list.end()); CPPUNIT_ASSERT(*tracker_list.find_url("http://2") == tracker_list[1]); CPPUNIT_ASSERT(tracker_list.find_url("http://3") != tracker_list.end()); CPPUNIT_ASSERT(*tracker_list.find_url("http://3") == tracker_list[2]); } void tracker_list_test::test_can_scrape() { TRACKER_SETUP(); torrent::Http::slot_factory() = std::tr1::bind(&http_factory); tracker_list.insert_url(0, "http://example.com/announce"); CPPUNIT_ASSERT((tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); CPPUNIT_ASSERT(torrent::Tracker::scrape_url_from(tracker_list.back()->url()) == "http://example.com/scrape"); tracker_list.insert_url(0, "http://example.com/x/announce"); CPPUNIT_ASSERT((tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); CPPUNIT_ASSERT(torrent::Tracker::scrape_url_from(tracker_list.back()->url()) == "http://example.com/x/scrape"); tracker_list.insert_url(0, "http://example.com/announce.php"); CPPUNIT_ASSERT((tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); CPPUNIT_ASSERT(torrent::Tracker::scrape_url_from(tracker_list.back()->url()) == "http://example.com/scrape.php"); tracker_list.insert_url(0, "http://example.com/a"); CPPUNIT_ASSERT(!(tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); tracker_list.insert_url(0, "http://example.com/announce?x2%0644"); CPPUNIT_ASSERT((tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); CPPUNIT_ASSERT(torrent::Tracker::scrape_url_from(tracker_list.back()->url()) == "http://example.com/scrape?x2%0644"); tracker_list.insert_url(0, "http://example.com/announce?x=2/4"); CPPUNIT_ASSERT(!(tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); tracker_list.insert_url(0, "http://example.com/x%064announce"); CPPUNIT_ASSERT(!(tracker_list.back()->flags() & torrent::Tracker::flag_can_scrape)); } void tracker_list_test::test_single_success() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_busy_not_scrape()); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == -1); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_NONE); tracker_list.send_state_idx(0, torrent::Tracker::EVENT_STARTED); CPPUNIT_ASSERT(tracker_0->is_busy()); CPPUNIT_ASSERT(tracker_0->is_busy_not_scrape()); CPPUNIT_ASSERT(tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == torrent::Tracker::EVENT_STARTED); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_STARTED); CPPUNIT_ASSERT(tracker_0->trigger_success()); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_busy_not_scrape()); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == -1); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_STARTED); CPPUNIT_ASSERT(success_counter == 1 && failure_counter == 0); CPPUNIT_ASSERT(tracker_0->success_counter() == 1); CPPUNIT_ASSERT(tracker_0->failed_counter() == 0); } void tracker_list_test::test_single_failure() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_0->trigger_failure()); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == -1); CPPUNIT_ASSERT(success_counter == 0 && failure_counter == 1); CPPUNIT_ASSERT(tracker_0->success_counter() == 0); CPPUNIT_ASSERT(tracker_0->failed_counter() == 1); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_0->trigger_success()); CPPUNIT_ASSERT(success_counter == 1 && failure_counter == 1); CPPUNIT_ASSERT(tracker_0->success_counter() == 1); CPPUNIT_ASSERT(tracker_0->failed_counter() == 0); } void tracker_list_test::test_single_closing() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); CPPUNIT_ASSERT(!tracker_0->is_open()); tracker_0->set_close_on_done(false); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->trigger_success()); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(tracker_0->is_open()); tracker_list.close_all(); tracker_list.clear_stats(); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->success_counter() == 0); CPPUNIT_ASSERT(tracker_0->failed_counter() == 0); } void tracker_list_test::test_multiple_success() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0_0); TRACKER_INSERT(0, tracker_0_1); TRACKER_INSERT(1, tracker_1_0); TRACKER_INSERT(1, tracker_1_1); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(!tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(!tracker_1_1->is_busy()); tracker_list.send_state_idx(0, 1); CPPUNIT_ASSERT(tracker_0_0->is_busy()); CPPUNIT_ASSERT(!tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(!tracker_1_1->is_busy()); CPPUNIT_ASSERT(tracker_0_0->trigger_success()); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(!tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(!tracker_1_1->is_busy()); tracker_list.send_state_idx(1, 1); tracker_list.send_state_idx(3, 1); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(tracker_1_1->is_busy()); CPPUNIT_ASSERT(tracker_1_1->trigger_success()); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(!tracker_1_1->is_busy()); CPPUNIT_ASSERT(tracker_0_1->trigger_success()); CPPUNIT_ASSERT(!tracker_0_0->is_busy()); CPPUNIT_ASSERT(!tracker_0_1->is_busy()); CPPUNIT_ASSERT(!tracker_1_0->is_busy()); CPPUNIT_ASSERT(!tracker_1_1->is_busy()); CPPUNIT_ASSERT(success_counter == 3 && failure_counter == 0); } void tracker_list_test::test_scrape_success() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); tracker_0->set_can_scrape(); tracker_list.send_scrape(tracker_0); CPPUNIT_ASSERT(tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_busy_not_scrape()); CPPUNIT_ASSERT(tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(tracker_0->trigger_scrape()); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_busy_not_scrape()); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == -1); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(success_counter == 0 && failure_counter == 0); CPPUNIT_ASSERT(scrape_success_counter == 1 && scrape_failure_counter == 0); CPPUNIT_ASSERT(tracker_0->success_counter() == 0); CPPUNIT_ASSERT(tracker_0->failed_counter() == 0); CPPUNIT_ASSERT(tracker_0->scrape_counter() == 1); } void tracker_list_test::test_scrape_failure() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); tracker_0->set_can_scrape(); tracker_list.send_scrape(tracker_0); CPPUNIT_ASSERT(tracker_0->trigger_failure()); CPPUNIT_ASSERT(!tracker_0->is_busy()); CPPUNIT_ASSERT(!tracker_0->is_open()); CPPUNIT_ASSERT(tracker_0->requesting_state() == -1); CPPUNIT_ASSERT(tracker_0->latest_event() == torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(success_counter == 0 && failure_counter == 0); CPPUNIT_ASSERT(scrape_success_counter == 0 && scrape_failure_counter == 1); CPPUNIT_ASSERT(tracker_0->success_counter() == 0); CPPUNIT_ASSERT(tracker_0->failed_counter() == 0); CPPUNIT_ASSERT(tracker_0->scrape_counter() == 0); } bool check_has_active_in_group(const torrent::TrackerList* tracker_list, const char* states, bool scrape) { int group = 0; while (*states != '\0') { bool result = scrape ? tracker_list->has_active_in_group(group++) : tracker_list->has_active_not_scrape_in_group(group++); if ((*states == '1' && !result) || (*states == '0' && result)) return false; states++; } return true; } void tracker_list_test::test_has_active() { TRACKER_SETUP(); TRACKER_INSERT(0, tracker_0); TRACKER_INSERT(0, tracker_1); TRACKER_INSERT(1, tracker_2); TRACKER_INSERT(3, tracker_3); TRACKER_INSERT(4, tracker_4); // TODO: Test scrape... TEST_TRACKERS_IS_BUSY_5("00000", "00000"); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", true)); tracker_list.send_state_idx(0, 1); TEST_TRACKERS_IS_BUSY_5("10000", "10000"); CPPUNIT_ASSERT( tracker_list.has_active()); CPPUNIT_ASSERT( tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "100000", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "100000", true)); CPPUNIT_ASSERT(tracker_0->trigger_success()); TEST_TRACKERS_IS_BUSY_5("00000", "00000"); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", true)); tracker_list.send_state_idx(1, 1); tracker_list.send_state_idx(3, 1); TEST_TRACKERS_IS_BUSY_5("01010", "01010"); CPPUNIT_ASSERT( tracker_list.has_active()); CPPUNIT_ASSERT( tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "100100", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "100100", true)); tracker_2->set_can_scrape(); tracker_list.send_scrape(tracker_2); tracker_list.send_state_idx(1, 1); tracker_list.send_state_idx(3, 1); TEST_TRACKERS_IS_BUSY_5("01110", "01110"); CPPUNIT_ASSERT( tracker_list.has_active()); CPPUNIT_ASSERT( tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "100100", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "110100", true)); CPPUNIT_ASSERT(tracker_1->trigger_success()); TEST_TRACKERS_IS_BUSY_5("00110", "00110"); CPPUNIT_ASSERT( tracker_list.has_active()); CPPUNIT_ASSERT( tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000100", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "010100", true)); CPPUNIT_ASSERT(tracker_2->trigger_scrape()); CPPUNIT_ASSERT(tracker_3->trigger_success()); TEST_TRACKERS_IS_BUSY_5("00000", "00000"); CPPUNIT_ASSERT(!tracker_list.has_active()); CPPUNIT_ASSERT(!tracker_list.has_active_not_scrape()); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", false)); CPPUNIT_ASSERT(check_has_active_in_group(&tracker_list, "000000", true)); } libtorrent-0.13.6/test/torrent/tracker_list_test.h000066400000000000000000000163571257211073700223600ustar00rootroot00000000000000#include #include #include #include class tracker_list_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_list_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_enable); CPPUNIT_TEST(test_close); CPPUNIT_TEST(test_tracker_flags); CPPUNIT_TEST(test_find_url); CPPUNIT_TEST(test_can_scrape); CPPUNIT_TEST(test_single_success); CPPUNIT_TEST(test_single_failure); CPPUNIT_TEST(test_single_closing); CPPUNIT_TEST(test_multiple_success); CPPUNIT_TEST(test_scrape_success); CPPUNIT_TEST(test_scrape_failure); CPPUNIT_TEST(test_has_active); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_enable(); void test_close(); void test_tracker_flags(); void test_find_url(); void test_can_scrape(); void test_single_success(); void test_single_failure(); void test_single_closing(); void test_multiple_success(); void test_scrape_success(); void test_scrape_failure(); void test_has_active(); }; class TrackerTest : public torrent::Tracker { public: static const int flag_close_on_done = max_flag_size << 0; static const int flag_scrape_on_success = max_flag_size << 1; // TODO: Clean up tracker related enums. TrackerTest(torrent::TrackerList* parent, const std::string& url, int flags = torrent::Tracker::flag_enabled) : torrent::Tracker(parent, url, flags), m_busy(false), m_open(false), m_requesting_state(-1) { m_flags |= flag_close_on_done; } virtual bool is_busy() const { return m_busy; } bool is_open() const { return m_open; } virtual Type type() const { return (Type)(TRACKER_DHT + 1); } int requesting_state() const { return m_requesting_state; } bool trigger_success(uint32_t new_peers = 0, uint32_t sum_peers = 0); bool trigger_success(torrent::TrackerList::address_list* address_list, uint32_t new_peers = 0); bool trigger_failure(); bool trigger_scrape(); void set_close_on_done(bool state) { if (state) m_flags |= flag_close_on_done; else m_flags &= ~flag_close_on_done; } void set_scrape_on_success(bool state) { if (state) m_flags |= flag_scrape_on_success; else m_flags &= ~flag_scrape_on_success; } void set_can_scrape() { m_flags |= flag_can_scrape; } void set_success(uint32_t counter, uint32_t time_last) { m_success_counter = counter; m_success_time_last = time_last; } void set_failed(uint32_t counter, uint32_t time_last) { m_failed_counter = counter; m_failed_time_last = time_last; } void set_latest_new_peers(uint32_t peers) { m_latest_new_peers = peers; } void set_latest_sum_peers(uint32_t peers) { m_latest_sum_peers = peers; } void set_new_normal_interval(uint32_t timeout) { set_normal_interval(timeout); } void set_new_min_interval(uint32_t timeout) { set_min_interval(timeout); } virtual void send_state(int state) { m_busy = true; m_open = true; m_requesting_state = m_latest_event = state; } virtual void send_scrape() { m_busy = true; m_open = true; m_requesting_state = m_latest_event = torrent::Tracker::EVENT_SCRAPE; } virtual void close() { m_busy = false; m_open = false; m_requesting_state = -1; } virtual void disown() { m_busy = false; m_open = false; m_requesting_state = -1; } private: bool m_busy; bool m_open; int m_requesting_state; }; extern uint32_t return_new_peers; inline uint32_t increment_value(int* value) { (*value)++; return return_new_peers; } bool check_has_active_in_group(const torrent::TrackerList* tracker_list, const char* states, bool scrape); #define TRACKER_SETUP() \ torrent::TrackerList tracker_list; \ int success_counter = 0; \ int failure_counter = 0; \ int scrape_success_counter = 0; \ int scrape_failure_counter = 0; \ tracker_list.slot_success() = tr1::bind(&increment_value, &success_counter); \ tracker_list.slot_failure() = tr1::bind(&increment_value, &failure_counter); \ tracker_list.slot_scrape_success() = tr1::bind(&increment_value, &scrape_success_counter); \ tracker_list.slot_scrape_failure() = tr1::bind(&increment_value, &scrape_failure_counter); #define TRACKER_INSERT(group, name) \ TrackerTest* name = new TrackerTest(&tracker_list, ""); \ tracker_list.insert(group, name); #define TEST_TRACKER_IS_BUSY(tracker, state) \ CPPUNIT_ASSERT(state == '0' || tracker->is_busy()); \ CPPUNIT_ASSERT(state == '1' || !tracker->is_busy()); #define TEST_MULTI3_IS_BUSY(original, rearranged) \ TEST_TRACKER_IS_BUSY(tracker_0_0, original[0]); \ TEST_TRACKER_IS_BUSY(tracker_0_1, original[1]); \ TEST_TRACKER_IS_BUSY(tracker_1_0, original[2]); \ TEST_TRACKER_IS_BUSY(tracker_2_0, original[3]); \ TEST_TRACKER_IS_BUSY(tracker_3_0, original[4]); \ TEST_TRACKER_IS_BUSY(tracker_list[0], rearranged[0]); \ TEST_TRACKER_IS_BUSY(tracker_list[1], rearranged[1]); \ TEST_TRACKER_IS_BUSY(tracker_list[2], rearranged[2]); \ TEST_TRACKER_IS_BUSY(tracker_list[3], rearranged[3]); \ TEST_TRACKER_IS_BUSY(tracker_list[4], rearranged[4]); #define TEST_GROUP_IS_BUSY(original, rearranged) \ TEST_TRACKER_IS_BUSY(tracker_0_0, original[0]); \ TEST_TRACKER_IS_BUSY(tracker_0_1, original[1]); \ TEST_TRACKER_IS_BUSY(tracker_0_2, original[2]); \ TEST_TRACKER_IS_BUSY(tracker_1_0, original[3]); \ TEST_TRACKER_IS_BUSY(tracker_1_1, original[4]); \ TEST_TRACKER_IS_BUSY(tracker_2_0, original[5]); \ TEST_TRACKER_IS_BUSY(tracker_list[0], rearranged[0]); \ TEST_TRACKER_IS_BUSY(tracker_list[1], rearranged[1]); \ TEST_TRACKER_IS_BUSY(tracker_list[2], rearranged[2]); \ TEST_TRACKER_IS_BUSY(tracker_list[3], rearranged[3]); \ TEST_TRACKER_IS_BUSY(tracker_list[4], rearranged[4]); \ TEST_TRACKER_IS_BUSY(tracker_list[5], rearranged[5]); #define TEST_TRACKERS_IS_BUSY_5(original, rearranged) \ TEST_TRACKER_IS_BUSY(tracker_0, original[0]); \ TEST_TRACKER_IS_BUSY(tracker_1, original[1]); \ TEST_TRACKER_IS_BUSY(tracker_2, original[2]); \ TEST_TRACKER_IS_BUSY(tracker_3, original[3]); \ TEST_TRACKER_IS_BUSY(tracker_4, original[4]); \ TEST_TRACKER_IS_BUSY(tracker_list[0], rearranged[0]); \ TEST_TRACKER_IS_BUSY(tracker_list[1], rearranged[1]); \ TEST_TRACKER_IS_BUSY(tracker_list[2], rearranged[2]); \ TEST_TRACKER_IS_BUSY(tracker_list[3], rearranged[3]); \ TEST_TRACKER_IS_BUSY(tracker_list[4], rearranged[4]); libtorrent-0.13.6/test/torrent/tracker_timeout_test.cc000066400000000000000000000136241257211073700232230ustar00rootroot00000000000000#include "config.h" #include #include #include "rak/priority_queue_default.h" #include "globals.h" #include "tracker_list_test.h" #include "tracker_timeout_test.h" namespace tr1 { using namespace std::tr1; } CPPUNIT_TEST_SUITE_REGISTRATION(tracker_timeout_test); void tracker_timeout_test::setUp() { torrent::cachedTime = rak::timer::current(); // torrent::cachedTime = rak::timer::current().round_seconds(); } void tracker_timeout_test::tearDown() { } void tracker_timeout_test::test_set_timeout() { TrackerTest tracker(NULL, ""); CPPUNIT_ASSERT(tracker.normal_interval() == 1800); tracker.set_new_normal_interval(100); CPPUNIT_ASSERT(tracker.normal_interval() == 600); tracker.set_new_normal_interval(4000); CPPUNIT_ASSERT(tracker.normal_interval() == 3600); tracker.set_new_min_interval(100); CPPUNIT_ASSERT(tracker.min_interval() == 300); tracker.set_new_min_interval(4000); CPPUNIT_ASSERT(tracker.min_interval() == 1800); } void tracker_timeout_test::test_timeout_tracker() { TrackerTest tracker(NULL, ""); int flags = 0; CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); torrent::cachedTime += rak::timer::from_seconds(3); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); flags = torrent::TrackerController::flag_active; CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == ~uint32_t()); tracker.send_state(torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.close(); tracker.set_success(1, torrent::cachedTime.seconds()); // Check also failed... CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 1800); tracker.send_state(torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == ~uint32_t()); tracker.send_state(torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 1800); tracker.close(); tracker.set_success(1, torrent::cachedTime.seconds() - 3); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 1800 - 3); tracker.set_success(1, torrent::cachedTime.seconds() + 3); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 1800 + 3); tracker.close(); flags = torrent::TrackerController::flag_active | torrent::TrackerController::flag_promiscuous_mode; CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == ~uint32_t()); tracker.send_state(torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); } void tracker_timeout_test::test_timeout_update() { TrackerTest tracker(NULL, ""); int flags = 0; flags = torrent::TrackerController::flag_active | torrent::TrackerController::flag_send_update; CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == ~uint32_t()); tracker.close(); tracker.set_failed(1, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.set_failed(0, torrent::cachedTime.seconds()); tracker.set_success(0, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); } void tracker_timeout_test::test_timeout_requesting() { TrackerTest tracker(NULL, ""); int flags = 0; flags = torrent::TrackerController::flag_active | torrent::TrackerController::flag_requesting; CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_SCRAPE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 0); tracker.send_state(torrent::Tracker::EVENT_NONE); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == ~uint32_t()); // tracker.set_latest_new_peers(10 - 1); tracker.close(); tracker.set_failed(1, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 5); tracker.set_failed(2, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 10); tracker.set_failed(6 + 1, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 320); tracker.set_failed(7 + 1, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 320); //std::cout << "timeout:" << torrent::tracker_next_timeout(&tracker, flags) << std::endl; tracker.set_failed(0, torrent::cachedTime.seconds()); tracker.set_success(0, torrent::cachedTime.seconds()); // CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 10); // tracker.set_success(1, torrent::cachedTime.seconds()); // CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 20); // tracker.set_success(2, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 600); tracker.set_success(6, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 600); tracker.set_success(1, torrent::cachedTime.seconds()); // tracker.set_latest_sum_peers(9); // CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 20); tracker.set_latest_sum_peers(10); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 600); tracker.set_latest_sum_peers(10); tracker.set_latest_new_peers(10); tracker.set_success(1, torrent::cachedTime.seconds()); CPPUNIT_ASSERT(torrent::tracker_next_timeout(&tracker, flags) == 600); } libtorrent-0.13.6/test/torrent/tracker_timeout_test.h000066400000000000000000000010341257211073700230550ustar00rootroot00000000000000#include #include "torrent/tracker_controller.h" class tracker_timeout_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_timeout_test); CPPUNIT_TEST(test_set_timeout); CPPUNIT_TEST(test_timeout_tracker); CPPUNIT_TEST(test_timeout_update); CPPUNIT_TEST(test_timeout_requesting); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_set_timeout(); void test_timeout_tracker(); void test_timeout_update(); void test_timeout_requesting(); }; libtorrent-0.13.6/test/torrent/utils/000077500000000000000000000000001257211073700176065ustar00rootroot00000000000000libtorrent-0.13.6/test/torrent/utils/log_buffer_test.cc000066400000000000000000000034661257211073700232770ustar00rootroot00000000000000#include "config.h" #include #include "globals.h" #include "log_buffer_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(utils_log_buffer_test); namespace tr1 { using namespace std::tr1; } void utils_log_buffer_test::setUp() { torrent::cachedTime = rak::timer::from_seconds(1000); } void utils_log_buffer_test::tearDown() { } void utils_log_buffer_test::test_basic() { torrent::log_buffer log; log.lock(); CPPUNIT_ASSERT(log.empty()); CPPUNIT_ASSERT(log.find_older(0) == log.end()); log.unlock(); log.lock_and_push_log("foobar", 6, -1); CPPUNIT_ASSERT(log.empty()); log.lock_and_push_log("foobar", 6, 0); CPPUNIT_ASSERT(log.size() == 1); CPPUNIT_ASSERT(log.back().timestamp == 1000); CPPUNIT_ASSERT(log.back().group == 0); CPPUNIT_ASSERT(log.back().message == "foobar"); torrent::cachedTime += rak::timer::from_milliseconds(1000); log.lock_and_push_log("barbaz", 6, 0); CPPUNIT_ASSERT(log.size() == 2); CPPUNIT_ASSERT(log.back().timestamp == 1001); CPPUNIT_ASSERT(log.back().group == 0); CPPUNIT_ASSERT(log.back().message == "barbaz"); } void utils_log_buffer_test::test_timestamps() { torrent::log_buffer log; log.lock_and_push_log("foobar", 6, 0); CPPUNIT_ASSERT(log.back().timestamp == 1000); CPPUNIT_ASSERT(log.find_older(1000 - 1) == log.begin()); CPPUNIT_ASSERT(log.find_older(1000) == log.end()); CPPUNIT_ASSERT(log.find_older(1000 + 1) == log.end()); torrent::cachedTime += rak::timer::from_milliseconds(10 * 1000); log.lock_and_push_log("foobar", 6, 0); CPPUNIT_ASSERT(log.back().timestamp == 1010); CPPUNIT_ASSERT(log.find_older(1010 - 10) == log.begin()); CPPUNIT_ASSERT(log.find_older(1010 - 1) == log.begin() + 1); CPPUNIT_ASSERT(log.find_older(1010) == log.end()); CPPUNIT_ASSERT(log.find_older(1010 + 1) == log.end()); } libtorrent-0.13.6/test/torrent/utils/log_buffer_test.h000066400000000000000000000005661257211073700231370ustar00rootroot00000000000000#include #include "torrent/utils/log_buffer.h" class utils_log_buffer_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(utils_log_buffer_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_timestamps); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_timestamps(); }; libtorrent-0.13.6/test/torrent/utils/log_test.cc000066400000000000000000000112161257211073700217360ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include "log_test.h" namespace torrent { typedef std::vector > log_output_list; extern log_output_list log_outputs; } CPPUNIT_TEST_SUITE_REGISTRATION(utils_log_test); namespace tr1 { using namespace std::tr1; } const char* expected_output = NULL; unsigned int output_mask; static void test_output(const char* output, unsigned int length, unsigned int mask) { CPPUNIT_ASSERT_MESSAGE("'" + std::string(output) + "' != '" + std::string(expected_output) + "'", std::strcmp(output, expected_output) == 0); CPPUNIT_ASSERT_MESSAGE("'" + std::string(output) + "'", std::strlen(output) == length); output_mask |= mask; } #define LTUNIT_ASSERT_OUTPUT(group, mask, expected, ...) \ output_mask = 0; expected_output = expected; \ lt_log_print(group, __VA_ARGS__); \ CPPUNIT_ASSERT(output_mask == (mask)); void utils_log_test::setUp() { // Don't initialize since this creates the group->child connections. // torrent::log_initialize(); } void utils_log_test::tearDown() { torrent::log_cleanup(); } void utils_log_test::test_basic() { CPPUNIT_ASSERT(!torrent::log_groups.empty()); CPPUNIT_ASSERT(torrent::log_groups.size() == torrent::LOG_GROUP_MAX_SIZE); CPPUNIT_ASSERT(std::find_if(torrent::log_groups.begin(), torrent::log_groups.end(), tr1::bind(&torrent::log_group::valid, tr1::placeholders::_1)) == torrent::log_groups.end()); } inline void open_output(const char* name, int mask = 0) { torrent::log_open_output(name, tr1::bind(&::test_output, tr1::placeholders::_1, tr1::placeholders::_2, mask)); } void utils_log_test::test_output_open() { CPPUNIT_ASSERT(torrent::log_groups[0].size_outputs() == 0); // Add test for unknown output names. open_output("test_output_1", 0x0); torrent::log_add_group_output(0, "test_output_1"); CPPUNIT_ASSERT(torrent::log_outputs.size() == 1); CPPUNIT_ASSERT(torrent::log_outputs[0].first == "test_output_1"); CPPUNIT_ASSERT(torrent::log_groups[0].outputs() == 0x1); CPPUNIT_ASSERT(torrent::log_groups[0].size_outputs() == 1); // Test inserting duplicate names, should catch. // CPPUNIT_ASSERT_THROW(torrent::log_open_output("test_output_1", torrent::log_slot());, torrent::input_error); try { torrent::log_open_output("test_output_1", torrent::log_slot()); } catch (torrent::input_error& e) { return; } CPPUNIT_ASSERT(false); // Test more than 64 entries. } // Test to make sure we don't call functions when using lt_log_print // on unused log items. void utils_log_test::test_print() { open_output("test_print_1", 0x1); open_output("test_print_2", 0x2); torrent::log_add_group_output(0, "test_print_1"); LTUNIT_ASSERT_OUTPUT(0, 0x1, "foo_bar", "foo_bar"); LTUNIT_ASSERT_OUTPUT(0, 0x1, "foo 123 bar", "foo %i %s", 123, "bar"); torrent::log_add_group_output(0, "test_print_2"); LTUNIT_ASSERT_OUTPUT(0, 0x1|0x2, "test_multiple", "test_multiple"); } enum { GROUP_PARENT_1, GROUP_PARENT_2, GROUP_CHILD_1, GROUP_CHILD_1_1 }; void utils_log_test::test_children() { open_output("test_children_1", 0x1); open_output("test_children_2", 0x2); torrent::log_add_group_output(GROUP_PARENT_1, "test_children_1"); torrent::log_add_group_output(GROUP_PARENT_2, "test_children_2"); torrent::log_add_child(GROUP_PARENT_1, GROUP_CHILD_1); torrent::log_add_child(GROUP_CHILD_1, GROUP_CHILD_1_1); // std::cout << "cached_output(" << torrent::log_groups[GROUP_PARENT_1].cached_outputs() << ')'; LTUNIT_ASSERT_OUTPUT(GROUP_PARENT_1, 0x1, "parent_1", "parent_1"); LTUNIT_ASSERT_OUTPUT(GROUP_CHILD_1, 0x1, "child_1", "child_1"); LTUNIT_ASSERT_OUTPUT(GROUP_CHILD_1_1, 0x1, "child_1", "child_1"); torrent::log_add_child(GROUP_PARENT_2, GROUP_CHILD_1); LTUNIT_ASSERT_OUTPUT(GROUP_PARENT_2, 0x2, "parent_2", "parent_2"); LTUNIT_ASSERT_OUTPUT(GROUP_CHILD_1, 0x3, "child_1", "child_1"); LTUNIT_ASSERT_OUTPUT(GROUP_CHILD_1_1, 0x3, "child_1", "child_1"); } void utils_log_test::test_file_output() { char* filename = tmpnam(NULL); torrent::log_open_file_output("test_file", filename); torrent::log_add_group_output(GROUP_PARENT_1, "test_file"); lt_log_print(GROUP_PARENT_1, "test_file"); torrent::log_cleanup(); // To ensure we flush the buffers. std::ifstream temp_file(filename); CPPUNIT_ASSERT(temp_file.good()); char buffer[256]; temp_file.getline(buffer, 256); CPPUNIT_ASSERT_MESSAGE(buffer, std::string(buffer).find("test_file") != std::string::npos); } libtorrent-0.13.6/test/torrent/utils/log_test.h000066400000000000000000000010121257211073700215710ustar00rootroot00000000000000#include #include "torrent/utils/log.h" class utils_log_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(utils_log_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_output_open); CPPUNIT_TEST(test_print); CPPUNIT_TEST(test_children); CPPUNIT_TEST(test_file_output); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_output_open(); void test_print(); void test_children(); void test_file_output(); }; libtorrent-0.13.6/test/torrent/utils/net_test.cc000066400000000000000000000014641257211073700217470ustar00rootroot00000000000000#include "config.h" #include #include #include "net_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(utils_net_test); namespace tr1 { using namespace std::tr1; } static void inc_value(int* value) { (*value)++; } #define LTUNIT_AI_CALL(lt_ai, lt_flags) { \ int test_value = 0; \ CPPUNIT_ASSERT(torrent::address_info_call(ai, 0, tr1::bind(&inc_value, &test_value))); \ CPPUNIT_ASSERT(test_value); } \ void utils_net_test::setUp() { } void utils_net_test::tearDown() { } void utils_net_test::test_basic() { addrinfo* ai = torrent::address_info_lookup("localhost", AF_INET, SOCK_STREAM); LTUNIT_AI_CALL(ai, 0); torrent::address_info_free(ai); } libtorrent-0.13.6/test/torrent/utils/net_test.h000066400000000000000000000004461257211073700216100ustar00rootroot00000000000000#include #include "torrent/utils/net.h" class utils_net_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(utils_net_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); }; libtorrent-0.13.6/test/torrent/utils/option_strings_test.cc000066400000000000000000000027201257211073700242360ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "option_strings_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(option_strings_test); namespace tr1 { using namespace std::tr1; } void option_strings_test::test_basic() { } #define TEST_ENTRY(group, name, value) \ { std::string result(torrent::option_as_string(torrent::group, value)); \ CPPUNIT_ASSERT_MESSAGE("Not found '" + result + "'", result == name); \ CPPUNIT_ASSERT(torrent::option_find_string(torrent::group, name) == value); } void option_strings_test::test_entries() { TEST_ENTRY(OPTION_CONNECTION_TYPE, "leech", torrent::Download::CONNECTION_LEECH); TEST_ENTRY(OPTION_CONNECTION_TYPE, "seed", torrent::Download::CONNECTION_SEED); TEST_ENTRY(OPTION_CONNECTION_TYPE, "initial_seed", torrent::Download::CONNECTION_INITIAL_SEED); TEST_ENTRY(OPTION_CONNECTION_TYPE, "metadata", torrent::Download::CONNECTION_METADATA); TEST_ENTRY(OPTION_LOG_GROUP, "critical", torrent::LOG_CRITICAL); TEST_ENTRY(OPTION_LOG_GROUP, "storage_notice", torrent::LOG_STORAGE_NOTICE); TEST_ENTRY(OPTION_LOG_GROUP, "torrent_debug", torrent::LOG_TORRENT_DEBUG); } libtorrent-0.13.6/test/torrent/utils/option_strings_test.h000066400000000000000000000005641257211073700241040ustar00rootroot00000000000000#include #include "torrent/utils/option_strings.h" class option_strings_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(option_strings_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_entries); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_entries(); }; libtorrent-0.13.6/test/torrent/utils/signal_bitfield_test.cc000066400000000000000000000100251257211073700242710ustar00rootroot00000000000000#include "config.h" #include #include #include #include "signal_bitfield_test.h" #include "thread_base_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(utils_signal_bitfield_test); namespace tr1 { using namespace std::tr1; } static void mark_index(uint32_t* bitfield, unsigned int index) { __sync_fetch_and_or(bitfield, 1 << index); } static bool check_index(uint32_t* bitfield, unsigned int index) { return *bitfield & (1 << index); } void utils_signal_bitfield_test::setUp() { } void utils_signal_bitfield_test::tearDown() { CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); torrent::thread_base::release_global_lock(); } static bool verify_did_internal_error(tr1::function func, bool should_throw) { bool did_throw = false; try { func(); } catch (torrent::internal_error& e) { did_throw = true; } return should_throw == did_throw; } #define SETUP_SIGNAL_BITFIELD() \ uint32_t marked_bitfield = 0; \ torrent::signal_bitfield signal_bitfield; #define SIGNAL_BITFIELD_DID_INTERNAL_ERROR(verify_slot, did_throw) \ CPPUNIT_ASSERT(verify_did_internal_error(tr1::bind(&torrent::signal_bitfield::add_signal, \ &signal_bitfield, \ torrent::signal_bitfield::slot_type(verify_slot)), \ did_throw)); void utils_signal_bitfield_test::test_basic() { SETUP_SIGNAL_BITFIELD(); CPPUNIT_ASSERT(torrent::signal_bitfield::max_size == sizeof(torrent::signal_bitfield::bitfield_type) * 8); SIGNAL_BITFIELD_DID_INTERNAL_ERROR(torrent::signal_bitfield::slot_type(), true); for (unsigned int i = 0; i < torrent::signal_bitfield::max_size; i++) CPPUNIT_ASSERT(signal_bitfield.add_signal(tr1::bind(&mark_index, &marked_bitfield, i)) == i); SIGNAL_BITFIELD_DID_INTERNAL_ERROR(tr1::bind(&mark_index, &marked_bitfield, torrent::signal_bitfield::max_size), true); } void utils_signal_bitfield_test::test_single() { SETUP_SIGNAL_BITFIELD(); CPPUNIT_ASSERT(signal_bitfield.add_signal(tr1::bind(&mark_index, &marked_bitfield, 0)) == 0); signal_bitfield.signal(0); CPPUNIT_ASSERT(marked_bitfield == 0x0); signal_bitfield.work(); CPPUNIT_ASSERT(marked_bitfield == 0x1); marked_bitfield = 0; signal_bitfield.work(); CPPUNIT_ASSERT(marked_bitfield == 0x0); } void utils_signal_bitfield_test::test_multiple() { SETUP_SIGNAL_BITFIELD(); for (unsigned int i = 0; i < torrent::signal_bitfield::max_size; i++) CPPUNIT_ASSERT(signal_bitfield.add_signal(tr1::bind(&mark_index, &marked_bitfield, i)) == i); signal_bitfield.signal(2); signal_bitfield.signal(31); CPPUNIT_ASSERT(marked_bitfield == 0x0); signal_bitfield.work(); CPPUNIT_ASSERT(marked_bitfield == (((unsigned int)1 << 2) | ((unsigned int)1 << 31))); marked_bitfield = 0; signal_bitfield.work(); CPPUNIT_ASSERT(marked_bitfield == 0x0); } void utils_signal_bitfield_test::test_thread() { uint32_t marked_bitfield = 0; thread_test* thread = new thread_test; // thread->set_test_flag(thread_test::test_flag_long_timeout); for (unsigned int i = 0; i < torrent::signal_bitfield::max_size; i++) CPPUNIT_ASSERT(thread->signal_bitfield()->add_signal(tr1::bind(&mark_index, &marked_bitfield, i)) == i); thread->init_thread(); thread->start_thread(); // Vary the various timeouts. for (int i = 0; i < 100; i++) { // thread->interrupt(); // usleep(0); thread->signal_bitfield()->signal(i % 20); // thread->interrupt(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&check_index, &marked_bitfield, i % 20))); __sync_fetch_and_and(&marked_bitfield, ~uint32_t()); } thread->stop_thread(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE))); delete thread; } // Test invalid signal added. // Test overflow signals added. // Test multiple signals triggered. // Stresstest with real thread/polling. libtorrent-0.13.6/test/torrent/utils/signal_bitfield_test.h000066400000000000000000000007511257211073700241400ustar00rootroot00000000000000#include #include "torrent/utils/signal_bitfield.h" class utils_signal_bitfield_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(utils_signal_bitfield_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_single); CPPUNIT_TEST(test_multiple); CPPUNIT_TEST(test_thread); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_single(); void test_multiple(); void test_thread(); }; libtorrent-0.13.6/test/torrent/utils/test_extents.cc000066400000000000000000000033001257211073700226420ustar00rootroot00000000000000#include "config.h" #include "test_extents.h" #include #include #include CPPUNIT_TEST_SUITE_REGISTRATION(ExtentsTest); void ExtentsTest::setUp() { } void ExtentsTest::tearDown() { } typedef torrent::extents extent_type_1; // typedef torrent::extents extent_type_3; template bool verify_extent_data(Extent& extent, const uint32_t* idx, const int* val) { while (*idx != *(idx + 1)) { if (!extent.is_equal_range(*idx, *(idx + 1) - 1, *val)) { // std::cout << *idx << ' ' << *(idx + 1) << ' ' << *val << std::endl; // std::cout << extent.at(*idx) << std::endl; // std::cout << extent.at(*(idx + 1)) << std::endl; return false; } idx++; val++; } return true; } static const uint32_t idx_empty[] = {0, 256, 256}; static const int val_empty[] = {0, 1}; static const uint32_t idx_basic_1[] = {0, 1, 255, 256, 256}; static const int val_basic_1[] = {1, 0, 1}; // static const uint32_t idx_basic_2[] = {0, 1, 16, 255, 256, 256}; // static const int val_basic_2[] = {1, 0, 2, 1}; void ExtentsTest::test_basic() { extent_type_1 extent_1; // Test empty. CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_empty, val_empty)); CPPUNIT_ASSERT(extent_1.at(0) == int()); CPPUNIT_ASSERT(extent_1.at(255) == int()); extent_1.insert(0, 0, 1); extent_1.insert(255, 0, 1); CPPUNIT_ASSERT(extent_1.at(0) == 1); CPPUNIT_ASSERT(extent_1.at(255) == 1); CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_1, val_basic_1)); // extent_1.insert(38, 3, 2); // CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_2, val_basic_2)); } libtorrent-0.13.6/test/torrent/utils/test_extents.h000066400000000000000000000004001257211073700225020ustar00rootroot00000000000000#include class ExtentsTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ExtentsTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); }; libtorrent-0.13.6/test/torrent/utils/test_queue_buckets.cc000066400000000000000000000156761257211073700240370ustar00rootroot00000000000000#include "config.h" #include "test_queue_buckets.h" #include "utils/instrumentation.h" #include "utils/queue_buckets.h" CPPUNIT_TEST_SUITE_REGISTRATION(TestQueueBuckets); struct test_constants { static const int bucket_count = 2; static const torrent::instrumentation_enum instrumentation_added[bucket_count]; static const torrent::instrumentation_enum instrumentation_moved[bucket_count]; static const torrent::instrumentation_enum instrumentation_removed[bucket_count]; static const torrent::instrumentation_enum instrumentation_total[bucket_count]; template static void destroy(Type& obj); }; const torrent::instrumentation_enum test_constants::instrumentation_added[bucket_count] = { torrent::INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_ADDED, torrent::INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_ADDED }; const torrent::instrumentation_enum test_constants::instrumentation_moved[bucket_count] = { torrent::INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_MOVED, torrent::INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_MOVED }; const torrent::instrumentation_enum test_constants::instrumentation_removed[bucket_count] = { torrent::INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_REMOVED, torrent::INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_REMOVED }; const torrent::instrumentation_enum test_constants::instrumentation_total[bucket_count] = { torrent::INSTRUMENTATION_TRANSFER_REQUESTS_QUEUED_TOTAL, torrent::INSTRUMENTATION_TRANSFER_REQUESTS_UNORDERED_TOTAL }; typedef torrent::queue_buckets buckets_type; static int items_destroyed = 0; template <> void test_constants::destroy(__UNUSED int& obj) { items_destroyed++; } struct test_queue_bucket_compare { test_queue_bucket_compare(int v) : m_v(v) {} bool operator () (int obj) { return m_v == obj; } int m_v; }; #define FILL_BUCKETS(s_0, s_1) \ for (int i = 0; i < s_0; i++) \ buckets.push_back(0, i); \ for (int i = 0; i < s_1; i++) \ buckets.push_back(1, s_0 + i); #define VERIFY_QUEUE_SIZES(s_0, s_1) \ CPPUNIT_ASSERT(buckets.queue_size(0) == s_0); \ CPPUNIT_ASSERT(buckets.queue_size(1) == s_1); #define VERIFY_INSTRUMENTATION(a_0, m_0, r_0, t_0, a_1, m_1, r_1, t_1) \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_added[0]] == a_0); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_moved[0]] == m_0); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_removed[0]] == r_0); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_total[0]] == t_0); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_added[1]] == a_1); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_moved[1]] == m_1); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_removed[1]] == r_1); \ CPPUNIT_ASSERT(torrent::instrumentation_values[test_constants::instrumentation_total[1]] == t_1); #define VERIFY_ITEMS_DESTROYED(count) \ CPPUNIT_ASSERT(items_destroyed == count); \ items_destroyed = 0; // // Basic tests: // void TestQueueBuckets::test_basic() { torrent::instrumentation_initialize(); buckets_type buckets; VERIFY_QUEUE_SIZES(0, 0); CPPUNIT_ASSERT(buckets.empty()); buckets.push_front(0, int()); VERIFY_QUEUE_SIZES(1, 0); buckets.push_back(0, int()); VERIFY_QUEUE_SIZES(2, 0); VERIFY_INSTRUMENTATION(2, 0, 0, 2, 0, 0, 0, 0); CPPUNIT_ASSERT(!buckets.empty()); buckets.push_front(1, int()); VERIFY_QUEUE_SIZES(2, 1); buckets.push_back(1, int()); VERIFY_QUEUE_SIZES(2, 2); VERIFY_INSTRUMENTATION(2, 0, 0, 2, 2, 0, 0, 2); CPPUNIT_ASSERT(!buckets.empty()); buckets.pop_front(0); VERIFY_QUEUE_SIZES(1, 2); buckets.pop_back(0); VERIFY_QUEUE_SIZES(0, 2); VERIFY_INSTRUMENTATION(2, 0, 2, 0, 2, 0, 0, 2); CPPUNIT_ASSERT(!buckets.empty()); buckets.pop_front(1); VERIFY_QUEUE_SIZES(0, 1); buckets.pop_back(1); VERIFY_QUEUE_SIZES(0, 0); VERIFY_INSTRUMENTATION(2, 0, 2, 0, 2, 0, 2, 0); CPPUNIT_ASSERT(buckets.empty()); } void TestQueueBuckets::test_erase() { items_destroyed = 0; torrent::instrumentation_initialize(); buckets_type buckets; FILL_BUCKETS(10, 5); VERIFY_QUEUE_SIZES(10, 5); VERIFY_INSTRUMENTATION(10, 0, 0, 10, 5, 0, 0, 5); buckets.destroy(0, buckets.begin(0) + 3, buckets.begin(0) + 7); VERIFY_ITEMS_DESTROYED(4); VERIFY_QUEUE_SIZES(6, 5); VERIFY_INSTRUMENTATION(10, 0, 4, 6, 5, 0, 0, 5); buckets.destroy(1, buckets.begin(1) + 0, buckets.begin(1) + 5); VERIFY_ITEMS_DESTROYED(5); VERIFY_QUEUE_SIZES(6, 0); VERIFY_INSTRUMENTATION(10, 0, 4, 6, 5, 0, 5, 0); } static buckets_type::const_iterator bucket_queue_find_in_queue(const buckets_type& buckets, int idx, int value) { return torrent::queue_bucket_find_if_in_queue(buckets, idx, test_queue_bucket_compare(value)); } static std::pair bucket_queue_find_in_any(const buckets_type& buckets, int value) { return torrent::queue_bucket_find_if_in_any(buckets, test_queue_bucket_compare(value)); } void TestQueueBuckets::test_find() { items_destroyed = 0; torrent::instrumentation_initialize(); buckets_type buckets; FILL_BUCKETS(10, 5); CPPUNIT_ASSERT(bucket_queue_find_in_queue(buckets, 0, 0) == buckets.begin(0)); CPPUNIT_ASSERT(bucket_queue_find_in_queue(buckets, 0, 10) == buckets.end(0)); CPPUNIT_ASSERT(bucket_queue_find_in_queue(buckets, 1, 10) == buckets.begin(1)); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 0).first == 0); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 0).second == buckets.begin(0)); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 10).first == 1); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 10).second == buckets.begin(1)); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 20).first == 2); CPPUNIT_ASSERT(bucket_queue_find_in_any(buckets, 20).second == buckets.end(1)); } void TestQueueBuckets::test_destroy_range() { items_destroyed = 0; torrent::instrumentation_initialize(); buckets_type buckets; FILL_BUCKETS(10, 5); VERIFY_QUEUE_SIZES(10, 5); VERIFY_INSTRUMENTATION(10, 0, 0, 10, 5, 0, 0, 5); buckets.destroy(0, buckets.begin(0) + 3, buckets.begin(0) + 7); VERIFY_ITEMS_DESTROYED(4); VERIFY_QUEUE_SIZES(6, 5); VERIFY_INSTRUMENTATION(10, 0, 4, 6, 5, 0, 0, 5); buckets.destroy(1, buckets.begin(1) + 0, buckets.begin(1) + 5); VERIFY_ITEMS_DESTROYED(5); VERIFY_QUEUE_SIZES(6, 0); VERIFY_INSTRUMENTATION(10, 0, 4, 6, 5, 0, 5, 0); } void TestQueueBuckets::test_move_range() { items_destroyed = 0; torrent::instrumentation_initialize(); buckets_type buckets; FILL_BUCKETS(10, 5); torrent::instrumentation_reset(); buckets.move_to(0, buckets.begin(0) + 3, buckets.begin(0) + 7, 1); VERIFY_ITEMS_DESTROYED(0); VERIFY_QUEUE_SIZES(6, 9); VERIFY_INSTRUMENTATION(0, 4, 0, 6, 4, 0, 0, 9); } libtorrent-0.13.6/test/torrent/utils/test_queue_buckets.h000066400000000000000000000010201257211073700236530ustar00rootroot00000000000000#include #include "protocol/request_list.h" class TestQueueBuckets : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestQueueBuckets); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_erase); CPPUNIT_TEST(test_find); CPPUNIT_TEST(test_destroy_range); CPPUNIT_TEST(test_move_range); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} void tearDown() {} void test_basic(); void test_erase(); void test_find(); void test_destroy_range(); void test_move_range(); }; libtorrent-0.13.6/test/torrent/utils/thread_base_test.cc000066400000000000000000000137101257211073700234170ustar00rootroot00000000000000#include "config.h" #include #include #include #include #include #include "thread_base_test.h" CPPUNIT_TEST_SUITE_REGISTRATION(utils_thread_base_test); namespace tr1 { using namespace std::tr1; } void throw_shutdown_exception() { throw torrent::shutdown_exception(); } thread_test::thread_test() : m_test_state(TEST_NONE), m_test_flags(0) { } void thread_test::init_thread() { m_state = STATE_INITIALIZED; m_test_state = TEST_PRE_START; m_poll = torrent::PollSelect::create(256); } void thread_test::call_events() { if ((m_test_flags & test_flag_pre_stop) && m_test_state == TEST_PRE_START && m_state == STATE_ACTIVE) __sync_lock_test_and_set(&m_test_state, TEST_PRE_STOP); if ((m_test_flags & test_flag_acquire_global)) { acquire_global_lock(); __sync_and_and_fetch(&m_test_flags, ~test_flag_acquire_global); __sync_or_and_fetch(&m_test_flags, test_flag_has_global); } if ((m_flags & flag_do_shutdown)) { if ((m_flags & flag_did_shutdown)) throw torrent::internal_error("Already trigged shutdown."); __sync_or_and_fetch(&m_flags, flag_did_shutdown); throw torrent::shutdown_exception(); } if ((m_test_flags & test_flag_pre_poke)) { } if ((m_test_flags & test_flag_do_work)) { usleep(10 * 1000); // TODO: Don't just sleep, as that give up core. __sync_and_and_fetch(&m_test_flags, ~test_flag_do_work); } if ((m_test_flags & test_flag_post_poke)) { } } bool wait_for_true(std::tr1::function test_function) { int i = 100; do { if (test_function()) return true; usleep(10 * 1000); } while (--i); return false; } void utils_thread_base_test::setUp() { } void utils_thread_base_test::tearDown() { CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); torrent::thread_base::release_global_lock(); } void utils_thread_base_test::test_basic() { thread_test* thread = new thread_test; CPPUNIT_ASSERT(thread->flags() == 0); CPPUNIT_ASSERT(!thread->is_main_polling()); CPPUNIT_ASSERT(!thread->is_active()); CPPUNIT_ASSERT(thread->global_queue_size() == 0); CPPUNIT_ASSERT(thread->poll() == NULL); // Check active... } void utils_thread_base_test::test_lifecycle() { thread_test* thread = new thread_test; CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_UNKNOWN); CPPUNIT_ASSERT(thread->test_state() == thread_test::TEST_NONE); thread->init_thread(); CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_INITIALIZED); CPPUNIT_ASSERT(thread->is_initialized()); CPPUNIT_ASSERT(thread->test_state() == thread_test::TEST_PRE_START); thread->set_pre_stop(); CPPUNIT_ASSERT(!wait_for_true(tr1::bind(&thread_test::is_test_state, thread, thread_test::TEST_PRE_STOP))); thread->start_thread(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_state, thread, thread_test::STATE_ACTIVE))); CPPUNIT_ASSERT(thread->is_active()); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_test_state, thread, thread_test::TEST_PRE_STOP))); thread->stop_thread(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE))); CPPUNIT_ASSERT(thread->is_inactive()); delete thread; } void utils_thread_base_test::test_global_lock_basic() { thread_test* thread = new thread_test; thread->init_thread(); thread->start_thread(); CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0); // Acquire main thread... CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock()); torrent::thread_base::release_global_lock(); CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock()); torrent::thread_base::release_global_lock(); torrent::thread_base::acquire_global_lock(); CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock()); thread->set_acquire_global(); CPPUNIT_ASSERT(!wait_for_true(tr1::bind(&thread_test::is_test_flags, thread, thread_test::test_flag_has_global))); torrent::thread_base::release_global_lock(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_test_flags, thread, thread_test::test_flag_has_global))); CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock()); torrent::thread_base::release_global_lock(); CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); // Test waive (loop). CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0); torrent::thread_base::release_global_lock(); thread->stop_thread(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE))); delete thread; } void utils_thread_base_test::test_interrupt() { thread_test* thread = new thread_test; thread->set_test_flag(thread_test::test_flag_long_timeout); thread->init_thread(); thread->start_thread(); // Vary the various timeouts. for (int i = 0; i < 100; i++) { thread->interrupt(); usleep(0); thread->set_test_flag(thread_test::test_flag_do_work); thread->interrupt(); // Wait for flag to clear. CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_not_test_flags, thread, thread_test::test_flag_do_work))); } thread->stop_thread(); CPPUNIT_ASSERT(wait_for_true(tr1::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE))); delete thread; } void utils_thread_base_test::test_stop() { CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); // torrent::thread_base::acquire_global_lock(); for (int i = 0; i < 20; i++) { CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock()); thread_test* thread = new thread_test; thread->set_test_flag(thread_test::test_flag_do_work); thread->init_thread(); thread->start_thread(); thread->stop_thread_wait(); CPPUNIT_ASSERT(thread->is_inactive()); delete thread; } torrent::thread_base::release_global_lock(); } libtorrent-0.13.6/test/torrent/utils/thread_base_test.h000066400000000000000000000055711257211073700232670ustar00rootroot00000000000000#include #include "torrent/utils/thread_base.h" class utils_thread_base_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(utils_thread_base_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_lifecycle); CPPUNIT_TEST(test_global_lock_basic); CPPUNIT_TEST(test_interrupt); CPPUNIT_TEST(test_stop); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_lifecycle(); void test_global_lock_basic(); void test_interrupt(); void test_interrupt_legacy(); void test_stop(); }; struct thread_management_type { thread_management_type() { CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); } ~thread_management_type() { torrent::thread_base::release_global_lock(); } }; #define SETUP_THREAD() \ thread_management_type thread_management; \ torrent::thread_disk* thread_disk = new torrent::thread_disk(); \ thread_disk->init_thread(); #define CLEANUP_THREAD() \ CPPUNIT_ASSERT(wait_for_true(tr1::bind(&torrent::thread_base::is_inactive, thread_disk))); \ delete thread_disk; bool wait_for_true(std::tr1::function test_function); class thread_test : public torrent::thread_base { public: enum test_state { TEST_NONE, TEST_PRE_START, TEST_PRE_STOP, TEST_STOP }; static const int test_flag_pre_stop = 0x1; static const int test_flag_long_timeout = 0x2; static const int test_flag_acquire_global = 0x10; static const int test_flag_has_global = 0x20; static const int test_flag_do_work = 0x100; static const int test_flag_pre_poke = 0x200; static const int test_flag_post_poke = 0x400; thread_test(); int test_state() const { return m_test_state; } bool is_state(int state) const { return m_state == state; } bool is_test_state(int state) const { return m_test_state == state; } bool is_test_flags(int flags) const { return (m_test_flags & flags) == flags; } bool is_not_test_flags(int flags) const { return !(m_test_flags & flags); } const char* name() const { return "test_thread"; } void init_thread(); void set_pre_stop() { __sync_or_and_fetch(&m_test_flags, test_flag_pre_stop); } void set_acquire_global() { __sync_or_and_fetch(&m_test_flags, test_flag_acquire_global); } void set_test_flag(int flags) { __sync_or_and_fetch(&m_test_flags, flags); } private: void call_events(); int64_t next_timeout_usec() { return (m_test_flags & test_flag_long_timeout) ? (10000 * 1000) : (100 * 1000); } int m_test_state lt_cacheline_aligned; int m_test_flags lt_cacheline_aligned; }; libtorrent-0.13.6/test/tracker/000077500000000000000000000000001257211073700164045ustar00rootroot00000000000000libtorrent-0.13.6/test/tracker/tracker_http_test.cc000066400000000000000000000003161257211073700224440ustar00rootroot00000000000000#include "config.h" #include "tracker_http_test.h" #include "tracker/tracker_http.h" void tracker_http_test::setUp() { } void tracker_http_test::tearDown() { } void tracker_http_test::test_basic() { } libtorrent-0.13.6/test/tracker/tracker_http_test.h000066400000000000000000000005431257211073700223100ustar00rootroot00000000000000#include #include "tracker/tracker_http.h" class tracker_http_test : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(tracker_http_test); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_scrape); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearDown(); void test_basic(); void test_scrape(); };