-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathntp_time.r2py
125 lines (90 loc) · 4.24 KB
/
ntp_time.r2py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""
Author: Justin Cappos
Edited by Eric Kimbrel, this was originally time.r2py
Start Date: 8 August 2008
Description:
This is an implementation of time_interface.r2py
This module handles getting the time from an external source, via UDP.
It gets the remote time once and then uses the offset from the local
clock from then on
to return the current time.
To use this module, first make a call to time_updatetime(localport) with a
local UDP port that you have permission to send/recv on. This will
contact some random subset of NTP servers to get and store the local time.
Then, to get the actual time, call time_gettime() which will return
the current time (in seconds). time_gettime() can be called at any point
after having called time_updatetime(localport) since time_gettime() simply
calculates how much time has elapsed since the local time was originally
acquired from one of the NTP servers.
Note that time_gettime() will raise TimeError if no NTP server responded or
if time_updatetime(localport) was never previously called. If time_gettime()
fails, then time_updatetime(localport) can be called again to sample time
from another random set of NTP servers.
"""
time_interface = dy_import_module('time_interface.r2py')
# Use for random sampling...
random = dy_import_module('random.r2py')
timeservers = ["time-a.nist.gov", "time-b.nist.gov",
"time-a.timefreq.bldrdoc.gov", "time-b.timefreq.bldrdoc.gov",
"time-c.timefreq.bldrdoc.gov", "utcnist.colorado.edu", "time.nist.gov",
"nist.netservicesgroup.com"]
#BUG: Do I need to compensate for the time taken to contact the time server? (#353)
def ntp_time_updatetime(localport):
"""
<Purpose>
Obtains and stores the local time from a subset of NTP servers.
<Arguments>
localport:
The local port to be used when contacting the NTP server(s).
<Exceptions>
TimeError when getmyip() fails or one of the subset of NTP servers will not
respond.
<Side Effects>
time_interface.time_settime(currenttime) is called as the subprocess of a subprocess, which
adjusts the current time.
<Returns>
None.
"""
ip = getmyip()
socketobj = listenformessage(ip, localport)
# always close the handle before returning...
try:
# try five random servers, and send requests to all of the servers and
# then wait for a response from any server for atmost 5 seconds. This way
# the total wait time is reduced to 5 seconds, since any one of the server's
# is guaranteed to give a response.
for servername in random.random_sample(timeservers,5):
# this sends a request, version 3 in "client mode"
ntp_request_string = chr(27)+chr(0)*47
try:
sendmessage(gethostbyname(servername), 123, ntp_request_string,\
ip, localport) # 123 is the NTP port
except NetworkAddressError:
# gethostbyname couldn't resolve servername, try with next name
pass
# Wait for 5 seconds to get a response from any of the 5 servers that was
# requested above.
starttime = getruntime()
responsetime = 5.0
while (getruntime() - starttime) < responsetime:
try:
(remoteip, remoteport, mess) = socketobj.getmessage()
except SocketWouldBlockError:
sleep(0.05) # Retry amount time...
else:
# XXX Check for matching address/port, #1268
time_interface.time_settime(_time_convert_timestamp_to_float(mess[40:48]))
return
finally:
socketobj.close()
# Failure, tried servers without luck...
raise time_interface.TimeError, "Time Server update failed. Perhaps retry later..."
### Do the conversion / decoding for NTP. More details about the
### format of NTP are at RFC 2030 (http://www.ietf.org/rfc/rfc2030.txt)
# this unpacks the data from the packet and changes it to a float
def _time_convert_timestamp_to_float(timestamp):
integerpart = (ord(timestamp[0])<<24) + (ord(timestamp[1])<<16) + (ord(timestamp[2])<<8) + (ord(timestamp[3]))
floatpart = (ord(timestamp[4])<<24) + (ord(timestamp[5])<<16) + (ord(timestamp[6])<<8) + (ord(timestamp[7]))
return integerpart + floatpart / float(2**32)
#register the update method
time_interface.time_register_method('ntp',ntp_time_updatetime)