diff --git a/app/dalton.py b/app/dalton.py index 33ffdff..17cace0 100644 --- a/app/dalton.py +++ b/app/dalton.py @@ -2636,6 +2636,7 @@ def page_coverage_summary(): "lua": { "enabled": True, "scripts-dir": "/opt/dalton-agent", + "path": "/opt/dalton-agent/?.lua", "scripts": ["http.lua", "tls.lua", "dns.lua"], } } diff --git a/dalton-agent/Dockerfiles/Dockerfile_suricata b/dalton-agent/Dockerfiles/Dockerfile_suricata index f0c973a..c5dc871 100644 --- a/dalton-agent/Dockerfiles/Dockerfile_suricata +++ b/dalton-agent/Dockerfiles/Dockerfile_suricata @@ -51,6 +51,7 @@ WORKDIR /opt/dalton-agent COPY dalton-agent.py /opt/dalton-agent/dalton-agent.py COPY dalton-agent.conf /opt/dalton-agent/dalton-agent.conf +COPY dalton-suricata.lua /opt/dalton-agent/dalton-suricata.lua COPY http.lua /opt/dalton-agent/http.lua COPY dns.lua /opt/dalton-agent/dns.lua COPY tls.lua /opt/dalton-agent/tls.lua diff --git a/dalton-agent/dalton-suricata.lua b/dalton-agent/dalton-suricata.lua new file mode 100644 index 0000000..53c8d3f --- /dev/null +++ b/dalton-agent/dalton-suricata.lua @@ -0,0 +1,300 @@ +-- Compatibility helpers for Suricata 7 (legacy globals) and Suricata 8 (suricata.* libraries). +-- Suricata 8: https://docs.suricata.io/en/suricata-8.0.5/output/lua-output.html +-- Suricata 7: https://docs.suricata.io/en/suricata-7.0.2/lua/lua-functions.html + +local M = {} + +local suri8 = false +local config, logger, flow_mod, packet_mod + +do + local ok, cfg = pcall(require, "suricata.config") + if ok then + suri8 = true + config = cfg + logger = require("suricata.log") + flow_mod = require("suricata.flow") + packet_mod = require("suricata.packet") + end +end + +M.suri8 = suri8 + +function M.log_path() + if suri8 then + local path, err = config.log_path() + if path == nil then + M.notice("config.log_path() failed: " .. tostring(err)) + return nil + end + return path + end + return SCLogPath() +end + +function M.is_tx(tx) + return type(tx) == "userdata" +end + +function M.open_log(name) + local path = M.log_path() + if path == nil then + return nil, "no log path" + end + return io.open(path .. "/" .. name, "a") +end + +function M.info(msg) + if suri8 then + logger.info(msg) + else + SCLogInfo(msg) + end +end + +function M.notice(msg) + if suri8 then + logger.notice(msg) + else + SCLogNotice(msg) + end +end + +function M.flow_tuple() + if suri8 then + local f = flow_mod.get() + return f:tuple() + end + return SCFlowTuple() +end + +function M.packet_timestring() + if suri8 then + local p = packet_mod.get() + return p:timestring_legacy() + end + return SCPacketTimeString() +end + +local function table_len(t) + if t == nil then + return 0 + end + local n = 0 + for _ in pairs(t) do + n = n + 1 + end + return n +end + +-- DNS ----------------------------------------------------------------------- + +local dns_mod + +local function dns_tx() + if not dns_mod then + dns_mod = require("suricata.dns") + end + return dns_mod.get_tx() +end + +function M.dns_queries() + if suri8 then + local tx = dns_tx() + if tx == nil then + return nil + end + return tx:queries() + end + return DnsGetQueries() +end + +function M.dns_answers() + if suri8 then + local tx = dns_tx() + if tx == nil then + return nil + end + return tx:answers() + end + return DnsGetAnswers() +end + +function M.dns_authorities() + if suri8 then + local tx = dns_tx() + if tx == nil then + return nil + end + return tx:authorities() + end + return DnsGetAuthorities() +end + +function M.dns_recursion_desired() + if suri8 then + local tx = dns_tx() + if tx == nil then + return false + end + return tx:recursion_desired() == true + end + return DnsGetRecursionDesired() == true +end + +function M.dns_query_count(queries) + if suri8 then + return table_len(queries) + end + if queries == nil then + return 0 + end + if table.maxn then + return table.maxn(queries) + 1 + end + return table_len(queries) +end + +-- HTTP ---------------------------------------------------------------------- + +local http_mod + +local function http_tx() + if not http_mod then + http_mod = require("suricata.http") + end + return http_mod.get_tx() +end + +local function http_body_first(body) + if body == nil then + return nil + end + if type(body) == "table" then + return body[1] + end + return body +end + +function M.http_request_line() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:request_line() + end + return HttpGetRequestLine() +end + +function M.http_request_uri_raw() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:request_uri_raw() + end + return HttpGetRequestUriRaw() +end + +function M.http_request_headers_raw() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:request_headers_raw() + end + return HttpGetRawRequestHeaders() +end + +function M.http_response_headers_raw() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:response_headers_raw() + end + return HttpGetRawResponseHeaders() +end + +function M.http_request_body() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return http_body_first(tx:request_body()) + end + return http_body_first(HttpGetRequestBody()) +end + +function M.http_response_body() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return http_body_first(tx:response_body()) + end + return http_body_first(HttpGetResponseBody()) +end + +function M.http_request_uri_normalized() + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:request_uri_normalized() + end + return HttpGetRequestUriNormalized() +end + +function M.http_request_header(name) + if suri8 then + local tx = http_tx() + if tx == nil then + return nil + end + return tx:request_header(name) + end + return HttpGetRequestHeader(name) +end + +-- TLS ----------------------------------------------------------------------- + +local tls_mod + +local function tls_tx() + if not tls_mod then + tls_mod = require("suricata.tls") + end + return tls_mod.get_tx() +end + +function M.tls_sni() + if suri8 then + local tx = tls_tx() + if tx == nil then + return nil + end + return tx:get_client_sni() + end + return TlsGetSNI() +end + +function M.tls_cert_info() + if suri8 then + local tx = tls_tx() + if tx == nil then + return nil, nil, nil, nil + end + return tx:get_server_cert_info() + end + return TlsGetCertInfo() +end + +return M diff --git a/dalton-agent/dns.lua b/dalton-agent/dns.lua index 5d17a2e..b110a29 100644 --- a/dalton-agent/dns.lua +++ b/dalton-agent/dns.lua @@ -1,69 +1,91 @@ +local dalton +do + local ok, mod = pcall(require, "dalton-suricata") + if ok then + dalton = mod + else + local chunk = loadfile("dalton-suricata.lua") + if chunk == nil then + chunk = loadfile("/opt/dalton-agent/dalton-suricata.lua") + end + if chunk then + dalton = chunk() + end + end +end +if dalton == nil then + error("failed to load dalton-suricata compatibility layer") +end + function init (args) local needs = {} needs["protocol"] = "dns" - needs["filter"] = "alerts" + needs["filter"] = "alerts" return needs end +local LOG_NAME = "dalton-dns-buffers.log" + function setup (args) - filename = SCLogPath() .. "/" .. "dalton-dns-buffers.log" - --filename = "/tmp/dalton-dns-buffers.log" - file, err = io.open(filename, "a") - if file then - SCLogInfo("DNS OPENED Log Filename " .. filename) - else - SCLogNotice("Error opening Lua log:" .. err) + -- File is opened on first log(); see comment in dalton-suricata.lua / agent README. + file = nil + dns_count = 0 +end + +function ensure_file() + if file == nil then + file, err = dalton.open_log(LOG_NAME) + if file then + dalton.info("DNS OPENED Log Filename " .. dalton.log_path() .. "/" .. LOG_NAME) + else + dalton.notice("Error opening Lua log: " .. tostring(err)) + end end - dns = 0 end function HexDump(buf) - local s = {} - s.output = '' - for byte=1, #buf, 16 do - local chunk = buf:sub(byte, byte+15) - s.output = s.output .. string.format('%08X ',byte-1) - chunk:gsub('.', function (c) s.output = s.output .. string.format('%02X ',string.byte(c)) end) - s.output = s.output .. string.rep(' ',3*(16-#chunk)) + if buf == nil or #buf == 0 then + return "" + end + local s = {} + s.output = '' + for byte=1, #buf, 16 do + local chunk = buf:sub(byte, byte+15) + s.output = s.output .. string.format('%08X ',byte-1) + chunk:gsub('.', function (c) s.output = s.output .. string.format('%02X ',string.byte(c)) end) + s.output = s.output .. string.rep(' ',3*(16-#chunk)) s.output = s.output .. string.format("|%-16s|", chunk:gsub('[^\32-\126]','.')) .. "\n" - end - return s.output + end + return s.output end function log(args) - --SCLogNotice("Pulling DNS buffers"); + ensure_file() + + local tx, err = nil, nil + if dalton.suri8 then + tx, err = require("suricata.dns").get_tx() + if not dalton.is_tx(tx) then + dalton.notice("DNS log skipped: " .. tostring(err or tx)) + return + end + end - dns_query = DnsGetQueries(); - dns_answers = DnsGetAnswers(); - dns_auth = DnsGetAuthorities(); - p = SCPacketPayload(); + dns_query = dalton.dns_queries() + dns_answers = dalton.dns_answers() + dns_auth = dalton.dns_authorities() - ip_version, src_ip, dst_ip, protocol, src_port, dst_port = SCFlowTuple() - timestring = SCPacketTimeString() + ip_version, src_ip, dst_ip, protocol, src_port, dst_port = dalton.flow_tuple() + timestring = dalton.packet_timestring() - outstring = "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n" - outstring = outstring .. "Raw Packet:\n" + outstring = "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n" + outstring = outstring .. "Raw Packet:\n" - -- It has BOTH for every packet as request/response are always returned and not nil. - --[[ - if dns_query ~= nil then - outstring = outstring .. "Query Packet\n" - end - if dns_answers ~= nil then - outstring = outstring .. "Response Packet\n" - end--]] - + rcount = 0 if dns_answers ~= nil then - rcount = 0 rstring = "" pairnames = "" - --pairnames = "All response names:\n" for n, t in pairs(dns_answers) do - --[[ - -- to inspect a table with undocumented values - for tname, tval in pairs(t) do - pairnames = pairnames .. tname .. "\n" - end--]] rrname = t["rrname"] rrtype = t["type"] ttl = t["ttl"] @@ -74,18 +96,11 @@ function log(args) end if rcount > 0 then if dns_auth then - --authnames = "All Auth names:\n" for n, t in pairs(dns_auth) do - --[[ - -- inspect all auth value names - for aname, aval in pairs(t) do - authnames = authnames .. aname - end- - outstring = outstring .. authnames .. "\n"-]] rrname = t["rrname"] rrtype = t["type"] ttl = t["ttl"] - + outstring = outstring .. "AUTHORITY: " .. timestring .. " " .. rrname .. " [**] " .. rrtype .. " [**] TTL:" .. ttl .. " [**] " .. src_ip .. ":" .. src_port .. " -> " .. dst_ip .. ":" .. dst_port .. "\n" @@ -93,49 +108,39 @@ function log(args) end outstring = outstring .. timestring .. " " .. dst_ip .. ":" .. dst_port .. " -> " .. src_ip .. ":" .. src_port .. "\n\n" outstring = outstring .. "DNS_RESPONSE\n" .. rstring - - end end - + if (dns_query ~= nil and rcount == 0) then outstring = outstring .. timestring .. " " .. src_ip .. ":" .. src_port .. " -> " .. dst_ip .. ":" .. dst_port .. "\n\n" - outstring = outstring .. "DNS_QUERY_DUMP, " .. tostring(table.maxn(dns_query)+1) .. "\n\n" - + outstring = outstring .. "DNS_QUERY_DUMP, " .. tostring(dalton.dns_query_count(dns_query)) .. "\n\n" - --pairnames = "All request names:\n" for n, t in pairs(dns_query) do - --[[ - for tname, tval in pairs(t) do - pairnames = pairnames .. tname .. "\n" - end--]] rrname = t["rrname"] rrtype = t["type"] outstring = outstring .. pairnames .. "REQUEST:" .. rrname .. ":" .. rrtype .. "\n" - if DnsGetRecursionDesired() == true then + if dalton.dns_recursion_desired() then outstring = outstring .. "Recursion Desired\n" else outstring = outstring .. "No Recursion\n" end - end - end - + end - outstring = outstring .. "\n" - if p then - --outstring = outstring .. HexDump(p) - end - file:write (outstring) - --SCLogNotice("Output to file: ".. outstring) - file:flush() + if file then + file:write (outstring) + file:flush() + end - dns = dns + 1 + dns_count = dns_count + 1 end function deinit (args) - SCLogInfo ("DNS transactions logged: " .. dns); - file:close(file) + dalton.info ("DNS transactions logged: " .. dns_count) + if file then + file:close() + file = nil + end end diff --git a/dalton-agent/http.lua b/dalton-agent/http.lua index b49f364..c92f780 100644 --- a/dalton-agent/http.lua +++ b/dalton-agent/http.lua @@ -1,3 +1,22 @@ +local dalton +do + local ok, mod = pcall(require, "dalton-suricata") + if ok then + dalton = mod + else + local chunk = loadfile("dalton-suricata.lua") + if chunk == nil then + chunk = loadfile("/opt/dalton-agent/dalton-suricata.lua") + end + if chunk then + dalton = chunk() + end + end +end +if dalton == nil then + error("failed to load dalton-suricata compatibility layer") +end + function init (args) local needs = {} needs["protocol"] = "http" @@ -5,18 +24,28 @@ function init (args) return needs end +local LOG_NAME = "dalton-http-buffers.log" + function setup (args) - filename = SCLogPath() .. "/" .. "dalton-http-buffers.log" - file, err = io.open(filename, "a") - if file then - SCLogInfo("HTTP OPENED Log Filename " .. filename) - else - SCLogNotice("Error opening Lua log:" .. err) + file = nil + http_count = 0 +end + +function ensure_file() + if file == nil then + file, err = dalton.open_log(LOG_NAME) + if file then + dalton.info("HTTP OPENED Log Filename " .. dalton.log_path() .. "/" .. LOG_NAME) + else + dalton.notice("Error opening Lua log: " .. tostring(err)) + end end - http = 0 end function HexDump(buf) + if buf == nil or #buf == 0 then + return "" + end local s = {} s.output = '' for byte=1, #buf, 16 do @@ -30,20 +59,29 @@ function HexDump(buf) end function log(args) - SCLogNotice("Pulling HTTP buffers"); + dalton.notice("Pulling HTTP buffers") + ensure_file() + + if dalton.suri8 then + local tx, err = require("suricata.http").get_tx() + if not dalton.is_tx(tx) then + dalton.notice("HTTP log skipped: " .. tostring(err or tx)) + return + end + end - http_req_line = HttpGetRequestLine() - http_raw_uri = HttpGetRequestUriRaw() - http_req_headers = HttpGetRawRequestHeaders() - http_resp_headers = HttpGetRawResponseHeaders() - http_req_body, o, e = HttpGetRequestBody() - http_resp_body, o, e = HttpGetResponseBody() - http_uri = HttpGetRequestUriNormalized() - http_ua = HttpGetRequestHeader("User-Agent") - http_cookie = HttpGetRequestHeader("Cookie") + http_req_line = dalton.http_request_line() + http_raw_uri = dalton.http_request_uri_raw() + http_req_headers = dalton.http_request_headers_raw() + http_resp_headers = dalton.http_response_headers_raw() + http_req_body = dalton.http_request_body() + http_resp_body = dalton.http_response_body() + http_uri = dalton.http_request_uri_normalized() + http_ua = dalton.http_request_header("User-Agent") + http_cookie = dalton.http_request_header("Cookie") - timestring = SCPacketTimeString() - ip_version, src_ip, dst_ip, protocol, src_port, dst_port = SCFlowTuple() + ip_version, src_ip, dst_ip, protocol, src_port, dst_port = dalton.flow_tuple() + timestring = dalton.packet_timestring() outstring = "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n" outstring = outstring .. "Raw Packet:\n" @@ -59,24 +97,24 @@ function log(args) outstring = outstring .. HexDump(http_raw_uri) .. "\n" end - if http_req_headers then + if http_req_headers then outstring = outstring .. "REQ_HEADERS_DUMP, " .. tostring(string.len(http_req_headers)) .. "\n\n" outstring = outstring .. HexDump(http_req_headers) .. "\n" end - if http_resp_headers then + if http_resp_headers then outstring = outstring .. "RESP_HEADERS_DUMP, " .. tostring(string.len(http_resp_headers)) .. "\n\n" outstring = outstring .. HexDump(http_resp_headers) .. "\n" end - if http_req_body then - outstring = outstring .. "REQ_BODY_DUMP, " .. tostring(string.len(http_req_body[1])) .. "\n\n" - outstring = outstring .. HexDump(http_req_body[1]) .. "\n" + if http_req_body then + outstring = outstring .. "REQ_BODY_DUMP, " .. tostring(string.len(http_req_body)) .. "\n\n" + outstring = outstring .. HexDump(http_req_body) .. "\n" end - if http_resp_body then - outstring = outstring .. "RESP_BODY_DUMP, " .. tostring(string.len(http_resp_body[1])) .. "\n\n" - outstring = outstring .. HexDump(http_resp_body[1]) .. "\n" + if http_resp_body then + outstring = outstring .. "RESP_BODY_DUMP, " .. tostring(string.len(http_resp_body)) .. "\n\n" + outstring = outstring .. HexDump(http_resp_body) .. "\n" end if http_uri then @@ -84,24 +122,28 @@ function log(args) outstring = outstring .. HexDump(http_uri) .. "\n" end - if http_ua then + if http_ua then outstring = outstring .. "USER_AGENT_DUMP, " .. tostring(string.len(http_ua)) .. "\n\n" outstring = outstring .. HexDump(http_ua) .. "\n" end - if http_cookie then + if http_cookie then outstring = outstring .. "COOKIE_DUMP, " .. tostring(string.len(http_cookie)) .. "\n\n" outstring = outstring .. HexDump(http_cookie) .. "\n" end - file:write (outstring) - --SCLogNotice("Output to file: ".. outstring) - file:flush() + if file then + file:write (outstring) + file:flush() + end - http = http + 1 + http_count = http_count + 1 end function deinit (args) - SCLogInfo ("HTTP transactions logged: " .. http); - file:close(file) + dalton.info ("HTTP transactions logged: " .. http_count) + if file then + file:close() + file = nil + end end diff --git a/dalton-agent/tls.lua b/dalton-agent/tls.lua index aa40e0f..ff8f372 100644 --- a/dalton-agent/tls.lua +++ b/dalton-agent/tls.lua @@ -1,70 +1,112 @@ +local dalton +do + local ok, mod = pcall(require, "dalton-suricata") + if ok then + dalton = mod + else + local chunk = loadfile("dalton-suricata.lua") + if chunk == nil then + chunk = loadfile("/opt/dalton-agent/dalton-suricata.lua") + end + if chunk then + dalton = chunk() + end + end +end +if dalton == nil then + error("failed to load dalton-suricata compatibility layer") +end + function init (args) local needs = {} needs["protocol"] = "tls" - needs["filter"] = "alerts" + needs["filter"] = "alerts" return needs end +local LOG_NAME = "dalton-tls-buffers.log" + function setup (args) - filename = SCLogPath() .. "/" .. "dalton-tls-buffers.log" - file, err = io.open(filename, "a") - if file then - SCLogInfo("TLS OPENED Log Filename " .. filename) - else - SCLogNotice("Error opening Lua log:" .. err) + file = nil + tls_count = 0 +end + +function ensure_file() + if file == nil then + file, err = dalton.open_log(LOG_NAME) + if file then + dalton.info("TLS OPENED Log Filename " .. dalton.log_path() .. "/" .. LOG_NAME) + else + dalton.notice("Error opening Lua log: " .. tostring(err)) + end end - tls = 0 end function HexDump(buf) - local s = {} - s.output = '' - for byte=1, #buf, 16 do - local chunk = buf:sub(byte, byte+15) - s.output = s.output .. string.format('%08X ',byte-1) - chunk:gsub('.', function (c) s.output = s.output .. string.format('%02X ',string.byte(c)) end) - s.output = s.output .. string.rep(' ',3*(16-#chunk)) + if buf == nil or #buf == 0 then + return "" + end + local s = {} + s.output = '' + for byte=1, #buf, 16 do + local chunk = buf:sub(byte, byte+15) + s.output = s.output .. string.format('%08X ',byte-1) + chunk:gsub('.', function (c) s.output = s.output .. string.format('%02X ',string.byte(c)) end) + s.output = s.output .. string.rep(' ',3*(16-#chunk)) s.output = s.output .. string.format("|%-16s|", chunk:gsub('[^\32-\126]','.')) .. "\n" - end - return s.output + end + return s.output end function log(args) - SCLogNotice("Pulling TLS buffers"); + dalton.notice("Pulling TLS buffers") + ensure_file() - tls_sni = TlsGetSNI() - tls_version, tls_subject, tls_issuer, tls_fingerprint = TlsGetCertInfo() + if dalton.suri8 then + local tx, err = require("suricata.tls").get_tx() + if not dalton.is_tx(tx) then + dalton.notice("TLS log skipped: " .. tostring(err or tx)) + return + end + end - timestring = SCPacketTimeString() - ip_version, src_ip, dst_ip, protocol, src_port, dst_port = SCFlowTuple() + tls_sni = dalton.tls_sni() + tls_version, tls_subject, tls_issuer, tls_fingerprint = dalton.tls_cert_info() - outstring = "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n" - outstring = outstring .. "Raw Packet:\n" - outstring = outstring .. timestring .. " " .. src_ip .. ":" .. src_port .. " -> " .. dst_ip .. ":" .. dst_port .. "\n\n" + ip_version, src_ip, dst_ip, protocol, src_port, dst_port = dalton.flow_tuple() + timestring = dalton.packet_timestring() - if tls_sni then - outstring = outstring .. "TLS_SNI_DUMP, " .. tostring(string.len(tls_sni)) .. "\n\n" - outstring = outstring .. HexDump(tls_sni) .. "\n" - end + outstring = "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n" + outstring = outstring .. "Raw Packet:\n" + outstring = outstring .. timestring .. " " .. src_ip .. ":" .. src_port .. " -> " .. dst_ip .. ":" .. dst_port .. "\n\n" - if tls_issuer then - outstring = outstring .. "TLS_ISSUER_DUMP, " .. tostring(string.len(tls_issuer)) .. "\n\n" - outstring = outstring .. HexDump(tls_issuer) .. "\n" - end + if tls_sni then + outstring = outstring .. "TLS_SNI_DUMP, " .. tostring(string.len(tls_sni)) .. "\n\n" + outstring = outstring .. HexDump(tls_sni) .. "\n" + end + + if tls_issuer then + outstring = outstring .. "TLS_ISSUER_DUMP, " .. tostring(string.len(tls_issuer)) .. "\n\n" + outstring = outstring .. HexDump(tls_issuer) .. "\n" + end - if tls_subject then - outstring = outstring .. "TLS_SUBJECT_DUMP, " .. tostring(string.len(tls_subject)) .. "\n\n" - outstring = outstring .. HexDump(tls_subject) .. "\n" - end + if tls_subject then + outstring = outstring .. "TLS_SUBJECT_DUMP, " .. tostring(string.len(tls_subject)) .. "\n\n" + outstring = outstring .. HexDump(tls_subject) .. "\n" + end - file:write (outstring) - --SCLogNotice("Output to file: ".. outstring) - file:flush() + if file then + file:write (outstring) + file:flush() + end - tls = tls + 1 + tls_count = tls_count + 1 end function deinit (args) - SCLogInfo ("TLS transactions logged: " .. tls); - file:close(file) + dalton.info ("TLS transactions logged: " .. tls_count) + if file then + file:close() + file = nil + end end