1 /* $Id: http_parser.c 20800 2012-01-19 05:13:45Z m-oki $ */
2
3 /*
4 * Copyright (c) 2012, Internet Initiative Japan, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * parse HTTP header, with authentication
32 */
33
34 #include "config.h"
35
36 #include <inttypes.h>
37 #include <string.h>
38
39 #include <libarms_log.h>
40 #include <libarms/queue.h>
41
42 #include <axp_extern.h>
43 #include <libarms/base64.h>
44 #include <libarms/malloc.h>
45 #include <transaction/transaction.h>
46 #include <protocol/arms_methods.h>
47 #include <http/http.h>
48
49 /*
50 * release tr->data and related resource.
51 */
52 static int
http_release(transaction * tr)53 http_release(transaction *tr)
54 {
55 if (tr->data != NULL)
56 FREE(tr->data);
57 return 0;
58 }
59
60 static int
http_parse_auth(struct http * http,const char * u,const char * p)61 http_parse_auth(struct http *http, const char *u, const char *p)
62 {
63 #define AUTH_HDR "Authorization: Basic "
64 if (!strncmp(http->linebuf, AUTH_HDR, sizeof(AUTH_HDR)-1)) {
65 const char *userpass = &http->linebuf[sizeof(AUTH_HDR)-1];
66 char *user, *pass;
67 static char buf[512];
68 memset(buf, 0, sizeof(buf));
69 arms_base64_decode(buf, sizeof(buf),
70 userpass, strlen(userpass));
71 user = buf;
72 pass = strchr(buf, ':');
73 if (!pass)
74 return 0;
75 *pass++ = '\0';
76 if (strcmp(u, user))
77 return 0;
78 if (strcmp(p, pass))
79 return 0;
80 /* authenticated. */
81 return 1;
82 }
83 return 0;
84 }
85
86 static int
http_parse_chunked(const char * linebuf)87 http_parse_chunked(const char *linebuf)
88 {
89 const int len = sizeof("Transfer-Encoding") - 1;
90
91 if (strncmp(linebuf, "Transfer-Encoding", len) != 0)
92 return 0;
93
94 if (strstr(&linebuf[len], "chunked") == NULL)
95 return 0;
96
97 return 1;
98 }
99
100 /*
101 * error code. positive value as length of line includes LF
102 */
103 #define TOO_LONG_LINE -1
104 #define LF_NOT_FOUND -2
105
106 /*
107 * copy part of buf, with LF (not terminated by NUL)
108 */
109 int
http_get_one_line(char * dst,int dstlen,const char * buf,int buflen)110 http_get_one_line(char *dst, int dstlen, const char *buf, int buflen)
111 {
112 char *lfp;
113 int copylen;
114
115 if (!(lfp = memchr(buf, '\n', buflen))) {
116 if (dstlen < buflen)
117 return TOO_LONG_LINE;
118 memcpy(dst, buf, buflen);
119 return LF_NOT_FOUND;
120 }
121 /* lfp points '\n' */
122 copylen = lfp - buf + 1;
123 if (copylen > dstlen)
124 return TOO_LONG_LINE;
125 /* dst includes \n */
126 memcpy(dst, buf, copylen);
127 return copylen;
128 }
129
130 static int
http_parse_req_line(struct http * http)131 http_parse_req_line(struct http *http)
132 {
133 int n;
134 char methodbuf[7 + 1];
135
136 /*
137 * XXX: magic numbers...
138 * 7 for Method: longest method on HTTP/1.1 is "OPTIONS"
139 * 80 for Request-URI: nantonaku...
140 */
141 n = sscanf(http->linebuf, "%7s %80s HTTP/%u.%u",
142 methodbuf, http->uri, &http->major, &http->minor);
143 if (n != 4)
144 return -1;
145 if (strcasecmp(methodbuf, "get") == 0)
146 http->method = HTTP_METHOD_GET;
147 else if (strcasecmp(methodbuf, "post") == 0)
148 http->method = HTTP_METHOD_POST;
149 else
150 return -1;
151 return 0;
152 }
153
154 static int
http_parse_status_line(struct http * http)155 http_parse_status_line(struct http *http)
156 {
157 int n;
158
159 n = sscanf(http->linebuf, "HTTP/%u.%u %u",
160 &http->major, &http->minor, &http->result);
161 if (n != 3)
162 return -1;
163 return 0;
164 }
165
166 /*
167 * HTTP request parser.
168 */
169 int
http_request_parser(transaction * tr,const char * buf,int len)170 http_request_parser(transaction *tr, const char *buf, int len)
171 {
172 struct http *http;
173 int rv;
174
175 if (tr->release_data == NULL) {
176 /*
177 * initial call. setup
178 * tr->data: application used,
179 * and release by tr->release_data().
180 */
181 tr->release_data = http_release;
182 tr->data = http = CALLOC(1, sizeof(*http));
183 if (http == NULL) {
184 return TR_FATAL_ERROR;
185 }
186 http->state = HTTP_PARSE_REQUEST_LINE;
187 http->authenticated = 0;
188 http->result = 200;
189
190 http->llen = 0;
191 } else {
192 /*
193 * continuous call.
194 */
195 http = tr->data;
196 }
197
198 for(; len != 0;) {
199 /* get 1 line */
200 rv = http_get_one_line(&http->linebuf[http->llen],
201 sizeof(http->linebuf) - http->llen,
202 buf, len);
203 switch (rv) {
204 case TOO_LONG_LINE:
205 http->result = 400;/*Bad Request*/
206 return TR_PARSE_ERROR;
207 case LF_NOT_FOUND:
208 http->llen += len;
209 return TR_WANT_READ;
210 default:
211 buf += rv;
212 http->llen += rv;
213 len -= rv;
214 }
215 if (!memcmp(http->linebuf, "\r\n", 2)) {
216 /* header terminated. */
217 if (!http->authenticated) {
218 http->result = 401;/*Unauthorized*/
219 return TR_HTTP_AUTH_ERROR;
220 }
221 http->result = 200;
222 /* change parser to body xml parser */
223 if (http->chunked) {
224 http->state = HTTP_CHUNK_HEADER;
225 SET_TR_PARSER(tr, http_req_chunk_parser);
226 } else {
227 http->state = HTTP_PARSE_BODY;
228 SET_TR_PARSER(tr, arms_req_parser);
229 }
230 /* parse remaining buffer */
231 return tr->parser(tr, buf, len);
232 }
233
234 /* make NUL terminated string */
235 http->linebuf[--http->llen] = '\0'; /* LF -> NUL */
236 if (http->linebuf[http->llen] == '\r') {
237 http->linebuf[--http->llen] = '\0'; /* CR -> NUL */
238 }
239 /* reset llen for next line */
240 http->llen = 0;
241 /* http->linebuf[] as complete line w/o CRLF */
242 switch (http->state) {
243 case HTTP_PARSE_REQUEST_LINE:
244 if (http_parse_req_line(http) < 0)
245 return TR_PARSE_ERROR;
246 http->state = HTTP_PARSE_HEADER;
247 continue;
248 case HTTP_PARSE_HEADER:
249 if (http_parse_chunked(http->linebuf)) {
250 http->chunked = 1;
251 }
252 if (http_parse_auth(http, tr->user, tr->passwd)) {
253 http->authenticated = 1;
254 }
255 continue;
256 }
257 }
258 return TR_WANT_READ;
259 }
260
261 /*
262 * HTTP response parser.
263 */
264 int
http_response_parser(transaction * tr,const char * buf,int len)265 http_response_parser(transaction *tr, const char *buf, int len)
266 {
267 struct http *http;
268 int rv;
269
270 http = tr->data;
271
272 for(; len != 0;) {
273 /* get 1 line */
274 rv = http_get_one_line(&http->linebuf[http->llen],
275 sizeof(http->linebuf) - http->llen,
276 buf, len);
277 switch (rv) {
278 case TOO_LONG_LINE:
279 return TR_PARSE_ERROR;
280 case LF_NOT_FOUND:
281 http->llen += len;
282 return TR_WANT_READ;
283 default:
284 buf += rv;
285 http->llen += rv;
286 len -= rv;
287 }
288 if (!memcmp(http->linebuf, "\r\n", 2)) {
289 /* header terminated. */
290 http->llen = 0;
291
292 /* change parser to body xml parser */
293 if (http->chunked) {
294 http->state = HTTP_CHUNK_HEADER;
295 SET_TR_PARSER(tr, http_res_chunk_parser);
296 } else {
297 SET_TR_PARSER(tr, arms_res_parser);
298 }
299 if (len > 0)
300 /* parse remaining buffer */
301 return tr->parser(tr, buf, len);
302 return TR_WANT_READ;
303 }
304
305 /* make NUL terminated string */
306 if (http->linebuf[http->llen - 1] == '\n') {
307 http->linebuf[--http->llen] = '\0'; /* LF -> NUL */
308 }
309 if (http->linebuf[http->llen - 1] == '\r') {
310 http->linebuf[--http->llen] = '\0'; /* CR -> NUL */
311 }
312 /* reset llen for next line */
313 http->llen = 0;
314 /* http->linebuf[] as complete line w/o CRLF */
315 switch (http->state) {
316 case HTTP_PARSE_STATUS_LINE:
317 if (http_parse_status_line(http) < 0)
318 return TR_PARSE_ERROR;
319 if (http->result >= 400) {
320 /* HTTP level access error */
321 libarms_log(ARMS_LOG_EHTTP,
322 "http response (%d)", http->result);
323 return TR_PARSE_ERROR;
324 }
325 http->state = HTTP_PARSE_HEADER;
326 continue;
327 case HTTP_PARSE_HEADER:
328 if (http_parse_chunked(http->linebuf)) {
329 http->chunked = 1;
330 }
331 continue;
332 }
333 }
334 return TR_WANT_READ;
335 }
336