2 #define _XOPEN_SOURCE 500 /* For strdup() */
6 #include "../test-tools.h"
11 #include <stdio.h> /* fprintf() */
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.
27 step_A
, step_B
, step_C
31 base64_encodestep step
;
33 int stepcount
; /* number of encoded octet triplets on a line,
34 or -1 to for end-less line */
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
;
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
;
65 result
= state_in
->result
;
67 switch (state_in
->step
) {
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;
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;
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) {
105 state_in
->stepcount
= 0;
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
) {
122 *codechar
++ = base64_encode_value(state_in
->result
);
127 *codechar
++ = base64_encode_value(state_in
->result
);
133 if (state_in
->stepcount
>= 0)
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
,
151 base64_encodestate state
;
153 char *buffer
, *new_buffer
;
156 if (length
) return NULL
;
157 /* Empty input is valid input */
161 base64_init_encodestate(&state
, one_line
);
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
,
171 code_length
+= base64_encode_blockend(((int8_t*)buffer
) + code_length
,
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
;
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')
190 if (digit
>= 'a' && digit
<= 'f')
191 return digit
- 'a' + 10;
192 if (digit
>= 'A' && digit
<= 'F')
193 return digit
- 'A' + 10;
198 /* Decode URI-coded string.
199 * @return allocated decoded string or NULL in case of error. */
200 static char *uri_decode(const char *coded
) {
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
++) {
210 digit1
= hex2i(coded
[1]);
215 digit2
= hex2i(coded
[2]);
220 *plain
= (digit1
<< 4) + digit2
;
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
) {
246 if (line
== NULL
) return HTTP_ERROR_SERVER
;
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
;
257 for (p
= *buffer
; p
< *buffer
+ *buffer_used
; p
++)
261 if (!(p
+ 1 < *buffer
+ *buffer_used
&& p
[1] == '\n'))
265 /* Crop by zero at EOL */
268 /* Copy read ahead data to new buffer and point line to original
271 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
272 memcpy(tmp
, p
, *buffer
+ *buffer_used
- p
);
274 *buffer_size
= BURST
;
275 *buffer_used
= *buffer
+ *buffer_used
- p
;
278 return HTTP_ERROR_SUCCESS
;
281 if (*buffer_size
== *buffer_used
) {
283 tmp
= realloc(*buffer
, *buffer_size
+ BURST
);
284 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
286 *buffer_size
+= BURST
;
290 got
= read(socket
, *buffer
+ *buffer_used
, *buffer_size
- *buffer_used
);
291 if (got
== -1 && errno
!= EINTR
) return HTTP_ERROR_CLIENT
;
294 if (got
== 0) return HTTP_ERROR_CLIENT
;
296 /* Move end of buffer */
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
) {
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
) {
331 if (line
== NULL
) return HTTP_ERROR_SERVER
;
333 fprintf(stderr
, "Response: <%s>\n", line
);
336 if ((error
= http_write_bulk(socket
, line
, strlen(line
))))
340 if ((error
= http_write_bulk(socket
, "\r\n", 2)))
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
) {
362 if (data
== NULL
) return HTTP_ERROR_SERVER
;
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
;
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
379 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
380 memcpy(tmp
, *buffer
+ data_length
, *buffer_used
- data_length
);
382 *buffer_size
= BURST
;
383 *buffer_used
= *buffer_used
- data_length
;
386 return HTTP_ERROR_SUCCESS
;
389 if (*buffer_size
== *buffer_used
) {
391 tmp
= realloc(*buffer
, *buffer_size
+ BURST
);
392 if (tmp
== NULL
) return HTTP_ERROR_SERVER
;
394 *buffer_size
+= BURST
;
398 got
= read(socket
, *buffer
+ *buffer_used
, *buffer_size
- *buffer_used
);
399 if (got
== -1 && errno
!= EINTR
) return HTTP_ERROR_CLIENT
;
402 if (got
== 0) return HTTP_ERROR_CLIENT
;
404 /* Move end of buffer */
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
) {
420 fprintf(stderr
, "Request: <%s>\n", line
);
423 p
= strchr(line
, ' ');
424 if (p
== NULL
) return HTTP_ERROR_SERVER
;
426 if (!strcmp(line
, "GET"))
427 request
->method
= HTTP_METHOD_GET
;
428 else if (!strcmp(line
, "POST"))
429 request
->method
= HTTP_METHOD_POST
;
431 request
->method
= HTTP_METHOD_UNKNOWN
;
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
) {
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
);
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 */
484 return HTTP_ERROR_CLIENT
; /* No previous header to continue */
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
;
491 strcpy(&header
->value
[old_length
], line
);
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';
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
;
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
) {
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
);
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
) {
565 long long int value
= strtol(header
->value
, &p
, 10);
567 return HTTP_ERROR_CLIENT
;
568 if ((value
== LLONG_MIN
|| value
== LLONG_MAX
) && errno
== ERANGE
)
569 return HTTP_ERROR_SERVER
;
571 return HTTP_ERROR_CLIENT
;
572 if (value
> SIZE_MAX
)
573 return HTTP_ERROR_SERVER
;
574 request
->body_length
= value
;
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
]);
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
) {
605 size_t buffer_size
= 0, buffer_used
= 0;
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
,
617 if ((error
= http_parse_request_header(line
, *request
)))
620 /* Get other headers */
622 if ((error
= http_read_line(socket
, &line
, &buffer
, &buffer_size
,
624 fprintf(stderr
, "Error while reading HTTP request line\n");
628 /* Check for headers delimiter */
629 if (line
== NULL
|| *line
== '\0') {
633 if ((error
= http_parse_header(line
, *request
))) {
634 fprintf(stderr
, "Error while parsing HTTP request line: <%s>\n",
641 if ((error
= find_content_length(*request
))) {
642 fprintf(stderr
, "Could not determine length of body\n");
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");
651 fprintf(stderr
, "Body of size %zu B has been received:\n",
652 (*request
)->body_length
);
653 dump_body((*request
)->body
, (*request
)->body_length
);
658 if (error
) http_request_free(request
);
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
) {
669 if (response
== NULL
) return HTTP_ERROR_SERVER
;
672 error
= http_write_response_status(socket
, response
);
673 if (error
) return error
;
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
;
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
);
701 return HTTP_ERROR_SUCCESS
;
705 /* Build Set-Cookie header. In case of error, return NULL. Caller must free
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",
727 if (cookie_path
!= NULL
)
728 if (-1 == test_asprintf(&path_parameter
, "; Path=%s",
732 if (-1 == test_asprintf(&header
->value
, "%s=%s%s%s",
734 (cookie_value
== NULL
) ? "" : cookie_value
,
735 (domain_parameter
== NULL
) ? "": domain_parameter
,
736 (path_parameter
== NULL
) ? "": path_parameter
))
741 http_header_free(&header
);
743 free(domain_parameter
);
744 free(path_parameter
);
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
) {
755 struct http_header
*header_cookie
= NULL
;
756 struct http_header header_contenttype
= {
757 .name
= "Content-Type",
758 .value
= (char *)type
,
761 struct http_response response
= {
765 .body_length
= body_length
,
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 */
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
);
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
) {
805 struct http_header
*header_cookie
= NULL
;
806 struct http_header header_location
= {
808 .value
= (char *) location
,
811 struct http_response response
= {
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
);
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
,
849 struct http_header header_text
= {
850 .name
= "X-Response-message-text",
851 .value
= (char *) text
,
854 struct http_header header_location
= {
856 .value
= (char *) location
,
859 struct http_response response
= {
867 /* Link defined headers */
868 if (location
!= NULL
) {
869 response
.headers
= &header_location
;
872 header_text
.next
= response
.headers
;
873 response
.headers
= &header_text
;
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
= {
889 .reason
= (reason
== NULL
) ? "Bad Request" : (char *) reason
,
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\"",
906 struct http_response response
= {
908 .reason
= "Unauthorized",
914 return http_write_response(client_socket
, &response
);
918 /* Send a 401 Unauthorized response with OTP authentication scheme header for
920 int http_send_response_401_otp(int client_socket
,
921 const char *method
, const char *code
, const char *text
) {
923 struct http_header header
= {
924 .name
= "WWW-Authenticate",
928 struct http_header header_code
= {
929 .name
= "X-Response-message-code",
930 .value
= (char *) code
,
933 struct http_header header_text
= {
934 .name
= "X-Response-message-text",
935 .value
= (char *) text
,
938 struct http_response response
= {
940 .reason
= "Unauthorized",
946 if (-1 == test_asprintf(&header
.value
, "%s realm=\"SimulatedISDSServer\"",
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
;
956 header_code
.next
= &header_text
;
958 header
.next
= &header_text
;
961 retval
= http_write_response(client_socket
, &response
);
967 /* Send a 403 Forbidden response */
968 int http_send_response_403(int client_socket
) {
969 struct http_response response
= {
971 .reason
= "Forbidden",
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
= {
986 .reason
= (NULL
== reason
) ? "Internal Server Error" : (char *) reason
,
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
= {
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"))
1029 /* Return true if request carries Authorization header */
1030 int http_client_authenticates(const struct http_request
*request
) {
1031 if (find_authorization(request
))
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
;
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
;
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
);
1100 /* Return cookie value by name or NULL if does not present. */
1101 const char *http_find_cookie(const struct http_request
*request
,
1103 const struct http_header
*header
;
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;
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
;
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
);
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
);
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
);
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
);