File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/http.c |
Location: | line 456, column 9 |
Description: | Branch condition evaluates to a garbage value |
1 | /* Copyright information is at the end of the file */ | |||||
2 | ||||||
3 | #define _XOPEN_SOURCE700 600 /* For strdup() */ | |||||
4 | #define _BSD_SOURCE1 /* For xmlrpc_strcaseeq() */ | |||||
5 | #ifndef _DEFAULT_SOURCE1 | |||||
6 | #define _DEFAULT_SOURCE1 | |||||
7 | #endif | |||||
8 | ||||||
9 | #include <ctype.h> | |||||
10 | #include <assert.h> | |||||
11 | #include <stdlib.h> | |||||
12 | #include <stdio.h> | |||||
13 | #include <string.h> | |||||
14 | #include <errno(*__errno_location ()).h> | |||||
15 | #include <time.h> | |||||
16 | ||||||
17 | #include "xmlrpc_config.h" | |||||
18 | #include "bool.h" | |||||
19 | #include "mallocvar.h" | |||||
20 | #include "xmlrpc-c/util.h" | |||||
21 | #include "xmlrpc-c/string_int.h" | |||||
22 | #include "xmlrpc-c/base64_int.h" | |||||
23 | #include "xmlrpc-c/abyss.h" | |||||
24 | ||||||
25 | #include "server.h" | |||||
26 | #include "session.h" | |||||
27 | #include "conn.h" | |||||
28 | #include "token.h" | |||||
29 | #include "date.h" | |||||
30 | #include "data.h" | |||||
31 | ||||||
32 | #include "http.h" | |||||
33 | ||||||
34 | /********************************************************************* | |||||
35 | ** Request Parser | |||||
36 | *********************************************************************/ | |||||
37 | ||||||
38 | /********************************************************************* | |||||
39 | ** Request | |||||
40 | *********************************************************************/ | |||||
41 | ||||||
42 | static void | |||||
43 | initRequestInfo(TRequestInfo * const requestInfoP, | |||||
44 | httpVersion const httpVersion, | |||||
45 | const char * const requestLine, | |||||
46 | TMethod const httpMethod, | |||||
47 | const char * const host, | |||||
48 | unsigned int const port, | |||||
49 | const char * const path, | |||||
50 | const char * const query) { | |||||
51 | /*---------------------------------------------------------------------------- | |||||
52 | Set up the request info structure. For information that is | |||||
53 | controlled by the header, use the defaults -- I.e. the value that | |||||
54 | applies if the request contains no applicable header field. | |||||
55 | -----------------------------------------------------------------------------*/ | |||||
56 | XMLRPC_ASSERT_PTR_OK(requestLine)do if (!((requestLine) != ((void*)0))) xmlrpc_assertion_failed ("../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 56); while (0); | |||||
57 | XMLRPC_ASSERT_PTR_OK(path)do if (!((path) != ((void*)0))) xmlrpc_assertion_failed("../../../../libs/xmlrpc-c/lib/abyss/src/http.c" , 57); while (0); | |||||
58 | ||||||
59 | requestInfoP->requestline = strdup(requestLine)(__extension__ (__builtin_constant_p (requestLine) && ((size_t)(const void *)((requestLine) + 1) - (size_t)(const void *)(requestLine) == 1) ? (((const char *) (requestLine))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (requestLine) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, requestLine, __len); __retval; })) : __strdup (requestLine ))); | |||||
60 | requestInfoP->method = httpMethod; | |||||
61 | requestInfoP->host = xmlrpc_strdupnull(host); | |||||
62 | requestInfoP->port = port; | |||||
63 | requestInfoP->uri = strdup(path)(__extension__ (__builtin_constant_p (path) && ((size_t )(const void *)((path) + 1) - (size_t)(const void *)(path) == 1) ? (((const char *) (path))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (path) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, path, __len) ; __retval; })) : __strdup (path))); | |||||
64 | requestInfoP->query = xmlrpc_strdupnull(query); | |||||
65 | requestInfoP->from = NULL((void*)0); | |||||
66 | requestInfoP->useragent = NULL((void*)0); | |||||
67 | requestInfoP->referer = NULL((void*)0); | |||||
68 | requestInfoP->user = NULL((void*)0); | |||||
69 | ||||||
70 | if (httpVersion.major > 1 || | |||||
71 | (httpVersion.major == 1 && httpVersion.minor >= 1)) | |||||
72 | requestInfoP->keepalive = TRUE1; | |||||
73 | else | |||||
74 | requestInfoP->keepalive = FALSE0; | |||||
75 | } | |||||
76 | ||||||
77 | ||||||
78 | ||||||
79 | static void | |||||
80 | freeRequestInfo(TRequestInfo * const requestInfoP) { | |||||
81 | ||||||
82 | xmlrpc_strfreenull(requestInfoP->host); | |||||
83 | ||||||
84 | xmlrpc_strfreenull(requestInfoP->user); | |||||
85 | ||||||
86 | xmlrpc_strfree(requestInfoP->uri); | |||||
87 | ||||||
88 | xmlrpc_strfree(requestInfoP->requestline); | |||||
89 | ||||||
90 | xmlrpc_strfree(requestInfoP->query); | |||||
91 | ||||||
92 | } | |||||
93 | ||||||
94 | ||||||
95 | ||||||
96 | void | |||||
97 | RequestInit(TSession * const sessionP, | |||||
98 | TConn * const connectionP) { | |||||
99 | ||||||
100 | sessionP->validRequest = false; /* Don't have valid request yet */ | |||||
101 | ||||||
102 | time(&sessionP->date); | |||||
103 | ||||||
104 | sessionP->connP = connectionP; | |||||
105 | ||||||
106 | sessionP->responseStarted = FALSE0; | |||||
107 | ||||||
108 | sessionP->chunkedwrite = FALSE0; | |||||
109 | sessionP->chunkedwritemode = FALSE0; | |||||
110 | ||||||
111 | sessionP->continueRequired = FALSE0; | |||||
112 | ||||||
113 | ListInit(&sessionP->cookies); | |||||
114 | ListInit(&sessionP->ranges); | |||||
115 | TableInit(&sessionP->requestHeaderFields); | |||||
116 | TableInit(&sessionP->responseHeaderFields); | |||||
117 | ||||||
118 | sessionP->status = 0; /* No status from handler yet */ | |||||
119 | ||||||
120 | StringAlloc(&(sessionP->header)); | |||||
121 | } | |||||
122 | ||||||
123 | ||||||
124 | ||||||
125 | void | |||||
126 | RequestFree(TSession * const sessionP) { | |||||
127 | ||||||
128 | if (sessionP->validRequest) | |||||
129 | freeRequestInfo(&sessionP->requestInfo); | |||||
130 | ||||||
131 | ListFree(&sessionP->cookies); | |||||
132 | ListFree(&sessionP->ranges); | |||||
133 | TableFree(&sessionP->requestHeaderFields); | |||||
134 | TableFree(&sessionP->responseHeaderFields); | |||||
135 | StringFree(&(sessionP->header)); | |||||
136 | } | |||||
137 | ||||||
138 | ||||||
139 | ||||||
140 | static char * | |||||
141 | firstLfPos(TConn * const connectionP, | |||||
142 | char * const lineStart) { | |||||
143 | /*---------------------------------------------------------------------------- | |||||
144 | Return a pointer in the connection's receive buffer to the first | |||||
145 | LF (linefeed aka newline) character in the buffer at or after 'lineStart'. | |||||
146 | ||||||
147 | If there is no LF in the buffer at or after 'lineStart', return NULL. | |||||
148 | -----------------------------------------------------------------------------*/ | |||||
149 | const char * const bufferEnd = | |||||
150 | connectionP->buffer.t + connectionP->buffersize; | |||||
151 | ||||||
152 | char * p; | |||||
153 | ||||||
154 | for (p = lineStart; p < bufferEnd && *p != LF'\n'; ++p); | |||||
155 | ||||||
156 | if (p < bufferEnd) | |||||
157 | return p; | |||||
158 | else | |||||
159 | return NULL((void*)0); | |||||
160 | } | |||||
161 | ||||||
162 | ||||||
163 | ||||||
164 | static void | |||||
165 | getLineInBuffer(TConn * const connectionP, | |||||
166 | char * const lineStart, | |||||
167 | time_t const deadline, | |||||
168 | char ** const lineEndP, | |||||
169 | bool * const errorP) { | |||||
170 | /*---------------------------------------------------------------------------- | |||||
171 | Get a line into the connection's read buffer, starting at position | |||||
172 | 'lineStart', if there isn't one already there. 'lineStart' is either | |||||
173 | within the buffer or just after it. | |||||
174 | ||||||
175 | Read the channel until we get a full line, except fail if we don't get | |||||
176 | one by 'deadline'. | |||||
177 | -----------------------------------------------------------------------------*/ | |||||
178 | bool error; | |||||
179 | char * lfPos; | |||||
180 | ||||||
181 | assert(lineStart <= connectionP->buffer.t + connectionP->buffersize)((lineStart <= connectionP->buffer.t + connectionP-> buffersize) ? (void) (0) : __assert_fail ("lineStart <= connectionP->buffer.t + connectionP->buffersize" , "../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 181, __PRETTY_FUNCTION__ )); | |||||
182 | ||||||
183 | error = FALSE0; /* initial value */ | |||||
184 | lfPos = NULL((void*)0); /* initial value */ | |||||
185 | ||||||
186 | while (!error && !lfPos) { | |||||
187 | int const timeLeft = (int)(deadline - time(NULL((void*)0))); | |||||
188 | if (timeLeft <= 0) | |||||
189 | error = TRUE1; | |||||
190 | else { | |||||
191 | lfPos = firstLfPos(connectionP, lineStart); | |||||
192 | if (!lfPos) { | |||||
193 | const char * readError; | |||||
194 | ConnRead(connectionP, timeLeft, NULL((void*)0), NULL((void*)0), &readError); | |||||
195 | if (readError) { | |||||
196 | error = TRUE1; | |||||
197 | xmlrpc_strfree(readError); | |||||
198 | } | |||||
199 | } | |||||
200 | } | |||||
201 | } | |||||
202 | *errorP = error; | |||||
203 | *lineEndP = lfPos + 1; | |||||
204 | } | |||||
205 | ||||||
206 | ||||||
207 | ||||||
208 | static bool | |||||
209 | isContinuationLine(const char * const line) { | |||||
210 | ||||||
211 | return (line[0] == ' ' || line[0] == '\t'); | |||||
212 | } | |||||
213 | ||||||
214 | ||||||
215 | ||||||
216 | static bool | |||||
217 | isEmptyLine(const char * const line) { | |||||
218 | ||||||
219 | return (line[0] == '\n' || (line[0] == '\r' && line[1] == '\n')); | |||||
220 | } | |||||
221 | ||||||
222 | ||||||
223 | ||||||
224 | static void | |||||
225 | convertLineEnd(char * const lineStart, | |||||
226 | char * const prevLineStart, | |||||
227 | char const newVal) { | |||||
228 | /*---------------------------------------------------------------------------- | |||||
229 | Assuming a line begins at 'lineStart' and the line before it (the | |||||
230 | "previous line") begins at 'prevLineStart', replace the line | |||||
231 | delimiter at the end of the previous line with the character 'newVal'. | |||||
232 | ||||||
233 | The line delimiter is either CRLF or LF. In the CRLF case, we replace | |||||
234 | both CR and LF with 'newVal'. | |||||
235 | -----------------------------------------------------------------------------*/ | |||||
236 | assert(lineStart >= prevLineStart + 1)((lineStart >= prevLineStart + 1) ? (void) (0) : __assert_fail ("lineStart >= prevLineStart + 1", "../../../../libs/xmlrpc-c/lib/abyss/src/http.c" , 236, __PRETTY_FUNCTION__)); | |||||
237 | *(lineStart-1) = newVal; | |||||
238 | if (prevLineStart + 1 < lineStart && | |||||
239 | *(lineStart-2) == CR'\r') | |||||
240 | *(lineStart-2) = newVal; | |||||
241 | } | |||||
242 | ||||||
243 | ||||||
244 | ||||||
245 | static void | |||||
246 | getRestOfField(TConn * const connectionP, | |||||
247 | char * const lineEnd, | |||||
248 | time_t const deadline, | |||||
249 | const char ** const fieldEndP, | |||||
250 | bool * const errorP) { | |||||
251 | /*---------------------------------------------------------------------------- | |||||
252 | Given that the read buffer for connection *connectionP contains (at | |||||
253 | its current read position) the first line of an HTTP header field, which | |||||
254 | ends at position 'lineEnd', find the rest of it. | |||||
255 | ||||||
256 | Some or all of the rest of the field may be in the buffer already; | |||||
257 | we read more from the connection as necessary, but not if it takes past | |||||
258 | 'deadline'. In the latter case, we fail. | |||||
259 | ||||||
260 | We return the location of the end of the whole field as *headerEndP. | |||||
261 | We do not remove the field from the buffer, but we do modify the | |||||
262 | buffer so as to join the multiple lines of the field into a single | |||||
263 | line, and to NUL-terminate the field. | |||||
264 | -----------------------------------------------------------------------------*/ | |||||
265 | char * const fieldStart = connectionP->buffer.t + connectionP->bufferpos; | |||||
266 | ||||||
267 | char * fieldEnd; | |||||
268 | /* End of the field lines we've seen at so far */ | |||||
269 | bool gotWholeField; | |||||
270 | bool error; | |||||
271 | ||||||
272 | fieldEnd = lineEnd; /* initial value - end of 1st line */ | |||||
273 | ||||||
274 | for (gotWholeField = FALSE0, error = FALSE0; | |||||
275 | !gotWholeField && !error;) { | |||||
276 | ||||||
277 | char * nextLineEnd; | |||||
278 | ||||||
279 | /* Note that we are guaranteed, assuming the HTTP stream is | |||||
280 | valid, that there is at least one more line in it. Worst | |||||
281 | case, it's the empty line that marks the end of the headers. | |||||
282 | */ | |||||
283 | getLineInBuffer(connectionP, fieldEnd, deadline, | |||||
284 | &nextLineEnd, &error); | |||||
285 | if (!error) { | |||||
286 | if (isContinuationLine(fieldEnd)) { | |||||
287 | /* Join previous line to this one */ | |||||
288 | convertLineEnd(fieldEnd, fieldStart, ' '); | |||||
289 | /* Add this line to the header */ | |||||
290 | fieldEnd = nextLineEnd; | |||||
291 | } else { | |||||
292 | gotWholeField = TRUE1; | |||||
293 | ||||||
294 | /* NUL-terminate the whole field */ | |||||
295 | convertLineEnd(fieldEnd, fieldStart, '\0'); | |||||
296 | } | |||||
297 | } | |||||
298 | } | |||||
299 | *fieldEndP = fieldEnd; | |||||
300 | *errorP = error; | |||||
301 | } | |||||
302 | ||||||
303 | ||||||
304 | ||||||
305 | static void | |||||
306 | readField(TConn * const connectionP, | |||||
307 | time_t const deadline, | |||||
308 | bool * const endOfHeaderP, | |||||
309 | char ** const fieldP, | |||||
310 | bool * const errorP) { | |||||
311 | /*---------------------------------------------------------------------------- | |||||
312 | Read an HTTP header field, or the end of header empty line, on connection | |||||
313 | *connectionP. | |||||
314 | ||||||
315 | An HTTP header field is basically a line, except that if a line starts | |||||
316 | with white space, it's a continuation of the previous line. A line | |||||
317 | is delimited by either LF or CRLF. | |||||
318 | ||||||
319 | The first line of an HTTP header field is never empty; an empty line | |||||
320 | signals the end of the HTTP header and beginning of the HTTP body. We | |||||
321 | call that empty line the EOH mark. | |||||
322 | ||||||
323 | We assume the connection is positioned to a header or EOH mark. | |||||
324 | ||||||
325 | In the course of reading, we read at least one character past the | |||||
326 | line delimiter at the end of the field or EOH mark; we may read | |||||
327 | much more. But we leave everything after the field or EOH (and | |||||
328 | its line delimiter) in the internal buffer, with the buffer pointer | |||||
329 | pointing to it. | |||||
330 | ||||||
331 | We use stuff already in the internal buffer (perhaps left by a | |||||
332 | previous call to this subroutine) before reading any more from from | |||||
333 | the channel. | |||||
334 | ||||||
335 | We return as *fieldP the next field as an ASCIIZ string, with no | |||||
336 | line delimiter. That string is stored in the "unused" portion of | |||||
337 | the connection's internal buffer. Iff there is no next field, we | |||||
338 | return *endOfHeaderP == true and nothing meaningful as *fieldP. | |||||
339 | -----------------------------------------------------------------------------*/ | |||||
340 | char * const bufferStart = connectionP->buffer.t + connectionP->bufferpos; | |||||
341 | ||||||
342 | bool error; | |||||
343 | char * lineEnd; | |||||
344 | ||||||
345 | getLineInBuffer(connectionP, bufferStart, deadline, &lineEnd, &error); | |||||
346 | ||||||
347 | if (!error) { | |||||
348 | if (isContinuationLine(bufferStart)) | |||||
349 | error = TRUE1; | |||||
350 | else if (isEmptyLine(bufferStart)) { | |||||
351 | /* Consume the EOH mark from the buffer */ | |||||
352 | connectionP->bufferpos = lineEnd - connectionP->buffer.t; | |||||
353 | *endOfHeaderP = TRUE1; | |||||
354 | } else { | |||||
355 | /* We have the first line of a field; there may be more. */ | |||||
356 | ||||||
357 | const char * fieldEnd; | |||||
358 | ||||||
359 | *endOfHeaderP = FALSE0; | |||||
360 | ||||||
361 | getRestOfField(connectionP, lineEnd, deadline, | |||||
362 | &fieldEnd, &error); | |||||
363 | ||||||
364 | if (!error) { | |||||
365 | *fieldP = bufferStart; | |||||
366 | ||||||
367 | /* Consume the header from the buffer (but be careful -- | |||||
368 | you can't reuse that part of the buffer because the | |||||
369 | string we will return is in it! | |||||
370 | */ | |||||
371 | connectionP->bufferpos = fieldEnd - connectionP->buffer.t; | |||||
372 | } | |||||
373 | } | |||||
374 | } | |||||
375 | *errorP = error; | |||||
376 | } | |||||
377 | ||||||
378 | ||||||
379 | ||||||
380 | static void | |||||
381 | skipToNonemptyLine(TConn * const connectionP, | |||||
382 | time_t const deadline, | |||||
383 | bool * const errorP) { | |||||
384 | ||||||
385 | char * const bufferStart = connectionP->buffer.t + connectionP->bufferpos; | |||||
386 | ||||||
387 | bool gotNonEmptyLine; | |||||
388 | bool error; | |||||
389 | char * lineStart; | |||||
390 | ||||||
391 | lineStart = bufferStart; /* initial value */ | |||||
392 | gotNonEmptyLine = FALSE0; /* initial value */ | |||||
393 | error = FALSE0; /* initial value */ | |||||
394 | ||||||
395 | while (!gotNonEmptyLine && !error) { | |||||
396 | char * lineEnd; | |||||
397 | ||||||
398 | getLineInBuffer(connectionP, lineStart, deadline, &lineEnd, &error); | |||||
399 | ||||||
400 | if (!error) { | |||||
401 | if (!isEmptyLine(lineStart)) | |||||
402 | gotNonEmptyLine = TRUE1; | |||||
403 | else | |||||
404 | lineStart = lineEnd; | |||||
405 | } | |||||
406 | } | |||||
407 | if (!error) { | |||||
408 | /* Consume all the empty lines; advance buffer pointer to first | |||||
409 | non-empty line. | |||||
410 | */ | |||||
411 | connectionP->bufferpos = lineStart - connectionP->buffer.t; | |||||
412 | } | |||||
413 | *errorP = error; | |||||
414 | } | |||||
415 | ||||||
416 | ||||||
417 | ||||||
418 | static void | |||||
419 | readRequestField(TSession * const sessionP, | |||||
420 | time_t const deadline, | |||||
421 | char ** const requestLineP, | |||||
422 | uint16_t * const httpErrorCodeP) { | |||||
423 | /*---------------------------------------------------------------------------- | |||||
424 | Read the HTTP request header field from session 'sessionP'. We read | |||||
425 | through the session's internal buffer; i.e. we may get data that was | |||||
426 | previously read from the network, or we may read more from the network. | |||||
427 | ||||||
428 | We assume the connection is presently positioned to the beginning of | |||||
429 | the HTTP document. We leave it positioned after the request field. | |||||
430 | ||||||
431 | We ignore any empty lines at the beginning of the stream, per | |||||
432 | RFC2616 Section 4.1. | |||||
433 | ||||||
434 | Fail if we can't get the field before 'deadline'. | |||||
435 | ||||||
436 | Return as *requestLineP the request field read. This ASCIIZ string is | |||||
437 | in the session's internal buffer. | |||||
438 | ||||||
439 | Return as *httpErrorCodeP the HTTP error code that describes how we | |||||
440 | are not able to read the request field, or 0 if we can. | |||||
441 | If we can't, *requestLineP is meaningless. | |||||
442 | -----------------------------------------------------------------------------*/ | |||||
443 | char * line; | |||||
444 | bool error; | |||||
445 | bool endOfHeader; | |||||
446 | ||||||
447 | skipToNonemptyLine(sessionP->connP, deadline, &error); | |||||
448 | ||||||
449 | if (!error) { | |||||
450 | readField(sessionP->connP, deadline, &endOfHeader, &line, &error); | |||||
451 | ||||||
452 | /* End of header is delimited by an empty line, and we skipped all | |||||
453 | the empty lines above, so readField() could not have encountered | |||||
454 | EOH: | |||||
455 | */ | |||||
456 | assert(!endOfHeader)((!endOfHeader) ? (void) (0) : __assert_fail ("!endOfHeader", "../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 456, __PRETTY_FUNCTION__ )); | |||||
| ||||||
457 | } | |||||
458 | if (error) | |||||
459 | *httpErrorCodeP = 408; /* Request Timeout */ | |||||
460 | else { | |||||
461 | *httpErrorCodeP = 0; | |||||
462 | *requestLineP = line; | |||||
463 | } | |||||
464 | } | |||||
465 | ||||||
466 | ||||||
467 | ||||||
468 | static void | |||||
469 | unescapeUri(char * const uri, | |||||
470 | bool * const errorP) { | |||||
471 | ||||||
472 | char * x; | |||||
473 | char * y; | |||||
474 | ||||||
475 | x = y = uri; | |||||
476 | ||||||
477 | *errorP = FALSE0; | |||||
478 | ||||||
479 | while (*x && !*errorP) { | |||||
480 | switch (*x) { | |||||
481 | case '%': { | |||||
482 | char c; | |||||
483 | ++x; | |||||
484 | c = tolower(*x++)(__extension__ ({ int __res; if (sizeof (*x++) > 1) { if ( __builtin_constant_p (*x++)) { int __c = (*x++); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c]; } else __res = tolower (*x++); } else __res = (*__ctype_tolower_loc ())[(int) (*x++)]; __res; })); | |||||
485 | if ((c >= '0') && (c <= '9')) | |||||
486 | c -= '0'; | |||||
487 | else if ((c >= 'a') && (c <= 'f')) | |||||
488 | c -= 'a' - 10; | |||||
489 | else | |||||
490 | *errorP = TRUE1; | |||||
491 | ||||||
492 | if (!*errorP) { | |||||
493 | char d; | |||||
494 | d = tolower(*x++)(__extension__ ({ int __res; if (sizeof (*x++) > 1) { if ( __builtin_constant_p (*x++)) { int __c = (*x++); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c]; } else __res = tolower (*x++); } else __res = (*__ctype_tolower_loc ())[(int) (*x++)]; __res; })); | |||||
495 | if ((d >= '0') && (d <= '9')) | |||||
496 | d -= '0'; | |||||
497 | else if ((d >= 'a') && (d <= 'f')) | |||||
498 | d -= 'a' - 10; | |||||
499 | else | |||||
500 | *errorP = TRUE1; | |||||
501 | ||||||
502 | if (!*errorP) | |||||
503 | *y++ = ((c << 4) | d); | |||||
504 | } | |||||
505 | } break; | |||||
506 | ||||||
507 | ||||||
508 | default: | |||||
509 | *y++ = *x++; | |||||
510 | break; | |||||
511 | } | |||||
512 | } | |||||
513 | *y = '\0'; | |||||
514 | } | |||||
515 | ||||||
516 | ||||||
517 | ||||||
518 | static void | |||||
519 | parseHostPort(const char * const hostport, | |||||
520 | const char ** const hostP, | |||||
521 | unsigned short * const portP, | |||||
522 | const char ** const errorP, | |||||
523 | uint16_t * const httpErrorCodeP) { | |||||
524 | /*---------------------------------------------------------------------------- | |||||
525 | Parse a 'hostport', a string in the form www.acme.com:8080 . | |||||
526 | ||||||
527 | Return the host name part (www.acme.com) as *hostP (in newly | |||||
528 | malloced storage), and the port part (8080) as *portP. | |||||
529 | ||||||
530 | Default the port to 80 if 'hostport' doesn't have the port part. | |||||
531 | -----------------------------------------------------------------------------*/ | |||||
532 | char * buffer; | |||||
533 | char * colonPos; | |||||
534 | ||||||
535 | buffer = strdup(hostport)(__extension__ (__builtin_constant_p (hostport) && (( size_t)(const void *)((hostport) + 1) - (size_t)(const void * )(hostport) == 1) ? (((const char *) (hostport))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (hostport) + 1; char *__retval = (char *) malloc (__len ); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval , hostport, __len); __retval; })) : __strdup (hostport))); | |||||
536 | ||||||
537 | colonPos = strrchr(buffer, ':'); | |||||
538 | if (colonPos) { | |||||
539 | const char * p; | |||||
540 | uint32_t port; | |||||
541 | ||||||
542 | *colonPos = '\0'; /* Split hostport at the colon */ | |||||
543 | ||||||
544 | ||||||
545 | for (p = colonPos + 1, port = 0; | |||||
546 | isdigit(*p)((*__ctype_b_loc ())[(int) ((*p))] & (unsigned short int) _ISdigit) && port < 65535; | |||||
547 | (port = port * 10 + (*p - '0')), ++p); | |||||
548 | ||||||
549 | if (*p || port == 0) { | |||||
550 | xmlrpc_asprintf(errorP, "There is nothing, or something " | |||||
551 | "non-numeric for the port number after the " | |||||
552 | "colon in '%s'", hostport); | |||||
553 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
554 | } else { | |||||
555 | *hostP = strdup(buffer)(__extension__ (__builtin_constant_p (buffer) && ((size_t )(const void *)((buffer) + 1) - (size_t)(const void *)(buffer ) == 1) ? (((const char *) (buffer))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (buffer) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, buffer, __len ); __retval; })) : __strdup (buffer))); | |||||
556 | *portP = port; | |||||
557 | *errorP = NULL((void*)0); | |||||
558 | } | |||||
559 | } else { | |||||
560 | *hostP = strdup(buffer)(__extension__ (__builtin_constant_p (buffer) && ((size_t )(const void *)((buffer) + 1) - (size_t)(const void *)(buffer ) == 1) ? (((const char *) (buffer))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (buffer) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, buffer, __len ); __retval; })) : __strdup (buffer))); | |||||
561 | *portP = 80; | |||||
562 | *errorP = NULL((void*)0); | |||||
563 | } | |||||
564 | free(buffer); | |||||
565 | } | |||||
566 | ||||||
567 | ||||||
568 | ||||||
569 | static void | |||||
570 | parseRequestUri(char * const requestUri, | |||||
571 | const char ** const hostP, | |||||
572 | unsigned short * const portP, | |||||
573 | const char ** const pathP, | |||||
574 | const char ** const queryP, | |||||
575 | uint16_t * const httpErrorCodeP) { | |||||
576 | /*---------------------------------------------------------------------------- | |||||
577 | Parse the request URI (in the request line | |||||
578 | "GET http://www.myserver.com:8080/myfile.cgi?parm HTTP/1.1", | |||||
579 | "http://www.myserver.com:8080/myfile.cgi?parm" is the request URI). | |||||
580 | ||||||
581 | Return as *hostP the "www.myserver.com" in the above example. If | |||||
582 | that part of the URI doesn't exist, return *hostP == NULL. | |||||
583 | ||||||
584 | Return as *portP the 8080 in the above example. If it doesn't exist, | |||||
585 | return 80. | |||||
586 | ||||||
587 | Return as *pathP the "/myfile.cgi" in the above example. If it | |||||
588 | doesn't exist, return "*". | |||||
589 | ||||||
590 | Return as *queryP the "parm" in the above example. If it doesn't | |||||
591 | exist, return *queryP == NULL. | |||||
592 | ||||||
593 | Return strings in newly malloc'ed storage. | |||||
594 | ||||||
595 | Return as *httpErrorCodeP the HTTP error code that describes how the | |||||
596 | URI is invalid, or 0 if it is valid. If it's invalid, other return | |||||
597 | values are meaningless. | |||||
598 | ||||||
599 | This destroys 'requestUri'. We should fix that. | |||||
600 | -----------------------------------------------------------------------------*/ | |||||
601 | bool error; | |||||
602 | ||||||
603 | unescapeUri(requestUri, &error); | |||||
604 | ||||||
605 | if (error) | |||||
606 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
607 | else { | |||||
608 | char * requestUriNoQuery; | |||||
609 | /* The request URI with any query (the stuff marked by a question | |||||
610 | mark at the end of a request URI) chopped off. | |||||
611 | */ | |||||
612 | { | |||||
613 | /* Split requestUri at the question mark */ | |||||
614 | char * const qmark = strchr(requestUri, '?')(__extension__ (__builtin_constant_p ('?') && !__builtin_constant_p (requestUri) && ('?') == '\0' ? (char *) __rawmemchr (requestUri, '?') : __builtin_strchr (requestUri, '?'))); | |||||
615 | ||||||
616 | if (qmark) { | |||||
617 | *qmark = '\0'; | |||||
618 | *queryP = strdup(qmark + 1)(__extension__ (__builtin_constant_p (qmark + 1) && ( (size_t)(const void *)((qmark + 1) + 1) - (size_t)(const void *)(qmark + 1) == 1) ? (((const char *) (qmark + 1))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (qmark + 1) + 1; char *__retval = (char *) malloc ( __len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, qmark + 1, __len); __retval; })) : __strdup (qmark + 1))); | |||||
619 | } else | |||||
620 | *queryP = NULL((void*)0); | |||||
621 | ||||||
622 | requestUriNoQuery = requestUri; | |||||
623 | } | |||||
624 | ||||||
625 | ||||||
626 | if (requestUriNoQuery[0] == '/') { | |||||
627 | *hostP = NULL((void*)0); | |||||
628 | *pathP = strdup(requestUriNoQuery)(__extension__ (__builtin_constant_p (requestUriNoQuery) && ((size_t)(const void *)((requestUriNoQuery) + 1) - (size_t)( const void *)(requestUriNoQuery) == 1) ? (((const char *) (requestUriNoQuery ))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (requestUriNoQuery) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, requestUriNoQuery, __len); __retval ; })) : __strdup (requestUriNoQuery))); | |||||
629 | *portP = 80; | |||||
630 | *httpErrorCodeP = 0; | |||||
631 | } else { | |||||
632 | if (!xmlrpc_strneq(requestUriNoQuery, "http://", 7)) | |||||
633 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
634 | else { | |||||
635 | char * const hostportpath = &requestUriNoQuery[7]; | |||||
636 | char * const slashPos = strchr(hostportpath, '/')(__extension__ (__builtin_constant_p ('/') && !__builtin_constant_p (hostportpath) && ('/') == '\0' ? (char *) __rawmemchr (hostportpath, '/') : __builtin_strchr (hostportpath, '/'))); | |||||
637 | ||||||
638 | const char * host; | |||||
639 | const char * path; | |||||
640 | unsigned short port; | |||||
641 | ||||||
642 | char * hostport; | |||||
643 | ||||||
644 | if (slashPos) { | |||||
645 | char * p; | |||||
646 | path = strdup(slashPos)(__extension__ (__builtin_constant_p (slashPos) && (( size_t)(const void *)((slashPos) + 1) - (size_t)(const void * )(slashPos) == 1) ? (((const char *) (slashPos))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (slashPos) + 1; char *__retval = (char *) malloc (__len ); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval , slashPos, __len); __retval; })) : __strdup (slashPos))); | |||||
647 | ||||||
648 | /* Nul-terminate the host name. To make space for | |||||
649 | it, slide the whole name back one character. | |||||
650 | This moves it into the space now occupied by | |||||
651 | the end of "http://", which we don't need. | |||||
652 | */ | |||||
653 | for (p = hostportpath; *p != '/'; ++p) | |||||
654 | *(p-1) = *p; | |||||
655 | *(p-1) = '\0'; | |||||
656 | ||||||
657 | hostport = hostportpath - 1; | |||||
658 | *httpErrorCodeP = 0; | |||||
659 | } else { | |||||
660 | path = strdup("*")(__extension__ (__builtin_constant_p ("*") && ((size_t )(const void *)(("*") + 1) - (size_t)(const void *)("*") == 1 ) ? (((const char *) ("*"))[0] == '\0' ? (char *) calloc ((size_t ) 1, (size_t) 1) : ({ size_t __len = strlen ("*") + 1; char * __retval = (char *) malloc (__len); if (__retval != ((void*)0 )) __retval = (char *) memcpy (__retval, "*", __len); __retval ; })) : __strdup ("*"))); | |||||
661 | hostport = hostportpath; | |||||
662 | *httpErrorCodeP = 0; | |||||
663 | } | |||||
664 | if (!*httpErrorCodeP) { | |||||
665 | const char * error; | |||||
666 | parseHostPort(hostport, &host, &port, | |||||
667 | &error, httpErrorCodeP); | |||||
668 | if (error) | |||||
669 | xmlrpc_strfree(error); | |||||
670 | else | |||||
671 | *httpErrorCodeP = 0; | |||||
672 | } | |||||
673 | if (*httpErrorCodeP) | |||||
674 | xmlrpc_strfree(path); | |||||
675 | ||||||
676 | *hostP = host; | |||||
677 | *portP = port; | |||||
678 | *pathP = path; | |||||
679 | } | |||||
680 | } | |||||
681 | } | |||||
682 | } | |||||
683 | ||||||
684 | ||||||
685 | ||||||
686 | static void | |||||
687 | parseRequestLine(char * const requestLine, | |||||
688 | TMethod * const httpMethodP, | |||||
689 | httpVersion * const httpVersionP, | |||||
690 | const char ** const hostP, | |||||
691 | unsigned short * const portP, | |||||
692 | const char ** const pathP, | |||||
693 | const char ** const queryP, | |||||
694 | bool * const moreLinesP, | |||||
695 | uint16_t * const httpErrorCodeP) { | |||||
696 | /*---------------------------------------------------------------------------- | |||||
697 | Modifies *requestLine! | |||||
698 | -----------------------------------------------------------------------------*/ | |||||
699 | const char * httpMethodName; | |||||
700 | char * p; | |||||
701 | ||||||
702 | p = requestLine; | |||||
703 | ||||||
704 | /* Jump over spaces */ | |||||
705 | NextToken((const char **)&p); | |||||
706 | ||||||
707 | httpMethodName = GetToken(&p); | |||||
708 | if (!httpMethodName) | |||||
709 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
710 | else { | |||||
711 | char * requestUri; | |||||
712 | ||||||
713 | if (xmlrpc_streq(httpMethodName, "GET")) | |||||
714 | *httpMethodP = m_get; | |||||
715 | else if (xmlrpc_streq(httpMethodName, "PUT")) | |||||
716 | *httpMethodP = m_put; | |||||
717 | else if (xmlrpc_streq(httpMethodName, "OPTIONS")) | |||||
718 | *httpMethodP = m_options; | |||||
719 | else if (xmlrpc_streq(httpMethodName, "DELETE")) | |||||
720 | *httpMethodP = m_delete; | |||||
721 | else if (xmlrpc_streq(httpMethodName, "POST")) | |||||
722 | *httpMethodP = m_post; | |||||
723 | else if (xmlrpc_streq(httpMethodName, "TRACE")) | |||||
724 | *httpMethodP = m_trace; | |||||
725 | else if (xmlrpc_streq(httpMethodName, "HEAD")) | |||||
726 | *httpMethodP = m_head; | |||||
727 | else | |||||
728 | *httpMethodP = m_unknown; | |||||
729 | ||||||
730 | /* URI and Query Decoding */ | |||||
731 | NextToken((const char **)&p); | |||||
732 | ||||||
733 | requestUri = GetToken(&p); | |||||
734 | if (!requestUri) | |||||
735 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
736 | else { | |||||
737 | const char * host; | |||||
738 | unsigned short port; | |||||
739 | const char * path; | |||||
740 | const char * query; | |||||
741 | ||||||
742 | parseRequestUri(requestUri, &host, &port, &path, &query, | |||||
743 | httpErrorCodeP); | |||||
744 | ||||||
745 | if (!*httpErrorCodeP) { | |||||
746 | const char * httpVersion; | |||||
747 | ||||||
748 | NextToken((const char **)&p); | |||||
749 | ||||||
750 | /* HTTP Version Decoding */ | |||||
751 | ||||||
752 | httpVersion = GetToken(&p); | |||||
753 | if (httpVersion) { | |||||
754 | uint32_t vmin, vmaj; | |||||
755 | if (sscanf(httpVersion, "HTTP/%d.%d", &vmaj, &vmin) != 2) | |||||
756 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
757 | else { | |||||
758 | httpVersionP->major = vmaj; | |||||
759 | httpVersionP->minor = vmin; | |||||
760 | *httpErrorCodeP = 0; /* no error */ | |||||
761 | } | |||||
762 | *moreLinesP = TRUE1; | |||||
763 | } else { | |||||
764 | /* There is no HTTP version, so this is a single | |||||
765 | line request. | |||||
766 | */ | |||||
767 | *httpErrorCodeP = 0; /* no error */ | |||||
768 | *moreLinesP = FALSE0; | |||||
769 | } | |||||
770 | if (*httpErrorCodeP) { | |||||
771 | xmlrpc_strfree(host); | |||||
772 | xmlrpc_strfree(path); | |||||
773 | xmlrpc_strfree(query); | |||||
774 | } | |||||
775 | *hostP = host; | |||||
776 | *portP = port; | |||||
777 | *pathP = path; | |||||
778 | *queryP = query; | |||||
779 | } | |||||
780 | } | |||||
781 | } | |||||
782 | } | |||||
783 | ||||||
784 | ||||||
785 | ||||||
786 | static void | |||||
787 | strtolower(char * const s) { | |||||
788 | ||||||
789 | char * t; | |||||
790 | ||||||
791 | t = &s[0]; | |||||
792 | while (*t) { | |||||
793 | *t = tolower(*t)(__extension__ ({ int __res; if (sizeof (*t) > 1) { if (__builtin_constant_p (*t)) { int __c = (*t); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c]; } else __res = tolower (*t); } else __res = (*__ctype_tolower_loc ())[(int) (*t)]; __res ; })); | |||||
794 | ++t; | |||||
795 | } | |||||
796 | } | |||||
797 | ||||||
798 | ||||||
799 | ||||||
800 | static void | |||||
801 | getFieldNameToken(char ** const pP, | |||||
802 | char ** const fieldNameP, | |||||
803 | const char ** const errorP, | |||||
804 | uint16_t * const httpErrorCodeP) { | |||||
805 | /*---------------------------------------------------------------------------- | |||||
806 | Assuming that *pP points to the place in an HTTP header where the field | |||||
807 | name belongs, return the field name and advance *pP past that token. | |||||
808 | ||||||
809 | The field name is the lower case representation of the value of the | |||||
810 | field name token. | |||||
811 | -----------------------------------------------------------------------------*/ | |||||
812 | char * fieldName; | |||||
813 | ||||||
814 | NextToken((const char **)pP); | |||||
815 | ||||||
816 | fieldName = GetToken(pP); | |||||
817 | if (!fieldName) { | |||||
818 | xmlrpc_asprintf(errorP, "The header has no field name token"); | |||||
819 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
820 | } else { | |||||
821 | if (fieldName[strlen(fieldName)-1] != ':') { | |||||
822 | /* Not a valid field name */ | |||||
823 | xmlrpc_asprintf(errorP, "The field name token '%s' " | |||||
824 | "does not end with a colon (:)", fieldName); | |||||
825 | *httpErrorCodeP = 400; /* Bad Request */ | |||||
826 | } else { | |||||
827 | fieldName[strlen(fieldName)-1] = '\0'; /* remove trailing colon */ | |||||
828 | ||||||
829 | strtolower(fieldName); | |||||
830 | ||||||
831 | *errorP = NULL((void*)0); | |||||
832 | } | |||||
833 | } | |||||
834 | *fieldNameP = fieldName; | |||||
835 | } | |||||
836 | ||||||
837 | ||||||
838 | ||||||
839 | static void | |||||
840 | processField(const char * const fieldName, | |||||
841 | char * const fieldValue, | |||||
842 | TSession * const sessionP, | |||||
843 | const char ** const errorP, | |||||
844 | uint16_t * const httpErrorCodeP) { | |||||
845 | /*---------------------------------------------------------------------------- | |||||
846 | We may modify *fieldValue, and we put pointers to *fieldValue and | |||||
847 | *fieldName into *sessionP. | |||||
848 | ||||||
849 | We must fix this some day. *sessionP should point to individual | |||||
850 | malloc'ed strings. | |||||
851 | -----------------------------------------------------------------------------*/ | |||||
852 | *errorP = NULL((void*)0); /* initial assumption */ | |||||
853 | ||||||
854 | if (xmlrpc_streq(fieldName, "connection")) { | |||||
855 | if (xmlrpc_strcaseeq(fieldValue, "keep-alive")) | |||||
856 | sessionP->requestInfo.keepalive = TRUE1; | |||||
857 | else | |||||
858 | sessionP->requestInfo.keepalive = FALSE0; | |||||
859 | } else if (xmlrpc_streq(fieldName, "host")) { | |||||
860 | if (sessionP->requestInfo.host) { | |||||
861 | xmlrpc_strfree(sessionP->requestInfo.host); | |||||
862 | sessionP->requestInfo.host = NULL((void*)0); | |||||
863 | } | |||||
864 | parseHostPort(fieldValue, &sessionP->requestInfo.host, | |||||
865 | &sessionP->requestInfo.port, errorP, httpErrorCodeP); | |||||
866 | } else if (xmlrpc_streq(fieldName, "from")) | |||||
867 | sessionP->requestInfo.from = fieldValue; | |||||
868 | else if (xmlrpc_streq(fieldName, "user-agent")) | |||||
869 | sessionP->requestInfo.useragent = fieldValue; | |||||
870 | else if (xmlrpc_streq(fieldName, "referer")) | |||||
871 | sessionP->requestInfo.referer = fieldValue; | |||||
872 | else if (xmlrpc_streq(fieldName, "range")) { | |||||
873 | if (xmlrpc_strneq(fieldValue, "bytes=", 6)) { | |||||
874 | bool succeeded; | |||||
875 | succeeded = ListAddFromString(&sessionP->ranges, &fieldValue[6]); | |||||
876 | if (!succeeded) { | |||||
877 | xmlrpc_asprintf(errorP, "ListAddFromString() failed for " | |||||
878 | "\"range: bytes=...\" header value '%s'", | |||||
879 | &fieldValue[6]); | |||||
880 | *httpErrorCodeP = 400; | |||||
881 | } | |||||
882 | } | |||||
883 | } else if (xmlrpc_streq(fieldName, "cookies")) { | |||||
884 | bool succeeded; | |||||
885 | succeeded = ListAddFromString(&sessionP->cookies, fieldValue); | |||||
886 | if (!succeeded) { | |||||
887 | xmlrpc_asprintf(errorP, "ListAddFromString() failed for " | |||||
888 | "cookies: header value '%s'", fieldValue); | |||||
889 | *httpErrorCodeP = 400; | |||||
890 | } | |||||
891 | } else if (xmlrpc_streq(fieldName, "expect")) { | |||||
892 | if (xmlrpc_strcaseeq(fieldValue, "100-continue")) | |||||
893 | sessionP->continueRequired = TRUE1; | |||||
894 | } | |||||
895 | } | |||||
896 | ||||||
897 | ||||||
898 | ||||||
899 | static void | |||||
900 | readAndProcessHeaderFields(TSession * const sessionP, | |||||
901 | time_t const deadline, | |||||
902 | const char ** const errorP, | |||||
903 | uint16_t * const httpErrorCodeP) { | |||||
904 | /*---------------------------------------------------------------------------- | |||||
905 | Read all the HTTP header fields from the session *sessionP, which has at | |||||
906 | least one field coming. Update *sessionP to reflect the information in the | |||||
907 | fields. | |||||
908 | ||||||
909 | If we find an error in the fields or while trying to read them, we return | |||||
910 | a text explanation of the problem as *errorP and an appropriate HTTP error | |||||
911 | code as *httpErrorCodeP. Otherwise, we return *errorP = NULL and nothing | |||||
912 | as *httpErrorCodeP. | |||||
913 | -----------------------------------------------------------------------------*/ | |||||
914 | bool endOfHeader; | |||||
915 | ||||||
916 | assert(!sessionP->validRequest)((!sessionP->validRequest) ? (void) (0) : __assert_fail ("!sessionP->validRequest" , "../../../../libs/xmlrpc-c/lib/abyss/src/http.c", 916, __PRETTY_FUNCTION__ )); | |||||
917 | /* Calling us doesn't make sense if there is already a valid request */ | |||||
918 | ||||||
919 | *errorP = NULL((void*)0); /* initial assumption */ | |||||
920 | endOfHeader = false; /* Caller assures us there is at least one header */ | |||||
921 | ||||||
922 | while (!endOfHeader && !*errorP) { | |||||
923 | char * field; | |||||
924 | bool error; | |||||
925 | readField(sessionP->connP, deadline, &endOfHeader, &field, &error); | |||||
926 | if (error) { | |||||
927 | xmlrpc_asprintf(errorP, "Failed to read header from " | |||||
928 | "client connection."); | |||||
929 | *httpErrorCodeP = 408; /* Request Timeout */ | |||||
930 | } else { | |||||
931 | if (!endOfHeader) { | |||||
932 | char * p; | |||||
933 | char * fieldName; | |||||
934 | ||||||
935 | p = &field[0]; | |||||
936 | getFieldNameToken(&p, &fieldName, errorP, httpErrorCodeP); | |||||
937 | if (!*errorP) { | |||||
938 | char * fieldValue; | |||||
939 | ||||||
940 | NextToken((const char **)&p); | |||||
941 | ||||||
942 | fieldValue = p; | |||||
943 | ||||||
944 | TableAdd(&sessionP->requestHeaderFields, | |||||
945 | fieldName, fieldValue); | |||||
946 | ||||||
947 | processField(fieldName, fieldValue, sessionP, errorP, | |||||
948 | httpErrorCodeP); | |||||
949 | } | |||||
950 | } | |||||
951 | } | |||||
952 | } | |||||
953 | } | |||||
954 | ||||||
955 | ||||||
956 | ||||||
957 | void | |||||
958 | RequestRead(TSession * const sessionP, | |||||
959 | uint32_t const timeout, | |||||
960 | const char ** const errorP, | |||||
961 | uint16_t * const httpErrorCodeP) { | |||||
962 | /*---------------------------------------------------------------------------- | |||||
963 | Read the headers of a new HTTP request (assuming nothing has yet been | |||||
964 | read on the session). | |||||
965 | ||||||
966 | Update *sessionP with the information from the headers. | |||||
967 | ||||||
968 | Leave the connection positioned to the body of the request, ready | |||||
969 | to be read by an HTTP request handler (via SessionRefillBuffer() and | |||||
970 | SessionGetReadData()). | |||||
971 | -----------------------------------------------------------------------------*/ | |||||
972 | time_t const deadline = time(NULL((void*)0)) + timeout; | |||||
973 | ||||||
974 | uint16_t httpErrorCode; /* zero for no error */ | |||||
975 | char * requestLine; /* In connection;s internal buffer */ | |||||
976 | ||||||
977 | readRequestField(sessionP, deadline, &requestLine, &httpErrorCode); | |||||
| ||||||
978 | if (httpErrorCode) { | |||||
979 | xmlrpc_asprintf(errorP, "Problem getting the request header"); | |||||
980 | *httpErrorCodeP = httpErrorCode; | |||||
981 | } else { | |||||
982 | TMethod httpMethod; | |||||
983 | const char * host; | |||||
984 | const char * path; | |||||
985 | const char * query; | |||||
986 | unsigned short port; | |||||
987 | bool moreFields; | |||||
988 | ||||||
989 | parseRequestLine(requestLine, &httpMethod, &sessionP->version, | |||||
990 | &host, &port, &path, &query, | |||||
991 | &moreFields, &httpErrorCode); | |||||
992 | ||||||
993 | if (httpErrorCode) { | |||||
994 | xmlrpc_asprintf(errorP, "Unable to parse the request header " | |||||
995 | "'%s'", requestLine); | |||||
996 | *httpErrorCodeP = httpErrorCode; | |||||
997 | } else { | |||||
998 | initRequestInfo(&sessionP->requestInfo, sessionP->version, | |||||
999 | requestLine, | |||||
1000 | httpMethod, host, port, path, query); | |||||
1001 | ||||||
1002 | if (moreFields) { | |||||
1003 | readAndProcessHeaderFields(sessionP, deadline, | |||||
1004 | errorP, httpErrorCodeP); | |||||
1005 | } else | |||||
1006 | *errorP = NULL((void*)0); | |||||
1007 | ||||||
1008 | if (!*errorP) | |||||
1009 | sessionP->validRequest = true; | |||||
1010 | ||||||
1011 | xmlrpc_strfreenull(host); | |||||
1012 | xmlrpc_strfree(path); | |||||
1013 | xmlrpc_strfreenull(query); | |||||
1014 | } | |||||
1015 | } | |||||
1016 | } | |||||
1017 | ||||||
1018 | ||||||
1019 | ||||||
1020 | char * | |||||
1021 | RequestHeaderValue(TSession * const sessionP, | |||||
1022 | const char * const name) { | |||||
1023 | ||||||
1024 | return (TableFind(&sessionP->requestHeaderFields, name)); | |||||
1025 | } | |||||
1026 | ||||||
1027 | ||||||
1028 | ||||||
1029 | bool | |||||
1030 | RequestValidURI(TSession * const sessionP) { | |||||
1031 | ||||||
1032 | if (!sessionP->requestInfo.uri) | |||||
1033 | return FALSE0; | |||||
1034 | ||||||
1035 | if (xmlrpc_streq(sessionP->requestInfo.uri, "*")) | |||||
1036 | return (sessionP->requestInfo.method != m_options); | |||||
1037 | ||||||
1038 | if (strchr(sessionP->requestInfo.uri, '*')(__extension__ (__builtin_constant_p ('*') && !__builtin_constant_p (sessionP->requestInfo.uri) && ('*') == '\0' ? (char *) __rawmemchr (sessionP->requestInfo.uri, '*') : __builtin_strchr (sessionP->requestInfo.uri, '*')))) | |||||
1039 | return FALSE0; | |||||
1040 | ||||||
1041 | return TRUE1; | |||||
1042 | } | |||||
1043 | ||||||
1044 | ||||||
1045 | ||||||
1046 | bool | |||||
1047 | RequestValidURIPath(TSession * const sessionP) { | |||||
1048 | ||||||
1049 | uint32_t i; | |||||
1050 | const char * p; | |||||
1051 | ||||||
1052 | p = sessionP->requestInfo.uri; | |||||
1053 | ||||||
1054 | i = 0; | |||||
1055 | ||||||
1056 | if (*p == '/') { | |||||
1057 | i = 1; | |||||
1058 | while (*p) | |||||
1059 | if (*(p++) == '/') { | |||||
1060 | if (*p == '/') | |||||
1061 | break; | |||||
1062 | else if ((xmlrpc_strneq(p,"./", 2)) || (xmlrpc_streq(p, "."))) | |||||
1063 | ++p; | |||||
1064 | else if ((xmlrpc_strneq(p, "../", 2)) || | |||||
1065 | (xmlrpc_streq(p, ".."))) { | |||||
1066 | p += 2; | |||||
1067 | --i; | |||||
1068 | if (i == 0) | |||||
1069 | break; | |||||
1070 | } | |||||
1071 | /* Prevent accessing hidden files (starting with .) */ | |||||
1072 | else if (*p == '.') | |||||
1073 | return FALSE0; | |||||
1074 | else | |||||
1075 | if (*p) | |||||
1076 | ++i; | |||||
1077 | } | |||||
1078 | } | |||||
1079 | return (*p == 0 && i > 0); | |||||
1080 | } | |||||
1081 | ||||||
1082 | ||||||
1083 | ||||||
1084 | bool | |||||
1085 | RequestAuth(TSession * const sessionP, | |||||
1086 | const char * const credential, | |||||
1087 | const char * const user, | |||||
1088 | const char * const pass) { | |||||
1089 | /*---------------------------------------------------------------------------- | |||||
1090 | Authenticate requester, in a very simplistic fashion. | |||||
1091 | ||||||
1092 | If the request executing on session *sessionP specifies basic | |||||
1093 | authentication (via Authorization header) with username 'user', password | |||||
1094 | 'pass', then return TRUE. Else, return FALSE and set up an authorization | |||||
1095 | failure response (HTTP response status 401) that says user must supply an | |||||
1096 | identity in the 'credential' domain. | |||||
1097 | ||||||
1098 | When we return TRUE, we also set the username in the request info for the | |||||
1099 | session to 'user' so that a future SessionGetRequestInfo can get it. | |||||
1100 | -----------------------------------------------------------------------------*/ | |||||
1101 | bool authorized; | |||||
1102 | char * authHdrPtr; | |||||
1103 | ||||||
1104 | authHdrPtr = RequestHeaderValue(sessionP, "authorization"); | |||||
1105 | if (authHdrPtr) { | |||||
1106 | const char * authType; | |||||
1107 | NextToken((const char **)&authHdrPtr); | |||||
1108 | GetTokenConst(&authHdrPtr, &authType); | |||||
1109 | authType = GetToken(&authHdrPtr); | |||||
1110 | if (authType) { | |||||
1111 | if (xmlrpc_strcaseeq(authType, "basic")) { | |||||
1112 | const char * userPass; | |||||
1113 | char userPassEncoded[80]; | |||||
1114 | ||||||
1115 | NextToken((const char **)&authHdrPtr); | |||||
1116 | ||||||
1117 | xmlrpc_asprintf(&userPass, "%s:%s", user, pass); | |||||
1118 | xmlrpc_base64Encode(userPass, userPassEncoded); | |||||
1119 | xmlrpc_strfree(userPass); | |||||
1120 | ||||||
1121 | if (xmlrpc_streq(authHdrPtr, userPassEncoded)) { | |||||
1122 | sessionP->requestInfo.user = strdup(user)(__extension__ (__builtin_constant_p (user) && ((size_t )(const void *)((user) + 1) - (size_t)(const void *)(user) == 1) ? (((const char *) (user))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (user) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, user, __len) ; __retval; })) : __strdup (user))); | |||||
1123 | authorized = TRUE1; | |||||
1124 | } else | |||||
1125 | authorized = FALSE0; | |||||
1126 | } else | |||||
1127 | authorized = FALSE0; | |||||
1128 | } else | |||||
1129 | authorized = FALSE0; | |||||
1130 | } else | |||||
1131 | authorized = FALSE0; | |||||
1132 | ||||||
1133 | if (!authorized) { | |||||
1134 | const char * hdrValue; | |||||
1135 | xmlrpc_asprintf(&hdrValue, "Basic realm=\"%s\"", credential); | |||||
1136 | ResponseAddField(sessionP, "WWW-Authenticate", hdrValue); | |||||
1137 | ||||||
1138 | xmlrpc_strfree(hdrValue); | |||||
1139 | ||||||
1140 | ResponseStatus(sessionP, 401); | |||||
1141 | } | |||||
1142 | return authorized; | |||||
1143 | } | |||||
1144 | ||||||
1145 | ||||||
1146 | ||||||
1147 | /********************************************************************* | |||||
1148 | ** Range | |||||
1149 | *********************************************************************/ | |||||
1150 | ||||||
1151 | abyss_bool | |||||
1152 | RangeDecode(char * const strArg, | |||||
1153 | xmlrpc_uint64_t const filesize, | |||||
1154 | xmlrpc_uint64_t * const start, | |||||
1155 | xmlrpc_uint64_t * const end) { | |||||
1156 | ||||||
1157 | char *str; | |||||
1158 | char *ss; | |||||
1159 | ||||||
1160 | str = strArg; /* initial value */ | |||||
1161 | ||||||
1162 | *start=0; | |||||
1163 | *end=filesize-1; | |||||
1164 | ||||||
1165 | if (*str=='-') | |||||
1166 | { | |||||
1167 | *start=filesize-strtol(str+1,&ss,10); | |||||
1168 | return ((ss!=str) && (!*ss)); | |||||
1169 | }; | |||||
1170 | ||||||
1171 | *start=strtol(str,&ss,10); | |||||
1172 | ||||||
1173 | if ((ss==str) || (*ss!='-')) | |||||
1174 | return FALSE0; | |||||
1175 | ||||||
1176 | str=ss+1; | |||||
1177 | ||||||
1178 | if (!*str) | |||||
1179 | return TRUE1; | |||||
1180 | ||||||
1181 | *end=strtol(str,&ss,10); | |||||
1182 | ||||||
1183 | if ((ss==str) || (*ss) || (*end<*start)) | |||||
1184 | return FALSE0; | |||||
1185 | ||||||
1186 | return TRUE1; | |||||
1187 | } | |||||
1188 | ||||||
1189 | /********************************************************************* | |||||
1190 | ** HTTP | |||||
1191 | *********************************************************************/ | |||||
1192 | ||||||
1193 | const char * | |||||
1194 | HTTPReasonByStatus(uint16_t const code) { | |||||
1195 | ||||||
1196 | struct _HTTPReasons { | |||||
1197 | uint16_t status; | |||||
1198 | const char * reason; | |||||
1199 | }; | |||||
1200 | ||||||
1201 | static struct _HTTPReasons const reasons[] = { | |||||
1202 | { 100,"Continue" }, | |||||
1203 | { 101,"Switching Protocols" }, | |||||
1204 | { 200,"OK" }, | |||||
1205 | { 201,"Created" }, | |||||
1206 | { 202,"Accepted" }, | |||||
1207 | { 203,"Non-Authoritative Information" }, | |||||
1208 | { 204,"No Content" }, | |||||
1209 | { 205,"Reset Content" }, | |||||
1210 | { 206,"Partial Content" }, | |||||
1211 | { 300,"Multiple Choices" }, | |||||
1212 | { 301,"Moved Permanently" }, | |||||
1213 | { 302,"Moved Temporarily" }, | |||||
1214 | { 303,"See Other" }, | |||||
1215 | { 304,"Not Modified" }, | |||||
1216 | { 305,"Use Proxy" }, | |||||
1217 | { 400,"Bad Request" }, | |||||
1218 | { 401,"Unauthorized" }, | |||||
1219 | { 402,"Payment Required" }, | |||||
1220 | { 403,"Forbidden" }, | |||||
1221 | { 404,"Not Found" }, | |||||
1222 | { 405,"Method Not Allowed" }, | |||||
1223 | { 406,"Not Acceptable" }, | |||||
1224 | { 407,"Proxy Authentication Required" }, | |||||
1225 | { 408,"Request Timeout" }, | |||||
1226 | { 409,"Conflict" }, | |||||
1227 | { 410,"Gone" }, | |||||
1228 | { 411,"Length Required" }, | |||||
1229 | { 412,"Precondition Failed" }, | |||||
1230 | { 413,"Request Entity Too Large" }, | |||||
1231 | { 414,"Request-URI Too Long" }, | |||||
1232 | { 415,"Unsupported Media Type" }, | |||||
1233 | { 500,"Internal Server Error" }, | |||||
1234 | { 501,"Not Implemented" }, | |||||
1235 | { 502,"Bad Gateway" }, | |||||
1236 | { 503,"Service Unavailable" }, | |||||
1237 | { 504,"Gateway Timeout" }, | |||||
1238 | { 505,"HTTP Version Not Supported" }, | |||||
1239 | { 000, NULL((void*)0) } | |||||
1240 | }; | |||||
1241 | const struct _HTTPReasons * reasonP; | |||||
1242 | ||||||
1243 | reasonP = &reasons[0]; | |||||
1244 | ||||||
1245 | while (reasonP->status <= code) | |||||
1246 | if (reasonP->status == code) | |||||
1247 | return reasonP->reason; | |||||
1248 | else | |||||
1249 | ++reasonP; | |||||
1250 | ||||||
1251 | return "No Reason"; | |||||
1252 | } | |||||
1253 | ||||||
1254 | ||||||
1255 | ||||||
1256 | int32_t | |||||
1257 | HTTPRead(TSession * const s ATTR_UNUSED__attribute__((__unused__)), | |||||
1258 | const char * const buffer ATTR_UNUSED__attribute__((__unused__)), | |||||
1259 | uint32_t const len ATTR_UNUSED__attribute__((__unused__))) { | |||||
1260 | ||||||
1261 | return 0; | |||||
1262 | } | |||||
1263 | ||||||
1264 | ||||||
1265 | ||||||
1266 | bool | |||||
1267 | HTTPWriteBodyChunk(TSession * const sessionP, | |||||
1268 | const char * const buffer, | |||||
1269 | uint32_t const len) { | |||||
1270 | ||||||
1271 | if (sessionP->chunkedwrite && sessionP->chunkedwritemode) { | |||||
1272 | char chunkHeader[16]; | |||||
1273 | ||||||
1274 | sprintf(chunkHeader, "%x\r\n", len); | |||||
1275 | ||||||
1276 | if (ConnWrite(sessionP->connP, chunkHeader, strlen(chunkHeader))) | |||||
1277 | if (ConnWrite(sessionP->connP, buffer, len)) | |||||
1278 | return ConnWrite(sessionP->connP, "\r\n", 2); | |||||
1279 | ||||||
1280 | return FALSE0; | |||||
1281 | } else | |||||
1282 | return ConnWrite(sessionP->connP, buffer, len); | |||||
1283 | ||||||
1284 | } | |||||
1285 | ||||||
1286 | ||||||
1287 | ||||||
1288 | bool | |||||
1289 | HTTPWriteEndChunk(TSession * const sessionP) { | |||||
1290 | ||||||
1291 | bool retval = TRUE1; | |||||
1292 | ||||||
1293 | if (sessionP->chunkedwritemode && sessionP->chunkedwrite) { | |||||
1294 | /* May be one day trailer dumping will be added */ | |||||
1295 | sessionP->chunkedwritemode = FALSE0; | |||||
1296 | retval = ConnWrite(sessionP->connP, "0\r\n\r\n", 5); | |||||
1297 | } | |||||
1298 | ||||||
1299 | return retval; | |||||
1300 | } | |||||
1301 | ||||||
1302 | ||||||
1303 | ||||||
1304 | bool | |||||
1305 | HTTPKeepalive(TSession * const sessionP) { | |||||
1306 | /*---------------------------------------------------------------------------- | |||||
1307 | Return value: the connection should be kept alive after the session | |||||
1308 | *sessionP is over. | |||||
1309 | -----------------------------------------------------------------------------*/ | |||||
1310 | return (sessionP->requestInfo.keepalive && | |||||
1311 | !sessionP->serverDeniesKeepalive && | |||||
1312 | sessionP->status < 400); | |||||
1313 | } | |||||
1314 | ||||||
1315 | ||||||
1316 | ||||||
1317 | bool | |||||
1318 | HTTPWriteContinue(TSession * const sessionP) { | |||||
1319 | ||||||
1320 | char const continueStatus[] = "HTTP/1.1 100 continue\r\n\r\n"; | |||||
1321 | /* This is a status line plus an end-of-headers empty line */ | |||||
1322 | ||||||
1323 | return ConnWrite(sessionP->connP, continueStatus, strlen(continueStatus)); | |||||
1324 | } | |||||
1325 | ||||||
1326 | ||||||
1327 | ||||||
1328 | /****************************************************************************** | |||||
1329 | ** | |||||
1330 | ** http.c | |||||
1331 | ** | |||||
1332 | ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>. | |||||
1333 | ** All rights reserved. | |||||
1334 | ** | |||||
1335 | ** Redistribution and use in source and binary forms, with or without | |||||
1336 | ** modification, are permitted provided that the following conditions | |||||
1337 | ** are met: | |||||
1338 | ** 1. Redistributions of source code must retain the above copyright | |||||
1339 | ** notice, this list of conditions and the following disclaimer. | |||||
1340 | ** 2. Redistributions in binary form must reproduce the above copyright | |||||
1341 | ** notice, this list of conditions and the following disclaimer in the | |||||
1342 | ** documentation and/or other materials provided with the distribution. | |||||
1343 | ** 3. The name of the author may not be used to endorse or promote products | |||||
1344 | ** derived from this software without specific prior written permission. | |||||
1345 | ** | |||||
1346 | ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
1347 | ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
1348 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
1349 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
1350 | ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
1351 | ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
1352 | ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
1353 | ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
1354 | ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
1355 | ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
1356 | ** SUCH DAMAGE. | |||||
1357 | ** | |||||
1358 | ******************************************************************************/ |