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