--- nselib/snmp.lua	2011-12-25 21:43:57.000000000 +0100
+++ /Users/patrik/Downloads/scripts/snmp.lua	2011-12-07 17:54:18.000000000 +0100
@@ -32,7 +32,7 @@
 		return bin.pack("HC4", '40 04', unpack(val))
  	
     -- counter or gauge or timeticks or opaque
-	elseif (val._snmp == '41' or val._snmp == '42' or val._snmp == '43' or val._snmp == '44') then
+	elseif (tonumber(val._snmp) and tonumber(val._snmp) >= 41) then
 		local val = self:encodeInt(val[1])
 		return bin.pack("HAA", val._snmp, self.encodeLength(#val), val)
 	end
@@ -59,7 +59,6 @@
 	local encoder = asn1.ASN1Encoder:new()
 	encoder:registerTagEncoders( tagEncoder )
    	
-
 	local encVal = encoder:encode(val)
 		
 	if encVal then
@@ -72,10 +71,6 @@
 -- SNMP ASN.1 Decoders
 local tagDecoder = {}
 
--- Application specific tags
---
--- IP Address
-
 -- Response-PDU
 -- TOOD: Figure out how to remove these dependancies
 tagDecoder["A2"] = function( self, encStr, elen, pos )
@@ -85,7 +80,6 @@
    seq._snmp = "A2"
    return pos, seq
 end
-
 tagDecoder["40"] = function( self, encStr, elen, pos )
 	local ip = {}
 	pos, ip[1], ip[2], ip[3], ip[4] = bin.unpack("C4", encStr, pos)
@@ -103,7 +97,7 @@
 function decode(encStr, pos)
 	local decoder = asn1.ASN1Decoder:new()
 	
-	if ( #tagDecoder == 0 ) then
+   if #tagDecoder == 0 then
 		decoder:registerBaseDecoders()
 		-- Application specific tags
 		-- tagDecoder["40"] = decoder.decoder["06"]  -- IP Address; same as OID
@@ -125,8 +119,6 @@
 		tagDecoder["A6"] = decoder.decoder["30"]  -- InformRequest-PDU (not implemented here yet)
 		tagDecoder["A7"] = decoder.decoder["30"]  -- SNMPv2-Trap-PDU (not implemented here yet)
 	end
-	
-	
 	decoder:registerTagDecoders( tagDecoder )
 
    return decoder:decode( encStr, pos )
@@ -200,7 +192,6 @@
    return req
 end
 
-
 --- 
 -- Create an SNMP Get Next Request PDU.
 -- @param options A table containing the following fields:
@@ -236,6 +227,40 @@
 end
 
 --- 
+-- Create an SNMP Get Bulk Request PDU.
+-- @param options A table containing the following fields:
+-- * <code>"reqId"</code>: Request ID.
+-- * <code>"nonRep"</code>: Non-repeaters.
+-- * <code>"maxRep"</code>: Max Repetitions.
+-- @param ... Object identifiers to be queried.
+-- @return Table representing PDU.
+function buildGetBulkRequest(options, ...)
+   if not options then options = {} end
+
+   if not options.reqId  then options.reqId = math.mod(nmap.clock_ms(), 65000) end
+   if not options.nonRep then options.nonRep = 0 end
+   if not options.maxRep then options.maxRep = 50 end
+
+   local req = {}
+   req._snmp = 'A5'
+   req[1] = options.reqId
+   req[2] = options.nonRep
+   req[3] = options.maxRep
+   
+   local payload = {}
+   for i=1, select('#', ...) do
+      payload[i] = {}
+      payload[i][1] = select(i, ...)
+      if type(payload[i][1]) == "string" then
+        payload[i][1] = str2oid(payload[i][1])
+      end
+      payload[i][2] = false
+   end
+   req[4] = payload
+   return req
+end
+
+--- 
 -- Create an SNMP Set Request PDU.
 --
 -- Takes one OID/value pair or an already prepared table.
@@ -407,30 +432,15 @@
       return {}
    end
 
-   local varBind
-   if (resp._snmp and resp._snmp == 'A2') then
-      varBind = resp[4]
-   elseif (resp[3]._snmp and resp[3]._snmp == 'A2') then
-      varBind = resp[3][4]
-   end
+   local varBind = resp[4]
+   if (resp[3]._snmp and resp[3]._snmp == 'A2') then varBind = resp[3][4] end
 
    if (varBind and type(varBind) == "table") then
       local result = {}
       for k, v in ipairs(varBind) do
 	 local val = v[2]
-	 if (type(v[2]) == "table") then
-	    if (v[2]._snmp == '40') then
-	       val = v[2][1] .. '.' .. v[2][2] .. '.' .. v[2][3] .. '.' .. v[2][4]
-	    elseif (v[2]._snmp == '41') then
-	       val = v[2][1]
-	    elseif (v[2]._snmp == '42') then
-	       val = v[2][1]
-	    elseif (v[2]._snmp == '43') then
-	       val = v[2][1]
-	    elseif (v[2]._snmp == '44') then
-	       val = v[2][1]
-	    end
-	 end
+         if (type(v[2]) == "table") then val = oid2str(v[2]) end
+         
 	 table.insert(result, {val, oid2str(v[1]), v[1]})
       end
       return result
@@ -466,10 +476,12 @@
 	local oid = base_oid
 	local status, err, payload
 	
+   snmp_table.baseoid = base_oid
+   
 	while ( true ) do
 		
 		local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
-		options.reqId = 28428 -- unnecessary?
+      -- TODO: Use getBulkRequest
 		payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
 
 		status, err = socket:send(payload)
@@ -480,8 +492,11 @@
 		
 		status, response = socket:receive_bytes(1) 
 		if ( not( status ) ) then
-			-- Unless we don't have a usefull error message, don't report it
-			if ( response ~= "ERROR" ) then
+         if     ( response == "TIMEOUT" and #snmp_table ) then  -- Might have received some data, but the socket timed out
+            stdnse.print_debug("snmp.snmpWalk: Partial results (received %d rows), but socket timed out", tostring(#snmp_table))
+            return true, snmp_table
+            
+         elseif ( response ~= "ERROR" ) then  -- Unless we don't have a useful error message, don't report it         
 				stdnse.print_debug("snmp.snmpWalk: Received no answer (%s)", response)
 				return false, response
 			end
@@ -504,8 +519,6 @@
 		
 	end
 
-	snmp_table.baseoid = base_oid
-
 	return true, snmp_table
 	
 end
