diff --git a/nselib/http.lua b/nselib/http.lua
index 958bf8d92..d3e14371e 100644
--- a/nselib/http.lua
+++ b/nselib/http.lua
@@ -428,6 +428,63 @@ request = function( host, port, data, options )
end
+local MONTH_MAP = {
+ Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6,
+ Jul = 7, Aug = 8, Sep = 9, Oct = 10, Nov = 11, Dec = 12
+}
+
+--- Parses an HTTP date string, in any of the following formats from section
+-- 3.3.1 of RFC 2616:
+-- * Sun, 06 Nov 1994 08:49:37 GMT (RFC 822, updated by RFC 1123)
+-- * Sunday, 06-Nov-94 08:49:37 GMT (RFC 850, obsoleted by RFC 1036)
+-- * Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format)
+-- @arg s the date string.
+-- @return a table with keys year, month,
+-- day, hour, min, sec, and
+-- isdst, relative to GMT, suitable for input to
+-- os.time.
+function parse_date(s)
+ local day, month, year, hour, min, sec, tz, month_name
+ -- RFC 2616, section 3.3.1:
+
+ -- Handle RFC 1123 and 1036 at once.
+ day, month_name, year, hour, min, sec, tz = s:match("^%w+, (%d+)[- ](%w+)[- ](%d+) (%d+):(%d+):(%d+) (%w+)$")
+ if not day then
+ month_name, day, hour, min, sec, year = s:match("%w+ (%w+) ?(%d+) (%d+):(%d+):(%d+) (%d+)")
+ tz = "GMT"
+ end
+ if not day then
+ stdnse.print_debug(1, "http.parse_date: can't parse date \"%s\": unknown format.", s)
+ return nil
+ end
+ -- Look up the numeric code for month.
+ month = MONTH_MAP[month_name]
+ if not month then
+ stdnse.print_debug(1, "http.parse_date: unknown month name \"%s\".", month_name)
+ return nil
+ end
+ if tz ~= "GMT" then
+ stdnse.print_debug(1, "http.parse_date: don't know time zone \"%s\", only \"GMT\".", tz)
+ return nil
+ end
+ day = tonumber(day)
+ year = tonumber(year)
+ hour = tonumber(hour)
+ min = tonumber(min)
+ sec = tonumber(sec)
+
+ if year < 100 then
+ -- Two-digit year. Make a guess.
+ if year < 70 then
+ year = year + 2000
+ else
+ year = year + 1900
+ end
+ end
+
+ return { year = year, month = month, day = day, hour = hour, min = min, sec = sec, isdst = false }
+end
+
get_default_timeout = function( nmap_timing )
local timeout = {}
if nmap_timing >= 0 and nmap_timing <= 3 then
diff --git a/scripts/http-date.nse b/scripts/http-date.nse
new file mode 100644
index 000000000..49474eba9
--- /dev/null
+++ b/scripts/http-date.nse
@@ -0,0 +1,80 @@
+description = [[
+Gets the date from HTTP-like services. Also prints how much the date
+differs from local time. Local time is the time the HTTP request was
+sent, so the difference includes at least the duration of one RTT.
+]]
+
+---
+-- @output
+-- 80/tcp open http
+-- |_ http-date: Mon, 13 Jul 2009 20:53:46 GMT; -5s from local time.
+
+author = "David Fifield "
+
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+
+categories = {"discovery", "safe"}
+
+require("http")
+require("shortport")
+
+portrule = shortport.port_or_service({80, 443, 631, 8080},
+ {"http", "https", "ipp", "http-alt"})
+
+-- Turn a positive or negative number of seconds into a string in one of the
+-- forms:
+-- 0s
+-- +2s
+-- -4s
+-- +02m38s
+-- -9h12m34s
+-- 5d17h05m06s
+local function format_difftime(t)
+ local s, sign, sec
+
+ if t > 0 then
+ sign = "+"
+ elseif t < 0 then
+ sign = "-"
+ else
+ sign = ""
+ end
+ t = math.abs(t)
+
+ -- Seconds.
+ sec = t % 60
+ s = string.format("%gs", sec)
+ t = math.floor(t / 60)
+ if t == 0 then return sign .. s end
+ -- Minutes.
+ s = string.format("%02dm%02ds", t % 60, sec)
+ t = math.floor(t / 60)
+ if t == 0 then return sign .. s end
+ -- Hours.
+ s = string.format("%dh", t % 24) .. s
+ t = math.floor(t / 24)
+ if t == 0 then return sign .. s end
+ -- Days.
+ s = string.format("%dd", t) .. s
+ return sign .. s
+end
+
+action = function(host, port)
+ -- Get local time in UTC.
+ local request_time = os.time(os.date("!*t"))
+ local response = http.get(host, port, "/")
+ if not response.status or not response.header["date"] then
+ return
+ end
+
+ local date = http.parse_date(response.header["date"])
+ if not date then
+ return
+ end
+
+ -- Should account for estimated RTT too.
+ local diff = os.difftime(os.time(date), request_time)
+
+ return string.format("%s; %s from local time.",
+ response.header["date"], format_difftime(diff))
+end