diff --git a/CHANGELOG b/CHANGELOG
index 14f8ad466..f0dc194a1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Add freelancer-info to gather information about the Freelancer
+ game server. Also added a related version detection probe and UDP
+ protocol payload for detecting the service. [Marin Maržić]
+
o [Ncat] Fixed compilation when --without-liblua is specified in
configure (an #include needed an ifdef guard). [Quentin Glidic]
diff --git a/nmap-payloads b/nmap-payloads
index 24d55fb98..a9b71e2ae 100644
--- a/nmap-payloads
+++ b/nmap-payloads
@@ -169,6 +169,11 @@ udp 2049
"\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
+# Freelancer game server status query
+# http://sourceforge.net/projects/gameq/
+# (relevant files: games.ini, packets.ini, freelancer.php)
+udp 2302 "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21"
+
# Sun Service Tag Discovery protocol (stdiscover)
# http://arc.opensolaris.org/caselog/PSARC/2006/638/stdiscover_protocolv2.pdf
# Would work better with a varying cookie; the second and later sends of this
diff --git a/nmap-service-probes b/nmap-service-probes
index 2ce7094c5..50025d0e8 100644
--- a/nmap-service-probes
+++ b/nmap-service-probes
@@ -12324,3 +12324,14 @@ rarity 9
ports 7887
match xmlsysd m|^Content-Length: [0-9]+\n\n<\?xml version=\"1\.0\"\?>\s*\s*\s*\s*([^<]*)\s*([^<]*)\s*\s*\s*\s*([^<]*)\s*\s*|s p/xmlsysd daemon/ h/$1/ i/IP: $2/ o/$3/ cpe:/a:wulfware:xmlsysd/
+
+##############################NEXT PROBE##############################
+# Freelancer game server status query
+# http://sourceforge.net/projects/gameq/
+# (relevant files: games.ini, packets.ini, freelancer.php)
+Probe UDP FreelancerStatus q|\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21|
+rarity 9
+ports 2302
+
+match freelancer m|^\x00\x03\xf1\x26.{88}(.*)\0\0(?:.*?:){5}(.*)\0\0$|s p/Freelancer/ i/name: $P(1); description: $P(2)/
+
diff --git a/scripts/freelancer-info.nse b/scripts/freelancer-info.nse
new file mode 100644
index 000000000..09ffa8cd7
--- /dev/null
+++ b/scripts/freelancer-info.nse
@@ -0,0 +1,104 @@
+local comm = require "comm"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local string = require "string"
+local bit = require "bit"
+local stdnse = require "stdnse"
+
+description = [[
+Detects the Freelancer game server (FLServer.exe) service by sending a
+status query UDP probe.
+
+When run as a version detection script (-sV), the script
+will report on the server name, current number of players, maximum
+number of players, and whether it has a password set. When run
+explicitly (--script freelancer-info), the script will
+additionally report on the server description, whether players can harm
+other players, and whether new players are allowed.
+
+See http://sourceforge.net/projects/gameq/
+(relevant files: games.ini, packets.ini, freelancer.php)
+]]
+
+-- @output
+-- PORT STATE SERVICE REASON VERSION
+-- 2302/udp open freelancer udp-response Freelancer (name: Discovery Freelancer RP 24/7; players: 152/225; password: no)
+-- | freelancer-info:
+-- | server name: Discovery Freelancer RP 24/7
+-- | server description: This is the official discovery freelancer RP server. To know more about the server, please visit www.discoverygc.com
+-- | players: 152
+-- | max. players: 225
+-- | password: no
+-- | allow players to harm other players: yes
+-- |_ allow new players: yes
+--
+-- @xmloutput
+-- Discovery Freelancer RP 24/7
+-- This is the official discovery freelancer RP server. To know more about the server, please visit www.discoverygc.com
+-- 152
+-- 225
+-- no
+-- yes
+-- yes
+
+author = "Marin Maržić"
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+categories = { "default", "discovery", "safe", "version" }
+
+portrule = shortport.version_port_or_service({2302}, "freelancer", "udp")
+
+action = function(host, port)
+ local status, data = comm.exchange(host, port.number,
+ "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21",
+ { proto = "udp", timeout = 3000 })
+ if not status then
+ return
+ end
+
+ -- port is open
+ nmap.set_port_state(host, port, "open")
+
+ local passwordbyte, maxplayers, numplayers, name, pvpallow, newplayersallow, description =
+ string.match(data, "^\x00\x03\xf1\x26............(.)...(.)...(.)...................................................................(.*)\0\0(.):(.):.*:.*:.*:(.*)\0\0$")
+ if not passwordbyte then
+ return
+ end
+
+ local o = stdnse.output_table()
+
+ o["server name"] = string.gsub(name, "[^%g%s]", "")
+ o["server description"] = string.gsub(description, "[^%g%s]", "")
+ o["players"] = numplayers:byte(1) - 1
+ o["max. players"] = maxplayers:byte(1) - 1
+
+ passwordbyte = passwordbyte:byte(1)
+ if bit.band(passwordbyte, 128) ~= 0 then
+ o["password"] = "yes"
+ else
+ o["password"] = "no"
+ end
+
+ o["allow players to harm other players"] = "n/a"
+ if pvpallow == "1" then
+ o["allow players to harm other players"] = "yes"
+ elseif pvpallow == "0" then
+ o["allow players to harm other players"] = "no"
+ end
+
+ o["allow new players"] = "n/a"
+ if newplayersallow == "1" then
+ o["allow new players"] = "yes"
+ elseif newplayersallow == "0" then
+ o["allow new players"] = "no"
+ end
+
+ port.version.name = "freelancer"
+ port.version.name_confidence = 10
+ port.version.product = "Freelancer"
+ port.version.extrainfo = "name: " .. o["server name"] .. "; players: " ..
+ o["players"] .. "/" .. o["max. players"] .. "; password: " .. o["password"]
+
+ nmap.set_port_version(host, port, "hardmatched")
+
+ return o
+end
diff --git a/scripts/script.db b/scripts/script.db
index 104aa2537..3c4f99c55 100644
--- a/scripts/script.db
+++ b/scripts/script.db
@@ -106,6 +106,7 @@ Entry { filename = "finger.nse", categories = { "default", "discovery", "safe",
Entry { filename = "firewalk.nse", categories = { "discovery", "safe", } }
Entry { filename = "firewall-bypass.nse", categories = { "intrusive", "vuln", } }
Entry { filename = "flume-master-info.nse", categories = { "default", "discovery", "safe", } }
+Entry { filename = "freelancer-info.nse", categories = { "default", "discovery", "safe", "version", } }
Entry { filename = "ftp-anon.nse", categories = { "auth", "default", "safe", } }
Entry { filename = "ftp-bounce.nse", categories = { "default", "safe", } }
Entry { filename = "ftp-brute.nse", categories = { "brute", "intrusive", } }