Skip to content

Commit efc1618

Browse files
committed
client: handling SMTP errors from server
In case server has some settings, which don't let some types of SMTP request to be handled, SMTP client may receive 5xx, 4xx, 3xx and malformed codes. This patch fixes the problem, when on the client side this SMTP codes are not handled normally. Instead of runtime error and unusual message that there is some connection error. Fixes #13
1 parent e17aad3 commit efc1618

File tree

3 files changed

+93
-6
lines changed

3 files changed

+93
-6
lines changed

smtp/smtpc.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ smtpc_request_new(struct smtpc_env *env, const char *url, const char *from)
241241
return NULL;
242242
}
243243
memset(req, 0, sizeof(*req));
244+
req->error = (char *) malloc(CURL_ERROR_SIZE + sizeof(" (SMTP error)"));
245+
if (req->error == NULL) {
246+
free(req);
247+
box_error_set(__FILE__, __LINE__, ER_MEMORY_ISSUE,
248+
"Can't alloc error message buffer");
249+
return NULL;
250+
}
251+
memset(req, 0, sizeof(*req->error));
244252
req->env = env;
245253

246254
req->easy = curl_easy_init();
@@ -252,6 +260,7 @@ smtpc_request_new(struct smtpc_env *env, const char *url, const char *from)
252260
}
253261
curl_easy_setopt(req->easy, CURLOPT_URL, url);
254262
curl_easy_setopt(req->easy, CURLOPT_MAIL_FROM, from);
263+
curl_easy_setopt(req->easy, CURLOPT_ERRORBUFFER, req->error);
255264

256265
return req;
257266
}
@@ -270,6 +279,8 @@ smtpc_request_delete(struct smtpc_request *req)
270279
if (req->easy != NULL)
271280
coio_call(smtpc_task_delete, req);
272281
free(req->body);
282+
if (req->error != NULL)
283+
free(req->error);
273284
if (req->recipients)
274285
curl_slist_free_all(req->recipients);
275286

@@ -380,6 +391,7 @@ smtpc_execute(struct smtpc_request *req, double timeout)
380391
{
381392
struct smtpc_env *env = req->env;
382393

394+
curl_easy_setopt(req->easy, CURLOPT_ERRORBUFFER, req->error);
383395
curl_easy_setopt(req->easy, CURLOPT_PRIVATE,
384396
(void *) &req);
385397

@@ -440,10 +452,21 @@ smtpc_execute(struct smtpc_request *req, double timeout)
440452
++env->stat.failed_requests;
441453
smtpc_request_delete(req);
442454
return -1;
455+
case CURLE_SEND_ERROR:
456+
case CURLE_RECV_ERROR:
457+
curl_easy_getinfo(req->easy, CURLINFO_RESPONSE_CODE, &longval);
458+
req->status = (int) longval;
459+
if (strcmp(req->error, "response reading failed") == 0) {
460+
req->status = -1;
461+
}
462+
strcat(req->error, " (SMTP error)");
463+
req->reason = req->error;
464+
++env->stat.failed_requests;
465+
break;
443466
default: {
444467
char error_msg[256];
445468
curl_easy_getinfo(req->easy, CURLINFO_OS_ERRNO, &longval);
446-
snprintf(error_msg, sizeof(error_msg), "SMTP error %i (os errno %li)", req->code, longval);
469+
snprintf(error_msg, sizeof(error_msg), "CURL error %i (os errno %li)", req->code, longval);
447470
box_error_set(__FILE__, __LINE__, ER_UNKNOWN, error_msg);
448471
++env->stat.failed_requests;
449472
smtpc_request_delete(req);

smtp/smtpc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct smtpc_request {
116116
int status;
117117
/** Error message. */
118118
const char *reason;
119+
char *error;
119120
};
120121

121122
/**

test/smtp.test.lua

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,32 @@ local function smtp_h(s)
2626
s:write('250 HELP\r\n')
2727
elseif l:find('MAIL FROM:') then
2828
mail.from = l:sub(11):sub(1, -3)
29-
s:write('250 OK\r\n')
29+
if l:find('3xx') then
30+
s:write('354 Start mail input\r\n')
31+
elseif l:find('4xx') then
32+
s:write('451 Requested action aborted: local error in processing\r\n')
33+
elseif l:find('5xx') then
34+
s:write('510 Bad email address\r\n')
35+
elseif l:find('10xx') then
36+
s:write('1000 UNKNOWN ERROR\r\n')
37+
return
38+
else
39+
s:write('250 OK\r\n')
40+
end
3041
elseif l:find('RCPT TO:') then
3142
mail.rcpt[#mail.rcpt + 1] = l:sub(9):sub(1, -3)
32-
s:write('250 OK\r\n')
43+
if l:find('3xx') then
44+
s:write('354 Start mail input\r\n')
45+
elseif l:find('4xx') then
46+
s:write('421 Service not available, closing transmission channel\r\n')
47+
elseif l:find('5xx') then
48+
s:write('510 Bad email address\r\n')
49+
elseif l:find('10xx') then
50+
s:write('1000 UNKNOWN ERROR\r\n')
51+
return
52+
else
53+
s:write('250 OK\r\n')
54+
end
3355
elseif l == 'DATA\r\n' then
3456
s:write('354 Enter message, ending with "." on a line by itself\r\n')
3557
while true do
@@ -56,7 +78,7 @@ local server = socket.tcp_server('127.0.0.1', 0, smtp_h)
5678
local addr = 'smtp://127.0.0.1:' .. server:name().port
5779

5880
test:test("smtp.client", function(test)
59-
test:plan(10)
81+
test:plan(18)
6082
local r
6183
local m
6284

@@ -143,9 +165,50 @@ test:test("smtp.client", function(test)
143165

144166
m = mails:get()
145167
subj = select(2, string.gsub(
146-
m.text,
147-
"Subject: =%?utf%-8%?b%?YWJjZGVmZ2hpamvRj2xtbm9wcXJzdHV2d3h5eg==%?=", ""))
168+
m.text,
169+
"Subject: =%?utf%-8%?b%?YWJjZGVmZ2hpamvRj2xtbm9wcXJzdHV2d3h5eg==%?=", ""))
148170
test:is(subj, 1, 'subject codes >127')
171+
172+
r = client:request(addr, '[email protected]',
173+
174+
'mail.body')
175+
test:is(r.reason, 'MAIL failed: 354 (SMTP error)', 'Errors 3xx')
176+
177+
r = client:request(addr, '[email protected]',
178+
179+
'mail.body')
180+
test:is(r.reason, 'MAIL failed: 451 (SMTP error)', 'service unavailable')
181+
182+
r = client:request(addr, '[email protected]',
183+
184+
'mail.body')
185+
test:is(r.reason, 'MAIL failed: 510 (SMTP error)', 'unexisting recipient')
186+
187+
r = client:request(addr, '[email protected]',
188+
189+
'mail.body')
190+
test:is(r.reason, 'response reading failed (SMTP error)', 'unexisting recipient')
191+
192+
r = client:request(addr, '[email protected]',
193+
194+
'mail.body')
195+
test:is(r.reason, 'RCPT failed: 354 (SMTP error)', 'Errors 3xx')
196+
197+
r = client:request(addr, '[email protected]',
198+
199+
'mail.body')
200+
test:is(r.reason, 'RCPT failed: 421 (SMTP error)', 'service unavailable')
201+
202+
r = client:request(addr, '[email protected]',
203+
204+
'mail.body')
205+
test:is(r.reason, 'RCPT failed: 510 (SMTP error)', 'unexisting recipient')
206+
207+
r = client:request(addr, '[email protected]',
208+
209+
'mail.body')
210+
test:is(r.reason, 'response reading failed (SMTP error)', 'unexisting recipient')
211+
149212
end)
150213

151214
os.exit(test:check() == true and 0 or -1)

0 commit comments

Comments
 (0)