This article proposes a simple function which use HAProxy cosocket for sending email throught SMTP.
First the main function. This function works only when it is executed inside HAProxy because it uses HAProxy cosocket. The implementation of the SMTP protocol is very limited, this function just send emails. It not support SSL, Authentication, StartTLS nor other SMTP functions. It is designed for communicating with a local or trusted SMTP server.
-- smtp : send SMTP message
--
-- Copyright 2018 Thierry Fournier
--
-- This lib is compliant with HAProxy cosockets
--
function smtp_send_email(server, port, domain, from, to, data)
local ret
local reason
local tcp = core.tcp()
local smtp_wait_code = function(tcp, code)
local ret
-- Read headers until we reac a 2.. code.
while true do
-- read line
ret = tcp:receive("*l")
if ret == nil then
return false, "Connection unexpectly closed"
end
-- expected code
if string.match(ret, code) ~= nil then
return true, nil
end
-- other code
if string.match(ret, '^%d%d%d ') ~= nil then
return false, ret
end
-- other informational message, wait.
end
end
if tcp:connect(server, port) == nil then
return false, "Can't connect to \""..server..":"..port.."\""
end
ret, reason = smtp_wait_code(tcp, '^220 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send("EHLO " .. domain .. "\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^250 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send("MAIL FROM: <" .. from .. ">\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^250 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send("RCPT TO: <" .. to .. ">\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^250 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send("DATA\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^354 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send(data .. "\r\n.\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^250 ')
if ret == false then
tcp:close()
return false, reason
end
if tcp:send("QUIT\r\n") == nil then
tcp:close()
return false, "Connection unexpectly closed"
end
ret, reason = smtp_wait_code(tcp, '^221 ')
if ret == false then
tcp:close()
return false, reason
end
tcp:close()
return true, nil
end
And now an example of the usage. The use case is simple: we want to send email on HAProxy event throught an action. If the sending fails, send a log. We register a new Lua action.
core.register_action("send_email", { "tcp-req", "http-req", "tcp-res", "http-res" }, function(txn)
local ret
local reason
local server = "127.0.0.1"
local port = 25
local domain = "arpalert.org"
local from = "haproxy-node1@arpalert.org"
local to = "admin@arpalert.org"
local msg = "From: " .. from .. "\r\n" ..
"To: " .. to .. "\r\n" ..
"Subject: test - " .. os.date() .. "\r\n" ..
"\r\n" ..
"test - " .. os.date() .. "\r\n"
ret, reason = smtp_send_email(server, port, domain, from, to, msg);
if ret == false then
txn:Warning("Can't send email: " .. reason)
end
end)
And the asoiated HAProxy configuration example:
global
lua-load samples.lua
stats socket /tmp/haproxy.sock mode 644 level admin
tune.ssl.default-dh-param 2048
defaults
timeout client 1m
timeout server 1m
listen sample3
mode http
bind *:10030
http-request lua.send_email
http-request redirect location /ok
No comments:
Post a Comment