TODO: OTP-authenticatd password change tested
[libisds.git] / test / simline / http.c
blobd4897282941a93d0b3f13fd8c91d4a71bc95a8d0
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() */
20 Base64 encoder is part of the libb64 project, and has been placed in the public domain.
21 For details, see http://sourceforge.net/projects/libb64
22 It's copy of ../../src/cencode.c due to symbol names.
26 typedef enum {
27 step_A, step_B, step_C
28 } base64_encodestep;
30 typedef struct {
31 base64_encodestep step;
32 int8_t result;
33 int stepcount; /* number of encoded octet triplets on a line,
34 or -1 to for end-less line */
35 } base64_encodestate;
37 static const int CHARS_PER_LINE = 72;
39 /* Initialize Base64 coder.
40 * @one_line is false for multi-line MIME encoding,
41 * true for endless one-line format. */
42 static void base64_init_encodestate(base64_encodestate* state_in, _Bool one_line) {
43 state_in->step = step_A;
44 state_in->result = 0;
45 state_in->stepcount = (one_line) ? -1 : 0;
49 static int8_t base64_encode_value(int8_t value_in) {
50 /* XXX: CHAR_BIT == 8 because of <stdint.h> */
51 static const int8_t encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
52 if (value_in > 63) return '=';
53 return encoding[value_in];
57 static size_t base64_encode_block(const int8_t* plaintext_in,
58 size_t length_in, int8_t *code_out, base64_encodestate* state_in) {
59 const int8_t *plainchar = plaintext_in;
60 const int8_t* const plaintextend = plaintext_in + length_in;
61 int8_t *codechar = code_out;
62 int8_t result;
63 int8_t fragment;
65 result = state_in->result;
67 switch (state_in->step) {
68 while (1) {
69 case step_A:
70 if (plainchar == plaintextend) {
71 state_in->result = result;
72 state_in->step = step_A;
73 return codechar - code_out;
75 fragment = *plainchar++;
76 result = (fragment & 0x0fc) >> 2;
77 *codechar++ = base64_encode_value(result);
78 result = (fragment & 0x003) << 4;
79 case step_B:
80 if (plainchar == plaintextend) {
81 state_in->result = result;
82 state_in->step = step_B;
83 return codechar - code_out;
85 fragment = *plainchar++;
86 result |= (fragment & 0x0f0) >> 4;
87 *codechar++ = base64_encode_value(result);
88 result = (fragment & 0x00f) << 2;
89 case step_C:
90 if (plainchar == plaintextend) {
91 state_in->result = result;
92 state_in->step = step_C;
93 return codechar - code_out;
95 fragment = *plainchar++;
96 result |= (fragment & 0x0c0) >> 6;
97 *codechar++ = base64_encode_value(result);
98 result = (fragment & 0x03f) >> 0;
99 *codechar++ = base64_encode_value(result);
101 if (state_in->stepcount >= 0) {
102 ++(state_in->stepcount);
103 if (state_in->stepcount == CHARS_PER_LINE/4) {
104 *codechar++ = '\n';
105 state_in->stepcount = 0;
108 } /* while */
109 } /* switch */
111 /* control should not reach here */
112 return codechar - code_out;
116 static size_t base64_encode_blockend(int8_t *code_out,
117 base64_encodestate* state_in) {
118 int8_t *codechar = code_out;
120 switch (state_in->step) {
121 case step_B:
122 *codechar++ = base64_encode_value(state_in->result);
123 *codechar++ = '=';
124 *codechar++ = '=';
125 break;
126 case step_C:
127 *codechar++ = base64_encode_value(state_in->result);
128 *codechar++ = '=';
129 break;
130 case step_A:
131 break;
133 if (state_in->stepcount >= 0)
134 *codechar++ = '\n';
136 return codechar - code_out;
140 /* Encode given data into MIME Base64 encoded zero terminated string.
141 * @plain are input data (binary stream)
142 * @length is length of @plain data in bytes
143 * @one_line is false for multi-line MIME encoding,
144 * true for endless one-line format.
145 * @return allocated string of base64 encoded plain data or NULL in case of
146 * error. You must free it. */
147 /* TODO: Allow one-line format */
148 static char *base64encode(const void *plain, const size_t length,
149 _Bool one_line) {
151 base64_encodestate state;
152 size_t code_length;
153 char *buffer, *new_buffer;
155 if (!plain) {
156 if (length) return NULL;
157 /* Empty input is valid input */
158 plain = "";
161 base64_init_encodestate(&state, one_line);
163 /* Allocate buffer
164 * (4 is padding, 1 is final new line, and 1 is string terminator) */
165 buffer = malloc(length * 2 + 4 + 1 + 1);
166 if (!buffer) return NULL;
168 /* Encode plain data */
169 code_length = base64_encode_block(plain, length, (int8_t *)buffer,
170 &state);
171 code_length += base64_encode_blockend(((int8_t*)buffer) + code_length,
172 &state);
174 /* Terminate string */
175 buffer[code_length++] = '\0';
177 /* Shrink the buffer */
178 new_buffer = realloc(buffer, code_length);
179 if (new_buffer) buffer = new_buffer;
181 return buffer;
185 /* Convert hexadecimal digit to integer. Return negative value if charcter is
186 * not valid hexadecimal digit. */
187 static int hex2i(char digit) {
188 if (digit >= '0' && digit <= '9')
189 return digit - '0';
190 if (digit >= 'a' && digit <= 'f')
191 return digit - 'a' + 10;
192 if (digit >= 'A' && digit <= 'F')
193 return digit - 'A' + 10;
194 return -1;
198 /* Decode URI-coded string.
199 * @return allocated decoded string or NULL in case of error. */
200 static char *uri_decode(const char *coded) {
201 char *plain, *p;
202 int digit1, digit2;
204 if (coded == NULL) return NULL;
205 plain = malloc(strlen(coded) + 1);
206 if (plain == NULL) return NULL;
208 for (p = plain; *coded != '\0'; p++, coded++) {
209 if (*coded == '%') {
210 digit1 = hex2i(coded[1]);
211 if (digit1 < 0) {
212 free(plain);
213 return NULL;
215 digit2 = hex2i(coded[2]);
216 if (digit2< 0) {
217 free(plain);
218 return NULL;
220 *plain = (digit1 << 4) + digit2;
221 coded += 2;
222 } else {
223 *p = *coded;
226 *p = '\0';
228 return plain;
232 /* Read a line from HTTP socket.
233 * @socket is descriptor to read from.
234 * @line is auto-allocated just read line. Will be NULL if EOF has been
235 * reached or error occured.
236 * @buffer is automatically reallocated buffer for the socket. It can preserve
237 * prematurately read socket data.
238 * @buffer_size is allocated size of @buffer
239 * @buffer_length is size of head of the buffer that holds read data.
240 * @return 0 in success. */
241 static int http_read_line(int socket, char **line,
242 char **buffer, size_t *buffer_size, size_t *buffer_used) {
243 ssize_t got;
244 char *p, *tmp;
246 if (line == NULL) return HTTP_ERROR_SERVER;
247 *line = NULL;
249 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
250 return HTTP_ERROR_SERVER;
251 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
252 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
254 #define BURST 1024
255 while (1) {
256 /* Check for EOL */
257 for (p = *buffer; p < *buffer + *buffer_used; p++)
259 if (*p != '\r')
260 continue;
261 if (!(p + 1 < *buffer + *buffer_used && p[1] == '\n'))
262 continue;
264 /* EOL found */
265 /* Crop by zero at EOL */
266 *p = '\0';
267 p += 2;
268 /* Copy read ahead data to new buffer and point line to original
269 * buffer. */
270 tmp = malloc(BURST);
271 if (tmp == NULL) return HTTP_ERROR_SERVER;
272 memcpy(tmp, p, *buffer + *buffer_used - p);
273 *line = *buffer;
274 *buffer_size = BURST;
275 *buffer_used = *buffer + *buffer_used - p;
276 *buffer = tmp;
277 /* And exit */
278 return HTTP_ERROR_SUCCESS;
281 if (*buffer_size == *buffer_used) {
282 /* Grow buffer */
283 tmp = realloc(*buffer, *buffer_size + BURST);
284 if (tmp == NULL) return HTTP_ERROR_SERVER;
285 *buffer = tmp;
286 *buffer_size += BURST;
289 /* Read data */
290 got = read(socket, *buffer + *buffer_used, *buffer_size - *buffer_used);
291 if (got == -1 && errno != EINTR) return HTTP_ERROR_CLIENT;
293 /* Check for EOF */
294 if (got == 0) return HTTP_ERROR_CLIENT;
296 /* Move end of buffer */
297 *buffer_used += got;
299 #undef BURST
301 return HTTP_ERROR_SERVER;
305 /* Write a bulk data into HTTP socket.
306 * @socket is descriptor to write to.
307 * @data are bitstream to send to client.
308 * @length is size of @data in bytes.
309 * @return 0 in success. */
310 static int http_write_bulk(int socket, const void *data, size_t length) {
311 ssize_t written;
312 const void *end;
314 if (data == NULL && length > 0) return HTTP_ERROR_SERVER;
316 for (end = data + length; data != end; data += written, length -= written) {
317 written = write(socket, data, length);
318 if (written == -1 && errno != EINTR) return HTTP_ERROR_CLIENT;
321 return HTTP_ERROR_SUCCESS;
325 /* Write a line into HTTP socket.
326 * @socket is descriptor to write to.
327 * @line is NULL terminated string to send to client. HTTP EOL will be added.
328 * @return 0 in success. */
329 static int http_write_line(int socket, const char *line) {
330 int error;
331 if (line == NULL) return HTTP_ERROR_SERVER;
333 fprintf(stderr, "Response: <%s>\n", line);
335 /* Send the line */
336 if ((error = http_write_bulk(socket, line, strlen(line))))
337 return error;
339 /* Send EOL */
340 if ((error = http_write_bulk(socket, "\r\n", 2)))
341 return error;
343 return HTTP_ERROR_SUCCESS;
347 /* Read data of given length from HTTP socket.
348 * @socket is descriptor to read from.
349 * @data is auto-allocated just read data bulk. Will be NULL if EOF has been
350 * reached or error occured.
351 * @data_length is size of bytes to read.
352 * @buffer is automatically reallocated buffer for the socket. It can preserve
353 * prematurately read socket data.
354 * @buffer_size is allocated size of @buffer
355 * @buffer_length is size of head of the buffer that holds read data.
356 * @return 0 in success. */
357 static int http_read_bulk(int socket, void **data, size_t data_length,
358 char **buffer, size_t *buffer_size, size_t *buffer_used) {
359 ssize_t got;
360 char *tmp;
362 if (data == NULL) return HTTP_ERROR_SERVER;
363 *data = NULL;
365 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
366 return HTTP_ERROR_SERVER;
367 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
368 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
370 if (data_length <= 0) return HTTP_ERROR_SUCCESS;
372 #define BURST 1024
373 while (1) {
374 /* Check whether enought data have been read */
375 if (*buffer_used >= data_length) {
376 /* Copy read ahead data to new buffer and point data to original
377 * buffer. */
378 tmp = malloc(BURST);
379 if (tmp == NULL) return HTTP_ERROR_SERVER;
380 memcpy(tmp, *buffer + data_length, *buffer_used - data_length);
381 *data = *buffer;
382 *buffer_size = BURST;
383 *buffer_used = *buffer_used - data_length;
384 *buffer = tmp;
385 /* And exit */
386 return HTTP_ERROR_SUCCESS;
389 if (*buffer_size == *buffer_used) {
390 /* Grow buffer */
391 tmp = realloc(*buffer, *buffer_size + BURST);
392 if (tmp == NULL) return HTTP_ERROR_SERVER;
393 *buffer = tmp;
394 *buffer_size += BURST;
397 /* Read data */
398 got = read(socket, *buffer + *buffer_used, *buffer_size - *buffer_used);
399 if (got == -1 && errno != EINTR) return HTTP_ERROR_CLIENT;
401 /* Check for EOF */
402 if (got == 0) return HTTP_ERROR_CLIENT;
404 /* Move end of buffer */
405 *buffer_used += got;
407 #undef BURST
409 return HTTP_ERROR_SERVER;
413 /* Parse HTTP request header.
414 * @request is pre-allocated HTTP request.
415 * @return 0 if success. */
416 static int http_parse_request_header(char *line,
417 struct http_request *request) {
418 char *p;
420 fprintf(stderr, "Request: <%s>\n", line);
422 /* Get method */
423 p = strchr(line, ' ');
424 if (p == NULL) return HTTP_ERROR_SERVER;
425 *p = '\0';
426 if (!strcmp(line, "GET"))
427 request->method = HTTP_METHOD_GET;
428 else if (!strcmp(line, "POST"))
429 request->method = HTTP_METHOD_POST;
430 else
431 request->method = HTTP_METHOD_UNKNOWN;
432 line = p + 1;
434 /* Get URI */
435 p = strchr(line, ' ');
436 if (p != NULL) *p = '\0';
437 request->uri = uri_decode(line);
438 if (request->uri == NULL) return HTTP_ERROR_SERVER;
440 /* Do not care about HTTP version */
442 fprintf(stderr, "Request-URI: <%s>\n", request->uri);
443 return HTTP_ERROR_SUCCESS;
447 /* Send HTTP response status line to client.
448 * @return 0 if success. */
449 static int http_write_response_status(int socket,
450 const struct http_response *response) {
451 char *buffer = NULL;
452 int error;
454 if (response == NULL) return HTTP_ERROR_SERVER;
456 if (-1 == test_asprintf(&buffer, "HTTP/1.0 %u %s", response->status,
457 (response->reason == NULL) ? "" : response->reason))
458 return HTTP_ERROR_SERVER;
459 error = http_write_line(socket, buffer);
460 free(buffer);
462 return error;
466 /* Parse generic HTTP header.
467 * @request is pre-allocated HTTP request.
468 * @return 0 if success, negative value if internal error, positive value if
469 * header is not valid. */
470 static int http_parse_header(char *line, struct http_request *request) {
471 struct http_header *header;
473 if (line == NULL || request == NULL) return HTTP_ERROR_SERVER;
475 fprintf(stderr, "Header: <%s>\n", line);
477 /* Find last used header */
478 for (header = request->headers; header != NULL && header->next != NULL;
479 header = header->next);
481 if (*line == ' ' || *line == '\t') {
482 /* Line is continuation of last header */
483 if (header == NULL)
484 return HTTP_ERROR_CLIENT; /* No previous header to continue */
485 line++;
486 size_t old_length = strlen(header->value);
487 char *tmp = realloc(header->value,
488 sizeof(header->value[0]) * (old_length + strlen(line) + 1));
489 if (tmp == NULL) return HTTP_ERROR_SERVER;
490 header->value = tmp;
491 strcpy(&header->value[old_length], line);
492 } else {
493 /* New header */
494 struct http_header *new_header = calloc(sizeof(*new_header), 1);
495 if (new_header == NULL) return HTTP_ERROR_SERVER;
497 char *p = strstr(line, ": ");
498 if (p == NULL) return HTTP_ERROR_CLIENT;
500 size_t length = p - line;
501 new_header->name = malloc(sizeof(line[0]) * (length + 1));
502 if (new_header->name == NULL) {
503 http_header_free(&new_header);
504 return HTTP_ERROR_SERVER;
506 strncpy(new_header->name, line, length);
507 new_header->name[length] = '\0';
509 p += 2;
510 length = strlen(p);
511 new_header->value = malloc(sizeof(p[0]) * (length + 1));
512 if (new_header->value == NULL) {
513 http_header_free(&new_header);
514 return HTTP_ERROR_SERVER;
516 strcpy(new_header->value, p);
518 if (request->headers == NULL)
519 request->headers = new_header;
520 else
521 header->next = new_header;
525 /* FIXME: Decode. After parsing all headers as we could decode begining
526 * and then got encoded continuation. */
527 return HTTP_ERROR_SUCCESS;
531 /* Send HTTP header to client.
532 * @return 0 if success. */
533 static int http_write_header(int socket, const struct http_header *header) {
534 char *buffer = NULL;
535 int error;
537 if (header == NULL) return HTTP_ERROR_SERVER;
539 if (header->name == NULL) return HTTP_ERROR_SERVER;
541 /* TODO: Quote, split long lines */
542 if (-1 == test_asprintf(&buffer, "%s: %s", header->name,
543 (header->value == NULL) ? "" : header->value))
544 return HTTP_ERROR_SERVER;
545 error = http_write_line(socket, buffer);
546 free(buffer);
548 return error;
552 /* Find Content-Length value in HTTP request headers and set it into @request.
553 * @return 0 in success. */
554 static int find_content_length(struct http_request *request) {
555 struct http_header *header;
556 if (request == NULL) return HTTP_ERROR_SERVER;
558 for (header = request->headers; header != NULL; header = header->next) {
559 if (header->name == NULL) continue;
560 if (!strcasecmp(header->name, "Content-Length")) break;
563 if (header != NULL && header->value != NULL) {
564 char *p;
565 long long int value = strtol(header->value, &p, 10);
566 if (*p != '\0')
567 return HTTP_ERROR_CLIENT;
568 if ((value == LLONG_MIN || value == LLONG_MAX) && errno == ERANGE)
569 return HTTP_ERROR_SERVER;
570 if (value < 0)
571 return HTTP_ERROR_CLIENT;
572 if (value > SIZE_MAX)
573 return HTTP_ERROR_SERVER;
574 request->body_length = value;
575 } else {
576 request->body_length = 0;
578 return HTTP_ERROR_SUCCESS;
582 /* Print binary stream to STDERR with some escapes */
583 static void dump_body(const void *data, size_t length) {
584 fprintf(stderr, "===BEGIN BODY===\n");
585 if (length > 0 && NULL != data) {
586 for (size_t i = 0; i < length; i++) {
587 if (isprint(((const unsigned char *)data)[i]))
588 fprintf(stderr, "%c", ((const unsigned char *)data)[i]);
589 else
590 fprintf(stderr, "\\x%02x", ((const unsigned char*)data)[i]);
592 fprintf(stderr, "\n");
594 fprintf(stderr, "===END BODY===\n");
598 /* Read a HTTP request from connected socket.
599 * @http_request is heap-allocated received HTTP request,
600 * or NULL in case of error.
601 * @return http_error code. */
602 http_error http_read_request(int socket, struct http_request **request) {
603 char *line = NULL;
604 char *buffer = NULL;
605 size_t buffer_size = 0, buffer_used = 0;
606 int error;
608 if (request == NULL) return HTTP_ERROR_SERVER;
610 *request = calloc(1, sizeof(**request));
611 if (*request == NULL) return HTTP_ERROR_SERVER;
613 /* Get request header */
614 if ((error = http_read_line(socket, &line, &buffer, &buffer_size,
615 &buffer_used)))
616 goto leave;
617 if ((error = http_parse_request_header(line, *request)))
618 goto leave;
620 /* Get other headers */
621 while (1) {
622 if ((error = http_read_line(socket, &line, &buffer, &buffer_size,
623 &buffer_used))) {
624 fprintf(stderr, "Error while reading HTTP request line\n");
625 goto leave;
628 /* Check for headers delimiter */
629 if (line == NULL || *line == '\0') {
630 break;
633 if ((error = http_parse_header(line, *request))) {
634 fprintf(stderr, "Error while parsing HTTP request line: <%s>\n",
635 line);
636 goto leave;
640 /* Get body */
641 if ((error = find_content_length(*request))) {
642 fprintf(stderr, "Could not determine length of body\n");
643 goto leave;
645 if ((error = http_read_bulk(socket,
646 &(*request)->body, (*request)->body_length,
647 &buffer, &buffer_size, &buffer_used))) {
648 fprintf(stderr, "Could not read request body\n");
649 goto leave;
651 fprintf(stderr, "Body of size %zu B has been received:\n",
652 (*request)->body_length);
653 dump_body((*request)->body, (*request)->body_length);
655 leave:
656 free(line);
657 free(buffer);
658 if (error) http_request_free(request);
659 return error;
663 /* Write a HTTP response to connected socket. Auto-add Content-Length header.
664 * @return 0 in case of success. */
665 int http_write_response(int socket, const struct http_response *response) {
666 char *buffer = NULL;
667 int error = -1;
669 if (response == NULL) return HTTP_ERROR_SERVER;
671 /* Status line */
672 error = http_write_response_status(socket, response);
673 if (error) return error;
675 /* Headers */
676 for (struct http_header *header = response->headers; header != NULL;
677 header = header->next) {
678 error = http_write_header(socket, header);
679 if (error) return error;
681 if (-1 == test_asprintf(&buffer, "Content-Length: %u",
682 response->body_length))
683 return HTTP_ERROR_SERVER;
684 error = http_write_line(socket, buffer);
685 if (error) return error;
687 /* Headers trailer */
688 error = http_write_line(socket, "");
689 if (error) return error;
691 /* Body */
692 if (response->body_length > 0) {
693 error = http_write_bulk(socket, response->body, response->body_length);
694 if (error) return error;
696 fprintf(stderr, "Body of size %zu B has been sent:\n",
697 response->body_length);
698 dump_body(response->body, response->body_length);
700 free(buffer);
701 return HTTP_ERROR_SUCCESS;
705 /* Build Set-Cookie header. In case of error, return NULL. Caller must free
706 * the header. */
707 static struct http_header *http_build_setcookie_header (
708 const char *cokie_name, const char *cookie_value,
709 const char *cookie_domain, const char *cookie_path) {
710 struct http_header *header = NULL;
711 char *domain_parameter = NULL;
712 char *path_parameter = NULL;
714 if (cokie_name == NULL) goto error;
716 header = calloc(1, sizeof(*header));
717 if (header == NULL) goto error;
719 header->name = strdup("Set-Cookie");
720 if (header->name == NULL) goto error;
722 if (cookie_domain != NULL)
723 if (-1 == test_asprintf(&domain_parameter, "; Domain=%s",
724 cookie_domain))
725 goto error;
727 if (cookie_path != NULL)
728 if (-1 == test_asprintf(&path_parameter, "; Path=%s",
729 cookie_path))
730 goto error;
732 if (-1 == test_asprintf(&header->value, "%s=%s%s%s",
733 cokie_name,
734 (cookie_value == NULL) ? "" : cookie_value,
735 (domain_parameter == NULL) ? "": domain_parameter,
736 (path_parameter == NULL) ? "": path_parameter))
737 goto error;
738 goto ok;
740 error:
741 http_header_free(&header);
743 free(domain_parameter);
744 free(path_parameter);
745 return header;
749 /* Send a 200 Ok response with a cookie */
750 int http_send_response_200_cookie(int client_socket,
751 const char *cokie_name, const char *cookie_value,
752 const char *cookie_domain, const char *cookie_path,
753 const void *body, size_t body_length, const char *type) {
754 int retval;
755 struct http_header *header_cookie = NULL;
756 struct http_header header_contenttype = {
757 .name = "Content-Type",
758 .value = (char *)type,
759 .next = NULL
761 struct http_response response = {
762 .status = 200,
763 .reason = "OK",
764 .headers = NULL,
765 .body_length = body_length,
766 .body = (void *)body
769 if (cokie_name != NULL) {
770 if (NULL == (header_cookie = http_build_setcookie_header(
771 cokie_name, cookie_value, cookie_domain, cookie_path)))
772 return http_send_response_500(client_socket,
773 "Could not build Set-Cookie header");
776 /* Link defined headers */
777 if (type != NULL) {
778 response.headers = &header_contenttype;
780 if (header_cookie != NULL) {
781 header_cookie->next = response.headers;
782 response.headers = header_cookie;
785 retval = http_write_response(client_socket, &response);
786 http_header_free(&header_cookie);
787 return retval;
791 /* Send a 200 Ok response */
792 int http_send_response_200(int client_socket,
793 const void *body, size_t body_length, const char *type) {
794 return http_send_response_200_cookie(client_socket,
795 NULL, NULL, NULL, NULL,
796 body, body_length, type);
800 /* Send a 302 Found response setting a cookie */
801 int http_send_response_302_cookie(int client_socket, const char *cokie_name,
802 const char *cookie_value, const char *cookie_domain,
803 const char *cookie_path, const char *location) {
804 int retval;
805 struct http_header *header_cookie = NULL;
806 struct http_header header_location = {
807 .name = "Location",
808 .value = (char *) location,
809 .next = NULL
811 struct http_response response = {
812 .status = 302,
813 .reason = "Found",
814 .headers = NULL,
815 .body_length = 0,
816 .body = NULL
819 if (cokie_name != NULL) {
820 if (NULL == (header_cookie = http_build_setcookie_header(
821 cokie_name, cookie_value, cookie_domain, cookie_path)))
822 return http_send_response_500(client_socket,
823 "Could not build Set-Cookie header");
826 /* Link defined headers */
827 if (location != NULL) {
828 response.headers = &header_location;
830 if (header_cookie != NULL) {
831 header_cookie->next = response.headers;
832 response.headers = header_cookie;
835 retval = http_write_response(client_socket, &response);
836 http_header_free(&header_cookie);
837 return retval;
841 /* Send a 302 Found response with totp authentication scheme header */
842 int http_send_response_302_totp(int client_socket,
843 const char *code, const char *text, const char *location) {
844 struct http_header header_code = {
845 .name = "X-Response-message-code",
846 .value = (char *) code,
847 .next = NULL
849 struct http_header header_text = {
850 .name = "X-Response-message-text",
851 .value = (char *) text,
852 .next = NULL
854 struct http_header header_location = {
855 .name = "Location",
856 .value = (char *) location,
857 .next = NULL
859 struct http_response response = {
860 .status = 302,
861 .reason = "Found",
862 .headers = NULL,
863 .body_length = 0,
864 .body = NULL
867 /* Link defined headers */
868 if (location != NULL) {
869 response.headers = &header_location;
871 if (text != NULL) {
872 header_text.next = response.headers;
873 response.headers = &header_text;
875 if (code != NULL) {
876 header_code.next = response.headers;
877 response.headers = &header_code;
880 return http_write_response(client_socket, &response);
884 /* Send a 400 Bad Request response.
885 * Use non-NULL @reason to override status message. */
886 int http_send_response_400(int client_socket, const char *reason) {
887 struct http_response response = {
888 .status = 400,
889 .reason = (reason == NULL) ? "Bad Request" : (char *) reason,
890 .headers = NULL,
891 .body_length = 0,
892 .body = NULL
895 return http_write_response(client_socket, &response);
899 /* Send a 401 Unauthorized response with Basic authentication scheme header */
900 int http_send_response_401_basic(int client_socket) {
901 struct http_header header = {
902 .name = "WWW-Authenticate",
903 .value = "Basic realm=\"SimulatedISDSServer\"",
904 .next = NULL
906 struct http_response response = {
907 .status = 401,
908 .reason = "Unauthorized",
909 .headers = &header,
910 .body_length = 0,
911 .body = NULL
914 return http_write_response(client_socket, &response);
918 /* Send a 401 Unauthorized response with OTP authentication scheme header for
919 * given @method. */
920 int http_send_response_401_otp(int client_socket,
921 const char *method, const char *code, const char *text) {
922 int retval;
923 struct http_header header = {
924 .name = "WWW-Authenticate",
925 .value = NULL,
926 .next = NULL
928 struct http_header header_code = {
929 .name = "X-Response-message-code",
930 .value = (char *) code,
931 .next = NULL
933 struct http_header header_text = {
934 .name = "X-Response-message-text",
935 .value = (char *) text,
936 .next = NULL
938 struct http_response response = {
939 .status = 401,
940 .reason = "Unauthorized",
941 .headers = &header,
942 .body_length = 0,
943 .body = NULL
946 if (-1 == test_asprintf(&header.value, "%s realm=\"SimulatedISDSServer\"",
947 method)) {
948 return http_send_response_500(client_socket,
949 "Could not build WWW-Authenticate header value");
952 /* Link defined headers */
953 if (code != NULL) header.next = &header_code;
954 if (text != NULL) {
955 if (code != NULL)
956 header_code.next = &header_text;
957 else
958 header.next = &header_text;
961 retval = http_write_response(client_socket, &response);
962 free(header.value);
963 return retval;
967 /* Send a 403 Forbidden response */
968 int http_send_response_403(int client_socket) {
969 struct http_response response = {
970 .status = 403,
971 .reason = "Forbidden",
972 .headers = NULL,
973 .body_length = 0,
974 .body = NULL
977 return http_write_response(client_socket, &response);
981 /* Send a 500 Internal Server Error response.
982 * Use non-NULL @reason to override status message. */
983 int http_send_response_500(int client_socket, const char *reason) {
984 struct http_response response = {
985 .status = 500,
986 .reason = (NULL == reason) ? "Internal Server Error" : (char *) reason,
987 .headers = NULL,
988 .body_length = 0,
989 .body = NULL
992 return http_write_response(client_socket, &response);
996 /* Send a 503 Service Temporarily Unavailable response */
997 int http_send_response_503(int client_socket,
998 const void *body, size_t body_length, const char *type) {
999 struct http_header header = {
1000 .name = "Content-Type",
1001 .value = (char *)type
1003 struct http_response response = {
1004 .status = 503,
1005 .reason = "Service Temporarily Unavailable",
1006 .headers = (type == NULL) ? NULL : &header,
1007 .body_length = body_length,
1008 .body = (void *)body
1011 return http_write_response(client_socket, &response);
1015 /* Returns Authorization header in given request */
1016 static const struct http_header *find_authorization(
1017 const struct http_request *request) {
1018 const struct http_header *header;
1019 if (request == NULL) return NULL;
1020 for (header = request->headers; header != NULL; header = header->next) {
1021 if (header->name != NULL &&
1022 !strcasecmp(header->name, "Authorization"))
1023 break;
1025 return header;
1029 /* Return true if request carries Authorization header */
1030 int http_client_authenticates(const struct http_request *request) {
1031 if (find_authorization(request))
1032 return 1;
1033 else
1034 return 0;
1038 /* Return HTTP_ERROR_SUCCESS if request carries valid Basic credentials.
1039 * NULL @username or @password equales to empty string. */
1040 http_error http_authenticate_basic(const struct http_request *request,
1041 const char *username, const char *password) {
1042 const struct http_header *header;
1043 const char *basic_cookie_client;
1044 char *basic_cookie_plain = NULL, *basic_cookie_encoded = NULL;
1045 _Bool is_valid;
1047 header = find_authorization(request);
1048 if (header == NULL) return HTTP_ERROR_CLIENT;
1050 if (strncmp(header->value, "Basic ", 6)) return HTTP_ERROR_CLIENT;
1051 basic_cookie_client = header->value + 6;
1053 if (-1 == test_asprintf(&basic_cookie_plain, "%s:%s",
1054 (username == NULL) ? "" : username,
1055 (password == NULL) ? "" : password)) {
1056 return HTTP_ERROR_SERVER;
1059 basic_cookie_encoded = base64encode(basic_cookie_plain,
1060 strlen(basic_cookie_plain), 1);
1061 if (basic_cookie_encoded == NULL) {
1062 free(basic_cookie_plain);
1063 return HTTP_ERROR_SERVER;
1066 fprintf(stderr, "Authenticating basic: got=<%s>, expected=<%s> (%s)\n",
1067 basic_cookie_client, basic_cookie_encoded, basic_cookie_plain);
1068 free(basic_cookie_plain);
1070 is_valid = !strcmp(basic_cookie_encoded, basic_cookie_client);
1071 free(basic_cookie_encoded);
1073 return (is_valid) ? HTTP_ERROR_SUCCESS : HTTP_ERROR_CLIENT;
1077 /* Return HTTP_ERROR_SUCCESS if request carries valid OTP credentials.
1078 * NULL @username or @password or @otp equal to empty string. */
1079 http_error http_authenticate_otp(const struct http_request *request,
1080 const char *username, const char *password, const char *otp) {
1081 char *basic_password = NULL;
1082 http_error retval;
1084 /* Concatenate password and OTP code */
1085 if (-1 == test_asprintf(&basic_password, "%s%s",
1086 (password == NULL) ? "": password,
1087 (otp == NULL) ? "" : otp)) {
1088 return HTTP_ERROR_SERVER;
1091 /* Use Basic authentication */
1092 /* XXX: Specification does not define authorization method string */
1093 retval = http_authenticate_basic(request, username, basic_password);
1095 free(basic_password);
1096 return retval;
1100 /* Return cookie value by name or NULL if does not present. */
1101 const char *http_find_cookie(const struct http_request *request,
1102 const char *name) {
1103 const struct http_header *header;
1104 size_t length;
1105 const char *value = NULL;
1107 if (request == NULL || name == NULL) return NULL;
1108 length = strlen(name);
1110 for (header = request->headers; header != NULL; header = header->next) {
1111 if (header->name != NULL && !strcasecmp(header->name, "Cookie") &&
1112 header->value != NULL) {
1113 if (!strncmp(header->value, name, length) &&
1114 header->value[length] == '=') {
1115 /* Return last cookie with the name */
1116 value = header->value + length + 1;
1120 return value;
1124 /* Return Host header value or NULL if does not present. Returned string is
1125 * statically allocated. */
1126 const char *http_find_host(const struct http_request *request) {
1127 const struct http_header *header;
1128 const char *value = NULL;
1130 if (request == NULL) return NULL;
1132 for (header = request->headers; header != NULL; header = header->next) {
1133 if (header->name != NULL && !strcmp(header->name, "Host")) {
1134 value = header->value;
1137 return value;
1141 /* Free a HTTP header and set it to NULL */
1142 void http_header_free(struct http_header **header) {
1143 if (header == NULL || *header == NULL) return;
1144 free((*header)->name);
1145 free((*header)->value);
1146 free(*header);
1147 *header = NULL;
1151 /* Free linked list of HTTP headers and set it to NULL */
1152 void http_headers_free(struct http_header **headers) {
1153 struct http_header *header, *next;
1155 if (headers == NULL || *headers == NULL) return;
1157 for (header = *headers; header != NULL;) {
1158 next = header->next;
1159 http_header_free(&header);
1160 header = next;
1163 *headers = NULL;
1166 /* Free HTTP request and set it to NULL */
1167 void http_request_free(struct http_request **request) {
1168 if (request == NULL || *request == NULL) return;
1169 free((*request)->uri);
1170 http_headers_free(&((*request)->headers));
1171 free((*request)->body);
1172 free(*request);
1173 *request = NULL;
1176 /* Free HTTP response and set it to NULL */
1177 void http_response_free(struct http_response **response) {
1178 if (response == NULL || *response == NULL) return;
1179 free((*response)->reason);
1180 http_headers_free(&((*response)->headers));
1181 free((*response)->body);
1182 free(*response);
1183 *response = NULL;