/src/dovecot/src/lib-smtp/smtp-command-parser.c
Line | Count | Source |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "buffer.h" |
5 | | #include "unichar.h" |
6 | | #include "safe-memset.h" |
7 | | #include "istream.h" |
8 | | #include "istream-failure-at.h" |
9 | | #include "istream-sized.h" |
10 | | #include "istream-dot.h" |
11 | | |
12 | | #include "smtp-parser.h" |
13 | | #include "smtp-command-parser.h" |
14 | | |
15 | | #include <ctype.h> |
16 | | |
17 | 118k | #define SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH 32 |
18 | | |
19 | | enum smtp_command_parser_state { |
20 | | SMTP_COMMAND_PARSE_STATE_INIT = 0, |
21 | | SMTP_COMMAND_PARSE_STATE_SKIP_LINE, |
22 | | SMTP_COMMAND_PARSE_STATE_COMMAND, |
23 | | SMTP_COMMAND_PARSE_STATE_SP, |
24 | | SMTP_COMMAND_PARSE_STATE_PARAMETERS, |
25 | | SMTP_COMMAND_PARSE_STATE_CR, |
26 | | SMTP_COMMAND_PARSE_STATE_LF, |
27 | | SMTP_COMMAND_PARSE_STATE_ERROR, |
28 | | }; |
29 | | |
30 | | struct smtp_command_parser_state_data { |
31 | | enum smtp_command_parser_state state; |
32 | | |
33 | | char *cmd_name; |
34 | | char *cmd_params; |
35 | | |
36 | | size_t poff; |
37 | | }; |
38 | | |
39 | | struct smtp_command_parser { |
40 | | struct istream *input; |
41 | | |
42 | | struct smtp_command_limits limits; |
43 | | |
44 | | const unsigned char *cur, *end; |
45 | | buffer_t *line_buffer; |
46 | | struct istream *data; |
47 | | |
48 | | struct smtp_command_parser_state_data state; |
49 | | |
50 | | enum smtp_command_parse_error error_code; |
51 | | char *error; |
52 | | |
53 | | bool auth_response:1; |
54 | | bool auth_response_buffered:1; |
55 | | }; |
56 | | |
57 | | static inline void ATTR_FORMAT(3, 4) |
58 | | smtp_command_parser_error(struct smtp_command_parser *parser, |
59 | | enum smtp_command_parse_error code, |
60 | | const char *format, ...) |
61 | 21.4k | { |
62 | 21.4k | va_list args; |
63 | | |
64 | 21.4k | parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR; |
65 | | |
66 | 21.4k | i_free(parser->error); |
67 | 21.4k | parser->error_code = code; |
68 | | |
69 | 21.4k | va_start(args, format); |
70 | 21.4k | parser->error = i_strdup_vprintf(format, args); |
71 | 21.4k | va_end(args); |
72 | 21.4k | } |
73 | | |
74 | | struct smtp_command_parser * |
75 | | smtp_command_parser_init(struct istream *input, |
76 | | const struct smtp_command_limits *limits) |
77 | 6.18k | { |
78 | 6.18k | struct smtp_command_parser *parser; |
79 | | |
80 | 6.18k | parser = i_new(struct smtp_command_parser, 1); |
81 | 6.18k | parser->input = input; |
82 | 6.18k | i_stream_ref(input); |
83 | | |
84 | 6.18k | if (limits != NULL) |
85 | 6.18k | parser->limits = *limits; |
86 | 6.18k | if (parser->limits.max_parameters_size == 0) { |
87 | 6.18k | parser->limits.max_parameters_size = |
88 | 6.18k | SMTP_COMMAND_DEFAULT_MAX_PARAMETERS_SIZE; |
89 | 6.18k | } |
90 | 6.18k | if (parser->limits.max_auth_size == 0) { |
91 | 6.18k | parser->limits.max_auth_size = |
92 | 6.18k | SMTP_COMMAND_DEFAULT_MAX_AUTH_SIZE; |
93 | 6.18k | } |
94 | 6.18k | if (parser->limits.max_data_size == 0) { |
95 | 6.18k | parser->limits.max_data_size = |
96 | 6.18k | SMTP_COMMAND_DEFAULT_MAX_DATA_SIZE; |
97 | 6.18k | } |
98 | | |
99 | 6.18k | return parser; |
100 | 6.18k | } |
101 | | |
102 | | void smtp_command_parser_clear(struct smtp_command_parser *parser) |
103 | 125k | { |
104 | 125k | if (parser->auth_response_buffered) { |
105 | 0 | if (parser->line_buffer != NULL) |
106 | 0 | buffer_clear_safe(parser->line_buffer); |
107 | 0 | if (parser->state.cmd_params != NULL) { |
108 | 0 | safe_memset(parser->state.cmd_params, 0, |
109 | 0 | strlen(parser->state.cmd_params)); |
110 | 0 | } |
111 | 0 | } |
112 | 125k | parser->auth_response_buffered = FALSE; |
113 | 125k | } |
114 | | |
115 | | void smtp_command_parser_deinit(struct smtp_command_parser **_parser) |
116 | 6.18k | { |
117 | 6.18k | struct smtp_command_parser *parser = *_parser; |
118 | | |
119 | 6.18k | smtp_command_parser_clear(parser); |
120 | | |
121 | 6.18k | i_stream_unref(&parser->data); |
122 | 6.18k | buffer_free(&parser->line_buffer); |
123 | 6.18k | i_free(parser->state.cmd_name); |
124 | 6.18k | i_free(parser->state.cmd_params); |
125 | 6.18k | i_free(parser->error); |
126 | 6.18k | i_stream_unref(&parser->input); |
127 | 6.18k | i_free(parser); |
128 | 6.18k | *_parser = NULL; |
129 | 6.18k | } |
130 | | |
131 | | static void smtp_command_parser_restart(struct smtp_command_parser *parser) |
132 | 118k | { |
133 | 118k | smtp_command_parser_clear(parser); |
134 | | |
135 | 118k | buffer_free(&parser->line_buffer); |
136 | 118k | i_free(parser->state.cmd_name); |
137 | 118k | i_free(parser->state.cmd_params); |
138 | | |
139 | 118k | i_zero(&parser->state); |
140 | 118k | } |
141 | | |
142 | | void smtp_command_parser_set_stream(struct smtp_command_parser *parser, |
143 | | struct istream *input) |
144 | 0 | { |
145 | 0 | i_stream_unref(&parser->input); |
146 | 0 | if (input != NULL) { |
147 | 0 | parser->input = input; |
148 | 0 | i_stream_ref(parser->input); |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | static inline const char *_chr_sanitize(unsigned char c) |
153 | 11.2k | { |
154 | 11.2k | if (c >= 0x20 && c < 0x7F) |
155 | 4.26k | return t_strdup_printf("`%c'", c); |
156 | 6.99k | if (c == 0x0a) |
157 | 0 | return "<LF>"; |
158 | 6.99k | if (c == 0x0d) |
159 | 538 | return "<CR>"; |
160 | 6.45k | return t_strdup_printf("<0x%02x>", c); |
161 | 6.99k | } |
162 | | |
163 | | static int smtp_command_parse_identifier(struct smtp_command_parser *parser) |
164 | 118k | { |
165 | 118k | const unsigned char *p; |
166 | | |
167 | | /* The commands themselves are alphabetic characters. |
168 | | */ |
169 | 118k | p = parser->cur + parser->state.poff; |
170 | 118k | i_assert(p <= parser->end); |
171 | 916k | while (p < parser->end && i_isalpha(*p)) |
172 | 798k | p++; |
173 | 118k | if ((p - parser->cur) > SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH) { |
174 | 734 | smtp_command_parser_error( |
175 | 734 | parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
176 | 734 | "Command name is too long"); |
177 | 734 | return -1; |
178 | 734 | } |
179 | 118k | parser->state.poff = p - parser->cur; |
180 | 118k | if (p == parser->end) |
181 | 218 | return 0; |
182 | 117k | parser->state.cmd_name = i_strdup_until(parser->cur, p); |
183 | 117k | parser->cur = p; |
184 | 117k | parser->state.poff = 0; |
185 | 117k | return 1; |
186 | 118k | } |
187 | | |
188 | | static int smtp_command_parse_parameters(struct smtp_command_parser *parser) |
189 | 73.4k | { |
190 | 73.4k | const unsigned char *p, *mp; |
191 | 73.4k | size_t max_size = (parser->auth_response ? |
192 | 0 | parser->limits.max_auth_size : |
193 | 73.4k | parser->limits.max_parameters_size); |
194 | 73.4k | size_t buf_size = (parser->line_buffer == NULL ? |
195 | 73.4k | 0 : parser->line_buffer->used); |
196 | 73.4k | int nch = 1; |
197 | | |
198 | 73.4k | i_assert(max_size == 0 || buf_size <= max_size); |
199 | 73.4k | if (max_size > 0 && buf_size == max_size) { |
200 | 0 | smtp_command_parser_error( |
201 | 0 | parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG, |
202 | 0 | "%s line is too long", |
203 | 0 | (parser->auth_response ? "AUTH response" : "Command")); |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | | /* We assume parameters to match textstr (HT, SP, Printable US-ASCII). |
208 | | For command parameters, we also accept valid UTF-8 characters. |
209 | | */ |
210 | 73.4k | p = parser->cur + parser->state.poff; |
211 | 6.82M | while (p < parser->end) { |
212 | 6.82M | unichar_t ch; |
213 | | |
214 | 6.82M | if (parser->auth_response) { |
215 | 0 | if ((*p & 0x80) != 0x00) |
216 | 0 | break; |
217 | 0 | ch = *p; |
218 | 6.82M | } else { |
219 | 6.82M | nch = uni_utf8_get_char_n(p, (size_t)(parser->end - p), |
220 | 6.82M | &ch); |
221 | 6.82M | } |
222 | 6.82M | if (nch == 0) |
223 | 115 | break; |
224 | 6.82M | if (nch < 0) { |
225 | 3.35k | smtp_command_parser_error( |
226 | 3.35k | parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
227 | 3.35k | "Invalid UTF-8 character in command parameters"); |
228 | 3.35k | return -1; |
229 | 3.35k | } |
230 | 6.82M | if (nch == 1 && !smtp_char_is_textstr((unsigned char)ch)) |
231 | 68.9k | break; |
232 | 6.75M | p += nch; |
233 | 6.75M | } |
234 | 70.0k | if (max_size > 0 && (size_t)(p - parser->cur) > (max_size - buf_size)) { |
235 | 358 | smtp_command_parser_error( |
236 | 358 | parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG, |
237 | 358 | "%s line is too long", |
238 | 358 | (parser->auth_response ? "AUTH response" : "Command")); |
239 | 358 | return -1; |
240 | 358 | } |
241 | 69.7k | parser->state.poff = p - parser->cur; |
242 | 69.7k | if (p == parser->end || nch == 0) { |
243 | | /* Parsed up to end of what is currently buffered in the input |
244 | | stream. */ |
245 | 1.10k | unsigned int ch_size = (p == parser->end ? |
246 | 993 | 0 : uni_utf8_char_bytes(*p)); |
247 | 1.10k | size_t max_input = i_stream_get_max_buffer_size(parser->input); |
248 | | |
249 | | /* Move parsed data to parser's line buffer if the input stream |
250 | | buffer is full. This can happen when the parser's limits |
251 | | exceed the input stream max buffer size. */ |
252 | 1.10k | if ((parser->state.poff + ch_size) >= max_input) { |
253 | 0 | if (parser->line_buffer == NULL) { |
254 | 0 | buf_size = (max_input < SIZE_MAX / 2 ? |
255 | 0 | max_input * 2 : SIZE_MAX); |
256 | 0 | buf_size = I_MAX(buf_size, 2048); |
257 | 0 | buf_size = I_MIN(buf_size, max_size); |
258 | |
|
259 | 0 | parser->line_buffer = buffer_create_dynamic( |
260 | 0 | default_pool, buf_size); |
261 | 0 | } |
262 | 0 | if (parser->auth_response) |
263 | 0 | parser->auth_response_buffered = TRUE; |
264 | 0 | buffer_append(parser->line_buffer, parser->cur, |
265 | 0 | (p - parser->cur)); |
266 | |
|
267 | 0 | parser->cur = p; |
268 | 0 | parser->state.poff = 0; |
269 | 0 | } |
270 | 1.10k | return 0; |
271 | 1.10k | } |
272 | | |
273 | | /* In the interest of improved interoperability, SMTP receivers SHOULD |
274 | | tolerate trailing white space before the terminating <CRLF>. |
275 | | |
276 | | WSP = SP / HTAB ; white space |
277 | | |
278 | | --> Trim the end of the buffer |
279 | | */ |
280 | 68.6k | mp = p; |
281 | 68.6k | if (mp > parser->cur) { |
282 | 146k | while (mp > parser->cur && (*(mp-1) == ' ' || *(mp-1) == '\t')) |
283 | 79.3k | mp--; |
284 | 67.5k | } |
285 | | |
286 | 68.6k | if (!parser->auth_response && mp > parser->cur && *parser->cur == ' ') { |
287 | 435 | smtp_command_parser_error( |
288 | 435 | parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
289 | 435 | "Duplicate space after command name"); |
290 | 435 | return -1; |
291 | 435 | } |
292 | | |
293 | 68.1k | if (parser->auth_response) |
294 | 0 | parser->auth_response_buffered = TRUE; |
295 | 68.1k | if (parser->line_buffer == NULL) { |
296 | | /* Buffered only in input stream */ |
297 | 68.1k | parser->state.cmd_params = i_strdup_until(parser->cur, mp); |
298 | 68.1k | } else { |
299 | | /* Buffered also in the parser */ |
300 | 0 | buffer_append(parser->line_buffer, parser->cur, |
301 | 0 | (mp - parser->cur)); |
302 | 0 | parser->state.cmd_params = |
303 | 0 | buffer_free_without_data(&parser->line_buffer); |
304 | 0 | } |
305 | 68.1k | parser->cur = p; |
306 | 68.1k | parser->state.poff = 0; |
307 | 68.1k | return 1; |
308 | 68.6k | } |
309 | | |
310 | | static int smtp_command_parse_line(struct smtp_command_parser *parser) |
311 | 124k | { |
312 | 124k | int ret; |
313 | | |
314 | | /* RFC 5321, Section 4.1.1: |
315 | | |
316 | | SMTP commands are character strings terminated by <CRLF>. The |
317 | | commands themselves are alphabetic characters terminated by <SP> if |
318 | | parameters follow and <CRLF> otherwise. (In the interest of improved |
319 | | interoperability, SMTP receivers SHOULD tolerate trailing white space |
320 | | before the terminating <CRLF>.) |
321 | | */ |
322 | 176k | for (;;) { |
323 | 176k | switch (parser->state.state) { |
324 | 118k | case SMTP_COMMAND_PARSE_STATE_INIT: |
325 | 118k | smtp_command_parser_restart(parser); |
326 | 118k | if (parser->auth_response) { |
327 | | /* Parse AUTH response as bare parameters */ |
328 | 0 | parser->state.state = |
329 | 0 | SMTP_COMMAND_PARSE_STATE_PARAMETERS; |
330 | 118k | } else { |
331 | 118k | parser->state.state = |
332 | 118k | SMTP_COMMAND_PARSE_STATE_COMMAND; |
333 | 118k | } |
334 | 118k | if (parser->cur == parser->end) |
335 | 97 | return 0; |
336 | 118k | if (parser->auth_response) |
337 | 0 | break; |
338 | | /* fall through */ |
339 | 118k | case SMTP_COMMAND_PARSE_STATE_COMMAND: |
340 | 118k | ret = smtp_command_parse_identifier(parser); |
341 | 118k | if (ret <= 0) |
342 | 952 | return ret; |
343 | 117k | parser->state.state = SMTP_COMMAND_PARSE_STATE_SP; |
344 | 117k | if (parser->cur == parser->end) |
345 | 0 | return 0; |
346 | | /* fall through */ |
347 | 117k | case SMTP_COMMAND_PARSE_STATE_SP: |
348 | 117k | if (*parser->cur == '\r') { |
349 | 1.73k | parser->state.state = |
350 | 1.73k | SMTP_COMMAND_PARSE_STATE_CR; |
351 | 1.73k | break; |
352 | 116k | } else if (*parser->cur == '\n') { |
353 | 35.6k | parser->state.state = |
354 | 35.6k | SMTP_COMMAND_PARSE_STATE_LF; |
355 | 35.6k | break; |
356 | 80.5k | } else if (*parser->cur != ' ') { |
357 | 7.98k | smtp_command_parser_error(parser, |
358 | 7.98k | SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
359 | 7.98k | "Unexpected character %s in command name", |
360 | 7.98k | _chr_sanitize(*parser->cur)); |
361 | 7.98k | return -1; |
362 | 7.98k | } |
363 | 72.5k | parser->cur++; |
364 | 72.5k | parser->state.state = |
365 | 72.5k | SMTP_COMMAND_PARSE_STATE_PARAMETERS; |
366 | 72.5k | if (parser->cur >= parser->end) |
367 | 47 | return 0; |
368 | | /* fall through */ |
369 | 73.4k | case SMTP_COMMAND_PARSE_STATE_PARAMETERS: |
370 | 73.4k | ret = smtp_command_parse_parameters(parser); |
371 | 73.4k | if (ret <= 0) |
372 | 5.25k | return ret; |
373 | 68.1k | parser->state.state = SMTP_COMMAND_PARSE_STATE_CR; |
374 | 68.1k | if (parser->cur == parser->end) |
375 | 0 | return 0; |
376 | | /* fall through */ |
377 | 69.9k | case SMTP_COMMAND_PARSE_STATE_CR: |
378 | 69.9k | if (*parser->cur == '\r') { |
379 | 1.89k | parser->cur++; |
380 | 68.0k | } else if (*parser->cur != '\n') { |
381 | 1.60k | smtp_command_parser_error(parser, |
382 | 1.60k | SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
383 | 1.60k | "Unexpected character %s in %s", |
384 | 1.60k | _chr_sanitize(*parser->cur), |
385 | 1.60k | (parser->auth_response ? |
386 | 0 | "AUTH response" : |
387 | 1.60k | "command parameters")); |
388 | 1.60k | return -1; |
389 | 1.60k | } |
390 | 68.2k | parser->state.state = SMTP_COMMAND_PARSE_STATE_LF; |
391 | 68.2k | if (parser->cur == parser->end) |
392 | 47 | return 0; |
393 | | /* fall through */ |
394 | 103k | case SMTP_COMMAND_PARSE_STATE_LF: |
395 | 103k | if (*parser->cur != '\n') { |
396 | 1.66k | smtp_command_parser_error(parser, |
397 | 1.66k | SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND, |
398 | 1.66k | "Expected LF after CR at end of %s, " |
399 | 1.66k | "but found %s", |
400 | 1.66k | (parser->auth_response ? |
401 | 1.66k | "AUTH response" : "command"), |
402 | 1.66k | _chr_sanitize(*parser->cur)); |
403 | 1.66k | return -1; |
404 | 1.66k | } |
405 | 102k | parser->cur++; |
406 | 102k | parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT; |
407 | 102k | return 1; |
408 | 19.6k | case SMTP_COMMAND_PARSE_STATE_ERROR: |
409 | | /* Skip until end of line */ |
410 | 33.0M | while (parser->cur < parser->end && |
411 | 33.0M | *parser->cur != '\n') |
412 | 33.0M | parser->cur++; |
413 | 19.6k | if (parser->cur == parser->end) |
414 | 4.22k | return 0; |
415 | 15.4k | parser->cur++; |
416 | 15.4k | parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT; |
417 | 15.4k | break; |
418 | 0 | default: |
419 | 0 | i_unreached(); |
420 | 176k | } |
421 | 176k | } |
422 | | |
423 | 124k | i_unreached(); |
424 | 124k | } |
425 | | |
426 | | static int smtp_command_parse(struct smtp_command_parser *parser) |
427 | 123k | { |
428 | 123k | const unsigned char *begin; |
429 | 123k | size_t size, old_bytes = 0; |
430 | 123k | int ret; |
431 | | |
432 | 129k | while ((ret = i_stream_read_data(parser->input, &begin, &size, |
433 | 129k | old_bytes)) > 0) { |
434 | 124k | parser->cur = begin; |
435 | 124k | parser->end = parser->cur + size; |
436 | | |
437 | 124k | ret = smtp_command_parse_line(parser); |
438 | 124k | i_stream_skip(parser->input, parser->cur - begin); |
439 | 124k | if (ret != 0) |
440 | 118k | return ret; |
441 | 5.73k | old_bytes = i_stream_get_data_size(parser->input); |
442 | 5.73k | } |
443 | 123k | i_assert(ret != -2); |
444 | | |
445 | 5.27k | if (ret < 0) { |
446 | 5.13k | i_assert(parser->input->eof); |
447 | 5.13k | if (parser->input->stream_errno == 0) { |
448 | 5.13k | if (parser->state.state == |
449 | 5.13k | SMTP_COMMAND_PARSE_STATE_INIT) |
450 | 4.03k | ret = -2; |
451 | 5.13k | smtp_command_parser_error( |
452 | 5.13k | parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_COMMAND, |
453 | 5.13k | "Premature end of input"); |
454 | 5.13k | } else { |
455 | 0 | smtp_command_parser_error( |
456 | 0 | parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM, |
457 | 0 | "%s", i_stream_get_disconnect_reason(parser->input)); |
458 | 0 | } |
459 | 5.13k | } |
460 | 5.27k | return ret; |
461 | 5.27k | } |
462 | | |
463 | | bool smtp_command_parser_pending_data(struct smtp_command_parser *parser) |
464 | 121k | { |
465 | 121k | if (parser->data == NULL) |
466 | 106k | return FALSE; |
467 | 14.5k | return i_stream_have_bytes_left(parser->data); |
468 | 121k | } |
469 | | |
470 | | static int smtp_command_parse_finish_data(struct smtp_command_parser *parser) |
471 | 123k | { |
472 | 123k | const unsigned char *data; |
473 | 123k | size_t size; |
474 | 123k | int ret; |
475 | | |
476 | 123k | parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE; |
477 | 123k | parser->error = NULL; |
478 | | |
479 | 123k | if (parser->data == NULL) |
480 | 109k | return 1; |
481 | 14.0k | if (parser->data->eof) { |
482 | 10.4k | i_stream_unref(&parser->data); |
483 | 10.4k | return 1; |
484 | 10.4k | } |
485 | | |
486 | 6.30k | while ((ret = i_stream_read_data(parser->data, &data, &size, 0)) > 0) |
487 | 2.77k | i_stream_skip(parser->data, size); |
488 | 3.53k | if (ret == 0 || parser->data->stream_errno != 0) { |
489 | 260 | switch (parser->data->stream_errno) { |
490 | 55 | case 0: |
491 | 55 | return 0; |
492 | 0 | case EMSGSIZE: |
493 | 0 | smtp_command_parser_error( |
494 | 0 | parser, SMTP_COMMAND_PARSE_ERROR_DATA_TOO_LARGE, |
495 | 0 | "Command data too large"); |
496 | 0 | break; |
497 | 205 | default: |
498 | 205 | smtp_command_parser_error( |
499 | 205 | parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM, |
500 | 205 | "%s", i_stream_get_disconnect_reason(parser->data)); |
501 | 260 | } |
502 | 205 | return -1; |
503 | 260 | } |
504 | 3.27k | i_stream_unref(&parser->data); |
505 | 3.27k | return 1; |
506 | 3.53k | } |
507 | | |
508 | | int smtp_command_parse_next(struct smtp_command_parser *parser, |
509 | | bool only_finish_previous, |
510 | | const char **cmd_name_r, const char **cmd_params_r, |
511 | | enum smtp_command_parse_error *error_code_r, |
512 | | const char **error_r) |
513 | 123k | { |
514 | 123k | int ret; |
515 | | |
516 | 123k | i_assert(!parser->auth_response || |
517 | 123k | parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT || |
518 | 123k | parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR); |
519 | 123k | parser->auth_response = FALSE; |
520 | | |
521 | 123k | *cmd_name_r = NULL; |
522 | 123k | *cmd_params_r = NULL; |
523 | 123k | *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE; |
524 | 123k | *error_r = NULL; |
525 | | |
526 | 123k | i_free_and_null(parser->error); |
527 | | |
528 | | /* Make sure we finished streaming payload from previous command |
529 | | before we continue. */ |
530 | 123k | ret = smtp_command_parse_finish_data(parser); |
531 | 123k | if (ret <= 0) { |
532 | 260 | if (ret < 0) { |
533 | 205 | *error_code_r = parser->error_code; |
534 | 205 | *error_r = parser->error; |
535 | 205 | } |
536 | 260 | return ret; |
537 | 260 | } |
538 | | |
539 | 123k | if (only_finish_previous) |
540 | 0 | return 1; |
541 | | |
542 | 123k | ret = smtp_command_parse(parser); |
543 | 123k | if (ret <= 0) { |
544 | 21.4k | if (ret < 0) { |
545 | 21.2k | *error_code_r = parser->error_code; |
546 | 21.2k | *error_r = parser->error; |
547 | 21.2k | parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR; |
548 | 21.2k | } |
549 | 21.4k | return ret; |
550 | 21.4k | } |
551 | | |
552 | 123k | i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT); |
553 | 102k | *cmd_name_r = parser->state.cmd_name; |
554 | 102k | *cmd_params_r = (parser->state.cmd_params == NULL ? |
555 | 66.4k | "" : parser->state.cmd_params); |
556 | 102k | return 1; |
557 | 102k | } |
558 | | |
559 | | struct istream * |
560 | | smtp_command_parse_data_with_size(struct smtp_command_parser *parser, |
561 | | uoff_t size) |
562 | 13.0k | { |
563 | 13.0k | i_assert(parser->data == NULL); |
564 | 13.0k | if (size > parser->limits.max_data_size) { |
565 | | /* Not supposed to happen; command should check size */ |
566 | 972 | parser->data = i_stream_create_error_str(EMSGSIZE, |
567 | 972 | "Command data size exceeds maximum " |
568 | 972 | "(%"PRIuUOFF_T" > %"PRIuUOFF_T")", |
569 | 972 | size, parser->limits.max_data_size); |
570 | 12.1k | } else { |
571 | | // FIXME: Make exact_size stream type |
572 | 12.1k | struct istream *limit_input = |
573 | 12.1k | i_stream_create_limit(parser->input, size); |
574 | 12.1k | parser->data = i_stream_create_min_sized(limit_input, size); |
575 | 12.1k | i_stream_unref(&limit_input); |
576 | 12.1k | } |
577 | 13.0k | i_stream_ref(parser->data); |
578 | 13.0k | return parser->data; |
579 | 13.0k | } |
580 | | |
581 | | struct istream * |
582 | | smtp_command_parse_data_with_dot(struct smtp_command_parser *parser) |
583 | 1.46k | { |
584 | 1.46k | struct istream *data; |
585 | 1.46k | i_assert(parser->data == NULL); |
586 | | |
587 | 1.46k | data = i_stream_create_dot(parser->input, ISTREAM_DOT_NO_TRIM | |
588 | 1.46k | ISTREAM_DOT_STRICT_EOT); |
589 | 1.46k | if (parser->limits.max_data_size != UOFF_T_MAX) { |
590 | 1.46k | parser->data = i_stream_create_failure_at( |
591 | 1.46k | data, parser->limits.max_data_size, EMSGSIZE, |
592 | 1.46k | t_strdup_printf("Command data size exceeds maximum " |
593 | 1.46k | "(> %"PRIuUOFF_T")", |
594 | 1.46k | parser->limits.max_data_size)); |
595 | 1.46k | i_stream_unref(&data); |
596 | 1.46k | } else { |
597 | 0 | parser->data = data; |
598 | 0 | } |
599 | 1.46k | i_stream_ref(parser->data); |
600 | 1.46k | return parser->data; |
601 | 1.46k | } |
602 | | |
603 | | int smtp_command_parse_auth_response( |
604 | | struct smtp_command_parser *parser, const char **line_r, |
605 | | enum smtp_command_parse_error *error_code_r, const char **error_r) |
606 | 0 | { |
607 | 0 | int ret; |
608 | |
|
609 | 0 | i_assert(parser->auth_response || |
610 | 0 | parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT || |
611 | 0 | parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR); |
612 | 0 | parser->auth_response = TRUE; |
613 | |
|
614 | 0 | *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE; |
615 | 0 | *error_r = NULL; |
616 | |
|
617 | 0 | i_free_and_null(parser->error); |
618 | | |
619 | | /* Make sure we finished streaming payload from previous command |
620 | | before we continue. */ |
621 | 0 | ret = smtp_command_parse_finish_data(parser); |
622 | 0 | if (ret <= 0) { |
623 | 0 | if (ret < 0) { |
624 | 0 | *error_code_r = parser->error_code; |
625 | 0 | *error_r = parser->error; |
626 | 0 | } |
627 | 0 | return ret; |
628 | 0 | } |
629 | | |
630 | 0 | ret = smtp_command_parse(parser); |
631 | 0 | if (ret <= 0) { |
632 | 0 | if (ret < 0) { |
633 | 0 | *error_code_r = parser->error_code; |
634 | 0 | *error_r = parser->error; |
635 | 0 | parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR; |
636 | 0 | } |
637 | 0 | return ret; |
638 | 0 | } |
639 | | |
640 | 0 | i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT); |
641 | 0 | *line_r = parser->state.cmd_params; |
642 | 0 | parser->auth_response = FALSE; |
643 | 0 | return 1; |
644 | 0 | } |