Add timestamp offset for block header
[p2pool.git] / nattraverso / portmapper.py
1 """
2 Generic NAT Port mapping interface.
3
4 TODO: Example
5
6 @author: Raphael Slinckx
7 @copyright: Copyright 2005
8 @license: LGPL
9 @contact: U{raphael@slinckx.net<mailto:raphael@slinckx.net>}
10 @version: 0.1.0
11 """
12
13 __revision__ = "$id"
14
15 from twisted.internet.base import BasePort
16
17 # Public API
18 def get_port_mapper(proto="TCP"):
19     """
20     Returns a L{NATMapper} instance, suited to map a port for
21     the given protocol. Defaults to TCP.
22     
23     For the moment, only upnp mapper is available. It accepts both UDP and TCP.
24     
25     @param proto: The protocol: 'TCP' or 'UDP'
26     @type proto: string
27     @return: A deferred called with a L{NATMapper} instance
28     @rtype: L{twisted.internet.defer.Deferred}
29     """
30     import nattraverso.pynupnp
31     return nattraverso.pynupnp.get_port_mapper()
32
33 class NATMapper:
34     """
35     Define methods to map port objects (as returned by twisted's listenXX).
36     This allows NAT to be traversed from incoming packets.
37     
38     Currently the only implementation of this class is the UPnP Mapper, which
39     can map UDP and TCP ports, if an UPnP Device exists.
40     """
41     def __init__(self):
42         raise NotImplementedError("Cannot instantiate the class")
43     
44     def map(self, port):
45         """
46         Create a mapping for the given twisted's port object.
47         
48         The deferred will call back with a tuple (extaddr, extport):
49             - extaddr: The ip string of the external ip address of this host
50             - extport: the external port number used to map the given Port object
51         
52         When called multiple times with the same Port,
53         callback with the existing mapping.
54         
55         @param port: The port object to map
56         @type port: a L{twisted.internet.interfaces.IListeningPort} object
57         @return: A deferred called with the above defined tuple
58         @rtype: L{twisted.internet.defer.Deferred}
59         """
60         raise NotImplementedError
61     
62     def info(self, port):
63         """
64         Returns the existing mapping for the given port object. That means map()
65         has to be called before.
66         
67         @param port: The port object to retreive info from
68         @type port: a L{twisted.internet.interfaces.IListeningPort} object
69         @raise ValueError: When there is no such existing mapping
70         @return: a tuple (extaddress, extport).
71         @see: L{map() function<map>}
72         """
73         raise NotImplementedError
74     
75     def unmap(self, port):
76         """
77         Remove an existing mapping for the given twisted's port object.
78         
79         @param port: The port object to unmap
80         @type port: a L{twisted.internet.interfaces.IListeningPort} object
81         @return: A deferred called with None
82         @rtype: L{twisted.internet.defer.Deferred}
83         @raise ValueError: When there is no such existing mapping
84         """
85         raise NotImplementedError
86     
87     def get_port_mappings(self):
88         """
89         Returns a deferred that will be called with a dictionnary of the
90         existing mappings.
91         
92         The dictionnary structure is the following:
93             - Keys: tuple (protocol, external_port)
94                 - protocol is "TCP" or "UDP".
95                 - external_port is the external port number, as see on the
96                     WAN side.
97             - Values:tuple (internal_ip, internal_port)
98                 - internal_ip is the LAN ip address of the host.
99                 - internal_port is the internal port number mapped
100                     to external_port.
101         
102         @return: A deferred called with the above defined dictionnary
103         @rtype: L{twisted.internet.defer.Deferred}
104         """
105         raise NotImplementedError
106     
107     def _check_valid_port(self, port):
108         """Various Port object validity checks. Raise a ValueError."""
109         if not isinstance(port, BasePort):
110             raise ValueError("expected a Port, got %r"%(port))
111         
112         if not port.connected:
113             raise ValueError("Port %r is not listening"%(port))
114         
115         loc_addr = port.getHost()
116         if loc_addr.port == 0:
117             raise ValueError("Port %r has port number of 0"%(port))
118