##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'

class Metasploit3 < Msf::Auxiliary

	include Msf::Exploit::Remote::SNMPClient
	include Msf::Auxiliary::Report
	include Msf::Auxiliary::Scanner

	def initialize(info = {})
		super(update_info(info,
			'Name'        => 'SNMP Enumeration Module',
			'Description' => 'This module allows enumeration of any devices with SNMP
				protocol support. It supports hardware, software, and network information.
				The default community used is "public".',
			'References'  =>
				[
					[ 'URL', 'http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol' ],
					[ 'URL', 'http://net-snmp.sourceforge.net/docs/man/snmpwalk.html' ],
					[ 'URL', 'http://www.nothink.org/perl/snmpcheck/' ],
				],
			'Author'      => 'Matteo Cantoni <goony[at]nothink.org>',
			'License'     => MSF_LICENSE
		))
	end

	def run_host(ip)

		begin
			snmp = connect_snmp

			#
			#
			#
			fields_order = [
				"Host IP", "Hostname", "Description", "Contact",
				"Location", "Uptime snmp", "Uptime system",
				"System date", "domain", "User accounts",
				"Network information", "Network interfaces",
				"Network IP", "Routing information",
				"TCP connections and listening ports", "Listening UDP ports",
				"Network services", "Share", "IIS server information",
				"Storage information", "File system information",
				"Device information", "Software components",
				"Processes"
			]

			output_data = {}
			output_data = {"Host IP"=>ip}

			sysName = snmp.get_value('1.3.6.1.2.1.1.5.0').to_s
			output_data["Hostname"] = sysName.strip

			# print connected status after the first query so if there are
			# any timeout or connectivity errors; the code would already
			# have jumped to error handling where the error status is
			# already being displayed.
			print_good("#{ip}, Connected.")

			sysDesc = snmp.get_value('1.3.6.1.2.1.1.1.0').to_s
			sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ')
			output_data["Description"] = sysDesc.strip

			sysContact = snmp.get_value('1.3.6.1.2.1.1.4.0').to_s
			output_data["Contact"] = sysContact.strip

			sysLocation = snmp.get_value('1.3.6.1.2.1.1.6.0').to_s
			output_data["Location"] = sysLocation.strip

			sysUpTimeInstance = snmp.get_value('1.3.6.1.2.1.1.3.0').to_s
			output_data["Uptime system"] = sysUpTimeInstance.strip

			hrSystemUptime = snmp.get_value('1.3.6.1.2.1.25.1.1.0').to_s
			output_data["Uptime snmp"] = hrSystemUptime.strip
			hrSystemUptime = '-' if hrSystemUptime.to_s =~ /Null/

			year = month = day = hour = minutes = seconds = tenths = 0

			systemDate = snmp.get_value('1.3.6.1.2.1.25.1.2.0')
			str = systemDate.to_s
			if (str.empty? or str =~ /Null/ or str =~ /^noSuch/)
				output_data["System date"] = '-'
			else

				# RFC 2579 - Textual Conventions for SMIv2
				# http://www.faqs.org/rfcs/rfc2579.html

				systemDate = systemDate.unpack('C*')

				year    = systemDate[0] * 256 + systemDate[1]
				month   = systemDate[2]
				day     = systemDate[3]
				hour    = systemDate[4]
				minutes = systemDate[5]
				seconds = systemDate[6]
				tenths  = systemDate[7]
				output_data["System date"] = sprintf("%d-%d-%d %02d:%02d:%02d.%d", year, month, day, hour, minutes, seconds, tenths)
			end

			#
			#
			if (sysDesc =~ /Windows/)
				usersLine = ""
				domPrimaryDomain = snmp.get_value('1.3.6.1.4.1.77.1.4.1.0').to_s

				output_data["Domain"] = domPrimaryDomain.strip

				#
				#
				#

				users = []

				snmp.walk(["1.3.6.1.4.1.77.1.2.25.1.1","1.3.6.1.4.1.77.1.2.25.1"]) do |user,entry|
					users.push([[user.value]])
				end

				if not users.empty?
					output_data["User accounts"] = users
				end
			end

			#
			#
			#

			network_information = {}

			ipForwarding = snmp.get_value('1.3.6.1.2.1.4.1.0')

			if ipForwarding == 0 || ipForwarding == 2
				ipForwarding = "no"
				network_information["IP forwarding enabled"] = ipForwarding
			elsif ipForwarding == 1
				ipForwarding = "yes"
				network_information["IP forwarding enabled"] = ipForwarding
			end

			ipDefaultTTL = snmp.get_value('1.3.6.1.2.1.4.2.0')
			if ipDefaultTTL.to_s !~ /Null/
				network_information["Default TTL"] = ipDefaultTTL
			end

			tcpInSegs = snmp.get_value('1.3.6.1.2.1.6.10.0')
			if tcpInSegs.to_s !~ /Null/
				network_information["TCP segments received"] = tcpInSegs
			end

			tcpOutSegs = snmp.get_value('1.3.6.1.2.1.6.11.0')
			if tcpOutSegs.to_s !~ /Null/
				network_information["TCP segments sent"] = tcpOutSegs
			end

			tcpRetransSegs = snmp.get_value('1.3.6.1.2.1.6.12.0')
			if tcpRetransSegs.to_s !~ /Null/
				network_information["TCP segments retrans"] = tcpRetransSegs
			end

			ipInReceives = snmp.get_value('1.3.6.1.2.1.4.3.0')
			if ipInReceives.to_s !~ /Null/
				network_information["Input datagrams"] = ipInReceives
			end

			ipInDelivers = snmp.get_value('1.3.6.1.2.1.4.9.0')
			if ipInDelivers.to_s !~ /Null/
				network_information["Delivered datagrams"]=ipInDelivers
			end

			ipOutRequests = snmp.get_value('1.3.6.1.2.1.4.10.0')
			if ipOutRequests.to_s !~ /Null/
				network_information["Output datagrams"]=ipOutRequests
			end

			if not network_information.empty?
				output_data["Network information"] = network_information
			end

			#
			#
			#

			network_interfaces = []

			snmp.walk([
				"1.3.6.1.2.1.2.2.1.1", "1.3.6.1.2.1.2.2.1.2", "1.3.6.1.2.1.2.2.1.6",
				"1.3.6.1.2.1.2.2.1.3", "1.3.6.1.2.1.2.2.1.4", "1.3.6.1.2.1.2.2.1.5",
				"1.3.6.1.2.1.2.2.1.10", "1.3.6.1.2.1.2.2.1.16", "1.3.6.1.2.1.2.2.1.7"
			]) do |index,descr,mac,type,mtu,speed,inoc,outoc,status|

				ifindex  = index.value
				ifdescr  = descr.value
				ifmac    = mac.value.unpack("H2H2H2H2H2H2").join(":")
				iftype   = type.value
				ifmtu    = mtu.value
				ifspeed  = speed.value.to_i
				ifinoc   = inoc.value
				ifoutoc  = outoc.value
				ifstatus = status.value

				case iftype
				when 1
					iftype = "other"
				when 2
					iftype = "regular1822"
				when 3
					iftype = "hdh1822"
				when 4
					iftype = "ddn-x25"
				when 5
					iftype = "rfc877-x25"
				when 6
					iftype = "ethernet-csmacd"
				when 7
					iftype = "iso88023-csmacd"
				when 8
					iftype = "iso88024-tokenBus"
				when 9
					iftype = "iso88025-tokenRing"
				when 10
					iftype = "iso88026-man"
				when 11
					iftype = "starLan"
				when 12
					iftype = "proteon-10Mbit"
				when 13
					iftype = "proteon-80Mbit"
				when 14
					iftype = "hyperchannel"
				when 15
					iftype = "fddi"
				when 16
					iftype = "lapb"
				when 17
					iftype = "sdlc"
				when 18
					iftype = "ds1"
				when 19
					iftype = "e1"
				when 20
					iftype = "basicISDN"
				when 21
					iftype = "primaryISDN"
				when 22
					iftype = "propPointToPointSerial"
				when 23
					iftype = "ppp"
				when 24
					iftype = "softwareLoopback"
				when 25
					iftype = "eon"
				when 26
					iftype = "ethernet-3Mbit"
				when 27
					iftype = "nsip"
				when 28
					iftype = "slip"
				when 29
					iftype = "ultra"
				when 30
					iftype = "ds3"
				when 31
					iftype = "sip"
				when 32
					iftype = "frame-relay"
				else
					iftype = "unknown"
				end

				case ifstatus
				when 1
					ifstatus = "up"
				when 2
					ifstatus = "down"
				when 3
					ifstatus = "testing"
				else
					ifstatus = "unknown"
				end

				ifspeed = ifspeed / 1000000

				network_interfaces.push({
					"Interface" => "[ #{ifstatus} ] #{ifdescr}",
					"Id" => ifindex,
					"Mac Address" => ifmac,
					"Type" => iftype,
					"Speed" => "#{ifspeed} Mbps",
					"MTU" => ifmtu,
					"In octets" => ifinoc,
					"Out octets" => ifoutoc
				})
			end

			if not network_interfaces.empty?
				output_data["Network interfaces"] = network_interfaces
			end

			#
			#
			#

			network_ip = []

			snmp.walk([
				"1.3.6.1.2.1.4.20.1.2", "1.3.6.1.2.1.4.20.1.1",
				"1.3.6.1.2.1.4.20.1.3", "1.3.6.1.2.1.4.20.1.4"
			]) do |ifid,ipaddr,netmask,bcast|
				network_ip.push([ifid.value, ipaddr.value, netmask.value, bcast.value])
			end

			if not network_ip.empty?
				output_data["Network IP"] = [["Id","IP Address","Netmask","Broadcast"]] + network_ip
			end

			#
			#
			#

			routing = []

			snmp.walk([
				"1.3.6.1.2.1.4.21.1.1", "1.3.6.1.2.1.4.21.1.7",
				"1.3.6.1.2.1.4.21.1.11","1.3.6.1.2.1.4.21.1.3"
			]) do |dest,hop,mask,metric|
				if (metric.value.to_s.empty?)
					metric.value = '-'
				end
				routing.push([dest.value, hop.value, mask.value, metric.value])
			end

			if not routing.empty?
				output_data["Routing information"] = [["Destination","Next hop","Mask","Metric"]] + routing
			end

			#
			#
			#

			tcp = []

			snmp.walk([
				"1.3.6.1.2.1.6.13.1.2","1.3.6.1.2.1.6.13.1.3","1.3.6.1.2.1.6.13.1.4",
				"1.3.6.1.2.1.6.13.1.5","1.3.6.1.2.1.6.13.1.1"
			]) do |ladd,lport,radd,rport,state|

				if (ladd.value.to_s.empty?  or ladd.value.to_s =~ /noSuchInstance/)
					ladd = "-"
				else
					ladd  = ladd.value
				end

				if (lport.value.to_s.empty? or lport.value.to_s =~ /noSuchInstance/)
					lport = "-"
				else
					lport = lport.value
				end

				if (radd.value.to_s.empty?  or radd.value.to_s =~ /noSuchInstance/)
					radd = "-"
				else
					radd  = radd.value
				end

				if (rport.value.to_s.empty? or rport.value.to_s =~ /noSuchInstance/)
					rport = "-"
				else
					rport = rport.value
				end

				case state.value
				when 1
					state = "closed"
				when 2
					state = "listen"
				when 3
					state = "synSent"
				when 4
					state = "synReceived"
				when 5
					state = "established"
				when 6
					state = "finWait1"
				when 7
					state = "finWait2"
				when 8
					state = "closeWait"
				when 9
					state = "lastAck"
				when 10
					state = "closing"
				when 11
					state = "timeWait"
				when 12
					state = "deleteTCB"
				else
					state = "unknown"
				end

				tcp.push([ladd, lport, radd, rport, state])
			end

			if not tcp.empty?
				output_data["TCP connections and listening ports"] = [["Local address","Local port","Remote address","Remote port","State"]] + tcp
			end

			#
			#
			#

			udp = []

			snmp.walk(["1.3.6.1.2.1.7.5.1.1","1.3.6.1.2.1.7.5.1.2"]) do |ladd,lport|
				udp.push([ladd.value, lport.value])
			end

			if not udp.empty?
				output_data["Listening UDP ports"] = [["Local address","Local port"]] + udp
			end

			#
			#
			#

			if (sysDesc =~ /Windows/)

				#
				#
				#

				network_services = []

				n = 0

				snmp.walk(["1.3.6.1.4.1.77.1.2.3.1.1","1.3.6.1.4.1.77.1.2.3.1.2"]) do |name,installed|
					network_services.push([n,name.value])
					n+=1
				end

				if not network_services.empty?
					output_data["Network services"] = [["Index","Name"]] + network_services
				end

				#
				#
				#

				share = []

				snmp.walk([
					"1.3.6.1.4.1.77.1.2.27.1.1","1.3.6.1.4.1.77.1.2.27.1.2","1.3.6.1.4.1.77.1.2.27.1.3"
				]) do |name,path,comment|
					share.push({" Name"=>name.value, "  Path"=>path.value, "  Comment"=>comment.value})
				end

				if not share.empty?
					output_data["Share"] = share
				end

				#
				#
				#

				iis = {}

				http_totalBytesSentLowWord = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.2.0')
				if http_totalBytesSentLowWord.to_s !~ /Null/
					iis["TotalBytesSentLowWord"] = http_totalBytesSentLowWord
				end

				http_totalBytesReceivedLowWord = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.4.0')
				if http_totalBytesReceivedLowWord.to_s !~ /Null/
					iis["TotalBytesReceivedLowWord"] = http_totalBytesReceivedLowWord
				end

				http_totalFilesSent = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.5.0')
				if http_totalFilesSent.to_s !~ /Null/
					iis["TotalFilesSent"] = http_totalFilesSent
				end

				http_currentAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.6.0')
				if http_currentAnonymousUsers.to_s !~ /Null/
					iis["CurrentAnonymousUsers"] = http_currentAnonymousUsers
				end

				http_currentNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.7.0')
				if http_currentNonAnonymousUsers.to_s !~ /Null/
					iis["CurrentNonAnonymousUsers"] = http_currentNonAnonymousUsers
				end

				http_totalAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.8.0')
				if http_totalAnonymousUsers.to_s !~ /Null/
					iis["TotalAnonymousUsers"] = http_totalAnonymousUsers
				end

				http_totalNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.9.0')
				if http_totalNonAnonymousUsers.to_s !~ /Null/
					iis["TotalNonAnonymousUsers"] = http_totalNonAnonymousUsers
				end

				http_maxAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.10.0')
				if http_maxAnonymousUsers.to_s !~ /Null/
					iis["MaxAnonymousUsers"] = http_maxAnonymousUsers
				end

				http_maxNonAnonymousUsers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.11.0')
				if http_maxNonAnonymousUsers.to_s !~ /Null/
					iis["MaxNonAnonymousUsers"] = http_maxNonAnonymousUsers
				end

				http_currentConnections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.12.0')
				if http_currentConnections.to_s !~ /Null/
					iis["CurrentConnections"] = http_currentConnections
				end

				http_maxConnections = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.13.0')
				if http_maxConnections.to_s !~ /Null/
					iis["MaxConnections"] = http_maxConnections
				end

				http_connectionAttempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.14.0')
				if http_connectionAttempts.to_s !~ /Null/
					iis["ConnectionAttempts"] = http_connectionAttempts
				end

				http_logonAttempts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.15.0')
				if http_logonAttempts.to_s !~ /Null/
					iis["LogonAttempts"] = http_logonAttempts
				end

				http_totalGets = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.16.0')
				if http_totalGets.to_s !~ /Null/
					iis["Gets"] = http_totalGets
				end

				http_totalPosts = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.17.0')
				if http_totalPosts.to_s !~ /Null/
					iis["Posts"] = http_totalPosts
				end

				http_totalHeads = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.18.0')
				if http_totalHeads.to_s !~ /Null/
					iis["Heads"] = http_totalHeads
				end

				http_totalOthers = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.19.0')
				if http_totalOthers.to_s !~ /Null/
					iis["Others"] = http_totalOthers
				end

				http_totalCGIRequests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.20.0')
				if http_totalCGIRequests.to_s !~ /Null/
					iis["CGIRequests"] = http_totalCGIRequests
				end

				http_totalBGIRequests = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.21.0')
				if http_totalBGIRequests.to_s !~ /Null/
					iis["BGIRequests"] = http_totalBGIRequests
				end

				http_totalNotFoundErrors = snmp.get_value('1.3.6.1.4.1.311.1.7.3.1.22.0')
				if http_totalNotFoundErrors.to_s !~ /Null/
					iis["NotFoundErrors"] = http_totalNotFoundErrors
				end

				if not iis.empty?
					output_data["IIS server information"] = iis
				end
			end

			#
			#
			#

			storage_information = []

			snmp.walk([
				"1.3.6.1.2.1.25.2.3.1.1", "1.3.6.1.2.1.25.2.3.1.2", "1.3.6.1.2.1.25.2.3.1.3",
				"1.3.6.1.2.1.25.2.3.1.4", "1.3.6.1.2.1.25.2.3.1.5", "1.3.6.1.2.1.25.2.3.1.6"
			]) do |index,type,descr,allocation,size,used|

				case type.value.to_s
				when /^1.3.6.1.2.1.25.2.1.1$/
					type.value = "Other"
				when /^1.3.6.1.2.1.25.2.1.2$/
					type.value = "Ram"
				when /^1.3.6.1.2.1.25.2.1.3$/
					type.value = "Virtual Memory"
				when /^1.3.6.1.2.1.25.2.1.4$/
					type.value = "Fixed Disk"
				when /^1.3.6.1.2.1.25.2.1.5$/
					type.value = "Removable Disk"
				when /^1.3.6.1.2.1.25.2.1.6$/
					type.value = "Floppy Disk"
				when /^1.3.6.1.2.1.25.2.1.7$/
					type.value = "Compact Disc"
				when /^1.3.6.1.2.1.25.2.1.8$/
					type.value = "RamDisk"
				when /^1.3.6.1.2.1.25.2.1.9$/
					type.value = "Flash Memory"
				when /^1.3.6.1.2.1.25.2.1.10$/
					type.value = "Network Disk"
				else
					type.value = "unknown"
				end

				allocation.value = "unknown" if allocation.value.to_s =~ /noSuchInstance/
				size.value       = "unknown" if size.value.to_s =~ /noSuchInstance/
				used.value       = "unknown" if used.value.to_s =~ /noSuchInstance/

				storage_information.push([[descr.value],[index.value],[type.value],[allocation.value],[size.value],[used.value]])
			end

			if not storage_information.empty?
				storage = []
				storage_information.each {|a,b,c,d,e,f|
					s = {}

					e = number_to_human_size(e,d)
					f = number_to_human_size(f,d)

					s["Description"]= a
					s["Device id"] = b
					s["Filesystem type"] = c
					s["Device unit"] = d
					s["Memory size"] = e
					s["Memory used"] = f

					storage.push(s)
				}
				output_data["Storage information"] = storage
			end

			#
			#
			#

			file_system = {}

			hrFSIndex = snmp.get_value('1.3.6.1.2.1.25.3.8.1.1.1')
			if hrFSIndex.to_s !~ /Null/
				file_system["Index"] = hrFSIndex
			end

			hrFSMountPoint = snmp.get_value('1.3.6.1.2.1.25.3.8.1.2.1')
			if hrFSMountPoint.to_s !~ /Null/
				file_system["Mount point"] = hrFSMountPoint
			end

			hrFSRemoteMountPoint = snmp.get_value('1.3.6.1.2.1.25.3.8.1.3.1')
			if hrFSRemoteMountPoint.to_s !~ /Null/ and hrFSRemoteMountPoint.to_s !~ /^noSuch/
				if hrFSRemoteMountPoint.empty?
					hrFSRemoteMountPoint = '-'
				end
				file_system["Remote mount point"] = hrFSRemoteMountPoint
			end

			hrFSType = snmp.get_value('1.3.6.1.2.1.25.3.8.1.4.1')

			case hrFSType.to_s
			when /^1.3.6.1.2.1.25.3.9.1$/
				hrFSType = "Other"
			when /^1.3.6.1.2.1.25.3.9.2$/
				hrFSType = "Unknown"
			when /^1.3.6.1.2.1.25.3.9.3$/
				hrFSType = "BerkeleyFFS"
			when /^1.3.6.1.2.1.25.3.9.4$/
				hrFSType = "Sys5FS"
			when /^1.3.6.1.2.1.25.3.9.5$/
				hrFSType = "Fat"
			when /^1.3.6.1.2.1.25.3.9.6$/
				hrFSType = "HPFS"
			when /^1.3.6.1.2.1.25.3.9.7$/
				hrFSType = "HFS"
			when /^1.3.6.1.2.1.25.3.9.8$/
				hrFSType = "MFS"
			when /^1.3.6.1.2.1.25.3.9.9$/
				hrFSType = "NTFS"
			when /^1.3.6.1.2.1.25.3.9.10$/
				hrFSType = "VNode"
			when /^1.3.6.1.2.1.25.3.9.11$/
				hrFSType = "Journaled"
			when /^1.3.6.1.2.1.25.3.9.12$/
				hrFSType = "iso9660"
			when /^1.3.6.1.2.1.25.3.9.13$/
				hrFSType = "RockRidge"
			when /^1.3.6.1.2.1.25.3.9.14$/
				hrFSType = "NFS"
			when /^1.3.6.1.2.1.25.3.9.15$/
				hrFSType = "Netware"
			when /^1.3.6.1.2.1.25.3.9.16$/
				hrFSType = "AFS"
			when /^1.3.6.1.2.1.25.3.9.17$/
				hrFSType = "DFS"
			when /^1.3.6.1.2.1.25.3.9.18$/
				hrFSType = "Appleshare"
			when /^1.3.6.1.2.1.25.3.9.19$/
				hrFSType = "RFS"
			when /^1.3.6.1.2.1.25.3.9.20$/
				hrFSType = "DGCFS"
			when /^1.3.6.1.2.1.25.3.9.21$/
				hrFSType = "BFS"
			when /^1.3.6.1.2.1.25.3.9.22$/
				hrFSType = "FAT32"
			when /^1.3.6.1.2.1.25.3.9.23$/
				hrFSType = "LinuxExt2"
			else
				hrFSType = "Null"
			end

			if hrFSType.to_s !~ /Null/
				file_system["Type"] = hrFSType
			end

			hrFSAccess = snmp.get_value('1.3.6.1.2.1.25.3.8.1.5.1')
			if hrFSAccess.to_s !~ /Null/
				file_system["Access"] = hrFSAccess
			end

			hrFSBootable = snmp.get_value('1.3.6.1.2.1.25.3.8.1.6.1')
			if hrFSBootable.to_s !~ /Null/
				file_system["Bootable"] = hrFSBootable
			end

			if not file_system.empty?
				output_data["File system information"] = file_system
			end

			#
			#
			#

			device_information = []

			snmp.walk([
				"1.3.6.1.2.1.25.3.2.1.1", "1.3.6.1.2.1.25.3.2.1.2",
				"1.3.6.1.2.1.25.3.2.1.5", "1.3.6.1.2.1.25.3.2.1.3"
			]) do |index,type,status,descr|

				case type.value.to_s
				when /^1.3.6.1.2.1.25.3.1.1$/
					type.value = "Other"
				when /^1.3.6.1.2.1.25.3.1.2$/
					type.value = "Unknown"
				when /^1.3.6.1.2.1.25.3.1.3$/
					type.value = "Processor"
				when /^1.3.6.1.2.1.25.3.1.4$/
					type.value = "Network"
				when /^1.3.6.1.2.1.25.3.1.5$/
					type.value = "Printer"
				when /^1.3.6.1.2.1.25.3.1.6$/
					type.value = "Disk Storage"
				when /^1.3.6.1.2.1.25.3.1.10$/
					type.value = "Video"
				when /^1.3.6.1.2.1.25.3.1.11$/
					type.value = "Audio"
				when /^1.3.6.1.2.1.25.3.1.12$/
					type.value = "Coprocessor"
				when /^1.3.6.1.2.1.25.3.1.13$/
					type.value = "Keyboard"
				when /^1.3.6.1.2.1.25.3.1.14$/
					type.value = "Modem"
				when /^1.3.6.1.2.1.25.3.1.15$/
					type.value = "Parallel Port"
				when /^1.3.6.1.2.1.25.3.1.16$/
					type.value = "Pointing"
				when /^1.3.6.1.2.1.25.3.1.17$/
					type.value = "Serial Port"
				when /^1.3.6.1.2.1.25.3.1.18$/
					type.value = "Tape"
				when /^1.3.6.1.2.1.25.3.1.19$/
					type.value = "Clock"
				when /^1.3.6.1.2.1.25.3.1.20$/
					type.value = "Volatile Memory"
				when /^1.3.6.1.2.1.25.3.1.21$/
					type.value = "Non Volatile Memory"
				else
					type.value = "unknown"
				end

				case status.value
				when 1
					status.value = "unknown"
				when 2
					status.value = "running"
				when 3
					status.value = "warning"
				when 4
					status.value = "testing"
				when 5
					status.value = "down"
				else
					status.value = "unknown"
				end

				descr.value = "unknown" if descr.value.to_s =~ /noSuchInstance/

				device_information.push([index.value, type.value, status.value, descr.value])
			end

			if not device_information.empty?
				output_data["Device information"] = [["Id","Type","Status","Descr"]] + device_information
			end

			#
			#
			#

			software_list = []

			snmp.walk(["1.3.6.1.2.1.25.6.3.1.1","1.3.6.1.2.1.25.6.3.1.2"]) do |index,name|
				software_list.push([index.value,name.value])
			end

			if not software_list.empty?
				output_data["Software components"] = [["Index","Name"]] + software_list
			end

			#
			#
			#

			process_interfaces = []

			snmp.walk([
				"1.3.6.1.2.1.25.4.2.1.1", "1.3.6.1.2.1.25.4.2.1.2", "1.3.6.1.2.1.25.4.2.1.4",
				"1.3.6.1.2.1.25.4.2.1.5", "1.3.6.1.2.1.25.4.2.1.7"
			]) do |id,name,path,param,status|

				if status.value == 1
					status.value = "running"
				elsif status.value == 2
					status.value = "runnable"
				else
					status.value = "unknown"
				end

				process_interfaces.push([id.value, status.value, name.value, path.value, param.value])
			end

			if not process_interfaces.empty?
				output_data["Processes"] = [["Id","Status","Name","Path","Parameters"]] + process_interfaces
			end

			#
			#
			#

			print_line("\n[*] System information:\n")

			line = ""
			width = 30  # name field width
			twidth = 32 # table like display cell width

			fields_order.each {|k|
				if not output_data.has_key?(k)
					next
				end

				v = output_data[k]

				case v
				when Array
					content = ""

					v.each{ |a|
						case a
						when Hash
							a.each{ |sk, sv|
								sk = truncate_to_twidth(sk, twidth)
								content << sprintf("%s%s: %s\n", sk, " "*([0,width-sk.length].max), sv)
							}
							content << "\n"
						when Array
							a.each { |sv|
								sv = sv.to_s.strip
								# I don't like cutting info
								#sv = truncate_to_twidth(sv, twidth)
								content << sprintf("%-20s", sv)
							}
							content << "\n"
						else
							content << sprintf("    %s\n", a)
							content << "\n"
						end
					}

					report_note(
						:host  => ip,
						:proto => 'udp',
						:sname => 'snmp',
						:port  => datastore['RPORT'].to_i,
						:type  => "snmp.#{k}",
						:data  => content
					)

					line << "\n[*] #{k}:\n\n#{content}"

				when Hash
					content = ""
					v.each{ |sk, sv|
						sk = truncate_to_twidth(sk,twidth)
						content << sprintf("%s%s: %s\n", sk, " "*([0,width-sk.length].max), sv)
					}

					report_note(
						:host  => ip,
						:proto => 'udp',
						:sname => 'snmp',
						:port  => datastore['RPORT'].to_i,
						:type  => "snmp.#{k}",
						:data  => content
					)

					line << "\n[*] #{k}:\n\n#{content}"
					content << "\n"
				else
					if (v.nil? or v.empty? or v =~ /Null/)
						v = '-'
					end

					report_note(
						:host  => ip,
						:proto => 'udp',
						:sname => 'snmp',
						:port  => datastore['RPORT'].to_i,
						:type  => "snmp.#{k}",
						:data  => v
					)

					k = truncate_to_twidth(k,twidth)
					line << sprintf("%s%s: %s\n", k, " "*([0,width-k.length].max), v)
				end
			}

			print_line(line)

			#
			#
			#

			print_line('')



		rescue SNMP::RequestTimeout
			vprint_status("#{ip} SNMP request timeout.")
		rescue Rex::ConnectionError
			print_status("#{ip} Connection refused.")
		rescue SNMP::InvalidIpAddress
			print_status("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.")
		rescue SNMP::UnsupportedVersion
			print_status("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
		rescue ::Interrupt
			raise $!
		rescue ::Exception => e
			print_status("Unknown error: #{e.class} #{e}")
		ensure
			disconnect_snmp
		end
	end

	def truncate_to_twidth(string,twidth)
		string.slice(0..twidth-2)
	end

	def number_to_human_size(size,unit)
		size = size.first.to_i * unit.first.to_i

		if size < 1024
			"#{size} bytes"
		elsif size < 1024.0 * 1024.0
			"%.02f KB" % (size / 1024.0)
		elsif size < 1024.0 * 1024.0 * 1024.0
			"%.02f MB" % (size / 1024.0 / 1024.0)
		else
			"%.02f GB" % (size / 1024.0 / 1024.0 / 1024.0)
		end
	end
end
