PageRenderTime 49ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/nselib/socks.lua

https://github.com/prakashgamit/nmap
Lua | 359 lines | 214 code | 55 blank | 90 comment | 28 complexity | 617dd3dc2c836bc672f4184efcc4ab09 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. ---
  2. -- A smallish SOCKS version 5 proxy protocol implementation
  3. --
  4. -- @author "Patrik Karlsson <patrik@cqure.net>"
  5. --
  6. local bin = require "bin"
  7. local nmap = require "nmap"
  8. local stdnse = require "stdnse"
  9. local string = require "string"
  10. _ENV = stdnse.module("socks", stdnse.seeall)
  11. -- SOCKS Authentication methods
  12. AuthMethod = {
  13. NONE = 0,
  14. GSSAPI = 1,
  15. USERPASS = 2,
  16. }
  17. Request = {
  18. -- Class that handles the connection request to the server
  19. Connect = {
  20. -- Creates a new instance of the class
  21. -- @param auth_method table of requested authentication methods
  22. -- @return o instance on success, nil on failure
  23. new = function(self, auth_method)
  24. local o = {
  25. version = 5,
  26. auth_method = ( "table" ~= type(auth_method) and { auth_method } or auth_method )
  27. }
  28. setmetatable(o, self)
  29. self.__index = self
  30. return o
  31. end,
  32. -- Converts the instance to string, so that it can be sent to the
  33. -- server.
  34. -- @return string containing the raw request
  35. __tostring = function(self)
  36. local methods = ""
  37. for _, m in ipairs(self.auth_method) do
  38. methods = methods .. string.char(m)
  39. end
  40. return bin.pack("Cp", self.version, methods)
  41. end,
  42. },
  43. -- Class that handles the authentication request to the server
  44. Authenticate = {
  45. -- Creates a new instance of the class
  46. -- @param auth_method number with the requested authentication method
  47. -- @param creds method specific table of credentials
  48. -- currently only user and pass authentication is supported
  49. -- this method requires two fields to be present
  50. -- <code>username</code> and <code>password</code>
  51. -- @return o instance on success, nil on failure
  52. new = function(self, auth_method, creds)
  53. local o = {
  54. auth_method = auth_method,
  55. creds = creds
  56. }
  57. setmetatable(o, self)
  58. self.__index = self
  59. if ( auth_method == 2 ) then
  60. return o
  61. end
  62. end,
  63. -- Converts the instance to string, so that it can be sent to the
  64. -- server.
  65. -- @return string containing the raw request
  66. __tostring = function(self)
  67. -- we really don't support anything but 2, but let's pretend that
  68. -- we actually do
  69. if ( 2 == self.auth_method ) then
  70. local version = 1
  71. local username= self.creds.username or ""
  72. local password= self.creds.password or ""
  73. username = (username == "") and "\0" or username
  74. password = (password == "") and "\0" or password
  75. return bin.pack("Cpp", version, username, password)
  76. end
  77. end,
  78. }
  79. }
  80. Response = {
  81. -- Class that handles the connection response
  82. Connect = {
  83. -- Creates a new instance of the class
  84. -- @param data string containing the data as received over the socket
  85. -- @return o instance on success, nil on failure
  86. new = function(self, data)
  87. local o = { data = data }
  88. setmetatable(o, self)
  89. self.__index = self
  90. if ( o:parse() ) then
  91. return o
  92. end
  93. end,
  94. -- Parses the received data and populates member variables
  95. -- @return true on success, false on failure
  96. parse = function(self)
  97. if ( #self.data ~= 2 ) then
  98. return
  99. end
  100. local pos
  101. pos, self.version, self.method = bin.unpack("CC", self.data)
  102. return true
  103. end
  104. },
  105. -- Class that handles the authentication response
  106. Authenticate = {
  107. Status = {
  108. SUCCESS = 0,
  109. -- could be anything but zero
  110. FAIL = 1,
  111. },
  112. -- Creates a new instance of the class
  113. -- @param data string containing the data as received over the socket
  114. -- @return o instance on success, nil on failure
  115. new = function(self, data)
  116. local o = { data = data }
  117. setmetatable(o, self)
  118. self.__index = self
  119. if ( o:parse() ) then
  120. return o
  121. end
  122. end,
  123. -- Parses the received data and populates member variables
  124. -- @return true on success, false on failure
  125. parse = function(self)
  126. if ( #self.data ~= 2 ) then
  127. return
  128. end
  129. local pos
  130. pos, self.version, self.status = bin.unpack("CC", self.data)
  131. return true
  132. end,
  133. -- checks if the authentication was successful or not
  134. -- @return true on success, false on failure
  135. isSuccess = function(self)
  136. return ( self.status == self.Status.SUCCESS )
  137. end,
  138. }
  139. }
  140. -- A buffered socket implementation
  141. Socket =
  142. {
  143. retries = 3,
  144. -- Creates a new socket instance
  145. -- @param host table containing the host table
  146. -- @param port table containing the port table
  147. -- @param options table containing options, currenlty supports:
  148. -- <code>timeout</code> - socket timeout in ms
  149. -- @return o new instance of Socket
  150. new = function(self, host, port, options)
  151. local o = {
  152. host = host,
  153. port = port,
  154. options = options or {}
  155. }
  156. setmetatable(o, self)
  157. self.__index = self
  158. o.Socket = nmap.new_socket()
  159. o.Buffer = nil
  160. return o
  161. end,
  162. -- Connects the socket to the server
  163. -- @return status true on success false on failure
  164. -- @return err string containing error message on failure
  165. connect = function( self )
  166. self.Socket:set_timeout(self.options.timeout or 10000)
  167. return self.Socket:connect( self.host, self.port )
  168. end,
  169. -- Closes an open connection.
  170. --
  171. -- @return Status (true or false).
  172. -- @return Error code (if status is false).
  173. close = function( self )
  174. return self.Socket:close()
  175. end,
  176. -- Opposed to the <code>socket:receive_bytes</code> function, that returns
  177. -- at least x bytes, this function returns the amount of bytes requested.
  178. --
  179. -- @param count of bytes to read
  180. -- @return true on success, false on failure
  181. -- @return data containing bytes read from the socket
  182. -- err containing error message if status is false
  183. recv = function( self, count )
  184. local status, data
  185. self.Buffer = self.Buffer or ""
  186. if ( #self.Buffer < count ) then
  187. status, data = self.Socket:receive_bytes( count - #self.Buffer )
  188. if ( not(status) or #data < count - #self.Buffer ) then
  189. return false, data
  190. end
  191. self.Buffer = self.Buffer .. data
  192. end
  193. data = self.Buffer:sub( 1, count )
  194. self.Buffer = self.Buffer:sub( count + 1)
  195. return true, data
  196. end,
  197. -- Sends data over the socket
  198. --
  199. -- @return Status (true or false).
  200. -- @return Error code (if status is false).
  201. send = function( self, data )
  202. return self.Socket:send( data )
  203. end,
  204. }
  205. -- The main script interface
  206. Helper = {
  207. -- Create a new instance of the class
  208. -- @param host table containing the host table
  209. -- @param port table containing the port table
  210. -- @param options table containing library options, currenlty:
  211. -- <code>timeout</code> - socket timeout in ms
  212. -- @return o instance of Helper
  213. new = function(self, host, port, options)
  214. local o = { host = host, port = port, options = options }
  215. setmetatable(o, self)
  216. self.__index = self
  217. return o
  218. end,
  219. -- Get the authentication method name by number
  220. -- @param method number containing the authentication method
  221. -- @return string containing the method name or Unknown
  222. authNameByNumber = function(self, method)
  223. local methods = {
  224. [0] = "No authentication",
  225. [1] = "GSSAPI",
  226. [2] = "Username and password",
  227. }
  228. return methods[method] or ("Unknown method (%d)"):format(method)
  229. end,
  230. -- Connects to the SOCKS server
  231. -- @param auth_method table containing the auth. methods to request
  232. -- @return status true on success, false on failure
  233. -- @return response table containing the respons or err string on failure
  234. connect = function(self, auth_method)
  235. self.socket = Socket:new(self.host, self.port, self.options)
  236. local status, err = self.socket:connect()
  237. if ( not(status) ) then
  238. return status, err
  239. end
  240. auth_method = auth_method or {AuthMethod.NONE, AuthMethod.GSSAPI, AuthMethod.USERPASS}
  241. status = self.socket:send( tostring(Request.Connect:new(auth_method)) )
  242. if ( not(status) ) then
  243. self.socket:close()
  244. return false, "Failed to send connection request to server"
  245. end
  246. local status, data = self.socket:recv(2)
  247. if ( not(status) ) then
  248. self.socket:close()
  249. return false, "Failed to receive connection response from server"
  250. end
  251. local response = Response.Connect:new(data)
  252. if ( not(response) ) then
  253. return false, "Failed to parse response from server"
  254. end
  255. if ( response.version ~= 5 ) then
  256. return false, ("Unsupported SOCKS version (%d)"):format(response.version)
  257. end
  258. if ( response.method == 0xFF ) then
  259. return false, "No acceptable authentication methods"
  260. end
  261. -- store the method so authenticate knows what to use
  262. self.auth_method = response.method
  263. return true, response
  264. end,
  265. -- Authenticates to the SOCKS server
  266. -- @param creds table containing authentication method specific fields
  267. -- currently only authentication method 2 (username and pass) is
  268. -- implemented. That method requires the following fields:
  269. -- <code>username</code> - containing the username
  270. -- <code>password</code> - containing the password
  271. -- @return status true on success, false on failure
  272. -- @return err string containing the error message
  273. authenticate = function(self, creds)
  274. if ( self.auth_method ~= 2 ) then
  275. return false, "Authentication method not supported"
  276. end
  277. local req = Request.Authenticate:new(self.auth_method, creds)
  278. if ( not(req) ) then
  279. return false, "Failed to create authentication request"
  280. end
  281. local status = self.socket:send(tostring(req))
  282. if ( not(status) ) then
  283. return false, "Failed to send authentication request"
  284. end
  285. if ( 2 == self.auth_method ) then
  286. local status, data = self.socket:recv(2)
  287. local auth = Response.Authenticate:new(data)
  288. if ( not(auth) ) then
  289. return false, "Failed to parse authentication response"
  290. end
  291. if ( auth:isSuccess() ) then
  292. return true, "Authentication was successfull"
  293. else
  294. return false, "Authentication failed"
  295. end
  296. end
  297. return false, "Unsupported authentication method"
  298. end,
  299. -- closes the connection to the server
  300. close = function(self)
  301. return self.socket:close()
  302. end,
  303. }
  304. return _ENV;