PageRenderTime 71ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/nselib/informix.lua

https://gitlab.com/g10h4ck/nmap-gsoc2015
Lua | 1328 lines | 746 code | 228 blank | 354 comment | 118 complexity | 11eaa4fc5d3cf9501fff284fe73683c2 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. ---
  2. -- Informix Library supporting a very limited subset of Informix operations
  3. --
  4. -- Summary
  5. -- -------
  6. -- Informix supports both The Open Group Distributed Relational Database
  7. -- Architecture (DRDA) protocol, and their own. This library attempts to
  8. -- implement a basic subset of operations. It currently supports;
  9. -- o Authentication using plain-text usernames and passwords
  10. -- o Simple SELECT, INSERT and UPDATE queries, possible more ...
  11. --
  12. -- Overview
  13. -- --------
  14. -- The library contains the following classes:
  15. --
  16. -- o Packet.*
  17. -- - The Packet classes contain specific packets and function to serialize
  18. -- them to strings that can be sent over the wire. Each class may also
  19. -- contain a function to parse the servers response.
  20. --
  21. -- o ColMetaData
  22. -- - A class holding the meta data for each column
  23. --
  24. -- o Comm
  25. -- - Implements a number of functions to handle communication over the
  26. -- the socket.
  27. --
  28. -- o Helper
  29. -- - A helper class that provides easy access to the rest of the library
  30. --
  31. -- In addition the library contains the following tables with decoder functions
  32. --
  33. -- o MetaDataDecoders
  34. -- - Contains functions to decode the column metadata per data type
  35. --
  36. -- o DataTypeDecoders
  37. -- - Contains function to decode each data-type in the query resultset
  38. --
  39. -- o MessageDecoders
  40. -- - Contains a decoder for each supported protocol message
  41. --
  42. -- Example
  43. -- -------
  44. -- The following sample code illustrates how scripts can use the Helper class
  45. -- to interface the library:
  46. --
  47. -- <code>
  48. -- helper = informix.Helper:new( host, port, "on_demo" )
  49. -- status, err = helper:Connect()
  50. -- status, res = helper:Login("informix", "informix")
  51. -- status, err = helper:Close()
  52. -- </code>
  53. --
  54. -- Additional information
  55. -- ----------------------
  56. -- The implementation is based on analysis of packet dumps and has been tested
  57. -- against:
  58. --
  59. -- x IBM Informix Dynamic Server Express Edition v11.50 32-bit on Ubuntu
  60. -- x IBM Informix Dynamic Server xxx 32-bit on Windows 2003
  61. --
  62. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  63. -- @author "Patrik Karlsson <patrik@cqure.net>"
  64. --
  65. -- @args informix.instance specifies the Informix instance to connect to
  66. --
  67. -- Version 0.1
  68. -- Created 07/23/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
  69. -- Revised 07/28/2010 - v0.2 - added support for SELECT, INSERT and UPDATE
  70. -- queries
  71. --
  72. local bin = require "bin"
  73. local nmap = require "nmap"
  74. local match = require "match"
  75. local stdnse = require "stdnse"
  76. local table = require "table"
  77. _ENV = stdnse.module("informix", stdnse.seeall)
  78. -- A bunch of constants
  79. Constants =
  80. {
  81. -- A subset of supported messages
  82. Message = {
  83. SQ_COMMAND = 0x01,
  84. SQ_PREPARE = 0x02,
  85. SQ_ID = 0x04,
  86. SQ_DESCRIBE = 0x08,
  87. SQ_EOT = 0x0c,
  88. SQ_ERR = 0x0d,
  89. SQ_TUPLE = 0x0e,
  90. SQ_DONE = 0x0f,
  91. SQ_DBLIST = 0x1a,
  92. SQ_DBOPEN = 0x24,
  93. SQ_EXIT = 0x38,
  94. SQ_INFO = 0x51,
  95. SQ_PROTOCOLS = 0x7e,
  96. },
  97. -- A subset of supported data types
  98. DataType = {
  99. CHAR = 0x00,
  100. SMALLINT = 0x01,
  101. INT = 0x02,
  102. FLOAT = 0x03,
  103. SERIAL = 0x06,
  104. DATE = 0x07,
  105. DATETIME = 0x0a,
  106. VARCHAR = 0x0d,
  107. },
  108. -- These were the ones I ran into when developing :-)
  109. ErrorMsg = {
  110. [-201] = "A syntax error has occurred.",
  111. [-206] = "The specified table is not in the database.",
  112. [-208] = "Memory allocation failed during query processing.",
  113. [-258] = "System error - invalid statement id received by the sqlexec process.",
  114. [-217] = "Column (%s) not found in any table in the query (or SLV is undefined).",
  115. [-310] = "Table (%s) already exists in database.",
  116. [-363] = "CURSOR not on SELECT statement.",
  117. [-555] = "Cannot use a select or any of the database statements in a multi-query prepare.",
  118. [-664] = "Wrong number of arguments to system function(%s).",
  119. [-761] = "INFORMIXSERVER does not match either DBSERVERNAME or DBSERVERALIASES.",
  120. [-951] = "Incorrect password or user is not known on the database server.",
  121. [-329] = "Database not found or no system permission.",
  122. [-9628] = "Type (%s) not found.",
  123. [-23101] = "Unable to load locale categories.",
  124. }
  125. }
  126. -- The ColMetaData class
  127. ColMetaData = {
  128. ---Creates a new ColMetaData instance
  129. --
  130. -- @return object a new instance of ColMetaData
  131. new = function(self)
  132. local o = {}
  133. setmetatable(o, self)
  134. self.__index = self
  135. return o
  136. end,
  137. --- Sets the datatype
  138. --
  139. -- @param typ number containing the datatype
  140. setType = function( self, typ ) self.type = typ end,
  141. --- Sets the name
  142. --
  143. -- @param name string containing the name
  144. setName = function( self, name) self.name = name end,
  145. --- Sets the length
  146. --
  147. -- @param len number containing the length of the column
  148. setLength = function( self, len ) self.len = len end,
  149. --- Gets the column type
  150. --
  151. -- @return typ the column type
  152. getType = function( self ) return self.type end,
  153. --- Gets the column name
  154. --
  155. -- @return name the column name
  156. getName = function( self ) return self.name end,
  157. --- Gets the column length
  158. --
  159. -- @return len the column length
  160. getLength = function( self ) return self.len end,
  161. }
  162. Packet = {}
  163. -- MetaData decoders used to decode the information for each data type in the
  164. -- meta data returned by the server
  165. --
  166. -- The decoders, should be self explanatory
  167. MetaDataDecoders = {
  168. [Constants.DataType.INT] = function( data )
  169. local col_md = ColMetaData:new( )
  170. local pos = 19
  171. if ( #data < pos ) then return false, "Failed to decode meta data for data type INT" end
  172. local _, len = bin.unpack(">S", data, pos)
  173. col_md:setLength(len)
  174. col_md:setType( Constants.DataType.INT )
  175. return true, col_md
  176. end,
  177. [Constants.DataType.CHAR] = function( data )
  178. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  179. if( not(status) ) then
  180. return false, "Failed to decode metadata for data type CHAR"
  181. end
  182. col_md:setType( Constants.DataType.CHAR )
  183. return true, col_md
  184. end,
  185. [Constants.DataType.VARCHAR] = function( data )
  186. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  187. if( not(status) ) then return false, "Failed to decode metadata for data type CHAR" end
  188. col_md:setType( Constants.DataType.VARCHAR )
  189. return true, col_md
  190. end,
  191. [Constants.DataType.SMALLINT] = function( data )
  192. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  193. if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end
  194. col_md:setType( Constants.DataType.SMALLINT )
  195. return true, col_md
  196. end,
  197. [Constants.DataType.SERIAL] = function( data )
  198. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  199. if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end
  200. col_md:setType( Constants.DataType.SERIAL )
  201. return true, col_md
  202. end,
  203. [Constants.DataType.DATETIME] = function( data )
  204. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  205. if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end
  206. col_md:setType( Constants.DataType.DATETIME )
  207. col_md:setLength(10)
  208. return true, col_md
  209. end,
  210. [Constants.DataType.FLOAT] = function( data )
  211. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  212. if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end
  213. col_md:setType( Constants.DataType.FLOAT )
  214. return true, col_md
  215. end,
  216. [Constants.DataType.DATE] = function( data )
  217. local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data )
  218. if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end
  219. col_md:setType( Constants.DataType.DATE )
  220. return true, col_md
  221. end,
  222. }
  223. -- DataType decoders used to decode result set returned from the server
  224. -- This class is still incomplete and some decoders just adjust the offset
  225. -- position rather than decode the value.
  226. --
  227. -- The decoders, should be self explanatory
  228. DataTypeDecoders = {
  229. [Constants.DataType.INT] = function( data, pos )
  230. return bin.unpack(">i", data, pos)
  231. end,
  232. [Constants.DataType.FLOAT] = function( data, pos )
  233. return bin.unpack(">d", data, pos)
  234. end,
  235. [Constants.DataType.DATE] = function( data, pos )
  236. return pos + 4, "DATE"
  237. end,
  238. [Constants.DataType.SERIAL] = function( data, pos )
  239. return bin.unpack(">I", data, pos)
  240. end,
  241. [Constants.DataType.SMALLINT] = function( data, pos )
  242. return bin.unpack(">s", data, pos)
  243. end,
  244. [Constants.DataType.CHAR] = function( data, pos, len )
  245. local pos, ret = bin.unpack("A" .. len, data, pos)
  246. return pos, Util.ifxToLuaString( ret )
  247. end,
  248. [Constants.DataType.VARCHAR] = function( data, pos, len )
  249. local pos, len = bin.unpack("C", data, pos)
  250. local ret
  251. pos, ret = bin.unpack("A" .. len, data, pos)
  252. return pos, Util.ifxToLuaString( ret )
  253. end,
  254. [Constants.DataType.DATETIME] = function( data, pos )
  255. return pos + 10, "DATETIME"
  256. end,
  257. }
  258. -- The MessageDecoders class "holding" the Response Decoders
  259. MessageDecoders = {
  260. --- Decodes the SQ_ERR error message
  261. --
  262. -- @param socket already connected to the Informix database server
  263. -- @return status true on success, false on failure
  264. -- @return errmsg, Informix error message or decoding error message if
  265. -- status is false
  266. [Constants.Message.SQ_ERR] = function( socket )
  267. local status, data = socket:receive_buf(match.numbytes(8), true)
  268. local _, svcerr, oserr, errmsg, str, len, pos
  269. if( not(status) ) then return false, "Failed to decode error response" end
  270. pos, svcerr, oserr, _, len = bin.unpack(">ssss", data )
  271. if( len and len > 0 ) then
  272. status, data = socket:receive_buf(match.numbytes(len), true)
  273. if( not(status) ) then return false, "Failed to decode error response" end
  274. _, str = bin.unpack("A" .. len, data)
  275. end
  276. status, data = socket:receive_buf(match.numbytes(2), true)
  277. errmsg = Constants.ErrorMsg[svcerr]
  278. if ( errmsg and str ) then
  279. errmsg = errmsg:format(str)
  280. end
  281. return false, errmsg or ("Informix returned an error (svcerror: %d, oserror: %d)"):format( svcerr, oserr )
  282. end,
  283. --- Decodes the SQ_PROTOCOLS message
  284. --
  285. -- @param socket already connected to the Informix database server
  286. -- @return status true on success, false on failure
  287. -- @return err error message if status is false
  288. [Constants.Message.SQ_PROTOCOLS] = function( socket )
  289. local status, data
  290. local len, _
  291. status, data = socket:receive_buf(match.numbytes(2), true)
  292. if( not(status) ) then return false, "Failed to decode SQ_PROTOCOLS response" end
  293. _, len = bin.unpack(">S", data )
  294. -- read the remaining data
  295. return socket:receive_buf(match.numbytes(len + 2), true)
  296. end,
  297. --- Decodes the SQ_EOT message
  298. --
  299. -- @return status, always true
  300. [Constants.Message.SQ_EOT] = function( socket )
  301. return true
  302. end,
  303. --- Decodes the SQ_DONE message
  304. --
  305. -- @param socket already connected to the Informix database server
  306. -- @return status true on success, false on failure
  307. -- @return err error message if status is false
  308. [Constants.Message.SQ_DONE] = function( socket )
  309. local status, data = socket:receive_buf(match.numbytes(2), true)
  310. local _, len, tmp
  311. if( not(status) ) then return false, "Failed to decode SQ_DONE response" end
  312. _, len = bin.unpack(">S", data )
  313. -- For some *@#! reason the SQ_DONE packet sometimes contains an
  314. -- length exceeding the length of the packet by one. Attempt to
  315. -- detect this and fix.
  316. status, data = socket:receive_buf(match.numbytes(len), true)
  317. _, tmp = bin.unpack(">S", data, len - 2)
  318. return socket:receive_buf(match.numbytes((tmp == 0) and 3 or 4), true)
  319. end,
  320. --- Decodes the metadata for a result set
  321. --
  322. -- @param socket already connected to the Informix database server
  323. -- @return status true on success, false on failure
  324. -- @return column_meta table containing the metadata
  325. [Constants.Message.SQ_DESCRIBE] = function( socket )
  326. local status, data = socket:receive_buf(match.numbytes(14), true)
  327. local pos, cols, col_type, col_name, col_len, col_md, stmt_id
  328. local coldesc_len, x
  329. local column_meta = {}
  330. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  331. pos, cols, coldesc_len = bin.unpack(">SS", data, 11)
  332. pos, stmt_id = bin.unpack(">S", data, 3)
  333. if ( cols <= 0 ) then
  334. -- We can end up here if we executed a CREATE, UPDATE OR INSERT statement
  335. local tmp
  336. status, data = socket:receive_buf(match.numbytes(2), true)
  337. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  338. pos, tmp = bin.unpack(">S", data)
  339. -- This was the result of a CREATE or UPDATE statement
  340. if ( tmp == 0x0f ) then
  341. status, data = socket:receive_buf(match.numbytes(26), true)
  342. -- This was the result of a INSERT statement
  343. elseif( tmp == 0x5e ) then
  344. status, data = socket:receive_buf(match.numbytes(46), true)
  345. end
  346. return true
  347. end
  348. status, data = socket:receive_buf(match.numbytes(6), true)
  349. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  350. for i=1, cols do
  351. status, data = socket:receive_buf(match.numbytes(2), true)
  352. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  353. pos, col_type = bin.unpack("C", data, 2)
  354. if ( MetaDataDecoders[col_type] ) then
  355. status, data = socket:receive_buf(match.numbytes(20), true)
  356. if( not(status) ) then
  357. return false, "Failed to read column meta data"
  358. end
  359. status, col_md = MetaDataDecoders[col_type]( data )
  360. if ( not(status) ) then
  361. return false, col_md
  362. end
  363. else
  364. return false, ("No metadata decoder for column type: %d"):format(col_type)
  365. end
  366. if ( i<cols ) then
  367. status, data = socket:receive_buf(match.numbytes(6), true)
  368. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  369. end
  370. col_md:setType( col_type )
  371. table.insert( column_meta, col_md )
  372. end
  373. status, data = socket:receive_buf(match.numbytes(( coldesc_len % 2 ) == 0 and coldesc_len or coldesc_len + 1), true)
  374. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  375. pos = 1
  376. for i=1, cols do
  377. local col_name
  378. pos, col_name = bin.unpack("z", data, pos)
  379. column_meta[i]:setName( col_name )
  380. end
  381. status, data = socket:receive_buf(match.numbytes(2), true)
  382. if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end
  383. pos, data = bin.unpack(">S", data)
  384. if( data == Constants.Message.SQ_DONE ) then
  385. status, data = socket:receive_buf(match.numbytes(26), true)
  386. else
  387. status, data = socket:receive_buf(match.numbytes(10), true)
  388. end
  389. return true, { metadata = column_meta, stmt_id = stmt_id }
  390. end,
  391. --- Processes the result from a query
  392. --
  393. -- @param socket already connected to the Informix database server
  394. -- @param info table containing the following fields:
  395. -- <code>metadata</code> as received from <code>SQ_DESCRIBE</code>
  396. -- <code>rows</code> containing already retrieved rows
  397. -- <code>id</code> containing the statement id as sent to SQ_ID
  398. -- @return status true on success, false on failure
  399. -- @return rows table containing the resulting columns and rows as:
  400. -- { { col, col2, col3 } }
  401. -- or error message if status is false
  402. [Constants.Message.SQ_TUPLE] = function( socket, info )
  403. local status, data
  404. local row = {}
  405. local count = 1
  406. if ( not( info.rows ) ) then info.rows = {} end
  407. while (true) do
  408. local pos = 1
  409. status, data = socket:receive_buf(match.numbytes(6), true)
  410. if( not(status) ) then return false, "Failed to read column data" end
  411. local _, total_len = bin.unpack(">I", data, 3)
  412. status, data = socket:receive_buf(match.numbytes(( total_len % 2 == 0 ) and total_len or total_len + 1), true)
  413. if( not(status) ) then return false, "Failed to read column data" end
  414. row = {}
  415. for _, col in ipairs(info.metadata) do
  416. local typ, len, name = col:getType(), col:getLength(), col:getName()
  417. local val
  418. if( DataTypeDecoders[typ] ) then
  419. pos, val = DataTypeDecoders[typ]( data, pos, len )
  420. else
  421. return false, ("No data type decoder for type: 0x%d"):format(typ)
  422. end
  423. table.insert( row, val )
  424. end
  425. status, data = socket:receive_buf(match.numbytes(2), true)
  426. local _, flags = bin.unpack(">S", data)
  427. count = count + 1
  428. table.insert( info.rows, row )
  429. -- Check if we're done
  430. if ( Constants.Message.SQ_DONE == flags ) then
  431. break
  432. end
  433. -- If there's more data we need to send a new SQ_ID packet
  434. if ( flags == Constants.Message.SQ_EOT ) then
  435. local status, tmp = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "continue" ) ) )
  436. local pkt_type
  437. status, tmp = socket:receive_buf(match.numbytes(2), true)
  438. pos, pkt_type = bin.unpack(">S", tmp)
  439. return MessageDecoders[pkt_type]( socket, info )
  440. end
  441. end
  442. -- read the remaining data
  443. status, data = socket:receive_buf(match.numbytes(26), true)
  444. if( not(status) ) then return false, "Failed to read column data" end
  445. -- signal finish reading
  446. status, data = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "end" ) ) )
  447. status, data = socket:receive_buf(match.numbytes(2), true)
  448. return true, info
  449. end,
  450. --- Decodes a SQ_DBLIST response
  451. --
  452. -- @param socket already connected to the Informix database server
  453. -- @return status true on success, false on failure
  454. -- @return databases array of database names
  455. [Constants.Message.SQ_DBLIST] = function( socket )
  456. local status, data, pos, len, db
  457. local databases = {}
  458. while( true ) do
  459. status, data = socket:receive_buf(match.numbytes(2), true)
  460. if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end
  461. pos, len = bin.unpack(">S", data)
  462. if ( 0 == len ) then break end
  463. status, data = socket:receive_buf(match.numbytes(len), true)
  464. if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end
  465. pos, db = bin.unpack("A" .. len, data )
  466. table.insert( databases, db )
  467. if ( len %2 == 1 ) then
  468. socket:receive_buf(match.numbytes(1), true)
  469. if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end
  470. end
  471. end
  472. -- read SQ_EOT
  473. status, data = socket:receive_buf(match.numbytes(2), true)
  474. return true, databases
  475. end,
  476. [Constants.Message.SQ_EXIT] = function( socket )
  477. local status, data = socket:receive_buf(match.numbytes(2), true)
  478. if ( not(status) ) then return false, "Failed to parse SQ_EXIT response" end
  479. return true
  480. end
  481. }
  482. -- Packet used to request a list of available databases
  483. Packet.SQ_DBLIST =
  484. {
  485. --- Creates a new Packet.SQ_DBLIST instance
  486. --
  487. -- @return object new instance of Packet.SQ_DBLIST
  488. new = function( self )
  489. local o = {}
  490. setmetatable(o, self)
  491. self.__index = self
  492. return o
  493. end,
  494. --- Converts the class to a string suitable to send over the socket
  495. --
  496. -- @return string containing the packet data
  497. __tostring = function(self)
  498. return bin.pack(">SS", Constants.Message.SQ_DBLIST, Constants.Message.SQ_EOT)
  499. end
  500. }
  501. -- Packet used to open the database
  502. Packet.SQ_DBOPEN =
  503. {
  504. --- Creates a new Packet.SQ_DBOPEN instance
  505. --
  506. -- @param database string containing the name of the database to open
  507. -- @return object new instance of Packet.SQ_DBOPEN
  508. new = function( self, database )
  509. local o = {}
  510. setmetatable(o, self)
  511. self.__index = self
  512. o.database = database
  513. return o
  514. end,
  515. --- Converts the class to a string suitable to send over the socket
  516. --
  517. -- @return string containing the packet data
  518. __tostring = function(self)
  519. return bin.pack(">SSASS", Constants.Message.SQ_DBOPEN, #self.database,
  520. Util.padToOdd(self.database), 0x00,
  521. Constants.Message.SQ_EOT)
  522. end
  523. }
  524. -- This packet is "a mess" and requires further analysis
  525. Packet.SQ_ID =
  526. {
  527. --- Creates a new Packet.SQ_ID instance
  528. --
  529. -- @param id number containing the statement identifier
  530. -- @param s1 number unknown, should be 0 on first call and 1 when more data is requested
  531. -- @return object new instance of Packet.SQ_ID
  532. new = function( self, id, id2, mode )
  533. local o = {}
  534. setmetatable(o, self)
  535. self.__index = self
  536. o.id = ("_ifxc%.13d"):format( id2 or 0 )
  537. o.seq = id
  538. o.mode = mode
  539. return o
  540. end,
  541. --- Converts the class to a string suitable to send over the socket
  542. --
  543. -- @return string containing the packet data
  544. __tostring = function(self)
  545. if ( self.mode == "continue" ) then
  546. return bin.pack( ">SSSSSS", Constants.Message.SQ_ID, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT )
  547. elseif ( self.mode == "end" ) then
  548. return bin.pack( ">SSSS", Constants.Message.SQ_ID, self.seq, 0x000a, Constants.Message.SQ_EOT)
  549. else
  550. return bin.pack(">SSSSASSSSSSS", Constants.Message.SQ_ID, self.seq, 0x0003, #self.id, self.id,
  551. 0x0006, 0x0004, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT )
  552. end
  553. end
  554. }
  555. Packet.SQ_INFO =
  556. {
  557. -- The default parameters
  558. DEFAULT_PARAMETERS = {
  559. [1] = { ["DBTEMP"] = "/tmp" },
  560. [2] = { ["SUBQCACHESZ"] = "10" },
  561. },
  562. --- Creates a new Packet.SQ_INFO instance
  563. --
  564. -- @param params containing any additional parameters to use
  565. -- @return object new instance of Packet.SQ_INFO
  566. new = function( self, params )
  567. local o = {}
  568. local params = params or Packet.SQ_INFO.DEFAULT_PARAMETERS
  569. setmetatable(o, self)
  570. self.__index = self
  571. o.parameters = {}
  572. for _, v in ipairs( params ) do
  573. for k2, v2 in pairs(v) do
  574. o:addParameter( k2, v2 )
  575. end
  576. end
  577. return o
  578. end,
  579. addParameter = function( self, key, value )
  580. table.insert( self.parameters, { [key] = value } )
  581. end,
  582. paramToString = function( self, key, value )
  583. return bin.pack(">SASA", #key, Util.padToOdd(key), #value, Util.padToOdd( value ) )
  584. end,
  585. --- Converts the class to a string suitable to send over the socket
  586. --
  587. -- @return string containing the packet data
  588. __tostring = function( self )
  589. local params = ""
  590. local data
  591. for _, v in ipairs( self.parameters ) do
  592. for k2, v2 in pairs( v ) do
  593. params = params .. self:paramToString( k2, v2 )
  594. end
  595. end
  596. data = bin.pack(">SSSSSASSS", Constants.Message.SQ_INFO, 0x0006,
  597. #params + 6, 0x000c, 0x0004, params, 0x0000, 0x0000,
  598. Constants.Message.SQ_EOT)
  599. return data
  600. end
  601. }
  602. -- Performs protocol negotiation?
  603. Packet.SQ_PROTOCOLS =
  604. {
  605. -- hex-encoded data to send as protocol negotiation
  606. data = "0007fffc7ffc3c8c8a00000c",
  607. --- Creates a new Packet.SQ_PROTOCOLS instance
  608. --
  609. -- @return object new instance of Packet.SQ_PROTOCOLS
  610. new = function( self )
  611. local o = {}
  612. setmetatable(o, self)
  613. self.__index = self
  614. return o
  615. end,
  616. --- Converts the class to a string suitable to send over the socket
  617. --
  618. -- @return string containing the packet data
  619. __tostring = function(self)
  620. return bin.pack(">SH", Constants.Message.SQ_PROTOCOLS, self.data)
  621. end
  622. }
  623. -- Packet used to execute SELECT Queries
  624. Packet.SQ_PREPARE =
  625. {
  626. --- Creates a new Packet.SQ_PREPARE instance
  627. --
  628. -- @param query string containing the query to execute
  629. -- @return object new instance of Packet.SQ_PREPARE
  630. new = function( self, query )
  631. local o = {}
  632. setmetatable(o, self)
  633. self.__index = self
  634. o.query = Util.padToEven(query)
  635. return o
  636. end,
  637. --- Converts the class to a string suitable to send over the socket
  638. --
  639. -- @return string containing the packet data
  640. __tostring = function(self)
  641. return bin.pack(">SIACSSS", Constants.Message.SQ_PREPARE, #self.query, self.query, 0, 0x0016, 0x0031, Constants.Message.SQ_EOT)
  642. end
  643. }
  644. -- Packet used to execute commands other than SELECT
  645. Packet.SQ_COMMAND =
  646. {
  647. --- Creates a new Packet.SQ_COMMAND instance
  648. --
  649. -- @param query string containing the query to execute
  650. -- @return object new instance of Packet.SQ_COMMAND
  651. new = function( self, query )
  652. local o = {}
  653. setmetatable(o, self)
  654. self.__index = self
  655. o.query = Util.padToEven(query)
  656. return o
  657. end,
  658. --- Converts the class to a string suitable to send over the socket
  659. --
  660. -- @return string containing the packet data
  661. __tostring = function(self)
  662. return bin.pack(">SIACSSSS", Constants.Message.SQ_COMMAND, #self.query, self.query, 0, 0x0016, 0x0007, 0x000b, Constants.Message.SQ_EOT)
  663. end
  664. }
  665. Packet.SQ_EXIT = {
  666. --- Creates a new Packet.SQ_EXIT instance
  667. --
  668. -- @return object new instance of Packet.SQ_EXIT
  669. new = function( self )
  670. local o = {}
  671. setmetatable(o, self)
  672. self.__index = self
  673. return o
  674. end,
  675. --- Converts the class to a string suitable to send over the socket
  676. --
  677. -- @return string containing the packet data
  678. __tostring = function(self)
  679. return bin.pack(">S", Constants.Message.SQ_EXIT)
  680. end
  681. }
  682. -- The Utility Class
  683. Util =
  684. {
  685. --- Converts a connection parameter to string
  686. --
  687. -- @param param string containing the parameter name
  688. -- @param value string containing the parameter value
  689. -- @return string containing the encoded parameter as string
  690. paramToString = function( param, value )
  691. return bin.pack(">PP", param, value )
  692. end,
  693. --- Pads a string to an even number of characters
  694. --
  695. -- @param str the string to pad
  696. -- @param pad the character to pad with
  697. -- @return result the padded string
  698. padToEven = function( str, pad )
  699. return (#str % 2 == 1) and str or str .. ( pad and pad or "\0")
  700. end,
  701. --- Pads a string to an odd number of characters
  702. --
  703. -- @param str the string to pad
  704. -- @param pad the character to pad with
  705. -- @return result the padded string
  706. padToOdd = function( str, pad )
  707. return (#str % 2 == 0) and str or str .. ( pad and pad or "\0")
  708. end,
  709. --- Formats a table to suitable script output
  710. --
  711. -- @param info as returned from ExecutePrepare
  712. -- @return table suitable for use by <code>stdnse.format_output</code>
  713. formatTable = function( info )
  714. local header, row = "", ""
  715. local result = {}
  716. local metadata = info.metadata
  717. local rows = info.rows
  718. if ( info.error ) then
  719. table.insert(result, info.error)
  720. return result
  721. end
  722. if ( info.info ) then
  723. table.insert(result, info.info)
  724. return result
  725. end
  726. if ( not(metadata) ) then return "" end
  727. for i=1, #metadata do
  728. if ( metadata[i]:getType() == Constants.DataType.CHAR and metadata[i]:getLength() < 50) then
  729. header = header .. ("%-" .. metadata[i]:getLength() .. "s "):format(metadata[i]:getName())
  730. else
  731. header = header .. metadata[i]:getName()
  732. if ( i<#metadata ) then
  733. header = header .. "\t"
  734. end
  735. end
  736. end
  737. table.insert( result, header )
  738. for j=1, #rows do
  739. row = ""
  740. for i=1, #metadata do
  741. row = row .. rows[j][i] .. " "
  742. if ( metadata[i]:getType() ~= Constants.DataType.CHAR and i<#metadata and metadata[i]:getLength() < 50 ) then row = row .. "\t" end
  743. end
  744. table.insert( result, row )
  745. end
  746. return result
  747. end,
  748. -- Removes trailing nulls
  749. --
  750. -- @param str containing the informix string
  751. -- @return ret the string with any trailing nulls removed
  752. ifxToLuaString = function( str )
  753. local ret
  754. if ( not(str) ) then return "" end
  755. if ( str:sub(-1, -1 ) ~= "\0" ) then
  756. return str
  757. end
  758. for i=1, #str do
  759. if ( str:sub(-i,-i) == "\0" ) then
  760. ret = str:sub(1, -i - 1)
  761. else
  762. break
  763. end
  764. end
  765. return ret
  766. end,
  767. }
  768. -- The connection Class, used to connect and authenticate to the server
  769. -- Currently only supports plain-text authentication
  770. --
  771. -- The unknown portions in the __tostring method have been derived from Java
  772. -- code connecting to Informix using JDBC.
  773. Packet.Connect = {
  774. -- default parameters sent using JDBC
  775. DEFAULT_PARAMETERS = {
  776. [1] = { ['LOCKDOWN'] = 'no' },
  777. [2] = { ['DBDATE'] = 'Y4MD-' },
  778. [3] = { ['SINGLELEVEL'] = 'no' },
  779. [4] = { ['NODEFDAC'] = 'no' },
  780. [5] = { ['CLNT_PAM_CAPABLE'] = '1' },
  781. [6] = { ['SKALL'] = '0' },
  782. [7] = { ['LKNOTIFY'] = 'yes' },
  783. [8] = { ['SKSHOW'] = '0' },
  784. [9] = { ['IFX_UPDDESC'] = '1' },
  785. [10] = { ['DBPATH'] = '.' },
  786. [11] = { ['CLIENT_LOCALE'] = 'en_US.8859-1' },
  787. [12] = { ['SKINHIBIT'] = '0' },
  788. },
  789. --- Creates a new Connection packet
  790. --
  791. -- @param username string containing the username for authentication
  792. -- @param password string containing the password for authentication
  793. -- @param instance string containing the instance to connect to
  794. -- @return a new Packet.Connect instance
  795. new = function(self, username, password, instance, parameters)
  796. local o = {}
  797. setmetatable(o, self)
  798. self.__index = self
  799. o.username = username and username .. "\0"
  800. o.password = password and password .. "\0"
  801. o.instance = instance and instance .. "\0"
  802. o.parameters = parameters
  803. return o
  804. end,
  805. --- Adds the default set of parameters
  806. addDefaultParameters = function( self )
  807. for _, v in ipairs( self.DEFAULT_PARAMETERS ) do
  808. for k2, v2 in pairs( v ) do
  809. self:addParameter( k2, v2 )
  810. end
  811. end
  812. end,
  813. --- Adds a parameter to the connection packet
  814. --
  815. -- @param param string containing the parameter name
  816. -- @param value string containing the parameter value
  817. -- @return status, always true
  818. addParameter = function( self, param, value )
  819. local tbl = {}
  820. tbl[param] = value
  821. table.insert( self.parameters, tbl )
  822. return true
  823. end,
  824. --- Retrieves the OS error code
  825. --
  826. -- @return oserror number containing the OS error code
  827. getOsError = function( self ) return self.oserror end,
  828. --- Retrieves the Informix service error
  829. --
  830. -- @return svcerror number containing the service error
  831. getSvcError = function( self ) return self.svcerror end,
  832. --- Retrieves the Informix error message
  833. --
  834. -- @return errmsg string containing the "mapped" error message
  835. getErrMsg = function( self ) return self.errmsg end,
  836. --- Reads and decodes the response to the connect packet from the server.
  837. --
  838. -- The function will return true even if the response contains an Informix
  839. -- error. In order to verify if the connection was successful, check for OS
  840. -- or service errors using the getSvcError and getOsError methods.
  841. --
  842. -- @param socket already connected to the server
  843. -- @return status true on success, false on failure
  844. -- @return err msg if status is false
  845. readResponse = function( self, socket )
  846. local status, data = socket:receive_buf(match.numbytes(2), true)
  847. local len, pos, tmp
  848. if ( not(status) ) then return false, data end
  849. pos, len = bin.unpack(">S", data)
  850. status, data = socket:receive_buf(match.numbytes(len - 2), true)
  851. if ( not(status) ) then return false, data end
  852. pos = 13
  853. pos, tmp = bin.unpack(">S", data, pos)
  854. pos = pos + tmp
  855. pos, tmp = bin.unpack(">S", data, pos)
  856. if ( 108 ~= tmp ) then
  857. return false, "Connect received unexpected response"
  858. end
  859. pos = pos + 12
  860. -- version
  861. pos, len = bin.unpack(">S", data, pos)
  862. pos, self.version = bin.unpack("A" .. len, data, pos)
  863. -- serial
  864. pos, len = bin.unpack(">S", data, pos)
  865. pos, self.serial = bin.unpack("A" .. len, data, pos)
  866. -- applid
  867. pos, len = bin.unpack(">S", data, pos)
  868. pos, self.applid = bin.unpack("A" .. len, data, pos)
  869. -- skip 14 bytes ahead
  870. pos = pos + 14
  871. -- do some more skipping
  872. pos, tmp = bin.unpack(">S", data, pos)
  873. pos = pos + tmp
  874. -- do some more skipping
  875. pos, tmp = bin.unpack(">S", data, pos)
  876. pos = pos + tmp
  877. -- skip another 24 bytes
  878. pos = pos + 24
  879. pos, tmp = bin.unpack(">S", data, pos)
  880. if ( tmp ~= 102 ) then
  881. return false, "Connect received unexpected response"
  882. end
  883. pos = pos + 6
  884. pos, self.svcerror = bin.unpack(">s", data, pos)
  885. pos, self.oserror = bin.unpack(">s", data, pos )
  886. if ( self.svcerror ~= 0 ) then
  887. self.errmsg = Constants.ErrorMsg[self.svcerror] or ("Unknown error %d occurred"):format( self.svcerror )
  888. end
  889. return true
  890. end,
  891. --- Converts the class to a string suitable to send over the socket
  892. --
  893. -- @return string containing the packet data
  894. __tostring = function( self )
  895. local data
  896. local unknown = [[
  897. 013c0000006400650000003d0006494545454d00006c73716c65786563000000
  898. 00000006392e32383000000c524453235230303030303000000573716c690000
  899. 00013300000000000000000001
  900. ]]
  901. local unknown2 = [[
  902. 6f6c0000000000000000003d746c697463700000000000010068000b
  903. 00000003
  904. ]]
  905. local unknown3 = [[
  906. 00000000000000000000006a
  907. ]]
  908. local unknown4 = [[ 007f ]]
  909. if ( not(self.parameters) ) then
  910. self.parameters = {}
  911. self:addDefaultParameters()
  912. end
  913. data = bin.pack(">HPPHPHS", unknown, self.username, self.password, unknown2, self.instance, unknown3, #self.parameters )
  914. if ( self.parameters ) then
  915. for _, v in ipairs( self.parameters ) do
  916. for k2, v2 in pairs( v ) do
  917. data = data .. Util.paramToString( k2 .. "\0", v2 .. "\0" )
  918. end
  919. end
  920. end
  921. data = data .. bin.pack("H", unknown4)
  922. data = bin.pack(">S", #data + 2) .. data
  923. return data
  924. end,
  925. }
  926. -- The communication class
  927. Comm =
  928. {
  929. --- Creates a new Comm instance
  930. --
  931. -- @param socket containing a buffered socket connected to the server
  932. -- @return a new Comm instance
  933. new = function(self, socket)
  934. local o = {}
  935. setmetatable(o, self)
  936. self.__index = self
  937. o.socket = socket
  938. return o
  939. end,
  940. --- Sends and packet and attempts to handle the response
  941. --
  942. -- @param packets an instance of a Packet.* class
  943. -- @param info any additional info to pass as the second parameter to the
  944. -- decoder
  945. -- @return status true on success, false on failure
  946. -- @return data returned from the ResponseDecoder
  947. exchIfxPacket = function( self, packet, info )
  948. local _, typ
  949. local status, data = self.socket:send( tostring(packet) )
  950. if ( not(status) ) then return false, data end
  951. status, data = self.socket:receive_buf(match.numbytes(2), true)
  952. _, typ = bin.unpack(">S", data)
  953. if ( MessageDecoders[typ] ) then
  954. status, data = MessageDecoders[typ]( self.socket, info )
  955. else
  956. return false, ("Unsupported data returned from server (type: 0x%x)"):format(typ)
  957. end
  958. return status, data
  959. end
  960. }
  961. -- The Helper class providing easy access to the other db functionality
  962. Helper = {
  963. --- Creates a new Helper instance
  964. --
  965. -- @param host table as passed to the action script function
  966. -- @param port table as passed to the action script function
  967. -- @param instance [optional] string containing the instance to connect to
  968. -- in case left empty it's populated by the informix.instance script
  969. -- argument.
  970. -- @return Helper instance
  971. new = function(self, host, port, instance)
  972. local o = {}
  973. setmetatable(o, self)
  974. self.__index = self
  975. o.host = host
  976. o.port = port
  977. o.socket = nmap.new_socket()
  978. o.instance = instance or "nmap_probe"
  979. return o
  980. end,
  981. --- Connects to the Informix server
  982. --
  983. -- @return true on success, false on failure
  984. -- @return err containing error message when status is false
  985. Connect = function( self )
  986. local status, data
  987. local conn, packet
  988. -- Some Informix server seem to take a LOT of time to respond?!
  989. self.socket:set_timeout(20000)
  990. status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" )
  991. if( not(status) ) then
  992. return status, data
  993. end
  994. self.comm = Comm:new( self.socket )
  995. return true
  996. end,
  997. --- Attempts to login to the Informix database server
  998. --
  999. -- The optional parameters parameter takes any informix specific parameters
  1000. -- used to connect to the database. In case it's omitted a set of default
  1001. -- parameters are set. Parameters should be past as key, value pairs inside
  1002. -- of a table array as the following example:
  1003. --
  1004. -- local params = {
  1005. -- [1] = { ["PARAM1"] = "VALUE1" },
  1006. -- [2] = { ["PARAM2"] = "VALUE2" },
  1007. -- }
  1008. --
  1009. -- @param username string containing the username for authentication
  1010. -- @param password string containing the password for authentication
  1011. -- @param parameters [optional] table of informix specific parameters
  1012. -- @param database [optional] database to connect to
  1013. -- @param retry [optional] used when autodetecting instance
  1014. -- @return status true on success, false on failure
  1015. -- @return err containing the error message if status is false
  1016. Login = function( self, username, password, parameters, database, retry )
  1017. local conn, status, data, len, packet
  1018. conn = Packet.Connect:new( username, password, self.instance, parameters )
  1019. status, data = self.socket:send( tostring(conn) )
  1020. if ( not(status) ) then return false, "Helper.Login failed to send login request" end
  1021. status = conn:readResponse( self.socket )
  1022. if ( not(status) ) then return false, "Helper.Login failed to read response" end
  1023. if ( status and ( conn:getOsError() ~= 0 or conn:getSvcError() ~= 0 ) ) then
  1024. -- Check if we didn't supply the correct instance name, if not attempt to
  1025. -- reconnect using the instance name returned by the server
  1026. if ( conn:getSvcError() == -761 and not(retry) ) then
  1027. self.instance = conn.applid
  1028. self:Close()
  1029. self:Connect()
  1030. return self:Login( username, password, parameters, database, 1 )
  1031. end
  1032. return false, conn:getErrMsg()
  1033. end
  1034. status, packet = self.comm:exchIfxPacket( Packet.SQ_PROTOCOLS:new() )
  1035. if ( not(status) ) then return false, packet end
  1036. status, packet = self.comm:exchIfxPacket( Packet.SQ_INFO:new() )
  1037. if ( not(status) ) then return false, packet end
  1038. -- If a database was supplied continue further protocol negotiation and
  1039. -- attempt to open the database.
  1040. if ( database ) then
  1041. status, packet = self:OpenDatabase( database )
  1042. if ( not(status) ) then return false, packet end
  1043. end
  1044. return true
  1045. end,
  1046. --- Opens a database
  1047. --
  1048. -- @param database string containing the database name
  1049. -- @return status true on success, false on failure
  1050. -- @return err string containing the error message if status is false
  1051. OpenDatabase = function( self, database )
  1052. return self.comm:exchIfxPacket( Packet.SQ_DBOPEN:new( database ) )
  1053. end,
  1054. --- Attempts to retrieve a list of available databases
  1055. --
  1056. -- @return status true on success, false on failure
  1057. -- @return databases array of database names or err on failure
  1058. GetDatabases = function( self )
  1059. return self.comm:exchIfxPacket( Packet.SQ_DBLIST:new() )
  1060. end,
  1061. Query = function( self, query )
  1062. local status, metadata, data, res
  1063. local id, seq = 0, 1
  1064. local result = {}
  1065. if ( type(query) == "string" ) then
  1066. query = stdnse.strsplit(";%s*", query)
  1067. end
  1068. for _, q in ipairs( query ) do
  1069. if ( q:upper():match("^%s*SELECT") ) then
  1070. status, data = self.comm:exchIfxPacket( Packet.SQ_PREPARE:new( q ) )
  1071. seq = seq + 1
  1072. else
  1073. status, data = self.comm:exchIfxPacket( Packet.SQ_COMMAND:new( q .. ";" ) )
  1074. end
  1075. if( status and data ) then
  1076. metadata = data.metadata
  1077. status, data = self.comm:exchIfxPacket( Packet.SQ_ID:new( data.stmt_id, seq, "begin" ), { metadata = metadata, id = id, rows = nil, query=q } )
  1078. -- check if any rows were returned
  1079. if ( not( data.rows ) ) then
  1080. data = { query = q, info = "No rows returned" }
  1081. end
  1082. --if( not(status) ) then return false, data end
  1083. elseif( not(status) ) then
  1084. data = { query = q, ["error"] = "ERROR: " .. data }
  1085. else
  1086. data = { query = q, info = "No rows returned" }
  1087. end
  1088. table.insert( result, data )
  1089. end
  1090. return true, result
  1091. end,
  1092. --- Closes the connection to the server
  1093. --
  1094. -- @return status true on success, false on failure
  1095. Close = function( self )
  1096. local status, packet = self.comm:exchIfxPacket( Packet.SQ_EXIT:new() )
  1097. return self.socket:close()
  1098. end,
  1099. }
  1100. return _ENV;