test: Fix memory leak if HTTP client header was missing a colon
[libisds.git] / test / simline / http.c
blobb340e5b903e497f048b07d3b485b758236f9b97d
1 #ifndef _XOPEN_SOURCE
2 #define _XOPEN_SOURCE 500 /* For strdup() */
3 #endif
5 #include "http.h"
6 #include "../test-tools.h"
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <stdio.h> /* fprintf() */
12 #include <limits.h>
13 #include <string.h> /* strdup() */
14 #include <strings.h> /* strcasecmp() */
15 #include <stdint.h> /* int8_t */
16 #include <stddef.h> /* size_t, NULL */
17 #include <ctype.h> /* isprint() */
18 #include <sys/socket.h> /* MSG_NOSIGNAL for http_send_callback() */
21 Base64 encoder is part of the libb64 project, and has been placed in the public domain.
22 For details, see http://sourceforge.net/projects/libb64
23 It's copy of ../../src/cencode.c due to symbol names.
27 typedef enum {
28 step_A, step_B, step_C
29 } base64_encodestep;
31 typedef struct {
32 base64_encodestep step;
33 int8_t result;
34 int stepcount; /* number of encoded octet triplets on a line,
35 or -1 to for end-less line */
36 } base64_encodestate;
38 static const int CHARS_PER_LINE = 72;
40 /* Initialize Base64 coder.
41 * @one_line is false for multi-line MIME encoding,
42 * true for endless one-line format. */
43 static void base64_init_encodestate(base64_encodestate* state_in, _Bool one_line) {
44 state_in->step = step_A;
45 state_in->result = 0;
46 state_in->stepcount = (one_line) ? -1 : 0;
50 static int8_t base64_encode_value(int8_t value_in) {
51 /* XXX: CHAR_BIT == 8 because of <stdint.h> */
52 static const int8_t encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
53 if (value_in > 63) return '=';
54 return encoding[value_in];
58 static size_t base64_encode_block(const int8_t* plaintext_in,
59 size_t length_in, int8_t *code_out, base64_encodestate* state_in) {
60 const int8_t *plainchar = plaintext_in;
61 const int8_t* const plaintextend = plaintext_in + length_in;
62 int8_t *codechar = code_out;
63 int8_t result;
64 int8_t fragment;
66 result = state_in->result;
68 switch (state_in->step) {
69 while (1) {
70 case step_A:
71 if (plainchar == plaintextend) {
72 state_in->result = result;
73 state_in->step = step_A;
74 return codechar - code_out;
76 fragment = *plainchar++;
77 result = (fragment & 0x0fc) >> 2;
78 *codechar++ = base64_encode_value(result);
79 result = (fragment & 0x003) << 4;
80 case step_B:
81 if (plainchar == plaintextend) {
82 state_in->result = result;
83 state_in->step = step_B;
84 return codechar - code_out;
86 fragment = *plainchar++;
87 result |= (fragment & 0x0f0) >> 4;
88 *codechar++ = base64_encode_value(result);
89 result = (fragment & 0x00f) << 2;
90 case step_C:
91 if (plainchar == plaintextend) {
92 state_in->result = result;
93 state_in->step = step_C;
94 return codechar - code_out;
96 fragment = *plainchar++;
97 result |= (fragment & 0x0c0) >> 6;
98 *codechar++ = base64_encode_value(result);
99 result = (fragment & 0x03f) >> 0;
100 *codechar++ = base64_encode_value(result);
102 if (state_in->stepcount >= 0) {
103 ++(state_in->stepcount);
104 if (state_in->stepcount == CHARS_PER_LINE/4) {
105 *codechar++ = '\n';
106 state_in->stepcount = 0;
109 } /* while */
110 } /* switch */
112 /* control should not reach here */
113 return codechar - code_out;
117 static size_t base64_encode_blockend(int8_t *code_out,
118 base64_encodestate* state_in) {
119 int8_t *codechar = code_out;
121 switch (state_in->step) {
122 case step_B:
123 *codechar++ = base64_encode_value(state_in->result);
124 *codechar++ = '=';
125 *codechar++ = '=';
126 break;
127 case step_C:
128 *codechar++ = base64_encode_value(state_in->result);
129 *codechar++ = '=';
130 break;
131 case step_A:
132 break;
134 if (state_in->stepcount >= 0)
135 *codechar++ = '\n';
137 return codechar - code_out;
141 /* Encode given data into MIME Base64 encoded zero terminated string.
142 * @plain are input data (binary stream)
143 * @length is length of @plain data in bytes
144 * @one_line is false for multi-line MIME encoding,
145 * true for endless one-line format.
146 * @return allocated string of base64 encoded plain data or NULL in case of
147 * error. You must free it. */
148 /* TODO: Allow one-line format */
149 static char *base64encode(const void *plain, const size_t length,
150 _Bool one_line) {
152 base64_encodestate state;
153 size_t code_length;
154 char *buffer, *new_buffer;
156 if (!plain) {
157 if (length) return NULL;
158 /* Empty input is valid input */
159 plain = "";
162 base64_init_encodestate(&state, one_line);
164 /* Allocate buffer
165 * (4 is padding, 1 is final new line, and 1 is string terminator) */
166 buffer = malloc(length * 2 + 4 + 1 + 1);
167 if (!buffer) return NULL;
169 /* Encode plain data */
170 code_length = base64_encode_block(plain, length, (int8_t *)buffer,
171 &state);
172 code_length += base64_encode_blockend(((int8_t*)buffer) + code_length,
173 &state);
175 /* Terminate string */
176 buffer[code_length++] = '\0';
178 /* Shrink the buffer */
179 new_buffer = realloc(buffer, code_length);
180 if (new_buffer) buffer = new_buffer;
182 return buffer;
186 /* Convert hexadecimal digit to integer. Return negative value if charcter is
187 * not valid hexadecimal digit. */
188 static int hex2i(char digit) {
189 if (digit >= '0' && digit <= '9')
190 return digit - '0';
191 if (digit >= 'a' && digit <= 'f')
192 return digit - 'a' + 10;
193 if (digit >= 'A' && digit <= 'F')
194 return digit - 'A' + 10;
195 return -1;
199 /* Decode URI-coded string.
200 * @return allocated decoded string or NULL in case of error. */
201 static char *uri_decode(const char *coded) {
202 char *plain, *p;
203 int digit1, digit2;
205 if (coded == NULL) return NULL;
206 plain = malloc(strlen(coded) + 1);
207 if (plain == NULL) return NULL;
209 for (p = plain; *coded != '\0'; p++, coded++) {
210 if (*coded == '%') {
211 digit1 = hex2i(coded[1]);
212 if (digit1 < 0) {
213 free(plain);
214 return NULL;
216 digit2 = hex2i(coded[2]);
217 if (digit2< 0) {
218 free(plain);
219 return NULL;
221 *plain = (digit1 << 4) + digit2;
222 coded += 2;
223 } else {
224 *p = *coded;
227 *p = '\0';
229 return plain;
233 /* Read a line from HTTP socket.
234 * @connection is HTTP connection to read from.
235 * @line is auto-reallocated just read line. Will be NULL if EOF has been
236 * reached or error occured.
237 * @buffer is automatically reallocated buffer for the socket. It can preserve
238 * prematurately read socket data.
239 * @buffer_size is allocated size of @buffer
240 * @buffer_length is size of head of the buffer that holds read data.
241 * @return 0 in success. */
242 static int http_read_line(const struct http_connection *connection,
243 char **line, char **buffer, size_t *buffer_size,
244 size_t *buffer_used) {
245 ssize_t got;
246 char *p, *tmp;
248 if (line == NULL) return HTTP_ERROR_SERVER;
249 free(*line);
250 *line = NULL;
252 if (connection == NULL || connection->recv_callback == NULL)
253 return HTTP_ERROR_SERVER;
254 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
255 return HTTP_ERROR_SERVER;
256 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
257 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
259 #define BURST 1024
260 while (1) {
261 /* Check for EOL */
262 for (p = *buffer; p < *buffer + *buffer_used; p++)
264 if (*p != '\r')
265 continue;
266 if (!(p + 1 < *buffer + *buffer_used && p[1] == '\n'))
267 continue;
269 /* EOL found */
270 /* Crop by zero at EOL */
271 *p = '\0';
272 p += 2;
273 /* Copy read ahead data to new buffer and point line to original
274 * buffer. */
275 tmp = malloc(BURST);
276 if (tmp == NULL) return HTTP_ERROR_SERVER;
277 memcpy(tmp, p, *buffer + *buffer_used - p);
278 *line = *buffer;
279 *buffer_size = BURST;
280 *buffer_used = *buffer + *buffer_used - p;
281 *buffer = tmp;
282 /* And exit */
283 return HTTP_ERROR_SUCCESS;
286 if (*buffer_size == *buffer_used) {
287 /* Grow buffer */
288 tmp = realloc(*buffer, *buffer_size + BURST);
289 if (tmp == NULL) return HTTP_ERROR_SERVER;
290 *buffer = tmp;
291 *buffer_size += BURST;
294 /* Read data */
295 got = connection->recv_callback(connection, *buffer + *buffer_used,
296 *buffer_size - *buffer_used);
297 if (got == -1) return HTTP_ERROR_CLIENT;
299 /* Check for EOF */
300 if (got == 0) return HTTP_ERROR_CLIENT;
302 /* Move end of buffer */
303 *buffer_used += got;
305 #undef BURST
307 return HTTP_ERROR_SERVER;
311 /* Write a bulk data into HTTP socket.
312 * @connection is HTTP connection to write to.
313 * @data are bitstream to send to client.
314 * @length is size of @data in bytes.
315 * @return 0 in success. */
316 static int http_write_bulk(const struct http_connection *connection,
317 const void *data, size_t length) {
318 ssize_t written;
319 const void *end;
321 if (connection == NULL || connection->send_callback == NULL)
322 return HTTP_ERROR_SERVER;
323 if (data == NULL && length > 0) return HTTP_ERROR_SERVER;
325 for (end = data + length; data != end; data += written, length -= written) {
326 written = connection->send_callback(connection, data, length);
327 if (written == -1) return HTTP_ERROR_CLIENT;
330 return HTTP_ERROR_SUCCESS;
334 /* Write a line into HTTP socket.
335 * @connection is HTTP connection to write to.
336 * @line is NULL terminated string to send to client. HTTP EOL will be added.
337 * @return 0 in success. */
338 static int http_write_line(const struct http_connection *connection,
339 const char *line) {
340 int error;
341 if (line == NULL) return HTTP_ERROR_SERVER;
343 fprintf(stderr, "Response: <%s>\n", line);
345 /* Send the line */
346 if ((error = http_write_bulk(connection, line, strlen(line))))
347 return error;
349 /* Send EOL */
350 if ((error = http_write_bulk(connection, "\r\n", 2)))
351 return error;
353 return HTTP_ERROR_SUCCESS;
357 /* Read data of given length from HTTP socket.
358 * @connection is HTTP connection to read from.
359 * @data is auto-allocated just read data bulk. Will be NULL if EOF has been
360 * reached or error occured.
361 * @data_length is size of bytes to read.
362 * @buffer is automatically reallocated buffer for the socket. It can preserve
363 * prematurately read socket data.
364 * @buffer_size is allocated size of @buffer
365 * @buffer_length is size of head of the buffer that holds read data.
366 * @return 0 in success. */
367 static int http_read_bulk(const struct http_connection *connection,
368 void **data, size_t data_length,
369 char **buffer, size_t *buffer_size, size_t *buffer_used) {
370 ssize_t got;
371 char *tmp;
373 if (data == NULL) return HTTP_ERROR_SERVER;
374 *data = NULL;
376 if (connection == NULL || connection->recv_callback == NULL)
377 return HTTP_ERROR_SERVER;
378 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
379 return HTTP_ERROR_SERVER;
380 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
381 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
383 if (data_length <= 0) return HTTP_ERROR_SUCCESS;
385 #define BURST 1024
386 while (1) {
387 /* Check whether enought data have been read */
388 if (*buffer_used >= data_length) {
389 /* Copy read ahead data to new buffer and point data to original
390 * buffer. */
391 tmp = malloc(BURST);
392 if (tmp == NULL) return HTTP_ERROR_SERVER;
393 memcpy(tmp, *buffer + data_length, *buffer_used - data_length);
394 *data = *buffer;
395 *buffer_size = BURST;
396 *buffer_used = *buffer_used - data_length;
397 *buffer = tmp;
398 /* And exit */
399 return HTTP_ERROR_SUCCESS;
402 if (*buffer_size == *buffer_used) {
403 /* Grow buffer */
404 tmp = realloc(*buffer, *buffer_size + BURST);
405 if (tmp == NULL) return HTTP_ERROR_SERVER;
406 *buffer = tmp;
407 *buffer_size += BURST;
410 /* Read data */
411 got = connection->recv_callback(connection, *buffer + *buffer_used,
412 *buffer_size - *buffer_used);
413 if (got == -1) return HTTP_ERROR_CLIENT;
415 /* Check for EOF */
416 if (got == 0) return HTTP_ERROR_CLIENT;
418 /* Move end of buffer */
419 *buffer_used += got;
421 #undef BURST
423 return HTTP_ERROR_SERVER;
427 /* Parse HTTP request header.
428 * @request is pre-allocated HTTP request.
429 * @return 0 if success. */
430 static int http_parse_request_header(char *line,
431 struct http_request *request) {
432 char *p;
434 fprintf(stderr, "Request: <%s>\n", line);
436 /* Get method */
437 p = strchr(line, ' ');
438 if (p == NULL) return HTTP_ERROR_SERVER;
439 *p = '\0';
440 if (!strcmp(line, "GET"))
441 request->method = HTTP_METHOD_GET;
442 else if (!strcmp(line, "POST"))
443 request->method = HTTP_METHOD_POST;
444 else
445 request->method = HTTP_METHOD_UNKNOWN;
446 line = p + 1;
448 /* Get URI */
449 p = strchr(line, ' ');
450 if (p != NULL) *p = '\0';
451 request->uri = uri_decode(line);
452 if (request->uri == NULL) return HTTP_ERROR_SERVER;
454 /* Do not care about HTTP version */
456 fprintf(stderr, "Request-URI: <%s>\n", request->uri);
457 return HTTP_ERROR_SUCCESS;
461 /* Send HTTP response status line to client.
462 * @return 0 if success. */
463 static int http_write_response_status(const struct http_connection *connection,
464 const struct http_response *response) {
465 char *buffer = NULL;
466 int error;
468 if (response == NULL) return HTTP_ERROR_SERVER;
470 if (-1 == test_asprintf(&buffer, "HTTP/1.0 %u %s", response->status,
471 (response->reason == NULL) ? "" : response->reason))
472 return HTTP_ERROR_SERVER;
473 error = http_write_line(connection, buffer);
474 free(buffer);
476 return error;
480 /* Parse generic HTTP header.
481 * @request is pre-allocated HTTP request.
482 * @return 0 if success, negative value if internal error, positive value if
483 * header is not valid. */
484 static int http_parse_header(char *line, struct http_request *request) {
485 struct http_header *header;
487 if (line == NULL || request == NULL) return HTTP_ERROR_SERVER;
489 fprintf(stderr, "Header: <%s>\n", line);
491 /* Find last used header */
492 for (header = request->headers; header != NULL && header->next != NULL;
493 header = header->next);
495 if (*line == ' ' || *line == '\t') {
496 /* Line is continuation of last header */
497 if (header == NULL)
498 return HTTP_ERROR_CLIENT; /* No previous header to continue */
499 line++;
500 size_t old_length = strlen(header->value);
501 char *tmp = realloc(header->value,
502 sizeof(header->value[0]) * (old_length + strlen(line) + 1));
503 if (tmp == NULL) return HTTP_ERROR_SERVER;
504 header->value = tmp;
505 strcpy(&header->value[old_length], line);
506 } else {
507 /* New header */
508 struct http_header *new_header = calloc(sizeof(*new_header), 1);
509 if (new_header == NULL) return HTTP_ERROR_SERVER;
511 char *p = strstr(line, ": ");
512 if (p == NULL) {
513 http_header_free(&new_header);
514 return HTTP_ERROR_CLIENT;
517 size_t length = p - line;
518 new_header->name = malloc(sizeof(line[0]) * (length + 1));
519 if (new_header->name == NULL) {
520 http_header_free(&new_header);
521 return HTTP_ERROR_SERVER;
523 strncpy(new_header->name, line, length);
524 new_header->name[length] = '\0';
526 p += 2;
527 length = strlen(p);
528 new_header->value = malloc(sizeof(p[0]) * (length + 1));
529 if (new_header->value == NULL) {
530 http_header_free(&new_header);
531 return HTTP_ERROR_SERVER;
533 strcpy(new_header->value, p);
535 if (request->headers == NULL)
536 request->headers = new_header;
537 else
538 header->next = new_header;
542 /* FIXME: Decode. After parsing all headers as we could decode begining
543 * and then got encoded continuation. */
544 return HTTP_ERROR_SUCCESS;
548 /* Send HTTP header to client.
549 * @return 0 if success. */
550 static int http_write_header(const struct http_connection *connection,
551 const struct http_header *header) {
552 char *buffer = NULL;
553 int error;
555 if (header == NULL) return HTTP_ERROR_SERVER;
557 if (header->name == NULL) return HTTP_ERROR_SERVER;
559 /* TODO: Quote, split long lines */
560 if (-1 == test_asprintf(&buffer, "%s: %s", header->name,
561 (header->value == NULL) ? "" : header->value))
562 return HTTP_ERROR_SERVER;
563 error = http_write_line(connection, buffer);
564 free(buffer);
566 return error;
570 /* Find Content-Length value in HTTP request headers and set it into @request.
571 * @return 0 in success. */
572 static int find_content_length(struct http_request *request) {
573 struct http_header *header;
574 if (request == NULL) return HTTP_ERROR_SERVER;
576 for (header = request->headers; header != NULL; header = header->next) {
577 if (header->name == NULL) continue;
578 if (!strcasecmp(header->name, "Content-Length")) break;
581 if (header != NULL && header->value != NULL) {
582 char *p;
583 long long int value = strtoll(header->value, &p, 10);
584 if (*p != '\0')
585 return HTTP_ERROR_CLIENT;
586 if ((value == LLONG_MIN || value == LLONG_MAX) && errno == ERANGE)
587 return HTTP_ERROR_SERVER;
588 if (value < 0)
589 return HTTP_ERROR_CLIENT;
590 if (value > SIZE_MAX)
591 return HTTP_ERROR_SERVER;
592 request->body_length = value;
593 } else {
594 request->body_length = 0;
596 return HTTP_ERROR_SUCCESS;
600 /* Print binary stream to STDERR with some escapes */
601 static void dump_body(const void *data, size_t length) {
602 fprintf(stderr, "===BEGIN BODY===\n");
603 if (length > 0 && NULL != data) {
604 for (size_t i = 0; i < length; i++) {
605 if (isprint(((const unsigned char *)data)[i]))
606 fprintf(stderr, "%c", ((const unsigned char *)data)[i]);
607 else
608 fprintf(stderr, "\\x%02x", ((const unsigned char*)data)[i]);
610 fprintf(stderr, "\n");
612 fprintf(stderr, "===END BODY===\n");
616 /* Read a HTTP request from connection.
617 * @http_request is heap-allocated received HTTP request,
618 * or NULL in case of error.
619 * @return http_error code. */
620 http_error http_read_request(const struct http_connection *connection,
621 struct http_request **request) {
622 char *line = NULL;
623 char *buffer = NULL;
624 size_t buffer_size = 0, buffer_used = 0;
625 int error;
627 if (request == NULL) return HTTP_ERROR_SERVER;
629 *request = calloc(1, sizeof(**request));
630 if (*request == NULL) return HTTP_ERROR_SERVER;
632 /* Get request header */
633 if ((error = http_read_line(connection, &line, &buffer, &buffer_size,
634 &buffer_used)))
635 goto leave;
636 if ((error = http_parse_request_header(line, *request)))
637 goto leave;
639 /* Get other headers */
640 while (1) {
641 if ((error = http_read_line(connection, &line, &buffer, &buffer_size,
642 &buffer_used))) {
643 fprintf(stderr, "Error while reading HTTP request line\n");
644 goto leave;
647 /* Check for headers delimiter */
648 if (line == NULL || *line == '\0') {
649 break;
652 if ((error = http_parse_header(line, *request))) {
653 fprintf(stderr, "Error while parsing HTTP request line: <%s>\n",
654 line);
655 goto leave;
659 /* Get body */
660 if ((error = find_content_length(*request))) {
661 fprintf(stderr, "Could not determine length of body\n");
662 goto leave;
664 if ((error = http_read_bulk(connection,
665 &(*request)->body, (*request)->body_length,
666 &buffer, &buffer_size, &buffer_used))) {
667 fprintf(stderr, "Could not read request body\n");
668 goto leave;
670 fprintf(stderr, "Body of size %zu B has been received:\n",
671 (*request)->body_length);
672 dump_body((*request)->body, (*request)->body_length);
674 leave:
675 free(line);
676 free(buffer);
677 if (error) http_request_free(request);
678 return error;
682 /* Write a HTTP response to connection. Auto-add Content-Length header.
683 * @return 0 in case of success. */
684 int http_write_response(const struct http_connection *connection,
685 const struct http_response *response) {
686 char *buffer = NULL;
687 int error = -1;
689 if (response == NULL) return HTTP_ERROR_SERVER;
691 /* Status line */
692 error = http_write_response_status(connection, response);
693 if (error) return error;
695 /* Headers */
696 for (struct http_header *header = response->headers; header != NULL;
697 header = header->next) {
698 error = http_write_header(connection, header);
699 if (error) return error;
701 if (-1 == test_asprintf(&buffer, "Content-Length: %u",
702 response->body_length))
703 return HTTP_ERROR_SERVER;
704 error = http_write_line(connection, buffer);
705 if (error) return error;
707 /* Headers trailer */
708 error = http_write_line(connection, "");
709 if (error) return error;
711 /* Body */
712 if (response->body_length > 0) {
713 error = http_write_bulk(connection, response->body,
714 response->body_length);
715 if (error) return error;
717 fprintf(stderr, "Body of size %zu B has been sent:\n",
718 response->body_length);
719 dump_body(response->body, response->body_length);
721 free(buffer);
722 return HTTP_ERROR_SUCCESS;
726 /* Build Set-Cookie header. In case of error, return NULL. Caller must free
727 * the header. */
728 static struct http_header *http_build_setcookie_header (
729 const char *cokie_name, const char *cookie_value,
730 const char *cookie_domain, const char *cookie_path) {
731 struct http_header *header = NULL;
732 char *domain_parameter = NULL;
733 char *path_parameter = NULL;
735 if (cokie_name == NULL) goto error;
737 header = calloc(1, sizeof(*header));
738 if (header == NULL) goto error;
740 header->name = strdup("Set-Cookie");
741 if (header->name == NULL) goto error;
743 if (cookie_domain != NULL)
744 if (-1 == test_asprintf(&domain_parameter, "; Domain=%s",
745 cookie_domain))
746 goto error;
748 if (cookie_path != NULL)
749 if (-1 == test_asprintf(&path_parameter, "; Path=%s",
750 cookie_path))
751 goto error;
753 if (-1 == test_asprintf(&header->value, "%s=%s%s%s",
754 cokie_name,
755 (cookie_value == NULL) ? "" : cookie_value,
756 (domain_parameter == NULL) ? "": domain_parameter,
757 (path_parameter == NULL) ? "": path_parameter))
758 goto error;
759 goto ok;
761 error:
762 http_header_free(&header);
764 free(domain_parameter);
765 free(path_parameter);
766 return header;
770 /* Send a 200 Ok response with a cookie */
771 int http_send_response_200_cookie(const struct http_connection *connection,
772 const char *cokie_name, const char *cookie_value,
773 const char *cookie_domain, const char *cookie_path,
774 const void *body, size_t body_length, const char *type) {
775 int retval;
776 struct http_header *header_cookie = NULL;
777 struct http_header header_contenttype = {
778 .name = "Content-Type",
779 .value = (char *)type,
780 .next = NULL
782 struct http_response response = {
783 .status = 200,
784 .reason = "OK",
785 .headers = NULL,
786 .body_length = body_length,
787 .body = (void *)body
790 if (cokie_name != NULL) {
791 if (NULL == (header_cookie = http_build_setcookie_header(
792 cokie_name, cookie_value, cookie_domain, cookie_path)))
793 return http_send_response_500(connection,
794 "Could not build Set-Cookie header");
797 /* Link defined headers */
798 if (type != NULL) {
799 response.headers = &header_contenttype;
801 if (header_cookie != NULL) {
802 header_cookie->next = response.headers;
803 response.headers = header_cookie;
806 retval = http_write_response(connection, &response);
807 http_header_free(&header_cookie);
808 return retval;
812 /* Send a 200 Ok response */
813 int http_send_response_200(const struct http_connection *connection,
814 const void *body, size_t body_length, const char *type) {
815 return http_send_response_200_cookie(connection,
816 NULL, NULL, NULL, NULL,
817 body, body_length, type);
821 /* Send a 302 Found response setting a cookie */
822 int http_send_response_302_cookie(const struct http_connection *connection,
823 const char *cokie_name, const char *cookie_value,
824 const char *cookie_domain, const char *cookie_path,
825 const char *location) {
826 int retval;
827 struct http_header *header_cookie = NULL;
828 struct http_header header_location = {
829 .name = "Location",
830 .value = (char *) location,
831 .next = NULL
833 struct http_response response = {
834 .status = 302,
835 .reason = "Found",
836 .headers = NULL,
837 .body_length = 0,
838 .body = NULL
841 if (cokie_name != NULL) {
842 if (NULL == (header_cookie = http_build_setcookie_header(
843 cokie_name, cookie_value, cookie_domain, cookie_path)))
844 return http_send_response_500(connection,
845 "Could not build Set-Cookie header");
848 /* Link defined headers */
849 if (location != NULL) {
850 response.headers = &header_location;
852 if (header_cookie != NULL) {
853 header_cookie->next = response.headers;
854 response.headers = header_cookie;
857 retval = http_write_response(connection, &response);
858 http_header_free(&header_cookie);
859 return retval;
863 /* Send a 302 Found response with totp authentication scheme header */
864 int http_send_response_302_totp(const struct http_connection *connection,
865 const char *code, const char *text, const char *location) {
866 struct http_header header_code = {
867 .name = "X-Response-message-code",
868 .value = (char *) code,
869 .next = NULL
871 struct http_header header_text = {
872 .name = "X-Response-message-text",
873 .value = (char *) text,
874 .next = NULL
876 struct http_header header_location = {
877 .name = "Location",
878 .value = (char *) location,
879 .next = NULL
881 struct http_response response = {
882 .status = 302,
883 .reason = "Found",
884 .headers = NULL,
885 .body_length = 0,
886 .body = NULL
889 /* Link defined headers */
890 if (location != NULL) {
891 response.headers = &header_location;
893 if (text != NULL) {
894 header_text.next = response.headers;
895 response.headers = &header_text;
897 if (code != NULL) {
898 header_code.next = response.headers;
899 response.headers = &header_code;
902 return http_write_response(connection, &response);
906 /* Send a 400 Bad Request response.
907 * Use non-NULL @reason to override status message. */
908 int http_send_response_400(const struct http_connection *connection,
909 const char *reason) {
910 struct http_response response = {
911 .status = 400,
912 .reason = (reason == NULL) ? "Bad Request" : (char *) reason,
913 .headers = NULL,
914 .body_length = 0,
915 .body = NULL
918 return http_write_response(connection, &response);
922 /* Send a 401 Unauthorized response with Basic authentication scheme header */
923 int http_send_response_401_basic(const struct http_connection *connection) {
924 struct http_header header = {
925 .name = "WWW-Authenticate",
926 .value = "Basic realm=\"SimulatedISDSServer\"",
927 .next = NULL
929 struct http_response response = {
930 .status = 401,
931 .reason = "Unauthorized",
932 .headers = &header,
933 .body_length = 0,
934 .body = NULL
937 return http_write_response(connection, &response);
941 /* Send a 401 Unauthorized response with OTP authentication scheme header for
942 * given @method. */
943 int http_send_response_401_otp(const struct http_connection *connection,
944 const char *method, const char *code, const char *text) {
945 int retval;
946 struct http_header header = {
947 .name = "WWW-Authenticate",
948 .value = NULL,
949 .next = NULL
951 struct http_header header_code = {
952 .name = "X-Response-message-code",
953 .value = (char *) code,
954 .next = NULL
956 struct http_header header_text = {
957 .name = "X-Response-message-text",
958 .value = (char *) text,
959 .next = NULL
961 struct http_response response = {
962 .status = 401,
963 .reason = "Unauthorized",
964 .headers = &header,
965 .body_length = 0,
966 .body = NULL
969 if (-1 == test_asprintf(&header.value, "%s realm=\"SimulatedISDSServer\"",
970 method)) {
971 return http_send_response_500(connection,
972 "Could not build WWW-Authenticate header value");
975 /* Link defined headers */
976 if (code != NULL) header.next = &header_code;
977 if (text != NULL) {
978 if (code != NULL)
979 header_code.next = &header_text;
980 else
981 header.next = &header_text;
984 retval = http_write_response(connection, &response);
985 free(header.value);
986 return retval;
990 /* Send a 403 Forbidden response */
991 int http_send_response_403(const struct http_connection *connection) {
992 struct http_response response = {
993 .status = 403,
994 .reason = "Forbidden",
995 .headers = NULL,
996 .body_length = 0,
997 .body = NULL
1000 return http_write_response(connection, &response);
1004 /* Send a 500 Internal Server Error response.
1005 * Use non-NULL @reason to override status message. */
1006 int http_send_response_500(const struct http_connection *connection,
1007 const char *reason) {
1008 struct http_response response = {
1009 .status = 500,
1010 .reason = (NULL == reason) ? "Internal Server Error" : (char *) reason,
1011 .headers = NULL,
1012 .body_length = 0,
1013 .body = NULL
1016 return http_write_response(connection, &response);
1020 /* Send a 503 Service Temporarily Unavailable response */
1021 int http_send_response_503(const struct http_connection *connection,
1022 const void *body, size_t body_length, const char *type) {
1023 struct http_header header = {
1024 .name = "Content-Type",
1025 .value = (char *)type
1027 struct http_response response = {
1028 .status = 503,
1029 .reason = "Service Temporarily Unavailable",
1030 .headers = (type == NULL) ? NULL : &header,
1031 .body_length = body_length,
1032 .body = (void *)body
1035 return http_write_response(connection, &response);
1039 /* Returns Authorization header in given request */
1040 static const struct http_header *find_authorization(
1041 const struct http_request *request) {
1042 const struct http_header *header;
1043 if (request == NULL) return NULL;
1044 for (header = request->headers; header != NULL; header = header->next) {
1045 if (header->name != NULL &&
1046 !strcasecmp(header->name, "Authorization"))
1047 break;
1049 return header;
1053 /* Return true if request carries Authorization header */
1054 int http_client_authenticates(const struct http_request *request) {
1055 if (find_authorization(request))
1056 return 1;
1057 else
1058 return 0;
1062 /* Return HTTP_ERROR_SUCCESS if request carries valid Basic credentials.
1063 * NULL @username or @password equales to empty string. */
1064 http_error http_authenticate_basic(const struct http_request *request,
1065 const char *username, const char *password) {
1066 const struct http_header *header;
1067 const char *basic_cookie_client;
1068 char *basic_cookie_plain = NULL, *basic_cookie_encoded = NULL;
1069 _Bool is_valid;
1071 header = find_authorization(request);
1072 if (header == NULL) return HTTP_ERROR_CLIENT;
1074 if (strncmp(header->value, "Basic ", 6)) return HTTP_ERROR_CLIENT;
1075 basic_cookie_client = header->value + 6;
1077 if (-1 == test_asprintf(&basic_cookie_plain, "%s:%s",
1078 (username == NULL) ? "" : username,
1079 (password == NULL) ? "" : password)) {
1080 return HTTP_ERROR_SERVER;
1083 basic_cookie_encoded = base64encode(basic_cookie_plain,
1084 strlen(basic_cookie_plain), 1);
1085 if (basic_cookie_encoded == NULL) {
1086 free(basic_cookie_plain);
1087 return HTTP_ERROR_SERVER;
1090 fprintf(stderr, "Authenticating basic: got=<%s>, expected=<%s> (%s)\n",
1091 basic_cookie_client, basic_cookie_encoded, basic_cookie_plain);
1092 free(basic_cookie_plain);
1094 is_valid = !strcmp(basic_cookie_encoded, basic_cookie_client);
1095 free(basic_cookie_encoded);
1097 return (is_valid) ? HTTP_ERROR_SUCCESS : HTTP_ERROR_CLIENT;
1101 /* Return HTTP_ERROR_SUCCESS if request carries valid OTP credentials.
1102 * NULL @username or @password or @otp equal to empty string. */
1103 http_error http_authenticate_otp(const struct http_request *request,
1104 const char *username, const char *password, const char *otp) {
1105 char *basic_password = NULL;
1106 http_error retval;
1108 /* Concatenate password and OTP code */
1109 if (-1 == test_asprintf(&basic_password, "%s%s",
1110 (password == NULL) ? "": password,
1111 (otp == NULL) ? "" : otp)) {
1112 return HTTP_ERROR_SERVER;
1115 /* Use Basic authentication */
1116 /* XXX: Specification does not define authorization method string */
1117 retval = http_authenticate_basic(request, username, basic_password);
1119 free(basic_password);
1120 return retval;
1124 /* Return cookie value by name or NULL if does not present. */
1125 const char *http_find_cookie(const struct http_request *request,
1126 const char *name) {
1127 const struct http_header *header;
1128 size_t length;
1129 const char *value = NULL;
1131 if (request == NULL || name == NULL) return NULL;
1132 length = strlen(name);
1134 for (header = request->headers; header != NULL; header = header->next) {
1135 if (header->name != NULL && !strcasecmp(header->name, "Cookie") &&
1136 header->value != NULL) {
1137 if (!strncmp(header->value, name, length) &&
1138 header->value[length] == '=') {
1139 /* Return last cookie with the name */
1140 value = header->value + length + 1;
1144 return value;
1148 /* Return Host header value or NULL if does not present. Returned string is
1149 * statically allocated. */
1150 const char *http_find_host(const struct http_request *request) {
1151 const struct http_header *header;
1152 const char *value = NULL;
1154 if (request == NULL) return NULL;
1156 for (header = request->headers; header != NULL; header = header->next) {
1157 if (header->name != NULL && !strcmp(header->name, "Host")) {
1158 value = header->value;
1161 return value;
1165 /* Free a HTTP header and set it to NULL */
1166 void http_header_free(struct http_header **header) {
1167 if (header == NULL || *header == NULL) return;
1168 free((*header)->name);
1169 free((*header)->value);
1170 free(*header);
1171 *header = NULL;
1175 /* Free linked list of HTTP headers and set it to NULL */
1176 void http_headers_free(struct http_header **headers) {
1177 struct http_header *header, *next;
1179 if (headers == NULL || *headers == NULL) return;
1181 for (header = *headers; header != NULL;) {
1182 next = header->next;
1183 http_header_free(&header);
1184 header = next;
1187 *headers = NULL;
1190 /* Free HTTP request and set it to NULL */
1191 void http_request_free(struct http_request **request) {
1192 if (request == NULL || *request == NULL) return;
1193 free((*request)->uri);
1194 http_headers_free(&((*request)->headers));
1195 free((*request)->body);
1196 free(*request);
1197 *request = NULL;
1200 /* Free HTTP response and set it to NULL */
1201 void http_response_free(struct http_response **response) {
1202 if (response == NULL || *response == NULL) return;
1203 free((*response)->reason);
1204 http_headers_free(&((*response)->headers));
1205 free((*response)->body);
1206 free(*response);
1207 *response = NULL;