└── scan_file.lua /scan_file.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | dec to 32 bytes hex 4 | 13 to 0000000d 5 | ]] 6 | local function decimalToHex(num) 7 | if num == 0 then 8 | return -1 9 | end 10 | local neg = false 11 | if num < 0 then 12 | neg = true 13 | num = num * -1 14 | end 15 | 16 | local hexstr = "0123456789ABCDEF" 17 | local result = "" 18 | local len = 0 19 | 20 | while num > 0 do 21 | local n = num % 16 22 | len = len + 1 23 | result = string.sub(hexstr, n + 1, n + 1) .. result 24 | num = math.floor(num / 16) 25 | end 26 | 27 | for i=len+1, 8, 1 do 28 | result = "0"..result 29 | end 30 | 31 | return result 32 | end 33 | 34 | 35 | 36 | --[[ 37 | send file to clamav 38 | @param retuen 1 virus found 39 | @param return 0 virus not found 40 | ]] 41 | local function check_file(data) 42 | 43 | lenHexStr = decimalToHex(string.len(data)) 44 | lenStr = "" 45 | for i = 1, string.len(lenHexStr), 2 do 46 | lenStr = lenStr .. string.char("0x"..string.sub(lenHexStr, i, i+1)) 47 | end 48 | 49 | local host, port = "127.0.0.1", 3310 50 | local socket = require("socket") 51 | local tcp = assert(socket.tcp()) 52 | 53 | tcp:connect(host, port); 54 | tcp:send("zINSTREAM\0"); 55 | tcp:send(lenStr); 56 | tcp:send(data); 57 | 58 | tcp:send(string.char(0x00).. string.char(0x00).. string.char(0x00).. string.char( 0x00 )); 59 | -- tcp:send("PING") 60 | while true do 61 | local s, status, partial = tcp:receive() 62 | 63 | tcp:close() 64 | ngx.say(partial) 65 | if string.sub(partial, 0, 10) == "stream: OK" then 66 | return 0 67 | end 68 | -- virus 69 | return 1 70 | --if status == "closed" then 71 | -- return 0 72 | -- return 0 73 | --end 74 | 75 | end 76 | 77 | -- ::close_tcp:: 78 | -- tcp:close() 79 | end 80 | 81 | 82 | 83 | 84 | --[[ 85 | proces file from buffer 86 | ]] 87 | local function process_file (file) 88 | -- check if is file? 89 | is_file = 0 90 | for line in file:gmatch("([^\n]*)\n?") do 91 | if ( string.len(line) >= string.len("Content-Type:") 92 | and "Content-Type:" == string.sub(line, 0, string.len("Content-Type:") )) then 93 | 94 | is_file = 1 95 | break 96 | 97 | end 98 | end 99 | 100 | 101 | if (is_file == 0 ) then 102 | ngx.say("data is not file") 103 | ngx.say(line) 104 | goto continue 105 | end 106 | 107 | -- get body 108 | idx = 0 109 | is_file = 0 110 | for line in file:gmatch("([^\n]*)\n?") do 111 | idx = idx + string.len(line) + 1 112 | if (string.byte(line) == 13 ) then 113 | is_file = 1 114 | break 115 | end 116 | end 117 | 118 | if (is_file == 0 ) then 119 | ngx.say("wrong form-data format [0x0d]") 120 | goto continue 121 | end 122 | 123 | -- data for clamav 124 | file = string.sub(file, idx + 1, string.len(file) - 1) 125 | 126 | if ( check_file(file) == 1 ) then 127 | return 1 128 | end 129 | 130 | ::continue:: 131 | return 0 132 | end 133 | 134 | 135 | local function process_from_buffer(data) 136 | 137 | local boundary = "" 138 | local boundary_end = "" 139 | local flag = 0 140 | local i = 0 141 | local idxs = {} 142 | local idx = 0 143 | local format = 0 144 | 145 | -- split by line 146 | -- search boundary 147 | for line in data:gmatch("([^\n]*)\n?") do 148 | idx = idx + string.len(line) + 1 -- for \n 149 | 150 | if ( flag == 0 ) then 151 | boundary = string.sub(line, 1, string.len(line) -1 ) 152 | boundary_end= string.sub(line, 1, string.len(line) -1) .. "-" .. "-" 153 | flag = 1 154 | goto update_indexes 155 | end 156 | 157 | if ( string.len(line) >= string.len(boundary_end) and string.match(line, boundary_end) ) then 158 | -- format is corect? 159 | format = 1 160 | 161 | goto update_indexes 162 | end 163 | 164 | if ( string.len(line) >= string.len(boundary) and string.match(line, boundary) ) then 165 | goto update_indexes 166 | end 167 | 168 | goto continue 169 | 170 | ::update_indexes:: 171 | idxs[i] = idx 172 | i = i +1 173 | 174 | 175 | ::continue:: 176 | end 177 | 178 | if format == 0 then 179 | ngx.say ("not form-data format") 180 | return 0 181 | end 182 | 183 | -- extract files 184 | local file = "" 185 | local is_file = 0 186 | for j=0, i-2, 1 do 187 | ngx.say(idxs[j]) 188 | if (j == i - 2) then 189 | file = string.sub(data, idxs[j], idxs[j+1] - string.len(boundary) - 5 ) 190 | else 191 | file = string.sub(data, idxs[j], idxs[j+1] - string.len(boundary) - 3 ) 192 | end 193 | 194 | if ( process_file(file) == 1) then 195 | return 1 196 | end 197 | 198 | end 199 | 200 | return 0 201 | 202 | end 203 | 204 | 205 | 206 | --[[ 207 | -- explicitly read the req body 208 | ]] 209 | ngx.req.read_body() 210 | 211 | local data = ngx.req.get_body_data() 212 | 213 | if data then 214 | -- if file in buffer 215 | if ( process_from_buffer(data) == 1) then 216 | -- virus found 217 | ngx.say("virus") 218 | else 219 | -- virus not found 220 | ngx.say("non-virus") 221 | end 222 | else 223 | -- body may get buffered in a temp file: 224 | -- > client_body_buffer_size 225 | ngx.say("file is to large") 226 | end 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | --------------------------------------------------------------------------------