r188 - in /debtorrent/trunk/DebTorrent/BT1: Encrypter.py FileSelector.py NatCheck.py
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Sat Jul 28 08:42:10 UTC 2007
Author: camrdale-guest
Date: Sat Jul 28 08:42:10 2007
New Revision: 188
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=188
Log:
Still more documentation.
Modified:
debtorrent/trunk/DebTorrent/BT1/Encrypter.py
debtorrent/trunk/DebTorrent/BT1/FileSelector.py
debtorrent/trunk/DebTorrent/BT1/NatCheck.py
Modified: debtorrent/trunk/DebTorrent/BT1/Encrypter.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Encrypter.py?rev=188&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Encrypter.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Encrypter.py Sat Jul 28 08:42:10 2007
@@ -1,8 +1,26 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Make encrypted connections to peers.
+
+ at type MAX_INCOMPLETE: C{int}
+ at var MAX_INCOMPLETE: the maximum number of incomplete connections to have
+ outstanding before new connections to initiate get queued
+ at type option_pattern: C{string}
+ at var option_pattern: the supported options to send to all peers
+ at type hexchars: C{string}
+ at var hexchars: the hex characters
+ at type hexmap: C{list} of C{string}
+ at var hexmap: a mapping from the first 256 integers to their 2-byte hex
+ representation
+ at type incompletecounter: L{IncompleteCounter}
+ at var incompletecounter: the counter to use to track the number of incomplete
+ connections outstanding
+
+"""
from cStringIO import StringIO
from binascii import b2a_hex
@@ -26,9 +44,27 @@
option_pattern = chr(0)*8
def toint(s):
+ """Convert a 2-byte big-endian string representation back to an integer.
+
+ @type s: C{string}
+ @param s: the 2 byte big-endian string representation to convert
+ @rtype: C{long}
+ @return: the integer
+
+ """
+
return long(b2a_hex(s), 16)
def tobinary16(i):
+ """Convert an integer to a 2-byte big-endian string representation.
+
+ @type i: C{int}
+ @param i: the integer to convert
+ @rtype: C{string}
+ @return: the 2 byte big-endian string representation
+
+ """
+
return chr((i >> 8) & 0xFF) + chr(i & 0xFF)
hexchars = '0123456789ABCDEF'
@@ -37,12 +73,31 @@
hexmap.append(hexchars[(i&0xF0)/16]+hexchars[i&0x0F])
def tohex(s):
+ """Convert a string of characters to it's hex representation.
+
+ @type s: C{string}
+ @param s: the string to convert
+ @rtype: C{string}
+ @return: the resulting hex string
+
+ """
+
r = []
for c in s:
r.append(hexmap[ord(c)])
return ''.join(r)
def make_readable(s):
+ """Convert a string to be human-readable.
+
+ @type s: C{string}
+ @param s: the string to convert
+ @rtype: C{string}
+ @return: the resulting hex string, or the original string if it was already
+ readable
+
+ """
+
if not s:
return ''
if quote(s).find('%') >= 0:
@@ -51,13 +106,33 @@
class IncompleteCounter:
+ """Keep track of the number of oustanding incomplete connections.
+
+ @type c: C{int}
+ @ivar c: the number of outstanding incomplete connections
+
+ """
+
def __init__(self):
+ """Initialize the counter."""
self.c = 0
+
def increment(self):
+ """Increment the counter."""
self.c += 1
+
def decrement(self):
+ """Decrement the counter."""
self.c -= 1
+
def toomany(self):
+ """Determine if the maximum number of incomplete connections has been reached.
+
+ @rtype: C{boolean}
+ @return: whether the maximum has been reached
+
+ """
+
return self.c >= MAX_INCOMPLETE
incompletecounter = IncompleteCounter()
@@ -66,8 +141,94 @@
# header, options, download id, my id, [length, message]
class Connection:
+ """A single, possibly encrypted, connection to a peer.
+
+ @type Encoder: L{Encoder}
+ @ivar Encoder: the collection of all connections
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @ivar connection: the low-level connection to the peer
+ @type connecter: L{Connecter.Connecter}
+ @ivar connecter: the Connecter instance to use
+ @type id: C{string}
+ @ivar id: the peer ID of the peer
+ @type locally_initiated: C{boolean}
+ @ivar locally_initiated: whether the connectio was initiated locally
+ @type readable_id: C{string}
+ @ivar readable_id: the human-readable ID of the peer
+ @type complete: C{boolean}
+ @ivar complete: whether the handshake is complete
+ @type keepalive: C{method}
+ @ivar keepalive: the method to call to send a keepalive message on the connection
+ @type closed: C{boolean}
+ @ivar closed: whether the connection has been closed
+ @type buffer: C{string}
+ @ivar buffer: the buffer of received data from the connection
+ @type bufferlen: C{int}
+ @ivar bufferlen: the length of the buffer
+ @type log: C{file}
+ @ivar log: the log file to write to
+ @type read: C{method}
+ @ivar read: the method to use to read from the connection
+ @type write: C{method}
+ @ivar write: the method to use to write to the connection
+ @type cryptmode: C{int}
+ @ivar cryptmode: the type of encryption being used
+ @type encrypter: L{DebTorrent.BTcrypto.Crypto}
+ @ivar encrypter: the already created Crypto instance, if the connection
+ was externally handshaked (optional, defaults to creating a new one)
+ @type encrypted: C{boolean}
+ @ivar encrypted: whether the connection is encrypted (will be None if
+ that is not yet known)
+ @type next_len: C{int}
+ @ivar next_len: the next amount of data to read from the connection
+ @type next_func: C{method}
+ @ivar next_func: the next method to use to process incoming data on the
+ connection
+ @type options: C{string}
+ @ivar options: the options read from the externally handshaked
+ connection (optional, defaults to None)
+ @type _logwritefunc: C{method}
+ @ivar _logwritefunc: the saved write method for intercepting connection
+ writes for logging purposes
+ @type _max_search: C{int}
+ @ivar _max_search: the number of remaining bytes to search for the pattern
+
+ @group Logging: _log_start, _log_write
+ @group Information: get_ip, get_id, get_readable_id, is_locally_initiated, is_encrypted, is_flushed
+ @group Encryption: read_header, _start_crypto, _end_crypto, read_crypto_header, _search_for_pattern, read_encrypted_header
+ @group Incoming Encryption: read_crypto_block3a, read_crypto_block3b,
+ read_crypto_block3c, read_crypto_pad3, read_crypto_ia, read_crypto_block3done
+ @group Outgoing Encryption: read_crypto_block4a, read_crypto_block4b, read_crypto_pad4, read_crypto_block4done
+ @group BitTorrent Handshake: _read_header, read_options, read_download_id, read_peer_id
+ @group BitTorrent Messages: read_len, read_message, read_dead
+ @group Data: send_message_raw, _write, data_came_in, _write_buffer, _read, _switch_to_read2, _read2
+ @group Connection: _auto_close, close, sever, connection_flushed, connection_lost
+
+ """
+
def __init__(self, Encoder, connection, id,
ext_handshake=False, encrypted = None, options = None):
+ """Initialize the instance and start handling the connection.
+
+ @type Encoder: L{Encoder}
+ @param Encoder: the collection of all connections
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the low-level connection to the peer
+ @type id: C{string}
+ @param id: the peer ID of the peer to connect to (will be None if
+ the connection is being initiated locally)
+ @type ext_handshake: C{boolean}
+ @param ext_handshake: whether the connection has already been
+ handshaked by another module (optional, defaults to False)
+ @type encrypted: C{DebTorrent.BT1Crypto.Crypto}
+ @param encrypted: the already created Crypto instance, if the connection
+ was externally handshaked (optional, defaults to creating a new one)
+ @type options: C{string}
+ @param options: the options read from the externally handshaked
+ connection (optional, defaults to None)
+
+ """
+
self.Encoder = Encoder
self.connection = connection
self.connecter = Encoder.connecter
@@ -114,7 +275,15 @@
self.Encoder.raw_server.add_task(self._auto_close, 30)
- def _log_start(self): # only called with DEBUG = True
+ def _log_start(self):
+ """Start logging detailed connection data.
+
+ Only called with DEBUG = True (i.e. never). Adds an intercept function
+ so that all connection writes are first written to the log file, then
+ out on the connection.
+
+ """
+
self.log = open('peerlog.'+self.get_ip()+'.txt','a')
self.log.write('connected - ')
if self.locally_initiated:
@@ -125,34 +294,105 @@
self.write = self._log_write
def _log_write(self, s):
+ """Write to the log file, then the connection.
+
+ @type s: C{string}
+ @param s: the data to write
+
+ """
+
self.log.write('w:'+b2a_hex(s)+'\n')
self._logwritefunc(s)
def get_ip(self, real=False):
+ """Get the IP address of the connection.
+
+ @type real: C{boolean}
+ @param real: whether to double check that it's the reall IP address
+ @rtype: C{string}
+ @return: the IP address
+
+ """
+
return self.connection.get_ip(real)
def get_id(self):
+ """Get the peer ID.
+
+ @rtype: C{string}
+ @return: the peer ID
+
+ """
+
return self.id
def get_readable_id(self):
+ """Get the human-readable peer ID.
+
+ @rtype: C{string}
+ @return: the peer ID
+
+ """
+
return self.readable_id
def is_locally_initiated(self):
+ """Determine whether the connection was locally initiated.
+
+ @rtype: C{boolean}
+ @return: whether the connection was locally initiated
+
+ """
+
return self.locally_initiated
def is_encrypted(self):
+ """Determine whether the connection is encrypted.
+
+ @rtype: C{boolean}
+ @return: whether the connection is encrypted
+
+ """
+
return bool(self.encrypted)
def is_flushed(self):
+ """Determine whether the connection is flushed.
+
+ @rtype: C{boolean}
+ @return: whether the connection is flushed
+
+ """
+
return self.connection.is_flushed()
def _read_header(self, s):
+ """Read the protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s == chr(len(protocol_name))+protocol_name:
return 8, self.read_options
return None
def read_header(self, s):
+ """Read the possibly encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if self._read_header(s):
if self.encrypted or self.Encoder.config['crypto_stealth']:
return None
@@ -170,6 +410,7 @@
################## ENCRYPTION SUPPORT ######################
def _start_crypto(self):
+ """Setup the connection for encrypted communication."""
self.encrypter.setrawaccess(self._read,self._write)
self.write = self.encrypter.write
self.read = self.encrypter.read
@@ -177,11 +418,22 @@
self.buffer = self.encrypter.decrypt(self.buffer)
def _end_crypto(self):
+ """Return the connection back to unencrypted communication."""
self.read = self._read
self.write = self._write
self.encrypter = None
def read_crypto_header(self, s):
+ """Read the encryption key.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
self.encrypter.received_key(s)
self.encrypter.set_skey(self.Encoder.download_id)
if self.locally_initiated:
@@ -205,6 +457,17 @@
return 0, self.read_crypto_block3a
def _search_for_pattern(self, s, pat):
+ """Search for a pattern in the encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @type pat: C{string}
+ @param pat: the pattern to find
+ @rtype: C{boolean}
+ @return: whether the pattern was found
+
+ """
+
p = s.find(pat)
if p < 0:
if len(s) >= len(pat):
@@ -220,11 +483,30 @@
### INCOMING CONNECTION ###
def read_crypto_block3a(self, s):
+ """Find the block3a crypto information in the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+
+ """
+
if not self._search_for_pattern(s,self.encrypter.block3a):
return -1, self.read_crypto_block3a # wait for more data
return len(self.encrypter.block3b), self.read_crypto_block3b
def read_crypto_block3b(self, s):
+ """Process the block3b crypto information in the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{boolean}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s != self.encrypter.block3b:
return None
self.Encoder.connecter.external_connection_made += 1
@@ -232,6 +514,16 @@
return 14, self.read_crypto_block3c
def read_crypto_block3c(self, s):
+ """Read the encrypted protocol mode.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{boolean}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s[:8] != ('\x00'*8): # check VC
return None
self.cryptmode = toint(s[8:12]) % 4
@@ -246,6 +538,16 @@
return padlen+2, self.read_crypto_pad3
def read_crypto_pad3(self, s):
+ """Read the encrypted protocol padding.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
s = s[-2:]
ialen = (ord(s[0])<<8)+ord(s[1])
if ialen > 65535:
@@ -264,6 +566,16 @@
return self.read_crypto_block3done()
def read_crypto_ia(self, s):
+ """Read the initial payload data from the connection.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if DEBUG:
self._log_start()
self.log.write('r:'+b2a_hex(s)+'(ia)\n')
@@ -272,6 +584,16 @@
return self.read_crypto_block3done(s)
def read_crypto_block3done(self, ia=''):
+ """Finish with the encrypted header.
+
+ @type ia: C{string}
+ @param ia: the initial payload data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if DEBUG:
if not self.log:
self._log_start()
@@ -285,12 +607,32 @@
### OUTGOING CONNECTION ###
def read_crypto_block4a(self, s):
+ """Read the encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if not self._search_for_pattern(s,self.encrypter.VC_pattern()):
return -1, self.read_crypto_block4a # wait for more data
self._start_crypto()
return 6, self.read_crypto_block4b
def read_crypto_block4b(self, s):
+ """Read the encrypted protocol mode and padding.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
self.cryptmode = toint(s[:4]) % 4
if self.cryptmode == 1: # only header encryption
if self.Encoder.config['crypto_only']:
@@ -305,10 +647,28 @@
return self.read_crypto_block4done()
def read_crypto_pad4(self, s):
+ """Read the encrypted protocol padding.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
# discard data
return self.read_crypto_block4done()
def read_crypto_block4done(self):
+ """Finish with the encrypted header.
+
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if DEBUG:
self._log_start()
if self.cryptmode == 1: # only handshake encryption
@@ -322,15 +682,45 @@
### START PROTOCOL OVER ENCRYPTED CONNECTION ###
def read_encrypted_header(self, s):
+ """Read the regular protocol name header from the encrypted stream.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
return self._read_header(s)
################################################
def read_options(self, s):
+ """Read the options from the header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
self.options = s
return 20, self.read_download_id
def read_download_id(self, s):
+ """Verify the torrent infohash from the header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if ( s != self.Encoder.download_id
or not self.Encoder.check_ip(ip=self.get_ip()) ):
return None
@@ -342,6 +732,16 @@
return 20, self.read_peer_id
def read_peer_id(self, s):
+ """Read/verify the peer's ID.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if not self.encrypted and self.Encoder.config['crypto_only']:
return None # allows older trackers to ping,
# but won't proceed w/ connections
@@ -363,29 +763,61 @@
return 4, self.read_len
def read_len(self, s):
+ """Read the length of the message.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
l = toint(s)
if l > self.Encoder.max_len:
return None
return l, self.read_message
def read_message(self, s):
+ """Read the message.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s != '':
self.connecter.got_message(self, s)
return 4, self.read_len
def read_dead(self, s):
+ """Return None to close the connection.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection (not used)
+ @rtype: None
+ @return: None
+
+ """
+
return None
def _auto_close(self):
+ """Close the connection if the handshake is not yet complete."""
if not self.complete:
self.close()
def close(self):
+ """Close the connection."""
if not self.closed:
self.connection.close()
self.sever()
def sever(self):
+ """Clean up the connection for closing."""
if self.log:
self.log.write('closed\n')
self.log.close()
@@ -397,19 +829,56 @@
incompletecounter.decrement()
def send_message_raw(self, message):
+ """Write a message out on the connection.
+
+ @type message: C{string}
+ @param message: the data to write to the connection
+
+ """
+
self.write(message)
def _write(self, message):
+ """Write a raw message out on the connection.
+
+ @type message: C{string}
+ @param message: the raw data to write to the connection
+
+ """
+
if not self.closed:
self.connection.write(message)
def data_came_in(self, connection, s):
+ """Process the incoming data on the connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection the data came in on (not used)
+ @type s: C{string}
+ @param s: the incoming data from the connection
+
+ """
+
self.read(s)
def _write_buffer(self, s):
+ """Write data back onto the buffer.
+
+ @type s: C{string}
+ @param s: the data to rebuffer
+
+ """
+
self.buffer = s+self.buffer
def _read(self, s):
+ """Process the data that comes in.
+
+ @type s: C{string}
+ @param s: the (unencrypted) incoming data from the connection
+
+ """
+
if self.log:
self.log.write('r:'+b2a_hex(s)+'\n')
self.Encoder.measurefunc(len(s))
@@ -445,6 +914,7 @@
return
def _switch_to_read2(self):
+ """Switch from _read to the more efficient _read2 method."""
self._write_buffer = None
if self.encrypter:
self.encrypter.setrawaccess(self._read2,self._write)
@@ -453,7 +923,18 @@
self.bufferlen = len(self.buffer)
self.buffer = [self.buffer]
- def _read2(self, s): # more efficient, requires buffer['',''] & bufferlen
+ def _read2(self, s):
+ """Efficiently process the data that comes in.
+
+ More efficient, buffers the incoming data by appending it to a list
+ rather than creating a new string by adding it to the end. Requires
+ buffer to be a list of strings, and bufferlen to be it's total length.
+
+ @type s: C{string}
+ @param s: the (unencrypted) incoming data from the connection
+
+ """
+
if self.log:
self.log.write('r:'+b2a_hex(s)+'\n')
self.Encoder.measurefunc(len(s))
@@ -502,22 +983,109 @@
def connection_flushed(self, connection):
+ """Flush the connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was flushed (not used)
+
+ """
+
if self.complete:
self.connecter.connection_flushed(self)
def connection_lost(self, connection):
+ """Sever the connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was lost (not used)
+
+ """
+
if self.Encoder.connections.has_key(connection):
self.sever()
class _dummy_banlist:
+ """A dummy list of banned peers."""
+
def includes(self, x):
+ """Check if a peer is banned (always returns False).
+
+ @type x: C{string}
+ @param x: the IP address of the peer to check
+ @rtype: C{boolean}
+ @return: whether the peer is banned
+
+ """
+
return False
class Encoder:
+ """The collection of all (possibly encrypted) connections.
+
+ @type raw_server: L{DebTorrent.RawServer.RawServer}
+ @ivar raw_server: the server instance to use
+ @type connecter: L{Connecter.Connecter}
+ @ivar connecter: the Connecter instance to use
+ @type my_id: C{string}
+ @ivar my_id: the peer ID to use
+ @type max_len: C{int}
+ @ivar max_len: the maximum length message to accept
+ @type schedulefunc: C{method}
+ @ivar schedulefunc: method to call to schedule future function invocation
+ @type keepalive_delay: C{int}
+ @ivar keepalive_delay: the delay between sending keepalive messages
+ @type download_id: C{string}
+ @ivar download_id: the infohash of the torrent being downloaded
+ @type measurefunc: C{method}
+ @ivar measurefunc: method to call with the size of incoming data
+ @type config: C{dictionary}
+ @ivar config: the configuration parameters
+ @type connections: C{dictionary}
+ @ivar connections: keys are the L{DebTorrent.SocketHandler.SingleSocket}
+ connections, values are the corresponding L{Connection} instances
+ @type banned: C{dictionary}
+ @ivar banned: keys are IP addresses that are banned
+ @type external_bans: C{class}
+ @ivar external_bans: the instance to check for banned peer's in
+ @type to_connect: C{list} of ((C{string}, C{int}), C{string}, C{boolean})
+ @ivar to_connect: the list of IP address, port, peer ID, and whether to encrypt
+ @type paused: C{boolean}
+ @ivar paused: whether the download is paused
+ @type max_connections: C{int}
+ @ivar max_connections: the maximum number of connections to accept
+
+ """
+
def __init__(self, connecter, raw_server, my_id, max_len,
schedulefunc, keepalive_delay, download_id,
measurefunc, config, bans=_dummy_banlist() ):
+ """Initialize the instance.
+
+ @type connecter: L{Connecter.Connecter}
+ @param connecter: the Connecter instance to use
+ @type raw_server: L{DebTorrent.RawServer.RawServer}
+ @param raw_server: the server instance to use
+ @type my_id: C{string}
+ @param my_id: the peer ID to use
+ @type max_len: C{int}
+ @param max_len: the maximum length message to accept
+ @type schedulefunc: C{method}
+ @param schedulefunc: method to call to schedule future function invocation
+ @type keepalive_delay: C{int}
+ @param keepalive_delay: the delay between sending keepalive messages
+ @type download_id: C{string}
+ @param download_id: the infohash of the torrent being downloaded
+ @type measurefunc: C{method}
+ @param measurefunc: method to call with the size of incoming data
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+ @type bans: C{class}
+ @param bans: the instance to check for banned peer's in
+ (optional, defaults to an instance of L{_dummy_banlist})
+
+ """
+
self.raw_server = raw_server
self.connecter = connecter
self.my_id = my_id
@@ -539,6 +1107,7 @@
schedulefunc(self.send_keepalives, keepalive_delay)
def send_keepalives(self):
+ """Periodically send keepalive messages on all the connections."""
self.schedulefunc(self.send_keepalives, self.keepalive_delay)
if self.paused:
return
@@ -546,11 +1115,19 @@
c.keepalive()
def start_connections(self, list):
+ """Add many connections from a list to a queue to start.
+
+ @type list: C{list} of ((C{string}, C{int}), C{string}, C{boolean})
+ @param list: the list of IP address, port, peer ID, and whether to encrypt
+
+ """
+
if not self.to_connect:
self.raw_server.add_task(self._start_connection_from_queue)
self.to_connect = list
def _start_connection_from_queue(self):
+ """Start a connection in the queue."""
if self.connecter.external_connection_made:
max_initiate = self.config['max_initiate']
else:
@@ -568,6 +1145,20 @@
self.raw_server.add_task(self._start_connection_from_queue, delay)
def start_connection(self, dns, id, encrypted = None):
+ """Start a connection to a peer.
+
+ @type dns: (C{string}, C{int})
+ @param dns: the IP address and port to connect to
+ @type id: C{string}
+ @param id: the peer ID of the peer
+ @type encrypted: C{boolean}
+ @param encrypted: whether to encrypt the connection
+ (optional, defaults to True)
+ @rtype: C{boolean}
+ @return: False if an error occurs
+
+ """
+
if ( self.paused
or len(self.connections) >= self.max_connections
or id == self.my_id
@@ -597,11 +1188,36 @@
return True
def _start_connection(self, dns, id, encrypted = None):
+ """Schedule the start of a connection to a peer.
+
+ @type dns: (C{string}, C{int})
+ @param dns: the IP address and port to connect to
+ @type id: C{string}
+ @param id: the peer ID of the peer
+ @type encrypted: C{boolean}
+ @param encrypted: whether to encrypt the connection
+ (optional, defaults to True)
+
+ """
+
def foo(self=self, dns=dns, id=id, encrypted=encrypted):
self.start_connection(dns, id, encrypted)
self.schedulefunc(foo, 0)
def check_ip(self, connection=None, ip=None):
+ """Check whether the connection to the IP is allowed.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection whose IP should be checked
+ (optional, but one of connection/ip must be specified)
+ @type ip: C{string}
+ @param ip: the IP address of the connection
+ (optional, but one of connection/ip must be specified)
+ @rtype: C{boolean}
+ @return: whether the connection is allowed
+
+ """
+
if not ip:
ip = connection.get_ip(True)
if self.config['security'] and self.banned.has_key(ip):
@@ -611,6 +1227,19 @@
return True
def got_id(self, connection):
+ """Check whether the connection to the peer ID is allowed.
+
+ Checks whether the peer ID is ours, or is already connected on another
+ connection. If the security config option is set, it also checks if
+ the IP is already connected, in which case this one is not allowed.
+
+ @type connection: L{Connection}
+ @param connection: the connection whose peer ID should be checked
+ @rtype: C{boolean}
+ @return: whether the connection is allowed
+
+ """
+
if connection.id == self.my_id:
self.connecter.external_connection_made -= 1
return False
@@ -627,6 +1256,15 @@
return True
def external_connection_made(self, connection):
+ """Process an externally made connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was made
+ @rtype: C{boolean}
+ @return: whether the connection is accepted
+
+ """
+
if self.paused or len(self.connections) >= self.max_connections:
connection.close()
return False
@@ -637,6 +1275,23 @@
def externally_handshaked_connection_made(self, connection, options,
already_read, encrypted = None):
+ """Process an externally handshaked connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was made
+ @type options: C{string}
+ @param options: the options read from the externally handshaked
+ connection (optional, defaults to None)
+ @type already_read: C{string}
+ @param already_read: the data that has already been received on the connection
+ @type encrypted: C{DebTorrent.BT1Crypto.Crypto}
+ @param encrypted: the already created Crypto instance, if the connection
+ was externally handshaked (optional, defaults to creating a new one)
+ @rtype: C{boolean}
+ @return: whether the connection is accepted
+
+ """
+
if ( self.paused
or len(self.connections) >= self.max_connections
or not self.check_ip(connection=connection) ):
@@ -651,12 +1306,29 @@
return True
def close_all(self):
+ """Close all the currently open connections."""
for c in self.connections.values():
c.close()
self.connections = {}
def ban(self, ip):
+ """Ban an IP address from ever connecting again.
+
+ @type ip: C{string}
+ @param ip: the IP address to ban
+
+ """
+
self.banned[ip] = 1
def pause(self, flag):
+ """Set the paused flag.
+
+ When paused, no new connections are made and no keepalives are sent.
+
+ @type flag: C{boolean}
+ @param flag: whether the download is paused
+
+ """
+
self.paused = flag
Modified: debtorrent/trunk/DebTorrent/BT1/FileSelector.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/FileSelector.py?rev=188&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/FileSelector.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/FileSelector.py Sat Jul 28 08:42:10 2007
@@ -161,7 +161,7 @@
def unpickle(self, d):
"""Unpickle the previously saved state.
- Data is in the format:
+ Data is in the format::
d['priority'] = [file #1 priority, file #2 priority, ...]
It is a list of download priorities for each file. The priority may be::
Modified: debtorrent/trunk/DebTorrent/BT1/NatCheck.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/NatCheck.py?rev=188&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/NatCheck.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/NatCheck.py Sat Jul 28 08:42:10 2007
@@ -168,7 +168,7 @@
################## ENCRYPTION SUPPORT ######################
def _start_crypto(self):
- """Setup the instance for the encrypted connection."""
+ """Setup the connection for encrypted communication."""
self.encrypter.setrawaccess(self._read,self._write)
self.write = self.encrypter.write
self.read = self.encrypter.read
@@ -202,7 +202,7 @@
return 1, self.read_crypto_block4a
def _search_for_pattern(self, s, pat):
- """Search for a patter in the encrypted protocol header.
+ """Search for a pattern in the encrypted protocol header.
@type s: C{string}
@param s: the incoming data from the connection
@@ -247,7 +247,7 @@
return 6, self.read_crypto_block4b
def read_crypto_block4b(self, s):
- """Read the encrypted protocol mode and padding.
+ """Read the encrypted protocol mode.
@type s: C{string}
@param s: the incoming data from the connection
More information about the Debtorrent-commits
mailing list