~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/lib-http/http-response-parser.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

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