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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
--- Functions for dealing with dates and timestamps
--
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
-- @class module
-- @name datetime
-- @author Daniel Miller
local stdnse = require "stdnse"
local os = require "os"
local math = require "math"
local string = require "string"
_ENV = stdnse.module("datetime", stdnse.seeall)
local difftime = os.difftime
local time = os.time
local date = os.date
local floor = math.floor
local fmod = math.fmod
local format = string.format
local match = string.match
--- Record a time difference between the scanner and the target
--
-- The skew will be recorded in the host's registry for later retrieval and
-- analysis. Adjusts for network distance by subtracting half the smoothed
-- round-trip time.
--
--@param host The host being scanned
--@param timestamp The target timestamp, in seconds.
--@param received The local time the stamp was received, in seconds.
function record_skew(host, timestamp, received)
local skew_tab = host.registry.datetime_skew
skew_tab = skew_tab or {}
-- No srtt? I suppose we'll ignore it, but this could cause problems
local srtt = host.times and host.times.srtt or 0
local adjusted = difftime(floor(timestamp), floor(received)) - srtt / 2.0
skew_tab[#skew_tab + 1] = adjusted
stdnse.debug2("record_skew: %s", adjusted)
host.registry.datetime_skew = skew_tab
end
-- Work around Windows error formatting time zones where 1970/1/1 UTC was 1969/12/31
local utc_offset_seconds
do
-- What does the calendar say locally?
local localtime = date("*t", 86400)
-- What does the calendar say in UTC?
local gmtime = date("!*t", 86400)
-- Interpret both as local calendar dates and find the difference.
utc_offset_seconds = difftime(time(localtime), time(gmtime))
end
-- The offset in seconds between local time and UTC.
--
-- That is, if we interpret a UTC date table as a local date table by passing
-- it to os.time, how much must be added to the resulting integer timestamp to
-- make it correct?
--
-- In other words, subtract this value from a timestamp if you intend to use it
-- in os.date.
function utc_offset() return utc_offset_seconds end
--- Convert a date table into an integer timestamp.
--
-- Unlike os.time, this does not assume that the date table represents a local
-- time. Rather, it takes an optional offset number of seconds representing the
-- time zone, and returns the timestamp that would result using that time zone
-- as local time. If the offset is omitted or 0, the date table is interpreted
-- as a UTC date. For example, 4:00 UTC is the same as 5:00 UTC+1:
-- <code>
-- date_to_timestamp({year=1970,month=1,day=1,hour=4,min=0,sec=0}) --> 14400
-- date_to_timestamp({year=1970,month=1,day=1,hour=4,min=0,sec=0}, 0) --> 14400
-- date_to_timestamp({year=1970,month=1,day=1,hour=5,min=0,sec=0}, 1*60*60) --> 14400
-- </code>
-- And 4:00 UTC+1 is an earlier time:
-- <code>
-- date_to_timestamp({year=1970,month=1,day=1,hour=4,min=0,sec=0}, 1*60*60) --> 10800
-- </code>
function date_to_timestamp(date_t, offset)
local status, tm = pcall(time, date_t)
if not status then
stdnse.debug1("Invalid date for this platform: %s", tm)
return nil
end
offset = offset or 0
return tm + utc_offset() - offset
end
local function format_tz(offset)
local sign, hh, mm
if not offset then
return ""
end
if offset < 0 then
sign = "-"
offset = -offset
else
sign = "+"
end
-- Truncate to minutes.
offset = floor(offset / 60)
hh = floor(offset / 60)
mm = floor(fmod(offset, 60))
return format("%s%02d:%02d", sign, hh, mm)
end
--- Format a date and time (and optional time zone) for structured output.
--
-- Formatting is done according to RFC 3339 (a profile of ISO 8601), except
-- that a time zone may be omitted to signify an unspecified local time zone.
-- Time zones are given as an integer number of seconds from UTC. Use
-- <code>0</code> to mark UTC itself. Formatted strings with a time zone look
-- like this:
-- <code>
-- format_timestamp(os.time(), 0) --> "2012-09-07T23:37:42+00:00"
-- format_timestamp(os.time(), 2*60*60) --> "2012-09-07T23:37:42+02:00"
-- </code>
-- Without a time zone they look like this:
-- <code>
-- format_timestamp(os.time()) --> "2012-09-07T23:37:42"
-- </code>
--
-- This function should be used for all dates emitted as part of NSE structured
-- output.
function format_timestamp(t, offset)
if type(t) == "table" then
return format(
"%d-%02d-%02dT%02d:%02d:%02d",
t.year, t.month, t.day, t.hour, t.min, t.sec
)
else
local tz_string = format_tz(offset)
offset = offset or 0
local status, result = pcall(date, "!%Y-%m-%dT%H:%M:%S", floor(t + offset))
if not status then
local tmp = floor(t + offset)
local extra_years
local seconds_in_year = 31556926
if tmp > 0xffffffff then
-- Maybe too far in the future?
extra_years = (tmp - 0xffffffff) // seconds_in_year + 1
elseif tmp < -utc_offset() then
-- Windows can't display times before the epoch
extra_years = tmp // seconds_in_year
end
if extra_years then
tmp = tmp - extra_years * seconds_in_year
status, result = pcall(date, "!*t", tmp)
if status then
-- seconds_in_year is imprecise, so we truncate to date only
result = format("%d-%02d-%02d?", result.year + extra_years, result.month, result.day)
end
end
end
if not status then
return ("Invalid timestamp: %s"):format(t)
end
return result .. tz_string
end
end
--- Format a time interval into a string
--
-- String is in the same format as format_difftime
-- @param interval A time interval
-- @param unit The time unit division as a number. If <code>interval</code> is
-- in milliseconds, this is 1000 for instance. Default: 1 (seconds)
-- @return The time interval in string format
function format_time(interval, unit)
local sign = ""
if interval < 0 then
sign = "-"
interval = math.abs(interval)
end
unit = unit or 1
local precision = floor(math.log(unit, 10))
local sec = (interval % (60 * unit)) / unit
interval = interval // (60 * unit)
local min = interval % 60
interval = interval // 60
local hr = interval % 24
interval = interval // 24
local s = format("%.0fd%02.0fh%02.0fm%02.".. precision .."fs",
interval, hr, min, sec)
-- trim off leading 0 and "empty" units
return sign .. (match(s, "([1-9].*)") or format("%0.".. precision .."fs", 0))
end
--- Format the difference between times <code>t2</code> and <code>t1</code>
-- into a string
--
-- String is in one of the forms (signs may vary):
-- * 0s
-- * -4s
-- * +2m38s
-- * -9h12m34s
-- * +5d17h05m06s
-- * -2y177d10h13m20s
-- The string shows <code>t2</code> relative to <code>t1</code>; i.e., the
-- calculation is <code>t2</code> minus <code>t1</code>.
function format_difftime(t2, t1)
local d, s, sign, yeardiff
d = difftime(time(t2), time(t1))
if d > 0 then
sign = "+"
elseif d < 0 then
sign = "-"
t2, t1 = t1, t2
d = -d
else
sign = ""
end
-- t2 is always later than or equal to t1 here.
-- The year is a tricky case because it's not a fixed number of days
-- the way a day is a fixed number of hours or an hour is a fixed
-- number of minutes. For example, the difference between 2008-02-10
-- and 2009-02-10 is 366 days because 2008 was a leap year, but it
-- should be printed as 1y0d0h0m0s, not 1y1d0h0m0s. We advance t1 to be
-- the latest year such that it is still before t2, which means that its
-- year will be equal to or one less than t2's. The number of years
-- skipped is stored in yeardiff.
if t2.year > t1.year then
local tmpyear = t1.year
-- Put t1 in the same year as t2.
t1.year = t2.year
d = difftime(time(t2), time(t1))
if d < 0 then
-- Too far. Back off one year.
t1.year = t2.year - 1
d = difftime(time(t2), time(t1))
end
yeardiff = t1.year - tmpyear
t1.year = tmpyear
else
yeardiff = 0
end
local s = format_time(d)
if yeardiff == 0 then return sign .. s end
-- Years.
s = format("%dy", yeardiff) .. s
return sign .. s
end
return _ENV
|