1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "istream.h"
6 #include "http-parser.h"
7 #include "http-date.h"
8 #include "http-message-parser.h"
9 #include "http-response-parser.h"
10
11 #include <ctype.h>
12
13 enum http_response_parser_state {
14 HTTP_RESPONSE_PARSE_STATE_INIT = 0,
15 HTTP_RESPONSE_PARSE_STATE_VERSION,
16 HTTP_RESPONSE_PARSE_STATE_SP1,
17 HTTP_RESPONSE_PARSE_STATE_STATUS,
18 HTTP_RESPONSE_PARSE_STATE_SP2,
19 HTTP_RESPONSE_PARSE_STATE_REASON,
20 HTTP_RESPONSE_PARSE_STATE_CR,
21 HTTP_RESPONSE_PARSE_STATE_LF,
22 HTTP_RESPONSE_PARSE_STATE_HEADER
23 };
24
25 struct http_response_parser {
26 struct http_message_parser parser;
27 enum http_response_parser_state state;
28
29 unsigned int response_status;
30 const char *response_reason;
31
32 uoff_t response_offset;
33 };
34
35 struct http_response_parser *
http_response_parser_init(struct istream * input,const struct http_header_limits * hdr_limits,enum http_response_parse_flags flags)36 http_response_parser_init(struct istream *input,
37 const struct http_header_limits *hdr_limits,
38 enum http_response_parse_flags flags)
39 {
40 struct http_response_parser *parser;
41 enum http_message_parse_flags msg_flags = 0;
42
43 /* FIXME: implement status line limit */
44 if ((flags & HTTP_RESPONSE_PARSE_FLAG_STRICT) != 0)
45 msg_flags |= HTTP_MESSAGE_PARSE_FLAG_STRICT;
46 parser = i_new(struct http_response_parser, 1);
47 http_message_parser_init(&parser->parser,
48 input, hdr_limits, 0, msg_flags);
49 return parser;
50 }
51
http_response_parser_deinit(struct http_response_parser ** _parser)52 void http_response_parser_deinit(struct http_response_parser **_parser)
53 {
54 struct http_response_parser *parser = *_parser;
55
56 *_parser = NULL;
57
58 http_message_parser_deinit(&parser->parser);
59 i_free(parser);
60 }
61
62 static void
http_response_parser_restart(struct http_response_parser * parser)63 http_response_parser_restart(struct http_response_parser *parser)
64 {
65 http_message_parser_restart(&parser->parser, NULL);
66 parser->response_status = 0;
67 parser->response_reason = NULL;
68 parser->response_offset = UOFF_T_MAX;
69 }
70
http_response_parse_status(struct http_response_parser * parser)71 static int http_response_parse_status(struct http_response_parser *parser)
72 {
73 const unsigned char *p = parser->parser.cur;
74 const size_t size = parser->parser.end - parser->parser.cur;
75
76 /* status-code = 3DIGIT
77 */
78 if (size < 3)
79 return 0;
80 if (!i_isdigit(p[0]) || !i_isdigit(p[1]) || !i_isdigit(p[2]))
81 return -1;
82 parser->response_status =
83 (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
84 if (parser->response_status < 100 ||
85 parser->response_status >= 600)
86 return -1;
87 parser->parser.cur += 3;
88 return 1;
89 }
90
http_response_parse_reason(struct http_response_parser * parser)91 static int http_response_parse_reason(struct http_response_parser *parser)
92 {
93 const unsigned char *p = parser->parser.cur;
94 pool_t pool;
95
96 /* reason-phrase = *( HTAB / SP / VCHAR / obs-text )
97 */
98 // FIXME: limit length
99 while (p < parser->parser.end && http_char_is_text(*p))
100 p++;
101
102 if (p == parser->parser.end)
103 return 0;
104 pool = http_message_parser_get_pool(&parser->parser);
105 parser->response_reason =
106 p_strdup_until(pool, parser->parser.cur, p);
107 parser->parser.cur = p;
108 return 1;
109 }
110
_reply_sanitize(struct http_message_parser * parser)111 static const char *_reply_sanitize(struct http_message_parser *parser)
112 {
113 string_t *str = t_str_new(32);
114 const unsigned char *p;
115 unsigned int i;
116 bool quote_open = FALSE;
117
118 i_assert(parser->cur < parser->end);
119 for (p = parser->cur, i = 0; p < parser->end && i < 20; p++, i++) {
120 if (*p >= 0x20 && *p < 0x7F) {
121 if (!quote_open) {
122 str_append_c(str, '`');
123 quote_open = TRUE;
124 }
125 str_append_c(str, *p);
126 } else {
127 if (quote_open) {
128 str_append_c(str, '\'');
129 quote_open = FALSE;
130 }
131 if (*p == 0x0a)
132 str_append(str, "<LF>");
133 else if (*p == 0x0d)
134 str_append(str, "<CR>");
135 else
136 str_printfa(str, "<0x%02x>", *p);
137 }
138 }
139 if (quote_open)
140 str_append_c(str, '\'');
141 return str_c(str);
142 }
143
http_response_parse(struct http_response_parser * parser)144 static int http_response_parse(struct http_response_parser *parser)
145 {
146 struct http_message_parser *_parser = &parser->parser;
147 int ret;
148
149 /* RFC 7230, Section 3.1.2: Status Line
150
151 status-line = HTTP-version SP status-code SP reason-phrase CRLF
152 status-code = 3DIGIT
153 reason-phrase = *( HTAB / SP / VCHAR / obs-text )
154 */
155 switch (parser->state) {
156 case HTTP_RESPONSE_PARSE_STATE_INIT:
157 parser->state = HTTP_RESPONSE_PARSE_STATE_VERSION;
158 parser->response_offset = _parser->input->v_offset +
159 (_parser->cur - _parser->begin);
160 /* fall through */
161 case HTTP_RESPONSE_PARSE_STATE_VERSION:
162 if ((ret=http_message_parse_version(_parser)) <= 0) {
163 if (ret < 0)
164 _parser->error = t_strdup_printf(
165 "Invalid HTTP version in response: %s",
166 _reply_sanitize(_parser));
167 return ret;
168 }
169 parser->state = HTTP_RESPONSE_PARSE_STATE_SP1;
170 if (_parser->cur == _parser->end)
171 return 0;
172 /* fall through */
173 case HTTP_RESPONSE_PARSE_STATE_SP1:
174 if (*_parser->cur != ' ') {
175 _parser->error = t_strdup_printf
176 ("Expected ' ' after response version, but found %s",
177 _reply_sanitize(_parser));
178 return -1;
179 }
180 _parser->cur++;
181 parser->state = HTTP_RESPONSE_PARSE_STATE_STATUS;
182 if (_parser->cur >= _parser->end)
183 return 0;
184 /* fall through */
185 case HTTP_RESPONSE_PARSE_STATE_STATUS:
186 if ((ret=http_response_parse_status(parser)) <= 0) {
187 if (ret < 0)
188 _parser->error = "Invalid HTTP status code in response";
189 return ret;
190 }
191 parser->state = HTTP_RESPONSE_PARSE_STATE_SP2;
192 if (_parser->cur == _parser->end)
193 return 0;
194 /* fall through */
195 case HTTP_RESPONSE_PARSE_STATE_SP2:
196 if (*_parser->cur != ' ') {
197 _parser->error = t_strdup_printf
198 ("Expected ' ' after response status code, but found %s",
199 _reply_sanitize(_parser));
200 return -1;
201 }
202 _parser->cur++;
203 parser->state = HTTP_RESPONSE_PARSE_STATE_REASON;
204 if (_parser->cur >= _parser->end)
205 return 0;
206 /* fall through */
207 case HTTP_RESPONSE_PARSE_STATE_REASON:
208 if ((ret=http_response_parse_reason(parser)) <= 0) {
209 i_assert(ret == 0);
210 return 0;
211 }
212 parser->state = HTTP_RESPONSE_PARSE_STATE_CR;
213 if (_parser->cur == _parser->end)
214 return 0;
215 /* fall through */
216 case HTTP_RESPONSE_PARSE_STATE_CR:
217 if (*_parser->cur == '\r')
218 _parser->cur++;
219 parser->state = HTTP_RESPONSE_PARSE_STATE_LF;
220 if (_parser->cur == _parser->end)
221 return 0;
222 /* fall through */
223 case HTTP_RESPONSE_PARSE_STATE_LF:
224 if (*_parser->cur != '\n') {
225 _parser->error = t_strdup_printf
226 ("Expected line end after response, but found %s",
227 _reply_sanitize(_parser));
228 return -1;
229 }
230 _parser->cur++;
231 parser->state = HTTP_RESPONSE_PARSE_STATE_HEADER;
232 return 1;
233 case HTTP_RESPONSE_PARSE_STATE_HEADER:
234 default:
235 break;
236 }
237
238 i_unreached();
239 return -1;
240 }
241
242 static int
http_response_parse_status_line(struct http_response_parser * parser)243 http_response_parse_status_line(struct http_response_parser *parser)
244 {
245 struct http_message_parser *_parser = &parser->parser;
246 const unsigned char *begin;
247 size_t size, old_bytes = 0;
248 int ret;
249
250 while ((ret = i_stream_read_bytes(_parser->input, &begin, &size,
251 old_bytes + 1)) > 0) {
252 _parser->begin = _parser->cur = begin;
253 _parser->end = _parser->begin + size;
254
255 if ((ret = http_response_parse(parser)) < 0)
256 return -1;
257
258 i_stream_skip(_parser->input, _parser->cur - begin);
259 if (ret > 0)
260 return 1;
261 old_bytes = i_stream_get_data_size(_parser->input);
262 }
263
264 if (ret == -2) {
265 _parser->error = "HTTP status line is too long";
266 return -1;
267 }
268 if (ret < 0) {
269 if (_parser->input->eof &&
270 parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
271 return 0;
272 _parser->error = t_strdup_printf("Stream error: %s",
273 i_stream_get_error(_parser->input));
274 return -1;
275 }
276 return 0;
277 }
278
279 static int
http_response_parse_retry_after(const char * hdrval,time_t resp_time,time_t * retry_after_r)280 http_response_parse_retry_after(const char *hdrval, time_t resp_time,
281 time_t *retry_after_r)
282 {
283 time_t delta;
284
285 /* RFC 7231, Section 7.1.3: Retry-After
286
287 The value of this field can be either an HTTP-date or a number of
288 seconds to delay after the response is received.
289
290 Retry-After = HTTP-date / delta-seconds
291
292 A delay-seconds value is a non-negative decimal integer, representing
293 time in seconds.
294
295 delta-seconds = 1*DIGIT
296 */
297 if (str_to_time(hdrval, &delta) >= 0) {
298 if (resp_time == (time_t)-1) {
299 return -1;
300 }
301 *retry_after_r = resp_time + delta;
302 return 0;
303 }
304
305 return (http_date_parse
306 ((const unsigned char *)hdrval, strlen(hdrval), retry_after_r) ? 0 : -1);
307 }
308
http_response_parser_get_last_offset(struct http_response_parser * parser)309 uoff_t http_response_parser_get_last_offset(struct http_response_parser *parser)
310 {
311 return parser->response_offset;
312 }
313
http_response_parse_next(struct http_response_parser * parser,enum http_response_payload_type payload_type,struct http_response * response,const char ** error_r)314 int http_response_parse_next(struct http_response_parser *parser,
315 enum http_response_payload_type payload_type,
316 struct http_response *response, const char **error_r)
317 {
318 const char *hdrval;
319 time_t retry_after = (time_t)-1;
320 int ret;
321
322 i_zero(response);
323
324 /* make sure we finished streaming payload from previous response
325 before we continue. */
326 if ((ret = http_message_parse_finish_payload(&parser->parser)) <= 0) {
327 *error_r = parser->parser.error;
328 return ret;
329 }
330
331 if (parser->state == HTTP_RESPONSE_PARSE_STATE_INIT)
332 http_response_parser_restart(parser);
333
334 /* RFC 7230, Section 3:
335
336 HTTP-message = start-line
337 *( header-field CRLF )
338 CRLF
339 [ message-body ]
340 */
341 if (parser->state != HTTP_RESPONSE_PARSE_STATE_HEADER) {
342 if ((ret = http_response_parse_status_line(parser)) <= 0) {
343 *error_r = parser->parser.error;
344 return ret;
345 }
346 }
347 if ((ret = http_message_parse_headers(&parser->parser)) <= 0) {
348 *error_r = parser->parser.error;
349 return ret;
350 }
351
352 /* RFC 7230, Section 3.3.2: Content-Length
353
354 A server MUST NOT send a Content-Length header field in any response
355 with a status code of 1xx (Informational) or 204 (No Content).
356 */
357 if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
358 parser->parser.msg.content_length > 0) {
359 *error_r = t_strdup_printf(
360 "Unexpected Content-Length header field for %u response "
361 "(length=%"PRIuUOFF_T")", parser->response_status,
362 parser->parser.msg.content_length);
363 return -1;
364 }
365
366 /* RFC 7230, Section 3.3.3: Message Body Length
367
368 1. Any response to a HEAD request and any response with a 1xx
369 (Informational), 204 (No Content), or 304 (Not Modified) status
370 code is always terminated by the first empty line after the
371 header fields, regardless of the header fields present in the
372 message, and thus cannot contain a message body.
373 */
374 if (parser->response_status / 100 == 1 || parser->response_status == 204
375 || parser->response_status == 304) { // HEAD is handled in caller
376 payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
377 }
378
379 if ((payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED) ||
380 (payload_type == HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL &&
381 parser->response_status / 100 != 2)) {
382 /* [ message-body ] */
383 if (http_message_parse_body(&parser->parser, FALSE) < 0) {
384 *error_r = parser->parser.error;
385 return -1;
386 }
387 }
388
389 /* RFC 7231, Section 7.1.3: Retry-After
390
391 Servers send the "Retry-After" header field to indicate how long the
392 user agent ought to wait before making a follow-up request. When
393 sent with a 503 (Service Unavailable) response, Retry-After indicates
394 how long the service is expected to be unavailable to the client.
395 When sent with any 3xx (Redirection) response, Retry-After indicates
396 the minimum time that the user agent is asked to wait before issuing
397 the redirected request.
398 */
399 if (parser->response_status == 503 || (parser->response_status / 100) == 3) {
400 hdrval = http_header_field_get(parser->parser.msg.header, "Retry-After");
401 if (hdrval != NULL) {
402 (void)http_response_parse_retry_after
403 (hdrval, parser->parser.msg.date, &retry_after);
404 /* broken Retry-After header is ignored */
405 }
406 }
407
408 parser->state = HTTP_RESPONSE_PARSE_STATE_INIT;
409
410 response->status = parser->response_status;
411 response->reason = parser->response_reason;
412 response->version_major = parser->parser.msg.version_major;
413 response->version_minor = parser->parser.msg.version_minor;
414 response->location = parser->parser.msg.location;
415 response->date = parser->parser.msg.date;
416 response->retry_after = retry_after;
417 response->payload = parser->parser.payload;
418 response->header = parser->parser.msg.header;
419 response->connection_options = parser->parser.msg.connection_options;
420 response->connection_close = parser->parser.msg.connection_close;
421 return 1;
422 }
423