/src/dovecot/src/lib-smtp/smtp-reply-parser.c
Line | Count | Source |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "array.h" |
5 | | #include "str.h" |
6 | | #include "strfuncs.h" |
7 | | #include "istream.h" |
8 | | #include "smtp-parser.h" |
9 | | |
10 | | #include "smtp-reply-parser.h" |
11 | | |
12 | | #include <ctype.h> |
13 | | |
14 | | /* From RFC 5321: |
15 | | |
16 | | Reply-line = *( Reply-code "-" [ textstring ] CRLF ) |
17 | | Reply-code [ SP textstring ] CRLF |
18 | | Reply-code = %x32-35 %x30-35 %x30-39 |
19 | | textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII |
20 | | |
21 | | Greeting = ( "220 " (Domain / address-literal) |
22 | | [ SP textstring ] CRLF ) / |
23 | | ( "220-" (Domain / address-literal) |
24 | | [ SP textstring ] CRLF |
25 | | *( "220-" [ textstring ] CRLF ) |
26 | | "220" [ SP textstring ] CRLF ) |
27 | | |
28 | | ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF ) |
29 | | / ( "250-" Domain [ SP ehlo-greet ] CRLF |
30 | | *( "250-" ehlo-line CRLF ) |
31 | | "250" SP ehlo-line CRLF ) |
32 | | ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127) |
33 | | ; string of any characters other than CR or LF |
34 | | ehlo-line = ehlo-keyword *( SP ehlo-param ) |
35 | | ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") |
36 | | ; additional syntax of ehlo-params depends on |
37 | | ; ehlo-keyword |
38 | | ehlo-param = 1*(%d33-126) |
39 | | ; any CHAR excluding <SP> and all |
40 | | ; control characters (US-ASCII 0-31 and 127 |
41 | | ; inclusive) |
42 | | |
43 | | From RFC 2034: |
44 | | |
45 | | status-code ::= class "." subject "." detail |
46 | | class ::= "2" / "4" / "5" |
47 | | subject ::= 1*3digit |
48 | | detail ::= 1*3digit |
49 | | */ |
50 | | |
51 | | enum smtp_reply_parser_state { |
52 | | SMTP_REPLY_PARSE_STATE_INIT = 0, |
53 | | SMTP_REPLY_PARSE_STATE_CODE, |
54 | | SMTP_REPLY_PARSE_STATE_SEP, |
55 | | SMTP_REPLY_PARSE_STATE_TEXT, |
56 | | SMTP_REPLY_PARSE_STATE_EHLO_SPACE, |
57 | | SMTP_REPLY_PARSE_STATE_EHLO_GREET, |
58 | | SMTP_REPLY_PARSE_STATE_CR, |
59 | | SMTP_REPLY_PARSE_STATE_CRLF, |
60 | | SMTP_REPLY_PARSE_STATE_LF |
61 | | }; |
62 | | |
63 | | struct smtp_reply_parser_state_data { |
64 | | enum smtp_reply_parser_state state; |
65 | | unsigned int line; |
66 | | |
67 | | struct smtp_reply *reply; |
68 | | ARRAY_TYPE(const_string) reply_lines; |
69 | | size_t reply_size; |
70 | | |
71 | | bool last_line:1; |
72 | | }; |
73 | | |
74 | | struct smtp_reply_parser { |
75 | | struct istream *input; |
76 | | |
77 | | size_t max_reply_size; |
78 | | |
79 | | const unsigned char *begin, *cur, *end; |
80 | | |
81 | | string_t *strbuf; |
82 | | |
83 | | struct smtp_reply_parser_state_data state; |
84 | | pool_t reply_pool; |
85 | | |
86 | | char *error; |
87 | | |
88 | | bool enhanced_codes:1; |
89 | | bool ehlo:1; |
90 | | }; |
91 | | |
92 | | bool smtp_reply_parse_enhanced_code(const char *text, |
93 | | struct smtp_reply_enhanced_code *enh_code_r, |
94 | | const char **pos_r) |
95 | 249 | { |
96 | 249 | const char *p = text; |
97 | 249 | unsigned int digits, x, y, z; |
98 | | |
99 | 249 | i_zero(enh_code_r); |
100 | | |
101 | | /* status-code ::= class "." subject "." detail |
102 | | class ::= "2" / "4" / "5" |
103 | | subject ::= 1*3digit |
104 | | detail ::= 1*3digit |
105 | | */ |
106 | | |
107 | | /* class */ |
108 | 249 | if (p[1] != '.' || (p[0] != '2' && p[0] != '4' && p[0] != '5')) |
109 | 0 | return FALSE; |
110 | 249 | x = p[0] - '0'; |
111 | 249 | p += 2; |
112 | | |
113 | | /* subject */ |
114 | 249 | digits = 0; |
115 | 249 | y = 0; |
116 | 498 | while (*p != '\0' && i_isdigit(*p) && digits++ < 3) { |
117 | 249 | y = y*10 + (*p - '0'); |
118 | 249 | p++; |
119 | 249 | } |
120 | 249 | if (digits == 0 || *p != '.') |
121 | 0 | return FALSE; |
122 | 249 | p++; |
123 | | |
124 | | /* detail */ |
125 | 249 | digits = 0; |
126 | 249 | z = 0; |
127 | 498 | while (*p != '\0' && i_isdigit(*p) && digits++ < 3) { |
128 | 249 | z = z*10 + (*p - '0'); |
129 | 249 | p++; |
130 | 249 | } |
131 | 249 | if (digits == 0 || (pos_r == NULL && *p != '\0')) |
132 | 0 | return FALSE; |
133 | | |
134 | 249 | if (pos_r != NULL) { |
135 | | /* code is syntactically valid; strip code from textstring */ |
136 | 0 | *pos_r = p; |
137 | 0 | } |
138 | | |
139 | 249 | enh_code_r->x = x; |
140 | 249 | enh_code_r->y = y; |
141 | 249 | enh_code_r->z = z; |
142 | 249 | return TRUE; |
143 | 249 | } |
144 | | |
145 | | static inline void ATTR_FORMAT(2, 3) |
146 | | smtp_reply_parser_error(struct smtp_reply_parser *parser, |
147 | | const char *format, ...) |
148 | 0 | { |
149 | 0 | va_list args; |
150 | |
|
151 | 0 | i_free(parser->error); |
152 | |
|
153 | 0 | va_start(args, format); |
154 | 0 | parser->error = i_strdup_vprintf(format, args); |
155 | 0 | va_end(args); |
156 | 0 | } |
157 | | |
158 | | struct smtp_reply_parser * |
159 | | smtp_reply_parser_init(struct istream *input, size_t max_reply_size) |
160 | 0 | { |
161 | 0 | struct smtp_reply_parser *parser; |
162 | |
|
163 | 0 | parser = i_new(struct smtp_reply_parser, 1); |
164 | 0 | parser->max_reply_size = |
165 | 0 | (max_reply_size > 0 ? max_reply_size : SIZE_MAX); |
166 | 0 | parser->input = input; |
167 | 0 | i_stream_ref(input); |
168 | 0 | parser->strbuf = str_new(default_pool, 128); |
169 | 0 | return parser; |
170 | 0 | } |
171 | | |
172 | | void smtp_reply_parser_deinit(struct smtp_reply_parser **_parser) |
173 | 0 | { |
174 | 0 | struct smtp_reply_parser *parser = *_parser; |
175 | |
|
176 | 0 | *_parser = NULL; |
177 | |
|
178 | 0 | str_free(&parser->strbuf); |
179 | 0 | pool_unref(&parser->reply_pool); |
180 | 0 | i_stream_unref(&parser->input); |
181 | 0 | i_free(parser->error); |
182 | 0 | i_free(parser); |
183 | 0 | } |
184 | | |
185 | | void smtp_reply_parser_set_stream(struct smtp_reply_parser *parser, |
186 | | struct istream *input) |
187 | 0 | { |
188 | 0 | i_stream_unref(&parser->input); |
189 | 0 | if (input != NULL) { |
190 | 0 | parser->input = input; |
191 | 0 | i_stream_ref(parser->input); |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | static void |
196 | | smtp_reply_parser_restart(struct smtp_reply_parser *parser) |
197 | 0 | { |
198 | 0 | str_truncate(parser->strbuf, 0); |
199 | 0 | pool_unref(&parser->reply_pool); |
200 | 0 | i_zero(&parser->state); |
201 | |
|
202 | 0 | parser->reply_pool = pool_alloconly_create("smtp_reply", 1024); |
203 | 0 | parser->state.reply = p_new(parser->reply_pool, struct smtp_reply, 1); |
204 | 0 | p_array_init(&parser->state.reply_lines, parser->reply_pool, 8); |
205 | |
|
206 | 0 | } |
207 | | |
208 | | static int smtp_reply_parse_code |
209 | | (struct smtp_reply_parser *parser, unsigned int *code_r) |
210 | 0 | { |
211 | 0 | const unsigned char *first = parser->cur; |
212 | 0 | const unsigned char *p; |
213 | | |
214 | | /* Reply-code = %x32-35 %x30-35 %x30-39 |
215 | | */ |
216 | 0 | while (parser->cur < parser->end && i_isdigit(*parser->cur)) |
217 | 0 | parser->cur++; |
218 | |
|
219 | 0 | if (str_len(parser->strbuf) + (parser->cur-first) > 3) |
220 | 0 | return -1; |
221 | | |
222 | 0 | str_append_data(parser->strbuf, first, parser->cur - first); |
223 | 0 | if (parser->cur == parser->end) |
224 | 0 | return 0; |
225 | 0 | if (str_len(parser->strbuf) != 3) |
226 | 0 | return -1; |
227 | 0 | p = str_data(parser->strbuf); |
228 | 0 | if (p[0] < '2' || p[0] > '5' || p[1] > '5') |
229 | 0 | return -1; |
230 | 0 | *code_r = (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0'); |
231 | 0 | str_truncate(parser->strbuf, 0); |
232 | 0 | return 1; |
233 | 0 | } |
234 | | |
235 | | static int smtp_reply_parse_textstring(struct smtp_reply_parser *parser) |
236 | 0 | { |
237 | 0 | const unsigned char *first = parser->cur; |
238 | | |
239 | | /* textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII |
240 | | */ |
241 | 0 | while (parser->cur < parser->end && smtp_char_is_textstr(*parser->cur)) |
242 | 0 | parser->cur++; |
243 | |
|
244 | 0 | if (((parser->cur-first) + parser->state.reply_size + |
245 | 0 | str_len(parser->strbuf)) > parser->max_reply_size) { |
246 | 0 | smtp_reply_parser_error(parser, |
247 | 0 | "Reply exceeds size limit"); |
248 | 0 | return -1; |
249 | 0 | } |
250 | | |
251 | 0 | str_append_data(parser->strbuf, first, parser->cur - first); |
252 | 0 | if (parser->cur == parser->end) |
253 | 0 | return 0; |
254 | 0 | return 1; |
255 | 0 | } |
256 | | |
257 | | static int smtp_reply_parse_ehlo_domain(struct smtp_reply_parser *parser) |
258 | 0 | { |
259 | 0 | const unsigned char *first = parser->cur; |
260 | | |
261 | | /* Domain [ SP ... |
262 | | */ |
263 | 0 | while (parser->cur < parser->end && *parser->cur != ' ' && |
264 | 0 | smtp_char_is_textstr(*parser->cur)) |
265 | 0 | parser->cur++; |
266 | |
|
267 | 0 | if (((parser->cur-first) + parser->state.reply_size + |
268 | 0 | str_len(parser->strbuf)) > parser->max_reply_size) { |
269 | 0 | smtp_reply_parser_error(parser, |
270 | 0 | "Reply exceeds size limit"); |
271 | 0 | return -1; |
272 | 0 | } |
273 | 0 | str_append_data(parser->strbuf, first, parser->cur - first); |
274 | 0 | if (parser->cur == parser->end) |
275 | 0 | return 0; |
276 | 0 | return 1; |
277 | 0 | } |
278 | | |
279 | | static int smtp_reply_parse_ehlo_greet(struct smtp_reply_parser *parser) |
280 | 0 | { |
281 | 0 | const unsigned char *first = parser->cur; |
282 | | |
283 | | /* ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127) |
284 | | * |
285 | | * The greet is not supposed to be empty, but we don't really care |
286 | | */ |
287 | |
|
288 | 0 | if (parser->cur == parser->end) |
289 | 0 | return 0; |
290 | 0 | if (smtp_char_is_ehlo_greet(*parser->cur)) { |
291 | 0 | for (;;) { |
292 | 0 | while (parser->cur < parser->end && |
293 | 0 | smtp_char_is_textstr(*parser->cur)) |
294 | 0 | parser->cur++; |
295 | |
|
296 | 0 | if (((parser->cur-first) + parser->state.reply_size + |
297 | 0 | str_len(parser->strbuf)) > |
298 | 0 | parser->max_reply_size) { |
299 | 0 | smtp_reply_parser_error(parser, |
300 | 0 | "Reply exceeds size limit"); |
301 | 0 | return -1; |
302 | 0 | } |
303 | | |
304 | | /* sanitize bad characters */ |
305 | 0 | str_append_data(parser->strbuf, |
306 | 0 | first, parser->cur - first); |
307 | |
|
308 | 0 | if (parser->cur == parser->end) |
309 | 0 | return 0; |
310 | 0 | if (!smtp_char_is_ehlo_greet(*parser->cur)) |
311 | 0 | break; |
312 | 0 | str_append_c(parser->strbuf, ' '); |
313 | 0 | parser->cur++; |
314 | 0 | first = parser->cur; |
315 | 0 | } |
316 | 0 | } |
317 | 0 | return 1; |
318 | 0 | } |
319 | | |
320 | | static inline const char *_chr_sanitize(unsigned char c) |
321 | 0 | { |
322 | 0 | if (c >= 0x20 && c < 0x7F) |
323 | 0 | return t_strdup_printf("'%c'", c); |
324 | 0 | return t_strdup_printf("0x%02x", c); |
325 | 0 | } |
326 | | |
327 | | static void |
328 | | smtp_reply_parser_parse_enhanced_code(const char *text, |
329 | | struct smtp_reply_parser *parser, |
330 | | const char **pos_r) |
331 | 0 | { |
332 | 0 | struct smtp_reply_enhanced_code code; |
333 | 0 | struct smtp_reply_enhanced_code *cur_code = |
334 | 0 | &parser->state.reply->enhanced_code; |
335 | |
|
336 | 0 | if (cur_code->x == 9) |
337 | 0 | return; /* failed on earlier line */ |
338 | | |
339 | 0 | if (!smtp_reply_parse_enhanced_code(text, &code, pos_r)) { |
340 | | /* failed to parse an enhanced code */ |
341 | 0 | i_zero(cur_code); |
342 | 0 | cur_code->x = 9; |
343 | 0 | return; |
344 | 0 | } |
345 | | |
346 | 0 | if (**pos_r != ' ' && **pos_r != '\r' && **pos_r != '\n') |
347 | 0 | return; |
348 | 0 | (*pos_r)++; |
349 | | |
350 | | /* check for match with status */ |
351 | 0 | if (code.x != parser->state.reply->status / 100) { |
352 | | /* ignore code */ |
353 | 0 | return; |
354 | 0 | } |
355 | | |
356 | | /* check for code consistency */ |
357 | 0 | if (parser->state.line > 0 && |
358 | 0 | (cur_code->x != code.x || cur_code->y != code.y || |
359 | 0 | cur_code->z != code.z)) { |
360 | | /* ignore code */ |
361 | 0 | return; |
362 | 0 | } |
363 | | |
364 | 0 | *cur_code = code; |
365 | 0 | } |
366 | | |
367 | | static void smtp_reply_parser_finish_line(struct smtp_reply_parser *parser) |
368 | 0 | { |
369 | 0 | const char *text = str_c(parser->strbuf); |
370 | |
|
371 | 0 | if (parser->enhanced_codes && str_len(parser->strbuf) > 5) |
372 | 0 | smtp_reply_parser_parse_enhanced_code(text, parser, &text); |
373 | |
|
374 | 0 | parser->state.line++; |
375 | 0 | parser->state.reply_size += str_len(parser->strbuf); |
376 | 0 | text = p_strdup(parser->reply_pool, text); |
377 | 0 | array_push_back(&parser->state.reply_lines, &text); |
378 | 0 | str_truncate(parser->strbuf, 0); |
379 | 0 | } |
380 | | |
381 | | static int smtp_reply_parse_more(struct smtp_reply_parser *parser) |
382 | 0 | { |
383 | 0 | unsigned int status; |
384 | 0 | int ret; |
385 | | |
386 | | /* |
387 | | Reply-line = *( Reply-code "-" [ textstring ] CRLF ) |
388 | | Reply-code [ SP textstring ] CRLF |
389 | | Reply-code = %x32-35 %x30-35 %x30-39 |
390 | | |
391 | | ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF ) |
392 | | / ( "250-" Domain [ SP ehlo-greet ] CRLF |
393 | | *( "250-" ehlo-line CRLF ) |
394 | | "250" SP ehlo-line CRLF ) |
395 | | */ |
396 | |
|
397 | 0 | for (;;) { |
398 | 0 | switch (parser->state.state) { |
399 | 0 | case SMTP_REPLY_PARSE_STATE_INIT: |
400 | 0 | smtp_reply_parser_restart(parser); |
401 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CODE; |
402 | | /* fall through */ |
403 | | /* Reply-code */ |
404 | 0 | case SMTP_REPLY_PARSE_STATE_CODE: |
405 | 0 | if ((ret=smtp_reply_parse_code(parser, &status)) <= 0) { |
406 | 0 | if (ret < 0) { |
407 | 0 | smtp_reply_parser_error(parser, |
408 | 0 | "Invalid status code in reply"); |
409 | 0 | } |
410 | 0 | return ret; |
411 | 0 | } |
412 | 0 | if (parser->state.line == 0) { |
413 | 0 | parser->state.reply->status = status; |
414 | 0 | } else if (status != parser->state.reply->status) { |
415 | 0 | smtp_reply_parser_error(parser, |
416 | 0 | "Inconsistent status codes in reply"); |
417 | 0 | return -1; |
418 | 0 | } |
419 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_SEP; |
420 | 0 | if (parser->cur == parser->end) |
421 | 0 | return 0; |
422 | | /* fall through */ |
423 | | /* "-" / SP / CRLF */ |
424 | 0 | case SMTP_REPLY_PARSE_STATE_SEP: |
425 | 0 | switch (*parser->cur) { |
426 | | /* "-" [ textstring ] CRLF */ |
427 | 0 | case '-': |
428 | 0 | parser->cur++; |
429 | 0 | parser->state.last_line = FALSE; |
430 | 0 | parser->state.state = |
431 | 0 | SMTP_REPLY_PARSE_STATE_TEXT; |
432 | 0 | break; |
433 | | /* SP [ textstring ] CRLF ; allow missing text */ |
434 | 0 | case ' ': |
435 | 0 | parser->cur++; |
436 | 0 | parser->state.state = |
437 | 0 | SMTP_REPLY_PARSE_STATE_TEXT; |
438 | 0 | parser->state.last_line = TRUE; |
439 | 0 | break; |
440 | | /* CRLF */ |
441 | 0 | case '\r': |
442 | 0 | case '\n': |
443 | 0 | parser->state.last_line = TRUE; |
444 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CR; |
445 | 0 | break; |
446 | 0 | default: |
447 | 0 | smtp_reply_parser_error(parser, |
448 | 0 | "Encountered unexpected %s after reply status code", |
449 | 0 | _chr_sanitize(*parser->cur)); |
450 | 0 | return -1; |
451 | 0 | } |
452 | 0 | if (parser->state.state != SMTP_REPLY_PARSE_STATE_TEXT) |
453 | 0 | break; |
454 | | /* fall through */ |
455 | | /* textstring / (Domain [ SP ehlo-greet ]) */ |
456 | 0 | case SMTP_REPLY_PARSE_STATE_TEXT: |
457 | 0 | if (parser->ehlo && |
458 | 0 | parser->state.reply->status == 250 && |
459 | 0 | parser->state.line == 0) { |
460 | | /* handle first line of EHLO success response |
461 | | differently because it can contain control |
462 | | characters (WHY??!) */ |
463 | 0 | if ((ret=smtp_reply_parse_ehlo_domain(parser)) <= 0) |
464 | 0 | return ret; |
465 | 0 | parser->state.state = |
466 | 0 | SMTP_REPLY_PARSE_STATE_EHLO_SPACE; |
467 | 0 | if (parser->cur == parser->end) |
468 | 0 | return 0; |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | if ((ret=smtp_reply_parse_textstring(parser)) <= 0) |
472 | 0 | return ret; |
473 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CR; |
474 | 0 | if (parser->cur == parser->end) |
475 | 0 | return 0; |
476 | | /* fall through */ |
477 | | /* CR */ |
478 | 0 | case SMTP_REPLY_PARSE_STATE_CR: |
479 | 0 | if (*parser->cur == '\r') { |
480 | 0 | parser->cur++; |
481 | 0 | parser->state.state = |
482 | 0 | SMTP_REPLY_PARSE_STATE_CRLF; |
483 | 0 | } else { |
484 | 0 | parser->state.state = |
485 | 0 | SMTP_REPLY_PARSE_STATE_LF; |
486 | 0 | } |
487 | 0 | if (parser->cur == parser->end) |
488 | 0 | return 0; |
489 | | /* fall through */ |
490 | | /* CRLF / LF */ |
491 | 0 | case SMTP_REPLY_PARSE_STATE_CRLF: |
492 | 0 | case SMTP_REPLY_PARSE_STATE_LF: |
493 | 0 | if (*parser->cur != '\n') { |
494 | 0 | if (parser->state.state == |
495 | 0 | SMTP_REPLY_PARSE_STATE_CRLF) { |
496 | 0 | smtp_reply_parser_error(parser, |
497 | 0 | "Encountered stray CR in reply text"); |
498 | 0 | } else { |
499 | 0 | smtp_reply_parser_error(parser, |
500 | 0 | "Encountered stray %s in reply text", |
501 | 0 | _chr_sanitize(*parser->cur)); |
502 | 0 | } |
503 | 0 | return -1; |
504 | 0 | } |
505 | 0 | parser->cur++; |
506 | 0 | smtp_reply_parser_finish_line(parser); |
507 | 0 | if (parser->state.last_line) { |
508 | 0 | parser->state.state = |
509 | 0 | SMTP_REPLY_PARSE_STATE_INIT; |
510 | 0 | return 1; |
511 | 0 | } |
512 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CODE; |
513 | 0 | break; |
514 | | /* SP ehlo-greet */ |
515 | 0 | case SMTP_REPLY_PARSE_STATE_EHLO_SPACE: |
516 | 0 | if (*parser->cur != ' ') { |
517 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CR; |
518 | 0 | break; |
519 | 0 | } |
520 | 0 | parser->cur++; |
521 | 0 | str_append_c(parser->strbuf, ' '); |
522 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_EHLO_GREET; |
523 | 0 | if (parser->cur == parser->end) |
524 | 0 | return 0; |
525 | | /* fall through */ |
526 | | /* ehlo-greet */ |
527 | 0 | case SMTP_REPLY_PARSE_STATE_EHLO_GREET: |
528 | 0 | if ((ret=smtp_reply_parse_ehlo_greet(parser)) <= 0) |
529 | 0 | return ret; |
530 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_CR; |
531 | 0 | if (parser->cur == parser->end) |
532 | 0 | return 0; |
533 | 0 | break; |
534 | 0 | default: |
535 | 0 | i_unreached(); |
536 | 0 | } |
537 | 0 | } |
538 | | |
539 | 0 | i_unreached(); |
540 | 0 | } |
541 | | |
542 | | static int smtp_reply_parse(struct smtp_reply_parser *parser) |
543 | 0 | { |
544 | 0 | size_t size; |
545 | 0 | int ret; |
546 | |
|
547 | 0 | while ((ret = i_stream_read_more(parser->input, |
548 | 0 | &parser->begin, &size)) > 0) { |
549 | 0 | parser->cur = parser->begin; |
550 | 0 | parser->end = parser->cur + size; |
551 | |
|
552 | 0 | if ((ret = smtp_reply_parse_more(parser)) < 0) |
553 | 0 | return -1; |
554 | | |
555 | 0 | i_stream_skip(parser->input, parser->cur - parser->begin); |
556 | 0 | if (ret > 0) |
557 | 0 | return 1; |
558 | 0 | } |
559 | | |
560 | 0 | i_assert(ret != -2); |
561 | 0 | if (ret < 0) { |
562 | 0 | i_assert(parser->input->eof); |
563 | 0 | if (parser->input->stream_errno == 0) { |
564 | 0 | if (parser->state.state == SMTP_REPLY_PARSE_STATE_INIT) |
565 | 0 | return 0; |
566 | 0 | smtp_reply_parser_error(parser, |
567 | 0 | "Premature end of input"); |
568 | 0 | } else { |
569 | 0 | smtp_reply_parser_error(parser, |
570 | 0 | "Stream error: %s", |
571 | 0 | i_stream_get_error(parser->input)); |
572 | 0 | } |
573 | 0 | } |
574 | 0 | return ret; |
575 | 0 | } |
576 | | |
577 | | int smtp_reply_parse_next(struct smtp_reply_parser *parser, |
578 | | bool enhanced_codes, struct smtp_reply **reply_r, |
579 | | const char **error_r) |
580 | 0 | { |
581 | 0 | int ret; |
582 | |
|
583 | 0 | i_assert(parser->state.state == SMTP_REPLY_PARSE_STATE_INIT || |
584 | 0 | (parser->enhanced_codes == enhanced_codes && !parser->ehlo)); |
585 | | |
586 | 0 | parser->enhanced_codes = enhanced_codes; |
587 | 0 | parser->ehlo = FALSE; |
588 | |
|
589 | 0 | i_free_and_null(parser->error); |
590 | | |
591 | | /* |
592 | | Reply-line = *( Reply-code "-" [ textstring ] CRLF ) |
593 | | Reply-code [ SP textstring ] CRLF |
594 | | Reply-code = %x32-35 %x30-35 %x30-39 |
595 | | textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII |
596 | | |
597 | | Greeting is not handled specially here. |
598 | | */ |
599 | 0 | if ((ret=smtp_reply_parse(parser)) <= 0) { |
600 | 0 | *error_r = parser->error; |
601 | 0 | return ret; |
602 | 0 | } |
603 | | |
604 | 0 | i_assert(array_count(&parser->state.reply_lines) > 0); |
605 | 0 | array_append_zero(&parser->state.reply_lines); |
606 | |
|
607 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_INIT; |
608 | 0 | parser->state.reply->text_lines = |
609 | 0 | array_front(&parser->state.reply_lines); |
610 | 0 | *reply_r = parser->state.reply; |
611 | 0 | return 1; |
612 | 0 | } |
613 | | |
614 | | int smtp_reply_parse_ehlo(struct smtp_reply_parser *parser, |
615 | | struct smtp_reply **reply_r, const char **error_r) |
616 | 0 | { |
617 | 0 | int ret; |
618 | |
|
619 | 0 | i_assert(parser->state.state == SMTP_REPLY_PARSE_STATE_INIT || |
620 | 0 | (!parser->enhanced_codes && parser->ehlo)); |
621 | | |
622 | 0 | parser->enhanced_codes = FALSE; |
623 | 0 | parser->ehlo = TRUE; |
624 | |
|
625 | 0 | i_free_and_null(parser->error); |
626 | | |
627 | | /* |
628 | | ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF ) |
629 | | / ( "250-" Domain [ SP ehlo-greet ] CRLF |
630 | | *( "250-" ehlo-line CRLF ) |
631 | | "250" SP ehlo-line CRLF ) |
632 | | ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127) |
633 | | ; string of any characters other than CR or LF |
634 | | ehlo-line = ehlo-keyword *( SP ehlo-param ) |
635 | | ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") |
636 | | ; additional syntax of ehlo-params depends on |
637 | | ; ehlo-keyword |
638 | | ehlo-param = 1*(%d33-126) |
639 | | ; any CHAR excluding <SP> and all |
640 | | ; control characters (US-ASCII 0-31 and 127 |
641 | | ; inclusive) |
642 | | */ |
643 | 0 | if ((ret=smtp_reply_parse(parser)) <= 0) { |
644 | 0 | *error_r = parser->error; |
645 | 0 | return ret; |
646 | 0 | } |
647 | | |
648 | 0 | i_assert(array_count(&parser->state.reply_lines) > 0); |
649 | 0 | array_append_zero(&parser->state.reply_lines); |
650 | |
|
651 | 0 | parser->state.state = SMTP_REPLY_PARSE_STATE_INIT; |
652 | 0 | parser->state.reply->text_lines = |
653 | 0 | array_front(&parser->state.reply_lines); |
654 | 0 | *reply_r = parser->state.reply; |
655 | 0 | return 1; |
656 | 0 | } |