天天看點

lua判斷字元不為空或空格_Lua - 空值判斷的幾種情況

【摘要】 在安全領域,lua程式設計語言因為其小巧在衆多工具上都作為插件開發語言,常見的有openresty,nmap等。是以筆者将會開辟一個Lua相關的系列文章,主要記錄工作過程中一些領悟或者是一些踩過的坑,希望能夠借此平台幫助到讀者們。

0x00 背景

最近在寫一段nginx+redis的代碼,主要基于openresty,其中使用到了lua-resty-redis庫。我平時寫代碼都比較小心,針對外部輸入的值一般都會進行異常判斷,大概的代碼如下:

local redis = require "redis"

local cjson = require "cjson"

--[[省略部分代碼]]

local ok, err = redis:get("key")

if not ok then

ngx.log(ngx.ERR, '[ERROR]:', err)

return

end

local data = cjson.decode(ok)

在decode這裡出現了錯誤提示,但是ok并沒有為空或者nil,不然代碼是走不到這裡來。

發現問題後,我們就在前面列印一下ok資料的類型吧,大概的代碼如下:

ngx.log(ngx.ERR, 'ok type: ', type(ok))

if not ok then

-- TODO

end

這個時候我們得到的結果是userdata,這個東西算是一種複雜結構體,一般都是跨語言産生的,比如ffi.C這些。當時我的思路大概也是這樣,肯定redis存放的資料是二進制的,但是呀,存放什麼資料都是我自己控制的,不可能有什麼畸形資料,是以這一點也排除了。最後在自己檢視中發現,其實就是這個key不存在。

0x01 分析

既然原因找到了,我們就去看看為什麼會這樣,主要通過閱讀lua-resty-redis的源碼:

local function _read_reply(self, sock)

local line, err = sock:receive()

if not line then

if err == "timeout" and not rawget(self, "_subscribed") then

sock:close()

end

return nil, err

end

local prefix = byte(line)

if prefix == 36 then -- char '$'

-- print("bulk reply")

local size = tonumber(sub(line, 2))

if size < 0 then

return null

end

local data, err = sock:receive(size)

if not data then

if err == "timeout" then

sock:close()

end

return nil, err

end

local dummy, err = sock:receive(2) -- ignore CRLF

if not dummy then

return nil, err

end

return data

elseif prefix == 43 then -- char '+'

-- print("status reply")

return sub(line, 2)

elseif prefix == 42 then -- char '*'

local n = tonumber(sub(line, 2))

-- print("multi-bulk reply: ", n)

if n < 0 then

return null

end

local vals = new_tab(n, 0)

local nvals = 0

for i = 1, n do

local res, err = _read_reply(self, sock)

if res then

nvals = nvals + 1

vals[nvals] = res

elseif res == nil then

return nil, err

else

-- be a valid redis error value

nvals = nvals + 1

vals[nvals] = {false, err}

end

end

return vals

elseif prefix == 58 then -- char ':'

-- print("integer reply")

return tonumber(sub(line, 2))

elseif prefix == 45 then -- char '-'

-- print("error reply: ", n)

return false, sub(line, 2)

else

-- when `line` is an empty string, `prefix` will be equal to nil.

return nil, "unknown prefix: \"" .. tostring(prefix) .. "\""

end

end

從上面的源碼可以看到,在讀取redis伺服器傳回資料的時候,如果某些格式不正确,比如資料長度的位元組小于0這樣的異常情況,函數就會傳回null,注意是null不是nil。

這個null的定義來自ngx.null,這個東西可以追溯到其官方文檔lua-nginx-module.

The ngx.null constant is a NULL light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library’s cjson.null constant.

從上面描述看,ngx.null就是一個代表null的userdata結構,類似一個自定義的類,但是沒有什麼具體含義,同時文檔裡面也提到了類似的值還有cjson.null,以後小心被坑。

0x02 擴充

同時文檔中還提到了,使用ngx.log對幾個空值進行字元串列印的時候

nil會顯示成“nil”,

邏輯值會顯示成“true”或者“false”,

ngx.null會被顯示成“null”。