description = [[ Attempts to discover Canon devices (Printers/Scanners) supporting the BJNP protocol by sending BJNP Discover requests to the network broadcast address for both ports associated with the protocol. The script then attempts to retrieve the model, version and some additional information for all discovered devices. ]] --- -- @usage -- nmap --script broadcast-bjnp-discover -- -- @output -- | broadcast-bjnp-discover: -- | 192.168.0.10 -- | Printer -- | Manufacturer: Canon -- | Model: MG5200 series -- | Description: Canon MG5200 series -- | Firmware version: 1.050 -- | Command: BJL,BJRaster3,BSCCe,NCCe,IVEC,IVECPLI -- | Scanner -- | Manufacturer: Canon -- | Model: MG5200 series -- | Description: Canon MG5200 series -- |_ Command: MultiPass 2.1,IVEC -- -- @args broadcast-bjnp-discover.timeout specifies the amount of seconds to sniff -- the network interface. (default 30s) author = "Patrik Karlsson" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"safe", "broadcast"} local bjnp = require("bjnp") local stdnse = require("stdnse") local coroutine = require("coroutine") local nmap = require("nmap") local table = require("table") local printer_port = { number = 8611, protocol = "udp"} local scanner_port = { number = 8612, protocol = "udp"} local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout")) prerule = function() if ( nmap.address_family() ~= 'inet' ) then stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME) return false end return true end local function identifyDevices(devices, devtype) local result local port = ( "printers" == devtype and printer_port or scanner_port ) for _, ip in ipairs(devices or {}) do local helper = bjnp.Helper:new({ ip = ip }, port) if ( helper:connect() ) then local status, attrs if ( "printers" == devtype ) then status, attrs = helper:getPrinterIdentity() end if ( "scanners" == devtype ) then status, attrs = helper:getScannerIdentity() end if ( status ) then result = result or {} result[ip] = attrs end end helper:close() end return result end local function identifyScanners(scanners) return identifyDevices(scanners, "scanners") end local function identifyPrinters(printers) return identifyDevices(printers, "printers") end local function getKeys(devices) local dupes = {} local function iter() for k, _ in pairs(devices) do for k2, _ in pairs(devices[k]) do if ( not(dupes[k2]) ) then dupes[k2] = true coroutine.yield(k2) end end end coroutine.yield(nil) end return coroutine.wrap(iter) end local function getPrinters(devices) local condvar = nmap.condvar(devices) local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, printer_port, { bcast = true, timeout = arg_timeout } ) if ( not(helper:connect()) ) then condvar "signal" return end local status, printers = helper:discoverPrinter() helper:close() if ( status ) then devices["printers"] = identifyPrinters(printers) end condvar "signal" end local function getScanners(devices) local condvar = nmap.condvar(devices) local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, scanner_port, { bcast = true, timeout = arg_timeout } ) if ( not(helper:connect()) ) then condvar "signal" return end local status, scanners = helper:discoverScanner() helper:close() if ( status ) then devices["scanners"] = identifyScanners(scanners) end condvar "signal" end action = function() arg_timeout = ( arg_timeout and arg_timeout * 1000 or 5000) local devices, result, threads = {}, {}, {} local condvar = nmap.condvar(devices) local co = stdnse.new_thread(getPrinters, devices) threads[co] = true co = stdnse.new_thread(getScanners, devices) threads[co] = true while(next(threads)) do for t in pairs(threads) do threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil end if ( next(threads) ) then condvar "wait" end end for ip in getKeys(devices) do local result_part = {} local printer = ( devices["printers"] and devices["printers"][ip] ) local scanner = ( devices["scanners"] and devices["scanners"][ip] ) if ( printer ) then printer.name = "Printer" table.insert(result_part, printer) end if ( scanner ) then scanner.name = "Scanner" table.insert(result_part, scanner) end if ( #result_part > 0 ) then result_part.name = ip table.insert(result, result_part) end end if ( result ) then return stdnse.format_output(true, result) end end