5 from BaseLib.Core.NATFirewall.NatCheck import GetNATType
6 from BaseLib.Core.NATFirewall.TimeoutCheck import GetTimeout
10 class ConnectionCheck:
14 def __init__(self, session):
15 if ConnectionCheck.__single:
16 raise RuntimeError, "ConnectionCheck is singleton"
17 ConnectionCheck.__single = self
18 self._lock = thread.allocate_lock()
20 self.session = session
21 self.permid = self.session.get_permid()
24 self._nat_callbacks = [] # list with callback functions that want to know the nat_type
25 self.natcheck_reply_callbacks = [] # list with callback functions that want to send a natcheck_reply message
28 def getInstance(*args, **kw):
29 if ConnectionCheck.__single is None:
30 ConnectionCheck(*args, **kw)
31 return ConnectionCheck.__single
33 def try_start(self, reply_callback = None):
35 if reply_callback: self.natcheck_reply_callbacks.append(reply_callback)
39 print >>sys.stderr, "natcheckmsghandler: the thread is already running"
41 print >>sys.stderr, "natcheckmsghandler: starting the thread"
44 thread.start_new_thread(self.run, ())
64 def timeout_check(self, pingback):
68 return GetTimeout(pingback)
70 def natcheck(self, in_port, server1, server2):
72 Find out NAT type and public address and port
74 nat_type, ex_ip, ex_port, in_ip = GetNATType(in_port, server1, server2)
75 if DEBUG: print >> sys.stderr, "NATCheck:", "NAT Type: " + nat_type[1]
76 if DEBUG: print >> sys.stderr, "NATCheck:", "Public Address: " + ex_ip + ":" + str(ex_port)
77 if DEBUG: print >> sys.stderr, "NATCheck:", "Private Address: " + in_ip + ":" + str(in_port)
78 return nat_type, ex_ip, ex_port, in_ip
80 def get_nat_type(self, callback=None):
82 When a callback parameter is supplied it will always be
83 called. When the NAT-type is already known the callback will
84 be made instantly. Otherwise, the callback will be made when
85 the NAT discovery has finished.
89 callback(self.nat_type)
93 self._nat_callbacks.append(callback)
95 return "Unknown NAT/Firewall"
97 def _perform_nat_type_notification(self):
98 nat_type = self.get_nat_type()
99 callbacks = self._nat_callbacks
100 self._nat_callbacks = []
102 for callback in callbacks:
108 def nat_discovery(self):
110 Main method of the class: launches nat discovery algorithm
112 in_port = self.session.get_puncturing_internal_port()
113 stun_servers = self.session.get_stun_servers()
115 random.shuffle(stun_servers)
116 stun1 = stun_servers[1]
117 stun2 = stun_servers[0]
118 pingback_servers = self.session.get_pingback_servers()
119 random.shuffle(pingback_servers)
121 if DEBUG: print >> sys.stderr, "NATCheck:", 'Starting ConnectionCheck on %s %s %s' % (in_port, stun1, stun2)
123 performed_nat_type_notification = False
125 # Check what kind of NAT the peer is behind
126 nat_type, ex_ip, ex_port, in_ip = self.natcheck(in_port, stun1, stun2)
127 self.nat_type = nat_type[1]
129 # notify any callbacks interested in the nat_type only
130 self._perform_nat_type_notification()
131 performed_nat_type_notification = True
134 # If there is any callback interested, check the UDP timeout of the NAT the peer is behind
135 if len(self.natcheck_reply_callbacks):
138 for pingback in pingback_servers:
139 if DEBUG: print >> sys.stderr, "NatCheck: pingback is:", pingback
140 self.nat_timeout = self.timeout_check(pingback)
141 if self.nat_timeout <= 0: break
142 if DEBUG: print >> sys.stderr, "NATCheck: Nat UDP timeout is: ", str(self.nat_timeout)
144 self.nat_params = [nat_type[1], nat_type[0], self.nat_timeout, ex_ip, int(ex_port), in_ip, in_port]
145 if DEBUG: print >> sys.stderr, "NATCheck:", str(self.nat_params)
147 # notify any callbacks interested in sending a natcheck_reply message
148 for reply_callback in self.natcheck_reply_callbacks:
149 reply_callback(self.nat_params)
150 self.natcheck_reply_callbacks = []
152 if not performed_nat_type_notification:
153 self._perform_nat_type_notification()