from twisted.internet.protocol import DatagramProtocol
from twisted.internet.error import CannotListenError
-from twisted.internet.interfaces import IReactorMulticast
-from twisted.python import log
from nattraverso.utils import is_rfc1918_ip, is_bogus_ip
# Public API
def get_port_mapper(proto="TCP"):
- """
- Returns a L{NATMapper} instance, suited to map a port for
- the given protocol. Defaults to TCP.
-
- For the moment, only upnp mapper is available. It accepts both UDP and TCP.
-
- @param proto: The protocol: 'TCP' or 'UDP'
- @type proto: string
- @return: A deferred called with a L{NATMapper} instance
- @rtype: L{twisted.internet.defer.Deferred}
- """
- import nattraverso.pynupnp
- return nattraverso.pynupnp.get_port_mapper()
-
-class NATMapper:
- """
- Define methods to map port objects (as returned by twisted's listenXX).
- This allows NAT to be traversed from incoming packets.
-
- Currently the only implementation of this class is the UPnP Mapper, which
- can map UDP and TCP ports, if an UPnP Device exists.
- """
- def __init__(self):
- raise NotImplementedError("Cannot instantiate the class")
-
- def map(self, port):
- """
- Create a mapping for the given twisted's port object.
-
- The deferred will call back with a tuple (extaddr, extport):
- - extaddr: The ip string of the external ip address of this host
- - extport: the external port number used to map the given Port object
-
- When called multiple times with the same Port,
- callback with the existing mapping.
-
- @param port: The port object to map
- @type port: a L{twisted.internet.interfaces.IListeningPort} object
- @return: A deferred called with the above defined tuple
- @rtype: L{twisted.internet.defer.Deferred}
- """
- raise NotImplementedError
-
- def info(self, port):
- """
- Returns the existing mapping for the given port object. That means map()
- has to be called before.
-
- @param port: The port object to retreive info from
- @type port: a L{twisted.internet.interfaces.IListeningPort} object
- @raise ValueError: When there is no such existing mapping
- @return: a tuple (extaddress, extport).
- @see: L{map() function<map>}
- """
- raise NotImplementedError
+ """
+ Returns a L{NATMapper} instance, suited to map a port for
+ the given protocol. Defaults to TCP.
+
+ For the moment, only upnp mapper is available. It accepts both UDP and TCP.
+
+ @param proto: The protocol: 'TCP' or 'UDP'
+ @type proto: string
+ @return: A deferred called with a L{NATMapper} instance
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ import nattraverso.pynupnp
+ return nattraverso.pynupnp.get_port_mapper()
- def unmap(self, port):
- """
- Remove an existing mapping for the given twisted's port object.
-
- @param port: The port object to unmap
- @type port: a L{twisted.internet.interfaces.IListeningPort} object
- @return: A deferred called with None
- @rtype: L{twisted.internet.defer.Deferred}
- @raise ValueError: When there is no such existing mapping
- """
- raise NotImplementedError
-
- def get_port_mappings(self):
- """
- Returns a deferred that will be called with a dictionnary of the
- existing mappings.
-
- The dictionnary structure is the following:
- - Keys: tuple (protocol, external_port)
- - protocol is "TCP" or "UDP".
- - external_port is the external port number, as see on the
- WAN side.
- - Values:tuple (internal_ip, internal_port)
- - internal_ip is the LAN ip address of the host.
- - internal_port is the internal port number mapped
- to external_port.
-
- @return: A deferred called with the above defined dictionnary
- @rtype: L{twisted.internet.defer.Deferred}
- """
- raise NotImplementedError
-
- def _check_valid_port(self, port):
- """Various Port object validity checks. Raise a ValueError."""
- if not isinstance(port, BasePort):
- raise ValueError("expected a Port, got %r"%(port))
-
- if not port.connected:
- raise ValueError("Port %r is not listening"%(port))
+class NATMapper:
+ """
+ Define methods to map port objects (as returned by twisted's listenXX).
+ This allows NAT to be traversed from incoming packets.
+
+ Currently the only implementation of this class is the UPnP Mapper, which
+ can map UDP and TCP ports, if an UPnP Device exists.
+ """
+ def __init__(self):
+ raise NotImplementedError("Cannot instantiate the class")
+
+ def map(self, port):
+ """
+ Create a mapping for the given twisted's port object.
+
+ The deferred will call back with a tuple (extaddr, extport):
+ - extaddr: The ip string of the external ip address of this host
+ - extport: the external port number used to map the given Port object
+
+ When called multiple times with the same Port,
+ callback with the existing mapping.
+
+ @param port: The port object to map
+ @type port: a L{twisted.internet.interfaces.IListeningPort} object
+ @return: A deferred called with the above defined tuple
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ raise NotImplementedError
+
+ def info(self, port):
+ """
+ Returns the existing mapping for the given port object. That means map()
+ has to be called before.
+
+ @param port: The port object to retreive info from
+ @type port: a L{twisted.internet.interfaces.IListeningPort} object
+ @raise ValueError: When there is no such existing mapping
+ @return: a tuple (extaddress, extport).
+ @see: L{map() function<map>}
+ """
+ raise NotImplementedError
+
+ def unmap(self, port):
+ """
+ Remove an existing mapping for the given twisted's port object.
+
+ @param port: The port object to unmap
+ @type port: a L{twisted.internet.interfaces.IListeningPort} object
+ @return: A deferred called with None
+ @rtype: L{twisted.internet.defer.Deferred}
+ @raise ValueError: When there is no such existing mapping
+ """
+ raise NotImplementedError
+
+ def get_port_mappings(self):
+ """
+ Returns a deferred that will be called with a dictionnary of the
+ existing mappings.
+
+ The dictionnary structure is the following:
+ - Keys: tuple (protocol, external_port)
+ - protocol is "TCP" or "UDP".
+ - external_port is the external port number, as see on the
+ WAN side.
+ - Values:tuple (internal_ip, internal_port)
+ - internal_ip is the LAN ip address of the host.
+ - internal_port is the internal port number mapped
+ to external_port.
+
+ @return: A deferred called with the above defined dictionnary
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ raise NotImplementedError
+
+ def _check_valid_port(self, port):
+ """Various Port object validity checks. Raise a ValueError."""
+ if not isinstance(port, BasePort):
+ raise ValueError("expected a Port, got %r"%(port))
+
+ if not port.connected:
+ raise ValueError("Port %r is not listening"%(port))
+
+ loc_addr = port.getHost()
+ if loc_addr.port == 0:
+ raise ValueError("Port %r has port number of 0"%(port))
- loc_addr = port.getHost()
- if loc_addr.port == 0:
- raise ValueError("Port %r has port number of 0"%(port))
-
from nattraverso.pynupnp.upnp import search_upnp_device, UPnPMapper
def get_external_ip():
- """
- Returns a deferred which will be called with the WAN ip address
- retreived through UPnP. The ip is a string of the form "x.x.x.x"
-
- @return: A deferred called with the external ip address of this host
- @rtype: L{twisted.internet.defer.Deferred}
- """
- return search_upnp_device().addCallback(lambda x: x.get_external_ip())
+ """
+ Returns a deferred which will be called with the WAN ip address
+ retreived through UPnP. The ip is a string of the form "x.x.x.x"
+
+ @return: A deferred called with the external ip address of this host
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ return search_upnp_device().addCallback(lambda x: x.get_external_ip())
def get_port_mapper():
- """
- Returns a deferred which will be called with a L{UPnPMapper} instance.
- This is a L{nattraverso.portmapper.NATMapper} implementation.
-
- @return: A deferred called with the L{UPnPMapper} instance.
- @rtype: L{twisted.internet.defer.Deferred}
- """
- return search_upnp_device().addCallback(lambda x: UPnPMapper(x))
+ """
+ Returns a deferred which will be called with a L{UPnPMapper} instance.
+ This is a L{nattraverso.portmapper.NATMapper} implementation.
+
+ @return: A deferred called with the L{UPnPMapper} instance.
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ return search_upnp_device().addCallback(lambda x: UPnPMapper(x))
Config.typed = False
class SoapError(Exception):
- """
- This is a SOAP error message, not an HTTP error message.
-
- The content of this error is a SOAPpy structure representing the
- SOAP error message.
- """
- pass
+ """
+ This is a SOAP error message, not an HTTP error message.
+
+ The content of this error is a SOAPpy structure representing the
+ SOAP error message.
+ """
+ pass
class SoapProxy:
- """
- Proxy for an url to which we send SOAP rpc calls.
- """
- def __init__(self, url, prefix):
- """
- Init the proxy, it will connect to the given url, using the
- given soap namespace.
-
- @param url: The url of the remote host to call
- @param prefix: The namespace prefix to use, eg.
- 'urn:schemas-upnp-org:service:WANIPConnection:1'
- """
- logging.debug("Soap Proxy: '%s', prefix: '%s'", url, prefix)
- self._url = url
- self._prefix = prefix
-
- def call(self, method, **kwargs):
- """
- Call the given remote method with the given arguments, as keywords.
-
- Returns a deferred, called with SOAPpy structure representing
- the soap response.
-
- @param method: The method name to call, eg. 'GetExternalIP'
- @param kwargs: The parameters of the call, as keywords
- @return: A deferred called with the external ip address of this host
- @rtype: L{twisted.internet.defer.Deferred}
- """
- payload = SOAPpy.buildSOAP(method=method, config=Config, namespace=self._prefix, kw=kwargs)
- # Here begins the nasty hack
- payload = payload.replace(
- # Upnp wants s: instead of SOAP-ENV
- 'SOAP-ENV','s').replace(
- # Doesn't seem to like these encoding stuff
- 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"', '').replace(
- 'SOAP-ENC:root="1"', '').replace(
- # And it wants u: instead of ns1 namespace for arguments..
- 'ns1','u')
-
- logging.debug("SOAP Payload:\n%s", payload)
-
- return client.getPage(self._url, postdata=payload, method="POST",
- headers={'content-type': 'text/xml', 'SOAPACTION': '%s#%s' % (self._prefix, method)}
- ).addCallbacks(self._got_page, self._got_error)
-
- def _got_page(self, result):
- """
- The http POST command was successful, we parse the SOAP
- answer, and return it.
-
- @param result: the xml content
- """
- parsed = SOAPpy.parseSOAPRPC(result)
-
- logging.debug("SOAP Answer:\n%s", result)
- logging.debug("SOAP Parsed Answer: %r", parsed)
-
- return parsed
-
- def _got_error(self, res):
- """
- The HTTP POST command did not succeed, depending on the error type:
- - it's a SOAP error, we parse it and return a L{SoapError}.
- - it's another type of error (http, other), we raise it as is
- """
- logging.debug("SOAP Error:\n%s", res)
-
- if isinstance(res.value, error.Error):
- try:
- logging.debug("SOAP Error content:\n%s", res.value.response)
- raise SoapError(SOAPpy.parseSOAPRPC(res.value.response)["detail"])
- except:
- raise
- raise Exception(res.value)
+ """
+ Proxy for an url to which we send SOAP rpc calls.
+ """
+ def __init__(self, url, prefix):
+ """
+ Init the proxy, it will connect to the given url, using the
+ given soap namespace.
+
+ @param url: The url of the remote host to call
+ @param prefix: The namespace prefix to use, eg.
+ 'urn:schemas-upnp-org:service:WANIPConnection:1'
+ """
+ logging.debug("Soap Proxy: '%s', prefix: '%s'", url, prefix)
+ self._url = url
+ self._prefix = prefix
+
+ def call(self, method, **kwargs):
+ """
+ Call the given remote method with the given arguments, as keywords.
+
+ Returns a deferred, called with SOAPpy structure representing
+ the soap response.
+
+ @param method: The method name to call, eg. 'GetExternalIP'
+ @param kwargs: The parameters of the call, as keywords
+ @return: A deferred called with the external ip address of this host
+ @rtype: L{twisted.internet.defer.Deferred}
+ """
+ payload = SOAPpy.buildSOAP(method=method, config=Config, namespace=self._prefix, kw=kwargs)
+ # Here begins the nasty hack
+ payload = payload.replace(
+ # Upnp wants s: instead of SOAP-ENV
+ 'SOAP-ENV','s').replace(
+ # Doesn't seem to like these encoding stuff
+ 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"', '').replace(
+ 'SOAP-ENC:root="1"', '').replace(
+ # And it wants u: instead of ns1 namespace for arguments..
+ 'ns1','u')
+
+ logging.debug("SOAP Payload:\n%s", payload)
+
+ return client.getPage(self._url, postdata=payload, method="POST",
+ headers={'content-type': 'text/xml', 'SOAPACTION': '%s#%s' % (self._prefix, method)}
+ ).addCallbacks(self._got_page, self._got_error)
+
+ def _got_page(self, result):
+ """
+ The http POST command was successful, we parse the SOAP
+ answer, and return it.
+
+ @param result: the xml content
+ """
+ parsed = SOAPpy.parseSOAPRPC(result)
+
+ logging.debug("SOAP Answer:\n%s", result)
+ logging.debug("SOAP Parsed Answer: %r", parsed)
+
+ return parsed
+
+ def _got_error(self, res):
+ """
+ The HTTP POST command did not succeed, depending on the error type:
+ - it's a SOAP error, we parse it and return a L{SoapError}.
+ - it's another type of error (http, other), we raise it as is
+ """
+ logging.debug("SOAP Error:\n%s", res)
+
+ if isinstance(res.value, error.Error):
+ try:
+ logging.debug("SOAP Error content:\n%s", res.value.response)
+ raise SoapError(SOAPpy.parseSOAPRPC(res.value.response)["detail"])
+ except:
+ raise
+ raise Exception(res.value)
# Allowed UPnP services to use when mapping ports/external addresses
WANSERVICES = ['urn:schemas-upnp-org:service:WANIPConnection:1',
- 'urn:schemas-upnp-org:service:WANPPPConnection:1']
+ 'urn:schemas-upnp-org:service:WANPPPConnection:1']
class UPnPXml:
- """
- This objects parses the XML definition, and stores the useful
- results in attributes.
+ """
+ This objects parses the XML definition, and stores the useful
+ results in attributes.
+
+ The device infos dictionnary may contain the following keys:
+ - friendlyname: A friendly name to call the device.
+ - manufacturer: A manufacturer name for the device.
+
+ Here are the different attributes:
+ - deviceinfos: A dictionnary of device infos as defined above.
+ - controlurl: The control url, this is the url to use when sending SOAP
+ requests to the device, relative to the base url.
+ - wanservice: The WAN service to be used, one of the L{WANSERVICES}
+ - urlbase: The base url to use when talking in SOAP to the device.
+
+ The full url to use is obtained by urljoin(urlbase, controlurl)
+ """
+
+ def __init__(self, xml):
+ """
+ Parse the given XML string for UPnP infos. This creates the attributes
+ when they are found, or None if no value was found.
+
+ @param xml: a xml string to parse
+ """
+ logging.debug("Got UPNP Xml description:\n%s", xml)
+ doc = minidom.parseString(xml)
+
+ # Fetch various device info
+ self.deviceinfos = {}
+ try:
+ attributes = {
+ 'friendlyname':'friendlyName',
+ 'manufacturer' : 'manufacturer'
+ }
+ device = doc.getElementsByTagName('device')[0]
+ for name, tag in attributes.iteritems():
+ try:
+ self.deviceinfos[name] = device.getElementsByTagName(
+ tag)[0].firstChild.datas.encode('utf-8')
+ except:
+ pass
+ except:
+ pass
+
+ # Fetch device control url
+ self.controlurl = None
+ self.wanservice = None
+
+ for service in doc.getElementsByTagName('service'):
+ try:
+ stype = service.getElementsByTagName(
+ 'serviceType')[0].firstChild.data.encode('utf-8')
+ if stype in WANSERVICES:
+ self.controlurl = service.getElementsByTagName(
+ 'controlURL')[0].firstChild.data.encode('utf-8')
+ self.wanservice = stype
+ break
+ except:
+ pass
+
+ # Find base url
+ self.urlbase = None
+ try:
+ self.urlbase = doc.getElementsByTagName(
+ 'URLBase')[0].firstChild.data.encode('utf-8')
+ except:
+ pass
- The device infos dictionnary may contain the following keys:
- - friendlyname: A friendly name to call the device.
- - manufacturer: A manufacturer name for the device.
-
- Here are the different attributes:
- - deviceinfos: A dictionnary of device infos as defined above.
- - controlurl: The control url, this is the url to use when sending SOAP
- requests to the device, relative to the base url.
- - wanservice: The WAN service to be used, one of the L{WANSERVICES}
- - urlbase: The base url to use when talking in SOAP to the device.
-
- The full url to use is obtained by urljoin(urlbase, controlurl)
- """
-
- def __init__(self, xml):
- """
- Parse the given XML string for UPnP infos. This creates the attributes
- when they are found, or None if no value was found.
-
- @param xml: a xml string to parse
- """
- logging.debug("Got UPNP Xml description:\n%s", xml)
- doc = minidom.parseString(xml)
-
- # Fetch various device info
- self.deviceinfos = {}
- try:
- attributes = {
- 'friendlyname':'friendlyName',
- 'manufacturer' : 'manufacturer'
- }
- device = doc.getElementsByTagName('device')[0]
- for name, tag in attributes.iteritems():
- try:
- self.deviceinfos[name] = device.getElementsByTagName(
- tag)[0].firstChild.datas.encode('utf-8')
- except:
- pass
- except:
- pass
-
- # Fetch device control url
- self.controlurl = None
- self.wanservice = None
-
- for service in doc.getElementsByTagName('service'):
- try:
- stype = service.getElementsByTagName(
- 'serviceType')[0].firstChild.data.encode('utf-8')
- if stype in WANSERVICES:
- self.controlurl = service.getElementsByTagName(
- 'controlURL')[0].firstChild.data.encode('utf-8')
- self.wanservice = stype
- break
- except:
- pass
-
- # Find base url
- self.urlbase = None
- try:
- self.urlbase = doc.getElementsByTagName(
- 'URLBase')[0].firstChild.data.encode('utf-8')
- except:
- pass
-
__revision__ = "$id"
def is_rfc1918_ip(ip):
- """
- Checks if the given ip address is a rfc1918 one.
-
- @param ip: The ip address to test
- @type ip: a string "x.x.x.x"
- @return: True if it's a LAN address, False otherwise
- """
- if isinstance(ip, basestring):
- ip = _ip_to_number(ip)
-
- for net, mask in _nets:
- if ip&mask == net:
- return True
-
- return False
+ """
+ Checks if the given ip address is a rfc1918 one.
+
+ @param ip: The ip address to test
+ @type ip: a string "x.x.x.x"
+ @return: True if it's a LAN address, False otherwise
+ """
+ if isinstance(ip, basestring):
+ ip = _ip_to_number(ip)
+
+ for net, mask in _nets:
+ if ip&mask == net:
+ return True
+
+ return False
def is_bogus_ip(ip):
- """
- Checks if the given ip address is bogus, i.e. 0.0.0.0 or 127.0.0.1.
-
- @param ip: The ip address to test
- @type ip: a string "x.x.x.x"
- @return: True if it's bogus, False otherwise
- """
- return ip.startswith('0.') or ip.startswith('127.')
+ """
+ Checks if the given ip address is bogus, i.e. 0.0.0.0 or 127.0.0.1.
+
+ @param ip: The ip address to test
+ @type ip: a string "x.x.x.x"
+ @return: True if it's bogus, False otherwise
+ """
+ return ip.startswith('0.') or ip.startswith('127.')
def _ip_to_number(ipstr):
- """
- Translate a string ip address to a packed number.
-
- @param ipstr: the ip address to transform
- @type ipstr: a string "x.x.x.x"
- @return: an int32 number representing the ip address
- """
- net = [ int(digit) for digit in ipstr.split('.') ] + [ 0, 0, 0 ]
- net = net[:4]
- return ((((((0L+net[0])<<8) + net[1])<<8) + net[2])<<8) +net[3]
+ """
+ Translate a string ip address to a packed number.
+
+ @param ipstr: the ip address to transform
+ @type ipstr: a string "x.x.x.x"
+ @return: an int32 number representing the ip address
+ """
+ net = [ int(digit) for digit in ipstr.split('.') ] + [ 0, 0, 0 ]
+ net = net[:4]
+ return ((((((0L+net[0])<<8) + net[1])<<8) + net[2])<<8) +net[3]
# List of rfc1918 net/mask
_rfc1918_networks = [('127', 8), ('192.168', 16), ('10', 8), ('172.16', 12)]
# Machine readable form of the above
_nets = [(_ip_to_number(net), (2L**32 -1)^(2L**(32-mask)-1))
- for net, mask in _rfc1918_networks]
-
+ for net, mask in _rfc1918_networks]
+
from __future__ import division
import hashlib
-import itertools
import struct
from . import base58, skiplists
-from p2pool.util import bases, math, skiplist, variable
+from p2pool.util import bases, math, variable
import p2pool
class EarlyEnd(Exception):
@classmethod
def from_header(cls, header):
return cls(bitcoin_data.block_header_type.hash256(header), header['previous_block'])
-
+
def __init__(self, hash, previous_hash):
self.hash, self.previous_hash = hash, previous_hash
def _load_backing(self):
open(self.backing, 'ab').close()
with open(self.backing, 'rb') as f:
- count = 0
- for line in f:
- try:
- hash, previous_hash, checksum = (int(x, 16) for x in line.strip().split(' '))
- except Exception:
- print "skipping over bad data in headers.dat"
- else:
- if (hash - previous_hash) % 2**256 != checksum:
- print "checksum failed"
- continue
- if previous_hash == 0: previous_hash = None
- count += 1
- if count % 10000 == 0 and count: print count
- if hash not in self.tracker.shares:
- self.tracker.add(HeaderWrapper(hash, previous_hash))
+ count = 0
+ for line in f:
+ try:
+ hash, previous_hash, checksum = (int(x, 16) for x in line.strip().split(' '))
+ except Exception:
+ print "skipping over bad data in headers.dat"
+ else:
+ if (hash - previous_hash) % 2**256 != checksum:
+ print "checksum failed"
+ continue
+ if previous_hash == 0: previous_hash = None
+ count += 1
+ if count % 10000 == 0 and count: print count
+ if hash not in self.tracker.shares:
+ self.tracker.add(HeaderWrapper(hash, previous_hash))
def think(self):
highest_head = max(self.tracker.heads, key=lambda h: self.tracker.get_height_and_last(h)[0]) if self.tracker.heads else None
import random
import time
-from twisted.internet import defer
from twisted.python import log
import p2pool
from p2pool import skiplists
from p2pool.bitcoin import data as bitcoin_data, script
-from p2pool.util import memoize, expiring_dict, math, deferral
+from p2pool.util import memoize, expiring_dict, math
merkle_branch_type = bitcoin_data.ListType(bitcoin_data.ComposedType([
self.verified = bitcoin_data.Tracker()
self.get_cumulative_weights = skiplists.WeightsSkipList(self)
-
+
def add(self, share, known_verified=False):
bitcoin_data.Tracker.add(self, share)
if known_verified:
import pygame
-import random
import time
-import itertools
import hashlib
import math
import StringIO
from PIL import Image
-from p2pool.util.vector import v, Vector
+from p2pool.util.vector import v
@apply
class color(object):
return x % bound
def get_pos(share, t, d):
- x = 5 + get_uniform(d.get_width() - 10, share.hash, "pos")
- y = d.get_height() - (t - share.time_seen)*10
- return v(x, y)
+ x = 5 + get_uniform(d.get_width() - 10, share.hash, "pos")
+ y = d.get_height() - (t - share.time_seen)*10
+ return v(x, y)
def get_color(data):
return [get_uniform(256, data, x) for x in "rgb"]
if args.upnp:
upnp_thread()
-
+
# start listening for workers with a JSON-RPC server
print 'Listening for workers on port %i...' % (args.worker_port,)
if element is None:
raise AssertionError()
share = self.tracker.shares[element]
- return 1, set([share.hash]) if share.nonce[:8] == self.run_identifier else set()
+ return 1, set([share.hash]) if share.nonce[:8] == self.run_identifier else set()
def combine_deltas(self, (share_count1, share_hashes1), (share_count2, share_hashes2)):
if share_hashes1 & share_hashes2:
from __future__ import division
-import random
import time
from twisted.internet import task