From 3de3ee8affbb462d933ccd5d371f32cd8adb33d9 Mon Sep 17 00:00:00 2001 From: paulino Date: Tue, 8 Jan 2019 21:34:37 +0000 Subject: [PATCH] Adds TN3270E support to the tn3270 library. Additionally adds support for logical unit setting. Closes #1318 --- nselib/tn3270.lua | 101 +++++++++++++++++++++++++++----------- scripts/tn3270-screen.nse | 11 ++++- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/nselib/tn3270.lua b/nselib/tn3270.lua index b31d090d2..a3ab14771 100644 --- a/nselib/tn3270.lua +++ b/nselib/tn3270.lua @@ -62,11 +62,11 @@ Telnet = { }, options = { - BINARY = "\000", - EOR = "\025", - TTYPE = "\024" - --TN3270 = "\040" -- if we enable this it would mean we support TN3270E - -- which we do not support + BINARY = "\000", + EOR = "\025", + TTYPE = "\024", + --TN3270 = "\028", + TN3270 = "\040" -- Technically TN3270E }, command = { @@ -218,8 +218,16 @@ Telnet = { NEG_OPERATION_CHECK = 0x02, NEG_COMPONENT_DISCONNECTED = 0x03, - -- Attention Identifiers (AID) - + -- TN3270E Negotiation Options + TN3270E_ASSOCIATE = 0x00, + TN3270E_CONNECT = 0x01, + TN3270E_DEVICE_TYPE = 0x02, + TN3270E_FUNCTIONS = 0x03, + TN3270E_IS = 0x04, + TN3270E_REASON = 0x05, + TN3270E_REJECT = 0x06, + TN3270E_REQUEST = 0x07, + TN3270E_SEND = 0x08, -- SFE Attributes SFE_3270 = "192", @@ -533,8 +541,7 @@ Telnet = { end elseif self.telnet_state == TNS_SB_IAC then stdnse.debug(3, "Processing SB options") - --nsedebug.print_hex(self.sb_options) - self.sb_options = self.sb_options .. data +-- self.sb_options = self.sb_options .. data -- looks like this is a bug? Why append F0 to the end? if data == self.commands.SE then self.telnet_state = TNS_DATA if self.sb_options:sub(1,1) == self.options.TTYPE and @@ -571,12 +578,14 @@ Telnet = { --- Function to negotiate TN3270 sub options negotiate_tn3270 = function ( self ) - stdnse.debug(3, "Processing tn data subnegotiation options") + stdnse.debug(3, "[TN3270] Processing tn data subnegotiation options ") local option = self.sb_options:sub(2,2) - + -- stdnse.debug("[TN3270E] We got this: 0x%s", stdnse.tohex(option)) + if option == self.tncommands.SEND then if self.sb_options:sub(3,3) == self.tncommands.DEVICETYPE then - self:send_data(self.commands.IAC .. + if self.connected_lu == '' then + self:send_data(self.commands.IAC .. self.commands.SB .. self.options.TN3270 .. self.tncommands.DEVICETYPE .. @@ -584,21 +593,40 @@ Telnet = { self.device_type .. self.commands.IAC .. self.commands.SE ) + else + stdnse.debug(3,"[TN3270] Sending LU: %s", self.connected_lu) + self:send_data(self.commands.IAC .. + self.commands.SB .. + self.options.TN3270 .. + self.tncommands.DEVICETYPE .. + self.tncommands.REQUEST .. + self.device_type .. + self.tncommands.CONNECT .. + self.connected_lu .. + self.commands.IAC .. + self.commands.SE ) + end else - stdnse.debug(3,"Received TN3270 Send but not device type. Weird.") + stdnse.debug(3,"[TN3270] Received TN3270 Send but not device type. Weird.") + return false end elseif option == self.tncommands.DEVICETYPE then -- Mainframe is confirming device type. Good! - if self.sb_options:sub(3,3) == self.tncommands.IS then + if self.sb_options:sub(3,3) == self.tncommands.REJECT then + -- Welp our LU request failed, shut it down + stdnse.debug(3,"[TN3270] Received TN3270 REJECT.") + return false + elseif self.sb_options:sub(3,3) == self.tncommands.IS then local tn_loc = 1 while self.sb_options:sub(4+tn_loc,4+tn_loc) ~= self.commands.SE and self.sb_options:sub(4+tn_loc,4+tn_loc) ~= self.tncommands.CONNECT do tn_loc = tn_loc + 1 end --XXX Unused variable??? Should this be tn_loc? - local sn_loc = 1 + -- local sn_loc = 1 if self.sb_options:sub(4+tn_loc,4+tn_loc) == self.tncommands.CONNECT then - self.connected_lu = self.sb_options:sub(5+tn_loc, #self.sb_options-1) + self.connected_lu = self.sb_options:sub(5+tn_loc, #self.sb_options) self.connected_dtype = self.sb_options:sub(4,3+tn_loc) + stdnse.debug(3,"[TN3270] Current LU: %s", self.connected_lu) end -- since We've connected lets send our options self:send_data(self.commands.IAC .. @@ -614,7 +642,7 @@ Telnet = { if self.sb_options:sub(3,3) == self.tncommands.IS then -- they accepted the function request, lets move on self.negotiated = true - stdnse.verbose(2,"TN3270 Option Negotiation Done!") + stdnse.debug(3,"[TN3270] Option Negotiation Done!") self:in3270() elseif self.sb_options:sub(3,3) == self.tncommands.REQUEST then -- dummy functions for now. Our client doesn't have any @@ -630,9 +658,7 @@ Telnet = { self.negotiated = true self:in3270() end - end - return true end, @@ -668,14 +694,16 @@ Telnet = { local reply = 0 stdnse.debug(3,"Processing TN3270 Data") if self.state == self.TN3270E_DATA then + stdnse.debug(3,"[TN3270E] Processing TN3270 Data header: %s", stdnse.tohex(self.tn_buffer:sub(1,5))) self.tn3270_header.data_type = self.tn_buffer:sub(1,1) self.tn3270_header.request_flag = self.tn_buffer:sub(2,2) self.tn3270_header.response_flag = self.tn_buffer:sub(3,3) self.tn3270_header.seq_number = self.tn_buffer:sub(4,5) if self.tn3270_header.data_type == "\000" then reply = self:process_3270(self.tn_buffer:sub(6)) + stdnse.debug(3,"[TN3270E] Reply: %s", reply) end - if reply < 0 and self.tn3270_header.request_flag ~= self.TN3270E_RSF_NO_RESPONSE then + if reply < 0 and self.tn3270_header.request_flag ~= self.TN3270E_RSF_NO_RESPONSE then self:tn3270e_nak(reply) elseif reply == self.NO_OUTPUT and self.tn3270_header.request_flag == self.ALWAYS_RESPONSE then @@ -740,7 +768,7 @@ Telnet = { process_3270 = function ( self, data ) -- the first byte will be the command we have to follow local com = data:sub(1,1) - stdnse.debug(3, "Value Received: 0x%s", stdnse.tohex(com)) + stdnse.debug(3, "[PROCESS 3270] Value Received: 0x%s", stdnse.tohex(com)) if com == self.command.EAU then stdnse.debug(3,"TN3270 Command: Erase All Unprotected") self:clear_unprotected() @@ -777,6 +805,7 @@ Telnet = { return self.BAD_COMMAND end + return 1 -- we may sometimes enter a state where we have nothing which is fine end, @@ -992,8 +1021,13 @@ Telnet = { \x87\x88\xa1\xa6\xa8\x96\x99\xb0\xb1\xb2\xb3\xb4\xb6\x00\x08\x81\z \x84\x00\x0a\x00\x04\x00\x06\x81\x99\x00\x00\xff\xef" stdnse.debug(3, "Current WSF : %s", stdnse.tohex(wsf_data:sub(4,4)) ) + + if self.state == self.TN3270E_DATA then + -- We need to add the header here since we're in TN3270E mode + query_options = "\x00\x00\x00\x00\x00" .. query_options + end self:send_data(query_options) - return true + return 1 end, @@ -1004,14 +1038,15 @@ Telnet = { send_tn3270 = function ( self, data ) local packet = '' if self.state == self.TN3270E_DATA then + packet = "\x00\x00\x00\x00\x00" -- we need to create the tn3270E (the E is important) header -- which, in basic 3270E is 5 bytes of 0x00 - packet = string.pack("BBB >I2", - self.DT_3270_DATA, -- type - 0, -- request - 0, -- response - 0 - ) + --packet = string.pack("BBB >I2", + -- self.DT_3270_DATA, -- type + -- 0, -- request + -- 0, -- response + -- 0 + -- ) --self.tn3270_header.seq_number end -- create send buffer and double up IACs @@ -1301,6 +1336,16 @@ Telnet = { return false end, + set_lu = function (self, LU) + -- Sets an LU + self.connected_lu = LU + end, + + get_lu = function ( self ) + return self.connected_lu + end, + + overwrite_data = function ( self ) if not self:any_overwritten() then return false diff --git a/scripts/tn3270-screen.nse b/scripts/tn3270-screen.nse index c37acd9bd..86133f7c4 100644 --- a/scripts/tn3270-screen.nse +++ b/scripts/tn3270-screen.nse @@ -1,5 +1,4 @@ local stdnse = require "stdnse" -local stringaux = require "stringaux" local shortport = require "shortport" local tn3270 = require "tn3270" @@ -45,11 +44,13 @@ Hidden fields will be listed below the screen with (row, col) coordinates. -- -- @args tn3270-screen.commands a semi-colon separated list of commands you want to -- issue before printing the screen +-- tn3270-screen.lu a logical unit you with to use fails if can't connect -- -- -- @changelog -- 2015-05-30 - v0.1 - created by Soldier of Fortran -- 2015-11-14 - v0.2 - added commands argument +-- 2018-09-07 - v0.3 - added support for Logical Units -- author = "Philip Young aka Soldier of Fortran" @@ -66,14 +67,19 @@ local hidden_field_mt = { action = function(host, port) local commands = stdnse.get_script_args(SCRIPT_NAME .. '.commands') + local lu = stdnse.get_script_args(SCRIPT_NAME .. '.lu') local t = tn3270.Telnet:new() + if lu then + stdnse.debug("Setting LU: %s", lu) + t:set_lu(lu) + end local status, err = t:initiate(host,port) if not status then stdnse.debug("Could not initiate TN3270: %s", err ) return else if commands then - local run = stringaux.strsplit(";%s*", commands) + local run = stdnse.strsplit(";%s*", commands) for i = 1, #run do stdnse.debug(1,"Issuing Command (#%s of %s): %s", i, #run ,run[i]) t:send_cursor(run[i]) @@ -101,6 +107,7 @@ action = function(host, port) local out = stdnse.output_table() out.screen = t:get_screen() out["hidden fields"] = hidden + out["logical unit"]= t:get_lu() return out end end