| 1 |
module(..., package.seeall) |
|---|
| 2 |
|
|---|
| 3 |
function onload(image) |
|---|
| 4 |
image.xml_description([=[ |
|---|
| 5 |
<module> |
|---|
| 6 |
<name>smtp</name> |
|---|
| 7 |
<description><para>Send an email via an SMTP server.</para></description> |
|---|
| 8 |
<loader>lua</loader> |
|---|
| 9 |
<object>noit.module.smtp</object> |
|---|
| 10 |
<moduleconfig /> |
|---|
| 11 |
<checkconfig> |
|---|
| 12 |
<parameter name="port" required="optional" default="25" |
|---|
| 13 |
allowed="\d+">Specifies the TCP port to connect to.</parameter> |
|---|
| 14 |
<parameter name="ehlo" required="optional" default="noit.local" |
|---|
| 15 |
allowed="\d+">Specifies the EHLO parameter.</parameter> |
|---|
| 16 |
<parameter name="from" required="optional" default="" |
|---|
| 17 |
allowed="\d+">Specifies the envelope sender.</parameter> |
|---|
| 18 |
<parameter name="to" required="required" |
|---|
| 19 |
allowed="\d+">Specifies the envelope recipient.</parameter> |
|---|
| 20 |
<parameter name="payload" required="optional" default="Subject: Testing" |
|---|
| 21 |
allowed="\d+">Specifies the payload sent (on the wire). CR LF DOT CR LF is appended automatically.</parameter> |
|---|
| 22 |
</checkconfig> |
|---|
| 23 |
<examples> |
|---|
| 24 |
<example> |
|---|
| 25 |
<title>Send an email to test SMTP service.</title> |
|---|
| 26 |
<para>The following example sends an email via 10.80.117.6 from test@omniti.com to devnull@omniti.com</para> |
|---|
| 27 |
<programlisting><![CDATA[ |
|---|
| 28 |
<noit> |
|---|
| 29 |
<modules> |
|---|
| 30 |
<loader image="lua" name="lua"> |
|---|
| 31 |
<config><directory>/opt/reconnoiter/libexec/modules-lua/?.lua</directory></config> |
|---|
| 32 |
</loader> |
|---|
| 33 |
<module loader="lua" name="smtp" object="noit.module.smtp"/> |
|---|
| 34 |
</modules> |
|---|
| 35 |
<checks> |
|---|
| 36 |
<check uuid="2d42adbc-7c7a-11dd-a48f-4f59e0b654d3" module="smtp" target="10.80.117.6"> |
|---|
| 37 |
<config> |
|---|
| 38 |
<from>test@omniti.com</from> |
|---|
| 39 |
<to>devnull@omniti.com</to> |
|---|
| 40 |
</config> |
|---|
| 41 |
</check> |
|---|
| 42 |
</checks> |
|---|
| 43 |
</noit> |
|---|
| 44 |
]]></programlisting> |
|---|
| 45 |
</example> |
|---|
| 46 |
</examples> |
|---|
| 47 |
</module> |
|---|
| 48 |
]=]); |
|---|
| 49 |
return 0 |
|---|
| 50 |
end |
|---|
| 51 |
|
|---|
| 52 |
function init(module) |
|---|
| 53 |
return 0 |
|---|
| 54 |
end |
|---|
| 55 |
|
|---|
| 56 |
function config(module, options) |
|---|
| 57 |
return 0 |
|---|
| 58 |
end |
|---|
| 59 |
|
|---|
| 60 |
local function read_cmd(e) |
|---|
| 61 |
local final_status, out |
|---|
| 62 |
final_status, out = 0, "" |
|---|
| 63 |
repeat |
|---|
| 64 |
local str = e.read("\r\n") |
|---|
| 65 |
local status, c, message = string.match(str, "^(%d+)([-%s])(.+)$") |
|---|
| 66 |
if not status then |
|---|
| 67 |
return 421, "[internal error]" |
|---|
| 68 |
end |
|---|
| 69 |
final_status = status |
|---|
| 70 |
if string.len(out) > 0 then |
|---|
| 71 |
out = string.format( "%s %s", out, message) |
|---|
| 72 |
else |
|---|
| 73 |
out = message |
|---|
| 74 |
end |
|---|
| 75 |
until c ~= "-" |
|---|
| 76 |
return (final_status+0), out |
|---|
| 77 |
end |
|---|
| 78 |
|
|---|
| 79 |
local function write_cmd(e, cmd) |
|---|
| 80 |
e.write(cmd); |
|---|
| 81 |
e.write("\r\n"); |
|---|
| 82 |
end |
|---|
| 83 |
|
|---|
| 84 |
local function mkaction(e, check) |
|---|
| 85 |
return function (phase, tosend, expected_code) |
|---|
| 86 |
local start_time = noit.timeval.now() |
|---|
| 87 |
local success = true |
|---|
| 88 |
if tosend then |
|---|
| 89 |
write_cmd(e, tosend) |
|---|
| 90 |
end |
|---|
| 91 |
local actual_code, message = read_cmd(e) |
|---|
| 92 |
if expected_code ~= actual_code then |
|---|
| 93 |
check.status(string.format("%d/%d %s", expected_code, actual_code, message)) |
|---|
| 94 |
check.bad() |
|---|
| 95 |
success = false |
|---|
| 96 |
else |
|---|
| 97 |
check.available() |
|---|
| 98 |
end |
|---|
| 99 |
local elapsed = noit.timeval.now() - start_time |
|---|
| 100 |
local elapsed_ms = math.floor(tostring(elapsed) * 1000) |
|---|
| 101 |
check.metric(phase .. "_time", elapsed_ms) |
|---|
| 102 |
return success |
|---|
| 103 |
end |
|---|
| 104 |
end |
|---|
| 105 |
|
|---|
| 106 |
function initiate(module, check) |
|---|
| 107 |
local e = noit.socket() |
|---|
| 108 |
local rv, err = e.connect(check.target, check.config.port or 25) |
|---|
| 109 |
check.unavailable() |
|---|
| 110 |
|
|---|
| 111 |
if rv ~= 0 then |
|---|
| 112 |
check.bad() |
|---|
| 113 |
check.status(err or message or "no connection") |
|---|
| 114 |
return |
|---|
| 115 |
end |
|---|
| 116 |
|
|---|
| 117 |
local ehlo = string.format("EHLO %s", check.config.ehlo or "noit.local") |
|---|
| 118 |
local mailfrom = string.format("MAIL FROM:<%s>", check.config.from or "") |
|---|
| 119 |
local rcptto = string.format("RCPT TO:<%s>", check.config.to) |
|---|
| 120 |
local payload = check.config.payload or "Subject: Test\n\nHello." |
|---|
| 121 |
payload = payload:gsub("\n", "\r\n") |
|---|
| 122 |
local action = mkaction(e, check) |
|---|
| 123 |
if action("banner", nil, 220) |
|---|
| 124 |
and action("ehlo", ehlo, 250) |
|---|
| 125 |
and action("mailfrom", mailfrom, 250) |
|---|
| 126 |
and action("rcptto", rcptto, 250) |
|---|
| 127 |
and action("data", "DATA", 354) |
|---|
| 128 |
and action("body", payload .. "\r\n.", 250) |
|---|
| 129 |
and action("quit", "QUIT", 221) |
|---|
| 130 |
then |
|---|
| 131 |
check.status("mail sent") |
|---|
| 132 |
check.good() |
|---|
| 133 |
end |
|---|
| 134 |
end |
|---|
| 135 |
|
|---|