instrumentation: add next-share/
[cs-p2p-next.git] / instrumentation / next-share / BaseLib / Core / NATFirewall / TimeoutFinder.py
1 # Written by Gertjan Halkes
2 # see LICENSE.txt for license information
3
4
5 import struct
6 import time
7 import sys
8
9 DEBUG = False
10
11 class TimeoutFinder:
12     PINGBACK_TIMES = [ 245, 235, 175, 115, 85, 55, 25, 10 ]
13     PINGBACK_ADDRESS = ("m23trial-udp.tribler.org", 7396)
14
15     def __init__(self, rawserver, initial_ping, reportback = None):
16         self.sockets = []
17         self.rawserver = rawserver
18         self.timeout_found = -1
19         self.initial_ping = initial_ping
20         self.reportback = reportback
21         self.timeout_index = 0
22
23         # Stagger the pings by 1 second to unsure minimum impact on other traffic
24         rawserver.add_task(self.ping, 1)
25         rawserver.add_task(self.report_done, TimeoutFinder.PINGBACK_TIMES[0] + 5)
26
27
28     def ping(self):
29         sock = self.rawserver.create_udpsocket(0, "0.0.0.0")
30         self.sockets.append(sock)
31         self.rawserver.start_listening_udp(sock, self)
32         if self.initial_ping:
33             sock.sendto(struct.pack("!Id", 0, float(TimeoutFinder.PINGBACK_TIMES[self.timeout_index])),
34                 TimeoutFinder.PINGBACK_ADDRESS)
35         else:
36             sock.sendto(struct.pack("!Id", TimeoutFinder.PINGBACK_TIMES[self.timeout_index],
37                 time.time()), TimeoutFinder.PINGBACK_ADDRESS)
38         self.timeout_index += 1
39         if self.timeout_index < len(TimeoutFinder.PINGBACK_TIMES):
40             self.rawserver.add_task(self.ping, 1)
41
42
43     def data_came_in(self, address, data):
44         if len(data) != 12:
45             return
46         #FIXME: the address should be checked, but that can only be done if
47         # the address is in dotted-decimal notation
48         #~ if address != TimeoutFinder.PINGBACK_ADDRESS:
49             #~ return
50
51         timeout = struct.unpack("!Id", data)
52         if timeout[0] == 0:
53             to_find = int(timeout[1])
54             for i in range(0, len(TimeoutFinder.PINGBACK_TIMES)):
55                 if to_find == TimeoutFinder.PINGBACK_TIMES[i]:
56                     self.sockets[i].sendto(struct.pack("!Id", to_find, time.time()), TimeoutFinder.PINGBACK_ADDRESS)
57                     break
58         else:
59             if DEBUG:
60                 print >>sys.stderr, ("Received ping with %d delay" % (timeout[0]))
61             self.timeout_found = timeout[0]
62             #FIXME: log reception of packet
63
64     def report_done(self):
65         for i in self.sockets:
66             self.rawserver.stop_listening_udp(i)
67             i.close()
68
69         if self.reportback:
70             self.reportback(self.timeout_found, self.initial_ping)
71
72
73 if __name__ == "__main__":
74     import BaseLib.Core.BitTornado.RawServer as RawServer
75     from threading import Event
76     import thread
77     from traceback import print_exc
78     import os
79
80     def fail(e):
81         print "Fatal error: " + str(e)
82         print_exc()
83
84     def error(e):
85         print "Non-fatal error: " + str(e)
86
87     def report(timeout, initial_ping):
88         if initial_ping:
89             with_ = "with"
90         else:
91             with_ = "without"
92
93         if DEBUG:
94             print >>sys.stderr, ("Timeout %s initial ping: %d" % (with_, timeout))
95
96     DEBUG = True
97
98     log = open("log-timeout.txt", "w")
99
100     rawserver_ = RawServer.RawServer(Event(),
101                            60.0,
102                            300.0,
103                            False,
104                            failfunc = fail,
105                            errorfunc = error)
106     thread.start_new_thread(rawserver_.listen_forever, (None,))
107     time.sleep(0.5)
108     TimeoutFinder(rawserver_, False, report)
109     TimeoutFinder(rawserver_, True, report)
110
111     print "TimeoutFinder started, press enter to quit"
112     sys.stdin.readline()