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