2 #include "../../config.h"
3 #define _XOPEN_SOURCE XOPEN_SOURCE_LEVEL_FOR_STRDUP
7 #include "../test-tools.h"
12 #include <stdio.h> /* fprintf() */
14 #include <string.h> /* strdup() */
15 #include <strings.h> /* strcasecmp() */
16 #include <stdint.h> /* int8_t */
17 #include <stddef.h> /* size_t, NULL */
18 #include <ctype.h> /* isprint() */
19 #include <sys/socket.h> /* MSG_NOSIGNAL for http_send_callback() */
22 Base64 encoder is part of the libb64 project, and has been placed in the public domain.
23 For details, see http://sourceforge.net/projects/libb64
24 It's copy of ../../src/cencode.c due to symbol names.
29 step_A
, step_B
, step_C
33 base64_encodestep step
;
35 int stepcount
; /* number of encoded octet triplets on a line,
36 or -1 to for end-less line */
39 static const int CHARS_PER_LINE
= 72;
41 /* Initialize Base64 coder.
42 * @one_line is false for multi-line MIME encoding,
43 * true for endless one-line format. */
44 static void base64_init_encodestate(base64_encodestate
* state_in
, _Bool one_line
) {
45 state_in
->step
= step_A
;
47 state_in
->stepcount
= (one_line
) ? -1 : 0;
51 static int8_t base64_encode_value(int8_t value_in
) {
52 /* XXX: CHAR_BIT == 8 because of <stdint.h> */
53 static const int8_t encoding
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54 if (value_in
> 63) return '=';
55 return encoding
[value_in
];
59 static size_t base64_encode_block(const int8_t* plaintext_in
,
60 size_t length_in
, int8_t *code_out
, base64_encodestate
* state_in
) {
61 const int8_t *plainchar
= plaintext_in
;
62 const int8_t* const plaintextend
= plaintext_in
+ length_in
;
63 int8_t *codechar
= code_out
;
67 result
= state_in
->result
;
69 switch (state_in
->step
) {
72 if (plainchar
== plaintextend
) {
73 state_in
->result
= result
;
74 state_in
->step
= step_A
;
75 return codechar
- code_out
;
77 fragment
= *plainchar
++;
78 result
= (fragment
& 0x0fc) >> 2;
79 *codechar
++ = base64_encode_value(result
);
80 result
= (fragment
& 0x003) << 4;
82 if (plainchar
== plaintextend
) {
83 state_in
->result
= result
;
84 state_in
->step
= step_B
;
85 return codechar
- code_out
;
87 fragment
= *plainchar
++;
88 result
|= (fragment
& 0x0f0) >> 4;
89 *codechar
++ = base64_encode_value(result
);
90 result
= (fragment
& 0x00f) << 2;
92 if (plainchar
== plaintextend
) {
93 state_in
->result
= result
;
94 state_in
->step
= step_C
;
95 return codechar
- code_out
;
97 fragment
= *plainchar
++;
98 result
|= (fragment
& 0x0c0) >> 6;
99 *codechar
++ = base64_encode_value(result
);
100 result
= (fragment
& 0x03f) >> 0;
101 *codechar
++ = base64_encode_value(result
);
103 if (state_in
->stepcount
>= 0) {
104 ++(state_in
->stepcount
);
105 if (state_in
->stepcount
== CHARS_PER_LINE
/4) {
107 state_in
->stepcount
= 0;
113 /* control should not reach here */
114 return codechar
- code_out
;
118 static size_t base64_encode_blockend(int8_t *code_out
,
119 base64_encodestate
* state_in
) {
120 int8_t *codechar
= code_out
;
122 switch (state_in
->step
) {
124 *codechar
++ = base64_encode_value(state_in
->result
);
129 *codechar
++ = base64_encode_value(state_in
->result
);
135 if (state_in
->stepcount
>= 0)
138 return codechar
- code_out
;
142 /* Encode given data into MIME Base64 encoded zero terminated string.
143 * @plain are input data (binary stream)
144 * @length is length of @plain data in bytes
145 * @one_line is false for multi-line MIME encoding,
146 * true for endless one-line format.
147 * @return allocated string of base64 encoded plain data or NULL in case of
148 * error. You must free it. */
149 /* TODO: Allow one-line format */
150 static char *base64encode(const void *plain
, const size_t length
,
153 base64_encodestate state
;
155 char *buffer
, *new_buffer
;
158 if (length
) return NULL
;
159 /* Empty input is valid input */
163 base64_init_encodestate(&state
, one_line
);
166 * (4 is padding, 1 is final new line, and 1 is string terminator) */
167 buffer
= malloc(length
* 2 + 4 + 1 + 1);
168 if (!buffer
) return NULL
;
170 /* Encode plain data */
171 code_length
= base64_encode_block(plain
, length
, (int8_t *)buffer
,
173 code_length
+= base64_encode_blockend(((int8_t*)buffer
) + code_length
,
176 /* Terminate string */
177 buffer
[code_length
++] = '\0';
179 /* Shrink the buffer */
180 new_buffer
= realloc(buffer
, code_length
);
181 if (new_buffer
) buffer
= new_buffer
;
187 /* Convert hexadecimal digit to integer. Return negative value if charcter is
188 * not valid hexadecimal digit. */
189 static int hex2i(char digit
) {
190 if (digit
>= '0' && digit
<= '9')
192 if (digit
>= 'a' && digit
<= 'f')
193 return digit
- 'a' + 10;
194 if (digit
>= 'A' && digit
<= 'F')
195 return digit
- 'A' + 10;
200 /* Decode URI-coded string.
201 * @return allocated decoded string or NULL in case of error. */
202 static char *uri_decode(const char *coded
) {
206 if (coded
== NULL
) return NULL
;
207 plain
= malloc(strlen(coded
) + 1);
208 if (plain
== NULL
) return NULL
;
210 for (p
= plain
; *coded
!= '\0'; p
++, coded
++) {
212 digit1
= hex2i(coded
[1]);
217 digit2
= hex2i(coded
[2]);
222 *plain
= (digit1
<< 4) + digit2
;
234 /* Read a line from HTTP socket.
235 * @connection is HTTP connection to read from.
236 * @line is auto-reallocated just read line. Will be NULL if EOF has been
237 * reached or error occured.
238 * @buffer is automatically reallocated buffer for the socket. It can preserve
239 * prematurately read socket data.
240 * @buffer_size is allocated size of @buffer
241 * @buffer_length is size of head of the buffer that holds read data.
242 * @return 0 in success. */
243 static int http_read_line(const struct http_connection
*connection
,
244 char **line
, char **buffer
, size_t *buffer_size
,
245 size_t *buffer_used
) {
249 if (line
== NULL
) return HTTP_ERROR_SERVER
;
253 if (connection
== NULL
|| connection
->recv_callback
== NULL
)
254 return HTTP_ERROR_SERVER
;
255 if (buffer
== NULL
|| buffer_size
== NULL
|| buffer_used
== NULL
)
256 return HTTP_ERROR_SERVER
;
257 if (*buffer
== NULL
&& *buffer_size
> 0) return HTTP_ERROR_SERVER
;
258 if (*buffer_size
< *buffer_used
) return HTTP_ERROR_SERVER
;
263 for (p
= *buffer
; p
< *buffer
+ *buffer_used
; p
++)
267 if (!(p
+ 1 < *buffer
+ *buffer_used
&& p
[1] == '\n'))
271 /* Crop by zero at EOL */
274 /* Copy read ahead data to new buffer and point line to original
277 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
278 memcpy(tmp
, p
, *buffer
+ *buffer_used
- p
);
280 *buffer_size
= BURST
;
281 *buffer_used
= *buffer
+ *buffer_used
- p
;
284 return HTTP_ERROR_SUCCESS
;
287 if (*buffer_size
== *buffer_used
) {
289 tmp
= realloc(*buffer
, *buffer_size
+ BURST
);
290 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
292 *buffer_size
+= BURST
;
296 got
= connection
->recv_callback(connection
, *buffer
+ *buffer_used
,
297 *buffer_size
- *buffer_used
);
298 if (got
== -1) return HTTP_ERROR_CLIENT
;
301 if (got
== 0) return HTTP_ERROR_CLIENT
;
303 /* Move end of buffer */
308 return HTTP_ERROR_SERVER
;
312 /* Write a bulk data into HTTP socket.
313 * @connection is HTTP connection to write to.
314 * @data are bitstream to send to client.
315 * @length is size of @data in bytes.
316 * @return 0 in success. */
317 static int http_write_bulk(const struct http_connection
*connection
,
318 const void *data
, size_t length
) {
322 if (connection
== NULL
|| connection
->send_callback
== NULL
)
323 return HTTP_ERROR_SERVER
;
324 if (data
== NULL
&& length
> 0) return HTTP_ERROR_SERVER
;
326 for (end
= data
+ length
; data
!= end
; data
+= written
, length
-= written
) {
327 written
= connection
->send_callback(connection
, data
, length
);
328 if (written
== -1) return HTTP_ERROR_CLIENT
;
331 return HTTP_ERROR_SUCCESS
;
335 /* Write a line into HTTP socket.
336 * @connection is HTTP connection to write to.
337 * @line is NULL terminated string to send to client. HTTP EOL will be added.
338 * @return 0 in success. */
339 static int http_write_line(const struct http_connection
*connection
,
342 if (line
== NULL
) return HTTP_ERROR_SERVER
;
344 fprintf(stderr
, "Response: <%s>\n", line
);
347 if ((error
= http_write_bulk(connection
, line
, strlen(line
))))
351 if ((error
= http_write_bulk(connection
, "\r\n", 2)))
354 return HTTP_ERROR_SUCCESS
;
358 /* Read data of given length from HTTP socket.
359 * @connection is HTTP connection to read from.
360 * @data is auto-allocated just read data bulk. Will be NULL if EOF has been
361 * reached or error occured.
362 * @data_length is size of bytes to read.
363 * @buffer is automatically reallocated buffer for the socket. It can preserve
364 * prematurately read socket data.
365 * @buffer_size is allocated size of @buffer
366 * @buffer_length is size of head of the buffer that holds read data.
367 * @return 0 in success. */
368 static int http_read_bulk(const struct http_connection
*connection
,
369 void **data
, size_t data_length
,
370 char **buffer
, size_t *buffer_size
, size_t *buffer_used
) {
374 if (data
== NULL
) return HTTP_ERROR_SERVER
;
377 if (connection
== NULL
|| connection
->recv_callback
== NULL
)
378 return HTTP_ERROR_SERVER
;
379 if (buffer
== NULL
|| buffer_size
== NULL
|| buffer_used
== NULL
)
380 return HTTP_ERROR_SERVER
;
381 if (*buffer
== NULL
&& *buffer_size
> 0) return HTTP_ERROR_SERVER
;
382 if (*buffer_size
< *buffer_used
) return HTTP_ERROR_SERVER
;
384 if (data_length
<= 0) return HTTP_ERROR_SUCCESS
;
388 /* Check whether enought data have been read */
389 if (*buffer_used
>= data_length
) {
390 /* Copy read ahead data to new buffer and point data to original
393 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
394 memcpy(tmp
, *buffer
+ data_length
, *buffer_used
- data_length
);
396 *buffer_size
= BURST
;
397 *buffer_used
= *buffer_used
- data_length
;
400 return HTTP_ERROR_SUCCESS
;
403 if (*buffer_size
== *buffer_used
) {
405 tmp
= realloc(*buffer
, *buffer_size
+ BURST
);
406 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
408 *buffer_size
+= BURST
;
412 got
= connection
->recv_callback(connection
, *buffer
+ *buffer_used
,
413 *buffer_size
- *buffer_used
);
414 if (got
== -1) return HTTP_ERROR_CLIENT
;
417 if (got
== 0) return HTTP_ERROR_CLIENT
;
419 /* Move end of buffer */
424 return HTTP_ERROR_SERVER
;
428 /* Parse HTTP request header.
429 * @request is pre-allocated HTTP request.
430 * @return 0 if success. */
431 static int http_parse_request_header(char *line
,
432 struct http_request
*request
) {
435 fprintf(stderr
, "Request: <%s>\n", line
);
438 p
= strchr(line
, ' ');
439 if (p
== NULL
) return HTTP_ERROR_SERVER
;
441 if (!strcmp(line
, "GET"))
442 request
->method
= HTTP_METHOD_GET
;
443 else if (!strcmp(line
, "POST"))
444 request
->method
= HTTP_METHOD_POST
;
446 request
->method
= HTTP_METHOD_UNKNOWN
;
450 p
= strchr(line
, ' ');
451 if (p
!= NULL
) *p
= '\0';
452 request
->uri
= uri_decode(line
);
453 if (request
->uri
== NULL
) return HTTP_ERROR_SERVER
;
455 /* Do not care about HTTP version */
457 fprintf(stderr
, "Request-URI: <%s>\n", request
->uri
);
458 return HTTP_ERROR_SUCCESS
;
462 /* Send HTTP response status line to client.
463 * @return 0 if success. */
464 static int http_write_response_status(const struct http_connection
*connection
,
465 const struct http_response
*response
) {
469 if (response
== NULL
) return HTTP_ERROR_SERVER
;
471 if (-1 == test_asprintf(&buffer
, "HTTP/1.0 %u %s", response
->status
,
472 (response
->reason
== NULL
) ? "" : response
->reason
))
473 return HTTP_ERROR_SERVER
;
474 error
= http_write_line(connection
, buffer
);
481 /* Parse generic HTTP header.
482 * @request is pre-allocated HTTP request.
483 * @return 0 if success, negative value if internal error, positive value if
484 * header is not valid. */
485 static int http_parse_header(char *line
, struct http_request
*request
) {
486 struct http_header
*header
;
488 if (line
== NULL
|| request
== NULL
) return HTTP_ERROR_SERVER
;
490 fprintf(stderr
, "Header: <%s>\n", line
);
492 /* Find last used header */
493 for (header
= request
->headers
; header
!= NULL
&& header
->next
!= NULL
;
494 header
= header
->next
);
496 if (*line
== ' ' || *line
== '\t') {
497 /* Line is continuation of last header */
499 return HTTP_ERROR_CLIENT
; /* No previous header to continue */
501 size_t old_length
= strlen(header
->value
);
502 char *tmp
= realloc(header
->value
,
503 sizeof(header
->value
[0]) * (old_length
+ strlen(line
) + 1));
504 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
506 strcpy(&header
->value
[old_length
], line
);
509 struct http_header
*new_header
= calloc(sizeof(*new_header
), 1);
510 if (new_header
== NULL
) return HTTP_ERROR_SERVER
;
512 char *p
= strstr(line
, ": ");
514 http_header_free(&new_header
);
515 return HTTP_ERROR_CLIENT
;
518 size_t length
= p
- line
;
519 new_header
->name
= malloc(sizeof(line
[0]) * (length
+ 1));
520 if (new_header
->name
== NULL
) {
521 http_header_free(&new_header
);
522 return HTTP_ERROR_SERVER
;
524 strncpy(new_header
->name
, line
, length
);
525 new_header
->name
[length
] = '\0';
529 new_header
->value
= malloc(sizeof(p
[0]) * (length
+ 1));
530 if (new_header
->value
== NULL
) {
531 http_header_free(&new_header
);
532 return HTTP_ERROR_SERVER
;
534 strcpy(new_header
->value
, p
);
536 if (request
->headers
== NULL
)
537 request
->headers
= new_header
;
539 header
->next
= new_header
;
543 /* FIXME: Decode. After parsing all headers as we could decode begining
544 * and then got encoded continuation. */
545 return HTTP_ERROR_SUCCESS
;
549 /* Send HTTP header to client.
550 * @return 0 if success. */
551 static int http_write_header(const struct http_connection
*connection
,
552 const struct http_header
*header
) {
556 if (header
== NULL
) return HTTP_ERROR_SERVER
;
558 if (header
->name
== NULL
) return HTTP_ERROR_SERVER
;
560 /* TODO: Quote, split long lines */
561 if (-1 == test_asprintf(&buffer
, "%s: %s", header
->name
,
562 (header
->value
== NULL
) ? "" : header
->value
))
563 return HTTP_ERROR_SERVER
;
564 error
= http_write_line(connection
, buffer
);
571 /* Find Content-Length value in HTTP request headers and set it into @request.
572 * @return 0 in success. */
573 static int find_content_length(struct http_request
*request
) {
574 struct http_header
*header
;
575 if (request
== NULL
) return HTTP_ERROR_SERVER
;
577 for (header
= request
->headers
; header
!= NULL
; header
= header
->next
) {
578 if (header
->name
== NULL
) continue;
579 if (!strcasecmp(header
->name
, "Content-Length")) break;
582 if (header
!= NULL
&& header
->value
!= NULL
) {
584 long long int value
= strtoll(header
->value
, &p
, 10);
586 return HTTP_ERROR_CLIENT
;
587 if ((value
== LLONG_MIN
|| value
== LLONG_MAX
) && errno
== ERANGE
)
588 return HTTP_ERROR_SERVER
;
590 return HTTP_ERROR_CLIENT
;
591 if ((unsigned long long)value
> SIZE_MAX
)
592 return HTTP_ERROR_SERVER
;
593 request
->body_length
= value
;
595 request
->body_length
= 0;
597 return HTTP_ERROR_SUCCESS
;
601 /* Print binary stream to STDERR with some escapes */
602 static void dump_body(const void *data
, size_t length
) {
603 fprintf(stderr
, "===BEGIN BODY===\n");
604 if (length
> 0 && NULL
!= data
) {
605 for (size_t i
= 0; i
< length
; i
++) {
606 if (isprint(((const unsigned char *)data
)[i
]))
607 fprintf(stderr
, "%c", ((const unsigned char *)data
)[i
]);
609 fprintf(stderr
, "\\x%02x", ((const unsigned char*)data
)[i
]);
611 fprintf(stderr
, "\n");
613 fprintf(stderr
, "===END BODY===\n");
617 /* Read a HTTP request from connection.
618 * @http_request is heap-allocated received HTTP request,
619 * or NULL in case of error.
620 * @return http_error code. */
621 http_error
http_read_request(const struct http_connection
*connection
,
622 struct http_request
**request
) {
625 size_t buffer_size
= 0, buffer_used
= 0;
628 if (request
== NULL
) return HTTP_ERROR_SERVER
;
630 *request
= calloc(1, sizeof(**request
));
631 if (*request
== NULL
) return HTTP_ERROR_SERVER
;
633 /* Get request header */
634 if ((error
= http_read_line(connection
, &line
, &buffer
, &buffer_size
,
637 if ((error
= http_parse_request_header(line
, *request
)))
640 /* Get other headers */
642 if ((error
= http_read_line(connection
, &line
, &buffer
, &buffer_size
,
644 fprintf(stderr
, "Error while reading HTTP request line\n");
648 /* Check for headers delimiter */
649 if (line
== NULL
|| *line
== '\0') {
653 if ((error
= http_parse_header(line
, *request
))) {
654 fprintf(stderr
, "Error while parsing HTTP request line: <%s>\n",
661 if ((error
= find_content_length(*request
))) {
662 fprintf(stderr
, "Could not determine length of body\n");
665 if ((error
= http_read_bulk(connection
,
666 &(*request
)->body
, (*request
)->body_length
,
667 &buffer
, &buffer_size
, &buffer_used
))) {
668 fprintf(stderr
, "Could not read request body\n");
671 fprintf(stderr
, "Body of size %zu B has been received:\n",
672 (*request
)->body_length
);
673 dump_body((*request
)->body
, (*request
)->body_length
);
678 if (error
) http_request_free(request
);
683 /* Write a HTTP response to connection. Auto-add Content-Length header.
684 * @return 0 in case of success. */
685 int http_write_response(const struct http_connection
*connection
,
686 const struct http_response
*response
) {
690 if (response
== NULL
) return HTTP_ERROR_SERVER
;
693 error
= http_write_response_status(connection
, response
);
694 if (error
) return error
;
697 for (struct http_header
*header
= response
->headers
; header
!= NULL
;
698 header
= header
->next
) {
699 error
= http_write_header(connection
, header
);
700 if (error
) return error
;
702 if (-1 == test_asprintf(&buffer
, "Content-Length: %u",
703 response
->body_length
))
704 return HTTP_ERROR_SERVER
;
705 error
= http_write_line(connection
, buffer
);
706 if (error
) return error
;
708 /* Headers trailer */
709 error
= http_write_line(connection
, "");
710 if (error
) return error
;
713 if (response
->body_length
> 0) {
714 error
= http_write_bulk(connection
, response
->body
,
715 response
->body_length
);
716 if (error
) return error
;
718 fprintf(stderr
, "Body of size %zu B has been sent:\n",
719 response
->body_length
);
720 dump_body(response
->body
, response
->body_length
);
723 return HTTP_ERROR_SUCCESS
;
727 /* Build Set-Cookie header. In case of error, return NULL. Caller must free
729 static struct http_header
*http_build_setcookie_header (
730 const char *cokie_name
, const char *cookie_value
,
731 const char *cookie_domain
, const char *cookie_path
) {
732 struct http_header
*header
= NULL
;
733 char *domain_parameter
= NULL
;
734 char *path_parameter
= NULL
;
736 if (cokie_name
== NULL
) goto error
;
738 header
= calloc(1, sizeof(*header
));
739 if (header
== NULL
) goto error
;
741 header
->name
= strdup("Set-Cookie");
742 if (header
->name
== NULL
) goto error
;
744 if (cookie_domain
!= NULL
)
745 if (-1 == test_asprintf(&domain_parameter
, "; Domain=%s",
749 if (cookie_path
!= NULL
)
750 if (-1 == test_asprintf(&path_parameter
, "; Path=%s",
754 if (-1 == test_asprintf(&header
->value
, "%s=%s%s%s",
756 (cookie_value
== NULL
) ? "" : cookie_value
,
757 (domain_parameter
== NULL
) ? "": domain_parameter
,
758 (path_parameter
== NULL
) ? "": path_parameter
))
763 http_header_free(&header
);
765 free(domain_parameter
);
766 free(path_parameter
);
771 /* Send a 200 Ok response with a cookie */
772 int http_send_response_200_cookie(const struct http_connection
*connection
,
773 const char *cokie_name
, const char *cookie_value
,
774 const char *cookie_domain
, const char *cookie_path
,
775 const void *body
, size_t body_length
, const char *type
) {
777 struct http_header
*header_cookie
= NULL
;
778 struct http_header header_contenttype
= {
779 .name
= "Content-Type",
780 .value
= (char *)type
,
783 struct http_response response
= {
787 .body_length
= body_length
,
791 if (cokie_name
!= NULL
) {
792 if (NULL
== (header_cookie
= http_build_setcookie_header(
793 cokie_name
, cookie_value
, cookie_domain
, cookie_path
)))
794 return http_send_response_500(connection
,
795 "Could not build Set-Cookie header");
798 /* Link defined headers */
800 response
.headers
= &header_contenttype
;
802 if (header_cookie
!= NULL
) {
803 header_cookie
->next
= response
.headers
;
804 response
.headers
= header_cookie
;
807 retval
= http_write_response(connection
, &response
);
808 http_header_free(&header_cookie
);
813 /* Send a 200 Ok response */
814 int http_send_response_200(const struct http_connection
*connection
,
815 const void *body
, size_t body_length
, const char *type
) {
816 return http_send_response_200_cookie(connection
,
817 NULL
, NULL
, NULL
, NULL
,
818 body
, body_length
, type
);
822 /* Send a 302 Found response setting a cookie */
823 int http_send_response_302_cookie(const struct http_connection
*connection
,
824 const char *cokie_name
, const char *cookie_value
,
825 const char *cookie_domain
, const char *cookie_path
,
826 const char *location
) {
828 struct http_header
*header_cookie
= NULL
;
829 struct http_header header_location
= {
831 .value
= (char *) location
,
834 struct http_response response
= {
842 if (cokie_name
!= NULL
) {
843 if (NULL
== (header_cookie
= http_build_setcookie_header(
844 cokie_name
, cookie_value
, cookie_domain
, cookie_path
)))
845 return http_send_response_500(connection
,
846 "Could not build Set-Cookie header");
849 /* Link defined headers */
850 if (location
!= NULL
) {
851 response
.headers
= &header_location
;
853 if (header_cookie
!= NULL
) {
854 header_cookie
->next
= response
.headers
;
855 response
.headers
= header_cookie
;
858 retval
= http_write_response(connection
, &response
);
859 http_header_free(&header_cookie
);
864 /* Send a 302 Found response with totp authentication scheme header */
865 int http_send_response_302_totp(const struct http_connection
*connection
,
866 const char *code
, const char *text
, const char *location
) {
867 struct http_header header_code
= {
868 .name
= "X-Response-message-code",
869 .value
= (char *) code
,
872 struct http_header header_text
= {
873 .name
= "X-Response-message-text",
874 .value
= (char *) text
,
877 struct http_header header_location
= {
879 .value
= (char *) location
,
882 struct http_response response
= {
890 /* Link defined headers */
891 if (location
!= NULL
) {
892 response
.headers
= &header_location
;
895 header_text
.next
= response
.headers
;
896 response
.headers
= &header_text
;
899 header_code
.next
= response
.headers
;
900 response
.headers
= &header_code
;
903 return http_write_response(connection
, &response
);
907 /* Send a 400 Bad Request response.
908 * Use non-NULL @reason to override status message. */
909 int http_send_response_400(const struct http_connection
*connection
,
910 const char *reason
) {
911 struct http_response response
= {
913 .reason
= (reason
== NULL
) ? "Bad Request" : (char *) reason
,
919 return http_write_response(connection
, &response
);
923 /* Send a 401 Unauthorized response with Basic authentication scheme header */
924 int http_send_response_401_basic(const struct http_connection
*connection
) {
925 struct http_header header
= {
926 .name
= "WWW-Authenticate",
927 .value
= "Basic realm=\"SimulatedISDSServer\"",
930 struct http_response response
= {
932 .reason
= "Unauthorized",
938 return http_write_response(connection
, &response
);
942 /* Send a 401 Unauthorized response with OTP authentication scheme header for
944 int http_send_response_401_otp(const struct http_connection
*connection
,
945 const char *method
, const char *code
, const char *text
) {
947 struct http_header header
= {
948 .name
= "WWW-Authenticate",
952 struct http_header header_code
= {
953 .name
= "X-Response-message-code",
954 .value
= (char *) code
,
957 struct http_header header_text
= {
958 .name
= "X-Response-message-text",
959 .value
= (char *) text
,
962 struct http_response response
= {
964 .reason
= "Unauthorized",
970 if (-1 == test_asprintf(&header
.value
, "%s realm=\"SimulatedISDSServer\"",
972 return http_send_response_500(connection
,
973 "Could not build WWW-Authenticate header value");
976 /* Link defined headers */
977 if (code
!= NULL
) header
.next
= &header_code
;
980 header_code
.next
= &header_text
;
982 header
.next
= &header_text
;
985 retval
= http_write_response(connection
, &response
);
991 /* Send a 403 Forbidden response */
992 int http_send_response_403(const struct http_connection
*connection
) {
993 struct http_response response
= {
995 .reason
= "Forbidden",
1001 return http_write_response(connection
, &response
);
1005 /* Send a 500 Internal Server Error response.
1006 * Use non-NULL @reason to override status message. */
1007 int http_send_response_500(const struct http_connection
*connection
,
1008 const char *reason
) {
1009 struct http_response response
= {
1011 .reason
= (NULL
== reason
) ? "Internal Server Error" : (char *) reason
,
1017 return http_write_response(connection
, &response
);
1021 /* Send a 503 Service Temporarily Unavailable response */
1022 int http_send_response_503(const struct http_connection
*connection
,
1023 const void *body
, size_t body_length
, const char *type
) {
1024 struct http_header header
= {
1025 .name
= "Content-Type",
1026 .value
= (char *)type
1028 struct http_response response
= {
1030 .reason
= "Service Temporarily Unavailable",
1031 .headers
= (type
== NULL
) ? NULL
: &header
,
1032 .body_length
= body_length
,
1033 .body
= (void *)body
1036 return http_write_response(connection
, &response
);
1040 /* Returns Authorization header in given request */
1041 static const struct http_header
*find_authorization(
1042 const struct http_request
*request
) {
1043 const struct http_header
*header
;
1044 if (request
== NULL
) return NULL
;
1045 for (header
= request
->headers
; header
!= NULL
; header
= header
->next
) {
1046 if (header
->name
!= NULL
&&
1047 !strcasecmp(header
->name
, "Authorization"))
1054 /* Return true if request carries Authorization header */
1055 int http_client_authenticates(const struct http_request
*request
) {
1056 if (find_authorization(request
))
1063 /* Return HTTP_ERROR_SUCCESS if request carries valid Basic credentials.
1064 * NULL @username or @password equales to empty string. */
1065 http_error
http_authenticate_basic(const struct http_request
*request
,
1066 const char *username
, const char *password
) {
1067 const struct http_header
*header
;
1068 const char *basic_cookie_client
;
1069 char *basic_cookie_plain
= NULL
, *basic_cookie_encoded
= NULL
;
1072 header
= find_authorization(request
);
1073 if (header
== NULL
) return HTTP_ERROR_CLIENT
;
1075 if (strncmp(header
->value
, "Basic ", 6)) return HTTP_ERROR_CLIENT
;
1076 basic_cookie_client
= header
->value
+ 6;
1078 if (-1 == test_asprintf(&basic_cookie_plain
, "%s:%s",
1079 (username
== NULL
) ? "" : username
,
1080 (password
== NULL
) ? "" : password
)) {
1081 return HTTP_ERROR_SERVER
;
1084 basic_cookie_encoded
= base64encode(basic_cookie_plain
,
1085 strlen(basic_cookie_plain
), 1);
1086 if (basic_cookie_encoded
== NULL
) {
1087 free(basic_cookie_plain
);
1088 return HTTP_ERROR_SERVER
;
1091 fprintf(stderr
, "Authenticating basic: got=<%s>, expected=<%s> (%s)\n",
1092 basic_cookie_client
, basic_cookie_encoded
, basic_cookie_plain
);
1093 free(basic_cookie_plain
);
1095 is_valid
= !strcmp(basic_cookie_encoded
, basic_cookie_client
);
1096 free(basic_cookie_encoded
);
1098 return (is_valid
) ? HTTP_ERROR_SUCCESS
: HTTP_ERROR_CLIENT
;
1102 /* Return HTTP_ERROR_SUCCESS if request carries valid OTP credentials.
1103 * NULL @username or @password or @otp equal to empty string. */
1104 http_error
http_authenticate_otp(const struct http_request
*request
,
1105 const char *username
, const char *password
, const char *otp
) {
1106 char *basic_password
= NULL
;
1109 /* Concatenate password and OTP code */
1110 if (-1 == test_asprintf(&basic_password
, "%s%s",
1111 (password
== NULL
) ? "": password
,
1112 (otp
== NULL
) ? "" : otp
)) {
1113 return HTTP_ERROR_SERVER
;
1116 /* Use Basic authentication */
1117 /* XXX: Specification does not define authorization method string */
1118 retval
= http_authenticate_basic(request
, username
, basic_password
);
1120 free(basic_password
);
1125 /* Return cookie value by name or NULL if does not present. */
1126 const char *http_find_cookie(const struct http_request
*request
,
1128 const struct http_header
*header
;
1130 const char *value
= NULL
;
1132 if (request
== NULL
|| name
== NULL
) return NULL
;
1133 length
= strlen(name
);
1135 for (header
= request
->headers
; header
!= NULL
; header
= header
->next
) {
1136 if (header
->name
!= NULL
&& !strcasecmp(header
->name
, "Cookie") &&
1137 header
->value
!= NULL
) {
1138 if (!strncmp(header
->value
, name
, length
) &&
1139 header
->value
[length
] == '=') {
1140 /* Return last cookie with the name */
1141 value
= header
->value
+ length
+ 1;
1149 /* Return Host header value or NULL if does not present. Returned string is
1150 * statically allocated. */
1151 const char *http_find_host(const struct http_request
*request
) {
1152 const struct http_header
*header
;
1153 const char *value
= NULL
;
1155 if (request
== NULL
) return NULL
;
1157 for (header
= request
->headers
; header
!= NULL
; header
= header
->next
) {
1158 if (header
->name
!= NULL
&& !strcmp(header
->name
, "Host")) {
1159 value
= header
->value
;
1166 /* Free a HTTP header and set it to NULL */
1167 void http_header_free(struct http_header
**header
) {
1168 if (header
== NULL
|| *header
== NULL
) return;
1169 free((*header
)->name
);
1170 free((*header
)->value
);
1176 /* Free linked list of HTTP headers and set it to NULL */
1177 void http_headers_free(struct http_header
**headers
) {
1178 struct http_header
*header
, *next
;
1180 if (headers
== NULL
|| *headers
== NULL
) return;
1182 for (header
= *headers
; header
!= NULL
;) {
1183 next
= header
->next
;
1184 http_header_free(&header
);
1191 /* Free HTTP request and set it to NULL */
1192 void http_request_free(struct http_request
**request
) {
1193 if (request
== NULL
|| *request
== NULL
) return;
1194 free((*request
)->uri
);
1195 http_headers_free(&((*request
)->headers
));
1196 free((*request
)->body
);
1201 /* Free HTTP response and set it to NULL */
1202 void http_response_free(struct http_response
**response
) {
1203 if (response
== NULL
|| *response
== NULL
) return;
1204 free((*response
)->reason
);
1205 http_headers_free(&((*response
)->headers
));
1206 free((*response
)->body
);