PageRenderTime 96ms CodeModel.GetById 32ms RepoModel.GetById 10ms app.codeStats 0ms

/nselib/rmi.lua

https://gitlab.com/g10h4ck/nmap-gsoc2015
Lua | 1548 lines | 942 code | 167 blank | 439 comment | 147 complexity | d4bd8afdf7c067a31fb964813d0c9461 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. --- Library method for communicating over RMI (JRMP + java serialization)
  2. --
  3. -- This is a not complete RMI implementation for Lua, which is meant to be able
  4. -- to invoke methods and parse returnvalues which are simple, basically the java primitives.
  5. -- This can be used to e.g dump out the registry, and perform authentication against
  6. -- e.g JMX-services.
  7. --
  8. -- This library also contains some classes which works pretty much like the
  9. -- java classes BufferedReader, BufferedWriter, DataOutputStream and DataInputStream.
  10. --
  11. -- Most of the methods in the RMIDataStream class is based on the OpenJDK RMI Implementation,
  12. -- and I have kept the methodnames as they are in java, so it should not be too hard to find
  13. -- the corresponding functionality in the jdk codebase to see how things 'should' be done, in case
  14. -- there are bugs or someone wants to make additions. I have only implemented the
  15. -- things that were needed to get things working, but it should be pretty simple to add more
  16. -- functionality by lifting over more stuff from the jdk.
  17. --
  18. -- The interesting classes in OpenJDK are:
  19. -- java.io.ObjectStreamConstants
  20. -- java.io.ObjectStreamClass
  21. -- java.io.ObjectInputStream
  22. -- sun.rmi.transport.StreamRemoteCall
  23. -- and a few more.
  24. --
  25. -- If you want to add calls to classes you know of, you can use e.g Jode to decompile the
  26. -- stub-class or skeleton class and find out the details that are needed to perform an
  27. -- RMI method invocation. Those are
  28. -- Class hashcode
  29. -- Method number (each method gets a number)
  30. -- Arguments f
  31. -- You also need the object id (so the remote server knows what instance you are talking to). That can be
  32. -- fetched from the registry (afaik) but not currently implemented. Some object ids are static : the registry is always 0
  33. --
  34. -- @author Martin Holst Swende
  35. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  36. -- @see java 1.4 RMI-spec: http://java.sun.com/j2se/1.4.2/docs/guide/rmi/
  37. -- @see java 5 RMI-spec: http://java.sun.com/j2se/1.5.0/docs/guide/rmi/spec/rmiTOC.html
  38. -- @see java 6 RMI-spec : http://java.sun.com/javase/6/docs/technotes/guides/rmi/index.html
  39. -- @see The protocol for Java object serializtion : http://java.sun.com/javase/6/docs/platform/serialization/spec/protocol.html
  40. -- Version 0.2
  41. -- Created 09/06/2010 - v0.1 - created by Martin Holst Swende <martin@swende.se>
  42. -- Fixed more documentation - v0.2 Martin Holst Swende <martin@swende.se>
  43. local bin = require "bin"
  44. local bit = require "bit"
  45. local nmap = require "nmap"
  46. local stdnse = require "stdnse"
  47. local string = require "string"
  48. local table = require "table"
  49. _ENV = stdnse.module("rmi", stdnse.seeall)
  50. -- Some lazy shortcuts
  51. local function dbg(str,...)
  52. local arg={...}
  53. stdnse.debug3("RMI:"..str, table.unpack(arg))
  54. end
  55. -- Convenience function to both print an error message and return <false, msg>
  56. -- Example usage :
  57. -- if foo ~= "gazonk" then
  58. -- return doh("Foo should be gazonk but was %s", foo)
  59. -- end
  60. local function doh(str,...)
  61. local arg={...}
  62. stdnse.debug1("RMI-ERR:"..tostring(str), table.unpack(arg))
  63. return false, str
  64. end
  65. ---
  66. -- BufferedWriter
  67. -- This buffering writer provide functionality much like javas BufferedWriter.
  68. --
  69. -- BufferedWriter wraps the pack-functionality from bin, and buffers data internally
  70. -- until flush is called. When flush is called, it either sends the data to the socket OR
  71. -- returns the data, if no socket has been set.
  72. --@usage:
  73. -- local bWriter = BufferedWriter:new(socket)
  74. -- local breader= BufferedReader:new(socket)
  75. --
  76. -- bWriter.pack('>i', integer)
  77. -- bWriter.flush() -- sends the data
  78. --
  79. -- if bsocket:canRead(4) then -- Waits until four bytes can be read
  80. -- local packetLength = bsocket:unpack('i') -- Read the four bytess
  81. -- if bsocket:canRead(packetLength) then
  82. -- -- ...continue reading packet values
  83. BufferedWriter = {
  84. new = function(self, socket)
  85. local o = {}
  86. setmetatable(o, self)
  87. self.__index = self
  88. o.writeBuffer = ''
  89. o.pos = 1
  90. o.socket = socket
  91. return o
  92. end,
  93. -- Sends data over the socket
  94. -- (Actually, just buffers until flushed)
  95. -- @return Status (true or false).
  96. -- @return Error code (if status is false).
  97. send = function( self, data )
  98. self.writeBuffer = self.writeBuffer .. data
  99. end,
  100. -- Convenience function, wraps bin
  101. pack = function(self, fmt, ... )
  102. local arg={...}
  103. self.writeBuffer = self.writeBuffer .. bin.pack( fmt, table.unpack(arg))
  104. end,
  105. -- This function flushes the buffer contents, thereby emptying
  106. -- the buffer. If a socket has been supplied, that's where it will be sent
  107. -- otherwise the buffer contents are returned
  108. --@return status
  109. --@return content of buffer, in case no socket was used
  110. flush = function(self)
  111. local content = self.writeBuffer
  112. self.writeBuffer = ''
  113. if not self.socket then
  114. return true, content
  115. end
  116. return self.socket:send(content)
  117. end,
  118. }
  119. ---
  120. -- BufferedReader reads data from the supplied socket and contains functionality
  121. -- to read all that is available and store all that is not currently needed, so the caller
  122. -- gets an exact number of bytes (which is not the case with the basic nmap socket implementation)
  123. -- If not enough data is available, it blocks until data is received, thereby handling the case
  124. -- if data is spread over several tcp packets (which is a pitfall for many scripts)
  125. --
  126. -- It wraps unpack from bin for the reading.
  127. -- OBS! You need to check before invoking skip or unpack that there is enough
  128. -- data to read. Since this class does not parse arguments to unpack, it does not
  129. -- know how much data to read ahead on those calls.
  130. --@usage:
  131. -- local bWriter = BufferedWriter:new(socket)
  132. -- local breader= BufferedReader:new(socket)
  133. --
  134. -- bWriter.pack('>i', integer)
  135. -- bWriter.flush() -- sends the data
  136. --
  137. -- if bsocket:canRead(4) then -- Waits until four bytes can be read
  138. -- local packetLength = bsocket:unpack('i') -- Read the four bytess
  139. -- if bsocket:canRead(packetLength) then
  140. -- -- ...continue reading packet values
  141. BufferedReader = {
  142. new = function(self, socket, readBuffer)
  143. local o = {}
  144. setmetatable(o, self)
  145. self.__index = self
  146. o.readBuffer = readBuffer -- May be nil
  147. o.pos = 1
  148. o.socket = socket -- May also be nil
  149. return o
  150. end,
  151. ---
  152. -- This method blocks until the specified number of bytes
  153. -- have been read from the socket and are available for
  154. -- the caller to read, e.g via the unpack function
  155. canRead= function(self,count)
  156. local status, data
  157. self.readBuffer = self.readBuffer or ""
  158. local missing = self.pos + count - #self.readBuffer -1
  159. if ( missing > 0) then
  160. if self.socket == nil then
  161. return doh("Not enough data in static buffer")
  162. end
  163. status, data = self.socket:receive_bytes( missing )
  164. if ( not(status) ) then
  165. return false, data
  166. end
  167. self.readBuffer = self.readBuffer .. data
  168. end
  169. -- Now and then, we flush the buffer
  170. if ( self.pos > 1024) then
  171. self.readBuffer = self.readBuffer:sub( self.pos )
  172. self.pos = 1
  173. end
  174. return true
  175. end,
  176. ---
  177. --@return Returns the number of bytes already available for reading
  178. bufferSize = function(self)
  179. return #self.readBuffer +1 -self.pos
  180. end,
  181. ---
  182. -- This function works just like bin.unpack (in fact, it is
  183. -- merely a wrapper around it. However, it uses the data
  184. -- already read into the buffer, and the internal position
  185. --@param format - see bin
  186. --@return the unpacked value (NOT the index)
  187. unpack = function(self,format)
  188. local ret = {bin.unpack(format, self.readBuffer, self.pos)}
  189. self.pos = ret[1]
  190. return table.unpack(ret,2)
  191. end,
  192. ---
  193. -- This function works just like bin.unpack (in fact, it is
  194. -- merely a wrapper around it. However, it uses the data
  195. -- already read into the buffer, and the internal position.
  196. -- This method does not update the current position, and the
  197. -- data can be read again
  198. --@param format - see bin
  199. --@return the unpacked value (NOT the index)
  200. peekUnpack = function(self,format)
  201. local ret = {bin.unpack(format, self.readBuffer, self.pos)}
  202. return table.unpack(ret,2)
  203. end,
  204. ---
  205. -- Tries to read a byte, without consuming it.
  206. --@return status
  207. --@return bytevalue
  208. peekByte = function(self)
  209. if self:canRead(1) then
  210. return true, self:peekUnpack('C')
  211. end
  212. return false
  213. end,
  214. ---
  215. -- Skips a number of bytes
  216. --@param len the number of bytes to skip
  217. skip = function(self, len)
  218. if(#self.readBuffer < len + self.pos) then
  219. return doh("ERROR: reading too far ahead")
  220. end
  221. local skipped = self.readBuffer:sub(self.pos, self.pos+len-1)
  222. self.pos = self.pos + len
  223. return true, skipped
  224. end,
  225. }
  226. -- The classes are generated when this file is loaded, by the definitions in the JavaTypes
  227. -- table. That table contains mappings between the format used by bin and the types
  228. -- available in java, aswell as the lengths (used for availability-checks) and the name which
  229. -- is prefixed by read* or write* when monkey-patching the classes and adding functions.
  230. -- For example: {name = 'Int', expr = '>i', len= 4}, will generate the functions
  231. -- writeInt(self, value) and readInt() respectively
  232. local JavaTypes = {
  233. {name = 'Int', expr = '>i', len= 4},
  234. {name = 'UnsignedInt', expr = '>I', len= 4},
  235. {name = 'Short', expr = '>s', len= 2},
  236. {name = 'UnsignedShort', expr = '>S', len= 2},
  237. {name = 'Long', expr = '>l', len= 8},
  238. {name = 'UnsignedLong', expr = '>L', len= 8},
  239. {name = 'Byte', expr = '>C', len= 1},
  240. }
  241. ---
  242. -- The JavaDOS classes
  243. -- The JavaDOS class is an approximation of a java DataOutputStream. It provides convenience functions
  244. -- for writing java types to an underlying BufferedWriter
  245. --
  246. -- When used in conjunction with the BufferedX- classes, they handle the availability-
  247. -- checks transparently, i.e the caller does not have to check if enough data is available
  248. --
  249. -- @usage:
  250. -- local dos = JavaDOS:new(BufferedWriter:new(socket))
  251. -- local dos = JavaDIS:new(BufferedReader:new(socket))
  252. -- dos:writeUTF("Hello world")
  253. -- dos:writeInt(3)
  254. -- dos:writeLong(3)
  255. -- dos:flush() -- send data
  256. -- local answer = dis:readUTF()
  257. -- local int = dis:readInt()
  258. -- local long = dis:readLong()
  259. JavaDOS = {
  260. new = function (self,bWriter)
  261. local o = {} -- create new object if user does not provide one
  262. setmetatable(o, self)
  263. self.__index = self -- DIY inheritance
  264. o.bWriter = bWriter
  265. return o
  266. end,
  267. -- This closure method generates all writer methods on the fly
  268. -- according to the definitions in JavaTypes
  269. _generateWriterFunc = function(self, javatype)
  270. local functionName = 'write'..javatype.name
  271. local newFunc = function(_self, value)
  272. --dbg(functionName .."(%s) called" ,tostring(value))
  273. return _self:pack(javatype.expr, value)
  274. end
  275. self[functionName] = newFunc
  276. end,
  277. writeUTF = function(self, text)
  278. -- TODO: Make utf-8 of it
  279. return self:pack('>P', text)
  280. end,
  281. pack = function(self, ...)
  282. local arg={...}
  283. return self.bWriter:pack(table.unpack(arg))
  284. end,
  285. write = function(self, data)
  286. return self.bWriter:send(data)
  287. end,
  288. flush = function(self)
  289. return self.bWriter:flush()
  290. end,
  291. }
  292. ---
  293. -- The JavaDIS class
  294. -- JavaDIS is close to java DataInputStream. It provides convenience functions
  295. -- for reading java types from an underlying BufferedReader
  296. --
  297. -- When used in conjunction with the BufferedX- classes, they handle the availability-
  298. -- checks transparently, i.e the caller does not have to check if enough data is available
  299. --
  300. -- @usage:
  301. -- local dos = JavaDOS:new(BufferedWriter:new(socket))
  302. -- local dos = JavaDIS:new(BufferedReader:new(socket))
  303. -- dos:writeUTF("Hello world")
  304. -- dos:writeInt(3)
  305. -- dos:writeLong(3)
  306. -- dos:flush() -- send data
  307. -- local answer = dis:readUTF()
  308. -- local int = dis:readInt()
  309. -- local long = dis:readLong()
  310. JavaDIS = {
  311. new = function (self,bReader)
  312. local o = {} -- create new object if user does not provide one
  313. setmetatable(o, self)
  314. self.__index = self -- DIY inheritance
  315. o.bReader = bReader
  316. return o
  317. end,
  318. -- This closure method generates all reader methods (except nonstandard ones) on the fly
  319. -- according to the definitions in JavaTypes.
  320. _generateReaderFunc = function(self, javatype)
  321. local functionName = 'read'..javatype.name
  322. local newFunc = function(_self)
  323. --dbg(functionName .."() called" )
  324. if not _self.bReader:canRead(javatype.len) then
  325. local err = ("Not enough data in buffer (%d required by %s)"):format(javatype.len, functionName)
  326. return doh(err)
  327. end
  328. return true, _self.bReader:unpack(javatype.expr)
  329. end
  330. self[functionName] = newFunc
  331. end,
  332. -- This is a bit special, since we do not know beforehand how many bytes must be read. Therefore
  333. -- this cannot be generated on the fly like the others.
  334. readUTF = function(self, text)
  335. -- First, we need to read the length, 2 bytes
  336. if not self.bReader:canRead(2) then-- Length of the string is two bytes
  337. return false, "Not enough data in buffer [0]"
  338. end
  339. -- We do it as a 'peek', so bin can reuse the data to unpack with 'P'
  340. local len = self.bReader:peekUnpack('>S')
  341. --dbg("Reading utf, len %d" , len)
  342. -- Check that we have data
  343. if not self.bReader:canRead(len) then
  344. return false, "Not enough data in buffer [1]"
  345. end
  346. -- For some reason, the 'P' switch does not work for me.
  347. -- Probably some idiot thing. This is a hack:
  348. local val = self.bReader.readBuffer:sub(self.bReader.pos+2, self.bReader.pos+len+2-1)
  349. self.bReader.pos = self.bReader.pos+len+2
  350. -- Someone smarter than me can maybe get this working instead:
  351. --local val = self.bReader:unpack('P')
  352. --dbg("Read UTF: %s", val)
  353. return true, val
  354. end,
  355. readLongAsHexString = function(self)
  356. if not self.bReader:canRead(8) then-- Length of the string is two bytes
  357. return false, "Not enough data in buffer [3]"
  358. end
  359. return true, self.bReader:unpack('H8')
  360. end,
  361. skip = function(self, len)
  362. return self.bReader:skip(len)
  363. end,
  364. canRead = function(self, len)
  365. return self.bReader:canRead(len)
  366. end,
  367. }
  368. -- Generate writer-functions on the JavaDOS/JavaDIS classes on the fly
  369. for _,x in ipairs(JavaTypes) do
  370. JavaDOS._generateWriterFunc(JavaDOS, x)
  371. JavaDIS._generateReaderFunc(JavaDIS, x)
  372. end
  373. ---
  374. -- This class represents a java class and is what is returned by the library
  375. -- when invoking a remote function. Therefore, this can also represent a java
  376. -- object instance.
  377. JavaClass = {
  378. new = function(self)
  379. local o = {}
  380. setmetatable(o, self)
  381. self.__index = self
  382. return o
  383. end,
  384. customDataFormatter = nil,
  385. setName = function( self, name )
  386. dbg("Setting class name to %s", name)
  387. self.name = name
  388. end,
  389. setSerialID = function( self, serial ) self.serial = serial end,
  390. setFlags = function( self, flags )
  391. self.flags = RMIUtils.flagsToString(flags)
  392. self._binaryflags = flags
  393. end,
  394. isExternalizable = function(self)
  395. if self._binaryFlags == nil then return false end
  396. return bit.band(self._binaryflags, RMIUtils.SC_EXTERNALIZABLE)
  397. end,
  398. addField = function( self, field )
  399. if self.fields == nil then self.fields = {} end
  400. table.insert( self.fields, field )
  401. --self[field.name] = field
  402. end,
  403. setSuperClass = function(self,super) self.superClass = super end,
  404. setCustomData = function(self, data) self.customData = data end,
  405. getCustomData = function(self) return self.customData end,
  406. setInterfaces = function(self,ifaces) self.ifaces = ifaces end,
  407. getName = function( self ) return self.name end,
  408. getSuperClass = function(self) return self.superClass end,
  409. getSerialID = function( self ) return self.serial end,
  410. getFlags = function( self ) return self.flags end,
  411. getFields = function( self ) return self.fields end,
  412. getFieldByName = function( self, name )
  413. if self.fields == nil then return end
  414. for i=1, #self.fields do
  415. if ( self.fields[i].name == name ) then
  416. return self.fields[i]
  417. end
  418. end
  419. end,
  420. __tostring = function( self )
  421. local data = {}
  422. if self.name ~=nil then
  423. data[#data+1] = ("%s "):format(self.name)
  424. else
  425. data[#data+1] = "???"
  426. end
  427. if self.superClass~=nil then
  428. data[#data+1] = " extends ".. tostring( self.superClass)
  429. end
  430. if self.ifaces ~= nil then
  431. data[#data+1] = " implements " .. self.ifaces
  432. end
  433. if self.fields ~=nil then
  434. for i=1, #self.fields do
  435. if i == 1 then
  436. data[#data+1] = "["
  437. end
  438. data[#data+1] = tostring(self.fields[i])
  439. if ( i < #self.fields ) then
  440. data[#data+1] = ";"
  441. else
  442. data[#data+1] = "]"
  443. end
  444. end
  445. end
  446. return table.concat(data)
  447. end,
  448. toTable = function(self, customDataFormatter)
  449. local data = {self.name}
  450. if self.externalData ~=nil then
  451. table.insert(data, tostring(self.externalData))
  452. end
  453. --if self.name ~=nil then
  454. -- data.class = self.name
  455. --end
  456. if self.ifaces ~= nil then
  457. table.insert(data, " implements " .. self.ifaces)
  458. end
  459. if self.superClass~=nil then
  460. local extends = self.superClass:toTable()
  461. table.insert(data ,"extends")
  462. table.insert(data, extends)
  463. --data.extends = self.superClass:toTable()
  464. end
  465. if self.fields ~=nil then
  466. table.insert(data, "fields")
  467. local f = {}
  468. for i=1, #self.fields do
  469. table.insert(f, self.fields[i]:toTable())
  470. end
  471. table.insert(data, f)
  472. end
  473. if self.customData ~=nil then
  474. local formatter = JavaClass['customDataFormatter']
  475. if formatter ~= nil then
  476. local title, cdata = formatter(self.name, self.customData)
  477. table.insert(data, title)
  478. table.insert(data, cdata)
  479. else
  480. table.insert(data, "Custom data")
  481. table.insert(data, self.customData)
  482. end
  483. end
  484. return data
  485. end,
  486. }
  487. --- Represents a field in an object, i.e an object member
  488. JavaField = {
  489. new = function(self, name, typ )
  490. local o = {}
  491. setmetatable(o, self)
  492. self.__index = self
  493. o.name = name
  494. o.type = typ
  495. return o
  496. end,
  497. setType = function( self, typ ) self.type = typ end,
  498. setSignature = function( self, sig ) self.signature = sig end,
  499. setName = function( self, name ) self.name = name end,
  500. setObjectType = function( self, ot ) self.object_type = ot end,
  501. setReference = function( self, ref ) self.ref = ref end,
  502. setValue = function (self, val)
  503. dbg("Setting field value to %s", tostring(val))
  504. self.value = val
  505. end,
  506. getType = function( self ) return self.type end,
  507. getSignature = function( self ) return self.signature end,
  508. getName = function( self ) return self.name end,
  509. getObjectType = function( self ) return self.object_type end,
  510. getReference = function( self ) return self.ref end,
  511. getValue = function( self ) return self.value end,
  512. __tostring = function( self )
  513. if self.value ~= nil then
  514. return string.format("%s %s = %s", self.type, self.name, self.value)
  515. else
  516. return string.format("%s %s", self.type, self.name)
  517. end
  518. end,
  519. toTable = function(self)
  520. local data = {tostring(self.type) .. " " .. tostring(self.name)}
  521. --print("FIELD VALUE:", self.value)
  522. if self.value ~= nil then
  523. if type(self.value) == 'table' then
  524. if self.value.toTable ~=nil then
  525. table.insert(data, self.value:toTable())
  526. else
  527. table.insert(data, self.value)
  528. end
  529. else
  530. table.insert(data, self.value)
  531. end
  532. end
  533. return data
  534. end,
  535. }
  536. ---
  537. -- Represents a java array. Internally, this is a lua list of JavaClass-instances
  538. JavaArray = {
  539. new = function(self)
  540. local o = {}
  541. setmetatable(o, self)
  542. self.__index = self
  543. o.values = {}
  544. return o
  545. end,
  546. setClass = function( self, class ) self.class = class end,
  547. setLength = function( self, length ) self.length = length end,
  548. setValue = function(self, index, object) self.values[index] = object end,
  549. __tostring=function(self)
  550. local data = {
  551. ("Array: %s [%d] = {"):format(tostring(self.class), self.length)
  552. }
  553. for i=1, #self.values do
  554. data[#data+1] = self.values[i]..","
  555. end
  556. data[#data+1] = "}"
  557. return table.concat(data)
  558. end,
  559. toTable = function(self)
  560. local title = ("Array: %s [%d] = {"):format(tostring(self.class), self.length)
  561. local t = {title = self.values}
  562. return t
  563. end,
  564. getValues = function(self) return self.values end
  565. }
  566. TC = {
  567. TC_NULL = 0x70,
  568. TC_REFERENCE = 0x71,
  569. TC_CLASSDESC = 0x72,
  570. TC_OBJECT = 0x73,
  571. TC_STRING = 0x74,
  572. TC_ARRAY = 0x75,
  573. TC_CLASS = 0x76,
  574. TC_BLOCKDATA = 0x77,
  575. TC_ENDBLOCKDATA = 0x78,
  576. TC_RESET = 0x79,
  577. TC_BLOCKDATALONG = 0x7A,
  578. TC_EXCEPTION = 0x7B,
  579. TC_LONGSTRING = 0x7C,
  580. TC_PROXYCLASSDESC = 0x7D,
  581. TC_ENUM = 0x7E,
  582. Integer = 0x49,
  583. Object = 0x4c,
  584. Strings = {
  585. [0x49] = "Integer",
  586. [0x4c] = "Object",
  587. [0x71] = "TC_REFERENCE",
  588. [0x70] = "TC_NULL",
  589. [0x71] = "TC_REFERENCE",
  590. [0x72] = "TC_CLASSDESC",
  591. [0x73] = "TC_OBJECT",
  592. [0x74] = "TC_STRING",
  593. [0x75] = "TC_ARRAY",
  594. [0x76] = "TC_CLASS",
  595. [0x77] = "TC_BLOCKDATA",
  596. [0x78] = "TC_ENDBLOCKDATA",
  597. [0x79] = "TC_RESET",
  598. [0x7A] = "TC_BLOCKDATALONG",
  599. [0x7B] = "TC_EXCEPTION",
  600. [0x7C] = "TC_LONGSTRING",
  601. [0x7D] = "TC_PROXYCLASSDESC",
  602. [0x7E] = "TC_ENUM",
  603. },
  604. }
  605. local Version= 0x02
  606. local Proto= {Stream=0x4b, SingleOp=0x4c, Multiplex=0x4d}
  607. ---
  608. -- RmiDataStream class
  609. -- This class can handle reading and writing JRMP, i.e RMI wire protocol and
  610. -- can do some very limited java deserialization. This implementation has
  611. -- borrowed from OpenJDK RMI implementation, but only implements an
  612. -- absolute minimum of what is required in order to perform some basic calls
  613. --
  614. RmiDataStream = {
  615. new = function (self,o)
  616. o = o or {} -- create object if user does not provide one
  617. setmetatable(o, self)
  618. self.__index = self -- DIY inheritance
  619. return o
  620. end,
  621. }
  622. -- An output stream in RMI consists of transport Header information followed by a sequence of Messages.
  623. -- Out:
  624. -- Header Messages
  625. -- HttpMessage
  626. -- Header:
  627. -- 0x4a 0x52 0x4d 0x49 Version Protocol
  628. -- (4a 52 4d 49 === JRMI)
  629. -- Version:
  630. -- 0x00 0x01
  631. -- Protocol:
  632. -- StreamProtocol
  633. -- SingleOpProtocol
  634. -- MultiplexProtocol
  635. -- StreamProtocol:
  636. -- 0x4b
  637. -- SingleOpProtocol:
  638. -- 0x4c
  639. -- MultiplexProtocol:
  640. -- 0x4d
  641. -- Messages:
  642. -- Message
  643. -- Messages Message
  644. ----
  645. -- Connects to a remote service. The connection process creates a
  646. -- socket and does some handshaking. If this is successful,
  647. -- we are definitely talking to an RMI service.
  648. function RmiDataStream:connect(host, port)
  649. local status, err
  650. local socket = nmap.new_socket()
  651. socket:set_timeout(5000)
  652. -- local bsocket = BufferedSocket:new()
  653. socket:connect(host,port, "tcp")
  654. -- Output and input
  655. local dos = JavaDOS:new(BufferedWriter:new(socket))
  656. local dis = JavaDIS:new(BufferedReader:new(socket))
  657. -- Start sending a message --
  658. -- Add Header, Version and Protocol
  659. --dos:write('JRMI' .. bin.pack('H', Version .. Proto.Stream))
  660. dos:writeInt(1246907721) -- == JRMI
  661. dos:writeShort(Version)
  662. dos:writeByte(Proto.Stream)
  663. status = dos:flush()
  664. if not status then
  665. return doh(err)
  666. end
  667. -- For the StreamProtocol and the MultiplexProtocol, the server must respond with a a byte 0x4e
  668. -- acknowledging support for the protocol, and an EndpointIdentifier that contains the host name
  669. -- and port number that the server can see is being used by the client.
  670. -- The client can use this information to determine its host name if it is otherwise unable to do that for security reasons.
  671. -- Read ack
  672. status, err = self:readAck(dis)
  673. if not status then
  674. return doh("No ack received from server:" .. tostring(err))
  675. end
  676. -- The client must then respond with another EndpointIdentifier that contains the clients
  677. -- default endpoint for accepting connections. This can be used by a server in the MultiplexProtocol case to identify the client.
  678. dos:writeUTF("127.0.0.1") -- TODO, write our own ip instead (perhaps not necessary, since we are not using MultiplexProtocol
  679. dos:writeInt(0) -- Port ( 0 works fine)
  680. dos:flush()
  681. self.dos = dos
  682. self.dis =dis
  683. return true
  684. end
  685. -- Reads a DgcAck message, which is sent during connection handshake
  686. --@param dis - a JavaDIS to read from
  687. --@return status
  688. --@return error message
  689. function RmiDataStream:readAck(dis)
  690. local status, ack = dis:readByte()
  691. if not status then return doh( "Could not read data") end
  692. if ack ~= 78 then
  693. return doh("No ack received: ".. tostring(ack))
  694. end
  695. local status, host = dis:readUTF()
  696. if not status then return false, "Could not read data" end
  697. local status, port = dis:readUnsignedInt()
  698. if not status then return false, "Could not read data" end
  699. dbg("RMI-Ack received (host %s, port: %d) " , host, port)
  700. return true
  701. end
  702. -- Sends an RMI method call
  703. --@param out - a JavaDos outputstream
  704. --@param objNum -object id (target of call)
  705. --@param hash - the hashcode for the class that is invoked
  706. --@param op - the operation number (method) invoked
  707. --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table
  708. -- or something else which has a getData() function to get binary data
  709. function RmiDataStream:writeMethodCall(out,objNum, hash, op, arguments)
  710. dbg("Invoking object %s, hash %s, opNum %s, args %s", tostring(objNum), tostring(hash), tostring(op), tostring(arguments))
  711. local dos = self.dos
  712. local dis = self.dis
  713. -- Send Call:
  714. dos:writeByte(0x50)
  715. -- Send Magic 0xaced
  716. dos:writeShort(0xACED)
  717. -- Send version 0x0005
  718. dos:writeShort(0x0005)
  719. -- Send TC_BLOKDATA
  720. dos:writeByte(0x77)
  721. -- send length (byte)
  722. dos:writeByte(0x22)
  723. -- From sun.rmi.transport.StreamRemoteCall :
  724. -- // write out remote call header info...
  725. -- // call header, part 1 (read by Transport)
  726. -- conn.getOutputStream().write(TransportConstants.Call);
  727. -- getOutputStream(); // creates a MarshalOutputStream
  728. -- id.write(out); // object id (target of call)
  729. -- // call header, part 2 (read by Dispatcher)
  730. -- out.writeInt(op); // method number (operation index)
  731. -- out.writeLong(hash); // stub/skeleton hash
  732. -- Send rest of the call
  733. local unique, time, count =0,0,0
  734. dos:writeLong(objNum);-- id objNum
  735. dos:writeInt(unique); -- space
  736. dos:writeLong(time);
  737. dos:writeShort(count);
  738. dos:writeInt(op)
  739. dos:pack('H',hash)
  740. -- And now, the arguments
  741. if arguments ~= nil then
  742. dos:write(arguments:getData())
  743. end
  744. dos:flush()
  745. end
  746. ---
  747. -- Invokes a method over RMI
  748. --@param methodData, a table which should contain the following
  749. --@param objNum -object id (target of call)
  750. --@param hash - the hashcode for the class that is invoked
  751. --@param op - the operation number (method) invoked
  752. --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table
  753. -- or something else which has a getData() function to get binary data
  754. --@return status
  755. --@return a JavaClass instance
  756. function RmiDataStream:invoke(objNum, hash, op, arguments)
  757. local status, data
  758. local out = self.out
  759. local dis = self.dis
  760. self:writeMethodCall(out,objNum,hash, op, arguments)
  761. local status, retByte = dis:readByte()
  762. if not status then return false, "No return data received from server" end
  763. if 0x51 ~= retByte then -- 0x51 : Returndata
  764. return false, "No return data received from server"
  765. end
  766. status, data = self:readReturnData(dis)
  767. return status, data
  768. end
  769. ---
  770. -- Reads an RMI ReturnData packet
  771. --@param dis a JavaDIS inputstream
  772. function RmiDataStream:readReturnData(dis)
  773. --[[
  774. From -http://turtle.ee.ncku.edu.tw/docs/java/jdk1.2.2/guide/rmi/spec/rmi-protocol.doc3.html :
  775. A ReturnValue of an RMI call consists of a return code to indicate either a normal or
  776. exceptional return, a UniqueIdentifier to tag the return value (used to send a DGCAck if necessary)
  777. followed by the return result: either the Value returned or the Exception thrown.
  778. CallData: ObjectIdentifier Operation Hash (Arguments)
  779. ReturnValue:
  780. 0x01 UniqueIdentifier (Value)
  781. 0x02 UniqueIdentifier Exception
  782. ObjectIdentifier: ObjectNumber UniqueIdentifier
  783. UniqueIdentifier: Number Time Count
  784. Arguments: Value Arguments Value
  785. Value: Object Primitive
  786. Example: [ac ed][00 05][77][0f][01][25 14 95 21][00 00 01 2b 16 9a 62 5a 80 0b]
  787. [magc][ver ][BL][L ][Ok][ --------------- not interesting atm ----------------------]
  788. --]]
  789. -- We need to be able to read at least 7 bytes
  790. -- If that is doable, we can ignore the status on the following readbyte operations
  791. if not dis:canRead(7) then
  792. return doh("Not enough data received")
  793. end
  794. local status, magic = dis:readShort() -- read magic
  795. local status, version = dis:readShort() -- read version
  796. local status, typ = dis:readByte()
  797. if typ ~= TC.TC_BLOCKDATA then
  798. return doh("Expected block data when reading return data")
  799. end
  800. local status, len = dis:readByte() -- packet length
  801. --dis:setReadLimit(len)
  802. local status, ex = dis:readByte() -- 1=ok, 2=exception thrown
  803. if ex ~= 1 then
  804. return doh("Remote call threw exception")
  805. end
  806. -- We can skip the rest of this block
  807. dis:skip(len -1)
  808. -- Now, the return value object:
  809. local status, x = readObject0(dis)
  810. dbg("Read object, got %d left in buffer", dis.bReader:bufferSize())
  811. if(dis.bReader:bufferSize() > 0) then
  812. local content = dis.bReader:unpack('H'..tostring(dis.bReader:bufferSize()))
  813. dbg("Buffer content: %s" ,content)
  814. end
  815. return status, x
  816. end
  817. ---
  818. -- Deserializes a serialized java object
  819. function readObject0(dis)
  820. local finished = false
  821. local data, status, responseType
  822. status, responseType = dis:readByte()
  823. if not status then
  824. return doh("Not enough data received")
  825. end
  826. dbg("Reading object of type : %s" , RMIUtils.tcString(responseType))
  827. local decoder = TypeDecoders[responseType]
  828. if decoder ~= nil then
  829. status, data = decoder(dis)
  830. if not status then return doh("readObject0: Could not read data %s", tostring(data)) end
  831. dbg("Read: %s", tostring(data))
  832. return true, data
  833. else
  834. return doh("No decoder found for responsetype: %s" , RMIUtils.tcString(responseType))
  835. end
  836. end
  837. function readString(dis)
  838. return dis:readUTF()
  839. end
  840. -- Reads return type array
  841. function readArray(dis)
  842. local array = JavaArray:new()
  843. dbg("Reading array class description")
  844. local status, classDesc = readClassDesc(dis)
  845. array:setClass(classDesc)
  846. dbg("Reading array length")
  847. local status, len = dis:readInt()
  848. if not status then
  849. return doh("Could not read data")
  850. end
  851. array:setLength(len)
  852. dbg("Reading array of length is %X", len)
  853. for i =1, len, 1 do
  854. local status, object = readObject0(dis)
  855. array:setValue(i,object)
  856. end
  857. return true, array
  858. end
  859. function readClassDesc(dis)
  860. local status, p = dis:readByte()
  861. if not status then return doh( "Could not read data" ) end
  862. dbg("reading classdesc: %s" , RMIUtils.tcString(p))
  863. local val
  864. if p == TC.TC_CLASSDESC then
  865. dbg("Reading TC_CLASSDESC")
  866. status, val = readNonProxyDesc(dis)
  867. elseif p == TC.TC_NULL then
  868. dbg("Reading TC_NULL")
  869. status, val = true, nil
  870. elseif p == TC.TC_PROXYCLASSDESC then
  871. dbg("Reading TC_PROXYCLASSDESC")
  872. status, val = readProxyDesc(dis)
  873. else
  874. return doh("TC_classdesc is other %d", p)
  875. end
  876. if not status then
  877. return doh("Error reading class description")
  878. end
  879. return status, val
  880. end
  881. function readOrdinaryObject(dis)
  882. local status, desc = readClassDesc(dis)
  883. if not status then
  884. return doh("Error reading ordinary object")
  885. end
  886. if desc:isExternalizable() then
  887. dbg("External content")
  888. local status, extdata = readExternalData(dis)
  889. if status then
  890. desc["externalData"] = extdata
  891. end
  892. else
  893. dbg("Serial content")
  894. local status, serdata = readExternalData(dis)
  895. if status then
  896. desc["externalData"] = serdata
  897. local status, data =parseExternalData(desc)
  898. if status then
  899. desc['externalData'] = data
  900. end
  901. end
  902. end
  903. return status, desc
  904. end
  905. -- Attempts to read some object-data, at least remove the block
  906. -- header. This method returns the external data in 'raw' form,
  907. -- since it is up to each class to define an readExternal method
  908. function readExternalData(dis)
  909. local data = {}
  910. while dis.bReader:bufferSize() > 0 do
  911. local status, tc= dis:readByte()
  912. if not status then
  913. return doh("Could not read external data")
  914. end
  915. dbg("readExternalData: %s", RMIUtils.tcString(tc))
  916. local status, len, content
  917. if tc == TC.TC_BLOCKDATA then
  918. status, len = dis:readByte()
  919. status, content = dis.bReader:skip(len)
  920. --print(bin.unpack("H"..tostring(#content),content))
  921. --print(makeStringReadable(content))
  922. dbg("Read external data (%d bytes): %s " ,len, content)
  923. --local object = ExternalClassParsers['java.rmi.server.RemoteObject'](dis)
  924. --print(object)
  925. return status, content
  926. elseif tc == TC.TC_BLOCKDATALONG then
  927. status, len = dis:readUnsignedInt()
  928. status, content = dis.bReader:skip(len)
  929. return status, content
  930. elseif tc == TC.TC_ENDBLOCKDATA then
  931. --noop
  932. else
  933. return doh("Got unexpected field in readExternalData: %s ", RMIUtils.tcString(tc))
  934. end
  935. end
  936. end
  937. ----
  938. -- ExternalClassParsers : External Java Classes
  939. -- This 'class' contains information about certain specific java classes,
  940. -- such as UnicastRef, UnicastRef2. After such an object has been read by
  941. -- the object serialization protocol, it will contain a lump of data which is
  942. -- in 'external' form, and needs to be read in a way which is specific for the class
  943. -- itself. This class contains the implementations for reading out the
  944. -- 'goodies' of e.g UnicastRef, which contain important information about
  945. -- where another RMI-socket is listening and waiting for someone to connect.
  946. ExternalClassParsers = {
  947. ---
  948. --@see sun.rmi.transport.tcp.TCPEndpoint
  949. --@see sun.rmi.server.UnicastRef
  950. --@see sun.rmi.server.UnicastRef2
  951. UnicastRef = function(dis)
  952. local stat, host = dis:readUTF();
  953. if not stat then return doh("Parsing external data, could not read host (UTF)") end
  954. local status, port = dis:readUnsignedInt();
  955. if not stat then return doh("Parsing external data, could not read port (int)") end
  956. dbg("a host: %s, port %d", host, port)
  957. return true, ("@%s:%d"):format(host,port)
  958. end,
  959. ---
  960. --@see sun.rmi.transport.tcp.TCPEndpoint
  961. --@see sun.rmi.server.UnicastRef
  962. --@see sun.rmi.server.UnicastRef2
  963. UnicastRef2 = function(dis)
  964. local stat, form = dis:readByte();
  965. if not stat then return doh("Parsing external data, could not read byte") end
  966. if form == 0 or form == 1 then-- FORMAT_HOST_PORT or FORMAT_HOST_PORT_FACTORY
  967. local stat, host = dis:readUTF();
  968. if not stat then return doh("Parsing external data, could not read host (UTF)") end
  969. local status, port = dis:readUnsignedInt();
  970. if not stat then return doh("Parsing external data, could not read port (int)") end
  971. dbg("b host: %s, port %d", host, port)
  972. if form ==0 then
  973. return true, ("@%s:%d"):format(host,port)
  974. end
  975. -- for FORMAT_HOST_PORT_FACTORY, there's an object left to read
  976. local status, object = readObject0(dis)
  977. return true, ("@%s:%d"):format(host,port)
  978. --return true, {host = host, port = port, factory = object}
  979. else
  980. return doh("Invalid endpoint format")
  981. end
  982. end
  983. }
  984. --@see java.rmi.server.RemoteObject:readObject()
  985. ExternalClassParsers['java.rmi.server.RemoteObject'] = function(dis)
  986. local status, refClassName = dis:readUTF()
  987. if not status then return doh("Parsing external data, could not read classname (UTF)") end
  988. if #refClassName == 0 then
  989. local status, ref = readObject0(dis)
  990. return status, ref
  991. end
  992. dbg("Ref class name: %s ", refClassName)
  993. local parser = ExternalClassParsers[refClassName]
  994. if parser == nil then
  995. return doh("No external class reader for %s" , refClassName)
  996. end
  997. local status, object = parser(dis)
  998. return status, object
  999. end
  1000. -- Attempts to parse the externalized data of an object.
  1001. --@return status, the object data
  1002. function parseExternalData(j_object)
  1003. if j_object == nil then
  1004. return doh("parseExternalData got nil object")
  1005. end
  1006. local className = j_object:getName()
  1007. -- Find parser for the object, move up the hierarchy
  1008. local obj = j_object
  1009. local parser = nil
  1010. while(className ~= nil) do
  1011. parser = ExternalClassParsers[className]
  1012. if parser ~= nil then break end
  1013. obj = obj:getSuperClass()
  1014. if obj== nil then break end-- No more super classes
  1015. className = obj:getName()
  1016. end
  1017. if parser == nil then
  1018. return doh("External reader for class %s is not implemented", tostring(className))
  1019. end
  1020. -- Read the actual object, start by creating a new dis based on the data-string
  1021. local dis = JavaDIS:new(BufferedReader:new(nil,j_object.externalData))
  1022. local status, object = parser(dis)
  1023. if not status then
  1024. return doh("Could not parse external data")
  1025. end
  1026. return true, object
  1027. end
  1028. -- Helper function to display data
  1029. -- returns the string with all non-printable chars
  1030. -- coded as hex
  1031. function makeStringReadable(data)
  1032. return data:gsub("[\x00-\x1f\x7f-\xff]", function (x)
  1033. return ("\\x%02x"):format(x:byte())
  1034. end)
  1035. end
  1036. function readNonProxyDesc(dis)
  1037. dbg("-- entering readNonProxyDesc--")
  1038. local j_class = JavaClass:new()
  1039. local status, classname = dis:readUTF()
  1040. if not status then return doh( "Could not read data" ) end
  1041. j_class:setName(classname)
  1042. local status, serialID = dis:readLongAsHexString()
  1043. if not status then return doh("Could not read data") end
  1044. j_class:setSerialID(serialID)
  1045. dbg("Set serial ID to %s", tostring(serialID))
  1046. local status, flags = dis:readByte()
  1047. if not status then return doh("Could not read data") end
  1048. j_class:setFlags(flags)
  1049. local status, fieldCount = dis:readShort()
  1050. if not status then return doh( "Could not read data") end
  1051. dbg("Fieldcount %d", fieldCount)
  1052. local fields = {}
  1053. for i =0, fieldCount-1,1 do
  1054. local status, fieldDesc = readFieldDesc(dis)
  1055. j_class:addField(fieldDesc)
  1056. -- Need to store in list, the field values need to be read
  1057. -- after we have finished reading the class description
  1058. -- hierarchy
  1059. table.insert(fields,fieldDesc)
  1060. end
  1061. local status, customStrings = skipCustomData(dis)
  1062. if status and customStrings ~= nil and #customStrings > 0 then
  1063. j_class:setCustomData(customStrings)
  1064. end
  1065. local _,superDescriptor = readClassDesc(dis)
  1066. j_class:setSuperClass(superDescriptor)
  1067. dbg("Superclass read, now reading %i field values", #fields)
  1068. --Read field values
  1069. for i=1, #fields, 1 do
  1070. local status, fieldType = dis:readByte()
  1071. local value = nil
  1072. if ( TypeDecoders[fieldType] ) then
  1073. status, value= TypeDecoders[fieldType](dis)
  1074. else
  1075. dbg("error reading".. RMIUtils.tcString(fieldType))
  1076. return
  1077. end
  1078. dbg("Read fieldvalue ".. tostring(value) .. " for field ".. tostring(fields[i]))
  1079. fields[i]:setValue(value)
  1080. end
  1081. dbg("-- leaving readNonProxyDesc--")
  1082. return true, j_class
  1083. end
  1084. function readProxyDesc(dis)
  1085. dbg("-- in readProxyDesc--")
  1086. local interfaces = ''
  1087. local superclass = nil
  1088. local status, ifaceNum= dis:readInt()
  1089. if not status then return doh("Could not read data") end
  1090. --dbg("# interfaces: %d" , ifaceNum)
  1091. while ifaceNum > 0 do
  1092. local status, iface = dis:readUTF()
  1093. if not status then return doh( "Could not read data") end
  1094. --table.insert(interfaces, iface)
  1095. interfaces = interfaces .. iface ..', '
  1096. dbg("Interface: %s " ,iface)
  1097. ifaceNum = ifaceNum-1
  1098. end
  1099. local j_class = JavaClass:new()
  1100. local status, customStrings = skipCustomData(dis)
  1101. if status and customStrings ~= nil and #customStrings > 0 then
  1102. j_class:setCustomData(customStrings)
  1103. end
  1104. local _,superDescriptor = readClassDesc(dis)
  1105. --print ("superdescriptor", superDescriptor)
  1106. j_class:setSuperClass(superDescriptor)
  1107. j_class:setInterfaces(interfaces)
  1108. dbg("-- leaving readProxyDesc--")
  1109. return true, j_class
  1110. end
  1111. --
  1112. -- Skips over all block data and objects until TC_ENDBLOCKDATA is
  1113. -- encountered.
  1114. -- @see java.io.ObjectInputStream.skipCustomData()
  1115. --@return status
  1116. --@return any strings found while searching
  1117. function skipCustomData(dis)
  1118. -- If we come across something interesting, just put it into
  1119. -- the returnData list
  1120. local returnData = {}
  1121. while true do
  1122. local status, p = dis:readByte()
  1123. if not status then
  1124. return doh("Could not read data")
  1125. end
  1126. if not status then return doh("Could not read data") end
  1127. dbg("skipCustomData read %s", RMIUtils.tcString(p))
  1128. if p == TC.TC_BLOCKDATA or p == TC.TC_BLOCKDATALONG then
  1129. dbg("continuing")
  1130. --return
  1131. elseif p == TC.TC_ENDBLOCKDATA then
  1132. return true, returnData
  1133. else
  1134. -- In the java impl, this is a function called readObject0. We just
  1135. -- use the read null, otherwise error
  1136. if p == TC.TC_NULL then
  1137. -- No op, already read the byte, continue reading
  1138. elseif p == TC.TC_STRING then
  1139. --dbg("A string is coming!")
  1140. local status, str = dis:readUTF()
  1141. if not status then
  1142. return doh("Could not read data")
  1143. end
  1144. dbg("Got a string, but don't know what to do with it! : %s",str)
  1145. -- Object serialization is a bit messy. I have seen the
  1146. -- classpath being sent over a customdata-field, so it is
  1147. -- definitely interesting. Quick fix to get it showing
  1148. -- is to just stick it onto the object we are currently at.
  1149. -- So, just put the string into the returnData and continue
  1150. table.insert(returnData, str)
  1151. else
  1152. return doh("Not implemented in skipcustomData:: %s", RMIUtils.tcString(p))
  1153. end
  1154. end
  1155. end
  1156. end
  1157. function readFieldDesc(dis)
  1158. -- fieldDesc:
  1159. -- primitiveDesc
  1160. -- objectDesc
  1161. -- primitiveDesc:
  1162. -- prim_typecode fieldName
  1163. -- objectDesc:
  1164. -- obj_typecode fieldName className1
  1165. -- prim_typecode:
  1166. -- `B' // byte
  1167. -- `C' // char
  1168. -- `D' // double
  1169. -- `F' // float
  1170. -- `I' // integer
  1171. -- `J' // long
  1172. -- `S' // short
  1173. -- `Z' // boolean
  1174. -- obj_typecode:
  1175. -- `[` // array
  1176. -- `L' // object
  1177. local j_field = JavaField:new()
  1178. local status, c = dis:readByte()
  1179. if not status then return doh("Could not read data") end
  1180. local char = string.char(c)
  1181. local status, name = dis:readUTF()
  1182. if not status then return doh("Could not read data") end
  1183. local fieldType = ('primitive type: (%s) '):format(char)
  1184. dbg("Fieldtype, char = %s, %s", tostring(fieldType), tostring(char))
  1185. if char == 'L' or char == '[' then
  1186. -- These also have classname which tells the type
  1187. -- on the field
  1188. local status, fieldclassname = readTypeString(dis)
  1189. if not status then return doh("Could not read data") end
  1190. if char == '[s' then
  1191. fieldType = fieldclassname .. " []"
  1192. else
  1193. fieldType = fieldclassname
  1194. end
  1195. end
  1196. if not status then
  1197. return false, fieldType
  1198. end
  1199. dbg("Field description: name: %s, type: %s", tostring(name), tostring(fieldType))
  1200. j_field:setType(fieldType)
  1201. j_field:setName(name)
  1202. -- setType = function( self, typ ) self.type = typ end,
  1203. -- setSignature = function( self, sig ) self.signature = sig end,
  1204. -- setName = function( self, name ) self.name = name end,
  1205. -- setObjectType = function( self, ot ) self.object_type = ot end,
  1206. -- setReference = function( self, ref ) self.ref = ref end,
  1207. dbg("Created java field:".. tostring(j_field))
  1208. return true, j_field
  1209. end
  1210. function readTypeString(dis)
  1211. local status, tc = dis:readByte()
  1212. if not status then return doh("Could not read data") end
  1213. if tc == TC.TC_NULL then
  1214. return true, nil
  1215. elseif tc== TC.TC_REFERENCE then
  1216. return doh("Not implemented, readTypeString(TC_REFERENCE)");
  1217. elseif tc == TC.TC_STRING then
  1218. return dis:readUTF()
  1219. elseif tc == TC.TC_LONGSTRING then
  1220. --TODO, add this (will throw error as is)
  1221. return dis:readLongUTF()
  1222. end
  1223. end
  1224. TypeDecoders =
  1225. {
  1226. [TC.TC_ARRAY] = readArray,
  1227. [TC.TC_CLASSDESC] = readClassDesc,
  1228. [TC.TC_STRING] = readString,
  1229. [TC.TC_OBJECT] = readOrdinaryObject,
  1230. }
  1231. ---
  1232. -- Registry
  1233. -- Class to represent the RMI Registry.
  1234. --@usage:
  1235. -- registry = rmi.Registry:new()
  1236. -- status, data = registry:list()
  1237. Registry ={
  1238. new = function (self,host, port)
  1239. local o ={} -- create object
  1240. setmetatable(o, self)
  1241. self.__index = self -- DIY inheritance
  1242. -- Hash code for sun.rmi.registry.RegistryImpl_Stub, which we are invoking :
  1243. -- hex: 0x44154dc9d4e63bdf, dec: 4905912898345647071
  1244. self.hash = '44154dc9d4e63bdf'
  1245. -- RmiRegistry object id is 0
  1246. self.objId = 0
  1247. o.host = host
  1248. o.port = port
  1249. return o
  1250. end
  1251. }
  1252. -- Connect to the remote registry.
  1253. --@return status
  1254. --@return error message
  1255. function Registry:_handshake()
  1256. local out = RmiDataStream:new()
  1257. local status, err = out:connect(self.host,self.port)
  1258. if not status then
  1259. return doh("Registry connection failed: %s", tostring(err))
  1260. end
  1261. dbg("Registry connection OK "..tostring(out.bsocket) )
  1262. self.out = out
  1263. return true
  1264. end
  1265. ---
  1266. -- List the named objects in the remote RMI registry
  1267. --@return status
  1268. --@return a table of strings , or error message
  1269. function Registry:list()
  1270. if not self:_handshake() then
  1271. return doh("Handshake failed")
  1272. end
  1273. -- Method list() is op number 1
  1274. return self.out:invoke(self.objId, self.hash,1)
  1275. end
  1276. ---
  1277. -- Perform a lookup on an object in the Registry,
  1278. -- takes the name which is bound in the registry
  1279. -- as argument
  1280. --@return status
  1281. --@return JavaClass-object
  1282. function Registry:lookup(name)
  1283. self:_handshake()
  1284. -- Method lookup() is op number 2
  1285. -- Takes a string as arguments
  1286. local a = Arguments:new()
  1287. a:addString(name)
  1288. return self.out:invoke(self.objId, self.hash,2, a)
  1289. end
  1290. ----
  1291. -- Arguments class
  1292. -- This class is meant to handle arguments which is sent to a method invoked
  1293. -- remotely. It is mean to contain functionality to add java primitive datatypes,
  1294. -- such as pushInt, pushString, pushLong etc. All of these are not implemented
  1295. -- currently
  1296. --@usage: When invoking a remote method
  1297. -- use this class in this manner:
  1298. -- Arguments a = Arguments:new()
  1299. -- a:addString("foo")
  1300. -- datastream:invoke{objNum=oid, hash=hash, opNum = opid, arguments=a}
  1301. -- ...
  1302. --
  1303. Arguments = {
  1304. new = function (self,o)
  1305. o = o or {} -- create object if user does not provide one
  1306. setmetatable(o, self)
  1307. self.__index = self -- DIY inheritance
  1308. -- We use a buffered socket just to be able to use a javaDOS for writing
  1309. self.dos = JavaDOS:new(BufferedWriter:new())
  1310. return o
  1311. end,
  1312. addString = function(self, str)
  1313. self.dos:writeByte(TC.TC_STRING)
  1314. self.dos:writeUTF(str)
  1315. end,
  1316. addRaw = function(self, str)
  1317. self.dos:write(str)
  1318. end,
  1319. getData = function(self)
  1320. local _, res = self.dos:flush()
  1321. return res
  1322. end
  1323. }
  1324. ---
  1325. -- RMIUtils class provides some some codes and definitions from Java
  1326. -- There are three types of output messages: Call, Ping and DgcAck.
  1327. -- A Call encodes a method invocation. A Ping is a transport-level message
  1328. -- for testing liveness of a remote virtual machine.
  1329. -- A DGCAck is an acknowledgment directed to a
  1330. -- server's distributed garbage collector that indicates that remote objects
  1331. -- in a return value from a server have been received by the client.
  1332. RMIUtils = {
  1333. -- Indicates a Serializable class defines its own writeObject method.
  1334. SC_WRITE_METHOD = 0x01,
  1335. -- Indicates Externalizable data written in Block Data mode.
  1336. SC_BLOCK_DATA = 0x08,
  1337. -- Bit mask for ObjectStreamClass flag. Indicates class is Serializable.
  1338. SC_SERIALIZABLE = 0x02,
  1339. --Bit mask for ObjectStreamClass flag. Indicates class is Externalizable.
  1340. SC_EXTERNALIZABLE = 0x04,
  1341. --Bit mask for ObjectStreamClass flag. Indicates class is an enum type.
  1342. SC_ENUM = 0x10,
  1343. flagsToString = function(flags)
  1344. local retval = ''
  1345. if ( bit.band(flags, RMIUtils.SC_WRITE_METHOD) ~= 0) then
  1346. retval = retval .. " WRITE_METHOD"
  1347. end
  1348. if ( bit.band(flags, RMIUtils.SC_BLOCK_DATA) ~= 0) then
  1349. retval = retval .. " BLOCK_DATA"
  1350. end
  1351. if ( bit.band(flags, RMIUtils.SC_EXTERNALIZABLE) ~= 0) then
  1352. retval = retval .. " EXTERNALIZABLE"
  1353. end
  1354. if ( bit.band(flags, RMIUtils.SC_SERIALIZABLE) ~= 0) then
  1355. retval = retval .. " SC_SERIALIZABLE"
  1356. end
  1357. if ( bit.band(flags, RMIUtils.SC_ENUM) ~= 0) then
  1358. retval = retval .. " SC_ENUM"
  1359. end
  1360. return retval
  1361. end,
  1362. tcString = function (constant)
  1363. local x = TC.Strings[constant] or "Unknown code"
  1364. return ("%s (0x%x)"):format(x,tostring(constant))
  1365. end,
  1366. }
  1367. local RMIMessage = {
  1368. Call = 0x50,
  1369. Ping = 0x52,
  1370. DgcAck= 0x54,
  1371. }
  1372. STREAM_MAGIC = 0xaced
  1373. STREAM_VERSION = 5
  1374. baseWireHandle = 0x7E0000
  1375. return _ENV;