metasploit-framework/lib/rex/proto/pjl/client.rb

218 lines
4.1 KiB
Ruby

# -*- coding: binary -*-
# https://en.wikipedia.org/wiki/Printer_Job_Language
# See external links for PJL spec
module Rex::Proto::PJL
class Client
def initialize(sock)
@sock = sock
end
# Begin a PJL job
#
# @return [void]
def begin_job
@sock.put("#{UEL}#{PREFIX}\n")
end
# End a PJL job
#
# @return [void]
def end_job
@sock.put(UEL)
end
# Send an INFO request and read the response
#
# @param category [String] INFO category
# @return [String] INFO response
def info(category)
categories = {
:id => Info::ID,
:status => Info::STATUS,
:variables => Info::VARIABLES,
:filesys => Info::FILESYS
}
unless categories.has_key?(category)
raise ArgumentError, "Unknown INFO category"
end
@sock.put("#{categories[category]}\n")
@sock.get(DEFAULT_TIMEOUT)
end
# Get version information
#
# @return [String] Version information
def info_id
id = nil
if info(:id) =~ /"(.*?)"/m
id = $1
end
id
end
# Get environment variables
#
# @return [String] Environment variables
def info_variables
env_vars = nil
if info(:variables) =~ /#{Info::VARIABLES}\r?\n(.*?)\f/m
env_vars = $1
end
env_vars
end
# List volumes
#
# @return [String] Volume listing
def info_filesys
filesys = nil
if info(:filesys) =~ /\[\d+ TABLE\]\r?\n(.*?)\f/m
filesys = $1
end
filesys
end
# Get the ready message
#
# @return [String] Ready message
def get_rdymsg
rdymsg = nil
if info(:status) =~ /DISPLAY="(.*?)"/m
rdymsg = $1
end
rdymsg
end
# Set the ready message
#
# @param message [String] Ready message
# @return [void]
def set_rdymsg(message)
@sock.put(%Q{#{RDYMSG} DISPLAY = "#{message}"\n})
end
# Initialize a volume
#
# @param volume [String] Volume
# @return [void]
def fsinit(volume)
if volume !~ /^[0-2]:$/
raise ArgumentError, "Volume must be 0:, 1:, or 2:"
end
@sock.put(%Q{#{FSINIT} VOLUME = "#{volume}"\n})
end
# Query a file
#
# @param path [String] Remote path
# @return [Boolean] True if file exists
def fsquery(path)
if path !~ /^[0-2]:/
raise ArgumentError, "Path must begin with 0:, 1:, or 2:"
end
file = false
@sock.put(%Q{#{FSQUERY} NAME = "#{path}"\n})
if @sock.get(DEFAULT_TIMEOUT) =~ /TYPE=(FILE|DIR)/m
file = true
end
file
end
# List a directory
#
# @param path [String] Remote path
# @param count [Fixnum] Number of entries to list
# @return [String] Directory listing
def fsdirlist(path, count = COUNT_MAX)
if path !~ /^[0-2]:/
raise ArgumentError, "Path must begin with 0:, 1:, or 2:"
end
listing = nil
@sock.put(%Q{#{FSDIRLIST} NAME = "#{path}" ENTRY=1 COUNT=#{count}\n})
if @sock.get(DEFAULT_TIMEOUT) =~ /ENTRY=1\r?\n(.*?)\f/m
listing = $1
end
listing
end
# Download a file
#
# @param path [String] Remote path
# @return [String] File as a string
def fsupload(path)
if path !~ /^[0-2]:/
raise ArgumentError, "Path must begin with 0:, 1:, or 2:"
end
file = nil
@sock.put(%Q{#{FSUPLOAD} NAME = "#{path}" OFFSET=0 SIZE=#{SIZE_MAX}\n})
if @sock.get(DEFAULT_TIMEOUT) =~ /SIZE=\d+\r?\n(.*)\f/m
file = $1
end
file
end
# Upload a file
#
# @param lpath [String] Local path
# @param rpath [String] Remote path
# @return [Boolean] True if the file was uploaded
def fsdownload(lpath, rpath)
if rpath !~ /^[0-2]:/
raise ArgumentError, "Path must begin with 0:, 1:, or 2:"
end
file = File.read(lpath)
@sock.put(
%Q{#{FSDOWNLOAD} FORMAT:BINARY SIZE=#{file.length} NAME = "#{rpath}"\n}
)
@sock.put(file)
@sock.put(UEL)
fsquery(rpath)
end
# Delete a file
#
# @param path [String] Remote path
# @return [Boolean] True if the file was deleted
def fsdelete(path)
if path !~ /^[0-2]:/
raise ArgumentError, "Path must begin with 0:, 1:, or 2:"
end
@sock.put(%Q{#{FSDELETE} NAME = "#{path}"\n})
!fsquery(path)
end
end
end