2 * copyright (c) 2001-2014 Emil Mikulic.
4 * http.c: embedded webserver.
5 * This borrows a lot of code from darkhttpd.
7 * You may use, modify and redistribute this file under the terms of the
8 * GNU General Public License version 2. (see COPYING.GPL)
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
40 static char *http_base_url
= NULL
;
41 static int http_base_len
= 0;
43 static const char mime_type_xml
[] = "text/xml";
44 static const char mime_type_html
[] = "text/html; charset=us-ascii";
45 static const char mime_type_css
[] = "text/css";
46 static const char mime_type_js
[] = "text/javascript";
47 static const char encoding_identity
[] = "identity";
48 static const char encoding_gzip
[] = "gzip";
50 static const char server
[] = PACKAGE_NAME
"/" PACKAGE_VERSION
;
51 static int idletime
= 60;
52 #define MAX_REQUEST_LENGTH 4000
54 static int *insocks
= NULL
;
55 static unsigned int insock_num
= 0;
58 LIST_ENTRY(connection
) entries
;
61 struct sockaddr_storage client
;
62 time_t last_active_mono
;
64 RECV_REQUEST
, /* receiving request */
65 SEND_HEADER_AND_REPLY
, /* try to send header+reply together */
66 SEND_HEADER
, /* sending generated header */
67 SEND_REPLY
, /* sending reply */
68 DONE
/* conn closed, need to remove from queue */
71 /* char request[request_length+1] is null-terminated */
73 size_t request_length
;
77 char *method
, *uri
, *query
; /* query can be NULL */
80 const char *mime_type
, *encoding
, *header_extra
;
81 size_t header_length
, header_sent
;
82 int header_dont_free
, header_only
, http_code
;
86 size_t reply_length
, reply_sent
;
88 unsigned int total_sent
; /* header + body = total, for logging */
91 static LIST_HEAD(conn_list_head
, connection
) connlist
=
92 LIST_HEAD_INITIALIZER(conn_list_head
);
94 struct bindaddr_entry
{
95 STAILQ_ENTRY(bindaddr_entry
) entries
;
98 static STAILQ_HEAD(bindaddrs_head
, bindaddr_entry
) bindaddrs
=
99 STAILQ_HEAD_INITIALIZER(bindaddrs
);
101 /* ---------------------------------------------------------------------------
102 * Decode URL by converting %XX (where XX are hexadecimal digits) to the
103 * character it represents. Don't forget to free the return value.
105 static char *urldecode(const char *url
)
107 size_t i
, len
= strlen(url
);
108 char *out
= xmalloc(len
+1);
111 for (i
=0, pos
=0; i
<len
; i
++)
113 if (url
[i
] == '%' && i
+2 < len
&&
114 isxdigit(url
[i
+1]) && isxdigit(url
[i
+2]))
117 #define HEX_TO_DIGIT(hex) ( \
118 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
119 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
122 out
[pos
++] = HEX_TO_DIGIT(url
[i
+1]) * 16 +
123 HEX_TO_DIGIT(url
[i
+2]);
136 /* don't really need to realloc here - it's probably a performance hit */
137 out
= xrealloc(out
, strlen(out
)+1); /* dealloc what we don't need */
144 /* ---------------------------------------------------------------------------
145 * Consolidate slashes in-place by shifting parts of the string over repeated
148 static void consolidate_slashes(char *s
)
150 size_t left
= 0, right
= 0;
155 while (s
[right
] != '\0')
159 if (s
[right
] == '/') right
++;
163 s
[left
++] = s
[right
++];
168 if (s
[right
] == '/') saw_slash
++;
169 s
[left
++] = s
[right
++];
177 /* ---------------------------------------------------------------------------
178 * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI
179 * is invalid/unsafe. Returned buffer needs to be deallocated.
181 static char *make_safe_uri(char *uri
)
184 unsigned int slashes
= 0, elements
= 0;
185 size_t urilen
, i
, j
, pos
;
190 consolidate_slashes(uri
);
191 urilen
= strlen(uri
);
193 /* count the slashes */
194 for (i
=0, slashes
=0; i
<urilen
; i
++)
195 if (uri
[i
] == '/') slashes
++;
197 /* make an array for the URI elements */
198 elem
= xmalloc(sizeof(*elem
) * slashes
);
199 for (i
=0; i
<slashes
; i
++)
202 /* split by slashes and build elem[] array */
205 /* look for the next slash */
206 for (j
=i
; j
<urilen
&& uri
[j
] != '/'; j
++)
209 /* process uri[i,j) */
210 if ((j
== i
+1) && (uri
[i
] == '.'))
212 else if ((j
== i
+2) && (uri
[i
] == '.') && (uri
[i
+1] == '.'))
218 * Unsafe string so free elem[]. All its elements are free
227 free(elem
[elements
]);
230 else elem
[elements
++] = split_string(uri
, i
, j
);
232 i
= j
+ 1; /* uri[j] is a slash - move along one */
236 out
= xmalloc(urilen
+1); /* it won't expand */
238 for (i
=0; i
<elements
; i
++)
240 size_t delta
= strlen(elem
[i
]);
242 assert(pos
<= urilen
);
245 assert(pos
+delta
<= urilen
);
246 memcpy(out
+pos
, elem
[i
], delta
);
252 if ((elements
== 0) || (uri
[urilen
-1] == '/')) out
[pos
++] = '/';
253 assert(pos
<= urilen
);
257 /* don't really need to do this and it's probably a performance hit: */
258 /* shorten buffer if necessary */
259 if (pos
!= urilen
) out
= xrealloc(out
, strlen(out
)+1);
264 /* ---------------------------------------------------------------------------
265 * Allocate and initialize an empty connection.
267 static struct connection
*new_connection(void)
269 struct connection
*conn
= xmalloc(sizeof(*conn
));
272 memset(&conn
->client
, 0, sizeof(conn
->client
));
273 conn
->last_active_mono
= now_mono();
274 conn
->request
= NULL
;
275 conn
->request_length
= 0;
276 conn
->accept_gzip
= 0;
281 conn
->mime_type
= NULL
;
282 conn
->encoding
= NULL
;
283 conn
->header_extra
= "";
284 conn
->header_length
= 0;
285 conn
->header_sent
= 0;
286 conn
->header_dont_free
= 0;
287 conn
->header_only
= 0;
290 conn
->reply_dont_free
= 0;
291 conn
->reply_length
= 0;
292 conn
->reply_sent
= 0;
293 conn
->total_sent
= 0;
295 /* Make it harmless so it gets garbage-collected if it should, for some
296 * reason, fail to be correctly filled out.
305 /* ---------------------------------------------------------------------------
306 * Accept a connection from sockin and add it to the connection queue.
308 static void accept_connection(const int sockin
)
310 struct sockaddr_storage addrin
;
312 struct connection
*conn
;
313 char ipaddr
[INET6_ADDRSTRLEN
], portstr
[12];
316 sin_size
= (socklen_t
)sizeof(addrin
);
317 sock
= accept(sockin
, (struct sockaddr
*)&addrin
, &sin_size
);
320 if (errno
== ECONNABORTED
|| errno
== EINTR
)
322 verbosef("accept() failed: %s", strerror(errno
));
325 /* else */ err(1, "accept()");
328 fd_set_nonblock(sock
);
330 /* allocate and initialise struct connection */
331 conn
= new_connection();
333 conn
->state
= RECV_REQUEST
;
334 memcpy(&conn
->client
, &addrin
, sizeof(conn
->client
));
335 LIST_INSERT_HEAD(&connlist
, conn
, entries
);
337 getnameinfo((struct sockaddr
*) &addrin
, sin_size
,
338 ipaddr
, sizeof(ipaddr
), portstr
, sizeof(portstr
),
339 NI_NUMERICHOST
| NI_NUMERICSERV
);
340 verbosef("accepted connection from %s:%s", ipaddr
, portstr
);
345 /* ---------------------------------------------------------------------------
346 * Log a connection, then cleanly deallocate its internals.
348 static void free_connection(struct connection
*conn
)
350 dverbosef("free_connection(%d)", conn
->socket
);
351 if (conn
->socket
!= -1)
357 if (!conn
->header_dont_free
)
359 if (!conn
->reply_dont_free
)
365 /* ---------------------------------------------------------------------------
366 * Format [when] as an RFC1123 date, stored in the specified buffer. The same
367 * buffer is returned for convenience.
369 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
370 static char *rfc1123_date(char *dest
, time_t when
) {
371 if (strftime(dest
, DATE_LEN
,
372 "%a, %d %b %Y %H:%M:%S %Z", gmtime(&when
) ) == 0)
373 errx(1, "strftime() failed [%s]", dest
);
377 static void generate_header(struct connection
*conn
,
378 const int code
, const char *text
)
382 assert(conn
->header
== NULL
);
383 assert(conn
->mime_type
!= NULL
);
384 if (conn
->encoding
== NULL
)
385 conn
->encoding
= encoding_identity
;
387 verbosef("http: %d %s (%s: %zu bytes)",
392 conn
->header_length
= xasprintf(&(conn
->header
),
396 "Vary: Accept-Encoding\r\n"
397 "Content-Type: %s\r\n"
398 "Content-Length: %qu\r\n"
399 "Content-Encoding: %s\r\n"
400 "X-Robots-Tag: noindex, noarchive\r\n"
404 rfc1123_date(date
, now_real()),
407 (qu
)conn
->reply_length
,
410 conn
->http_code
= code
;
415 /* ---------------------------------------------------------------------------
416 * A default reply for any (erroneous) occasion.
418 static void default_reply(struct connection
*conn
,
419 const int errcode
, const char *errname
, const char *format
, ...)
421 static void default_reply(struct connection
*conn
,
422 const int errcode
, const char *errname
, const char *format
, ...)
427 va_start(va
, format
);
428 xvasprintf(&reason
, format
, va
);
431 conn
->reply_length
= xasprintf(&(conn
->reply
),
432 "<html><head><title>%d %s</title></head><body>\n"
433 "<h1>%s</h1>\n" /* errname */
438 errcode
, errname
, errname
, reason
, server
);
441 /* forget any dangling metadata */
442 conn
->mime_type
= mime_type_html
;
443 conn
->encoding
= encoding_identity
;
445 generate_header(conn
, errcode
, errname
);
450 /* ---------------------------------------------------------------------------
451 * Parses a single HTTP request field. Returns string from end of [field] to
452 * first \r, \n or end of request string. Returns NULL if [field] can't be
455 * You need to remember to deallocate the result.
456 * example: parse_field(conn, "Referer: ");
458 static char *parse_field(const struct connection
*conn
, const char *field
)
460 size_t bound1
, bound2
;
464 pos
= strstr(conn
->request
, field
);
467 bound1
= pos
- conn
->request
+ strlen(field
);
470 for (bound2
= bound1
;
471 conn
->request
[bound2
] != '\r' &&
472 bound2
< conn
->request_length
; bound2
++)
476 return (split_string(conn
->request
, bound1
, bound2
));
481 /* ---------------------------------------------------------------------------
482 * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method
483 * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will
484 * accept gzip encoding. Remember to deallocate all these buffers. Query
485 * can be NULL. The method will be returned in uppercase.
487 static int parse_request(struct connection
*conn
)
489 size_t bound1
, bound2
, mid
;
493 for (bound1
= 0; bound1
< conn
->request_length
&&
494 conn
->request
[bound1
] != ' '; bound1
++)
497 conn
->method
= split_string(conn
->request
, 0, bound1
);
498 strntoupper(conn
->method
, bound1
);
501 for (; bound1
< conn
->request_length
&&
502 conn
->request
[bound1
] == ' '; bound1
++)
505 if (bound1
== conn
->request_length
)
506 return (0); /* fail */
508 for (bound2
=bound1
+1; bound2
< conn
->request_length
&&
509 conn
->request
[bound2
] != ' ' &&
510 conn
->request
[bound2
] != '\r'; bound2
++)
513 /* find query string */
514 for (mid
=bound1
; mid
<bound2
&& conn
->request
[mid
] != '?'; mid
++)
517 if (conn
->request
[mid
] == '?') {
518 conn
->query
= split_string(conn
->request
, mid
+1, bound2
);
522 conn
->uri
= split_string(conn
->request
, bound1
, bound2
);
524 /* parse important fields */
525 accept_enc
= parse_field(conn
, "Accept-Encoding: ");
526 if (accept_enc
!= NULL
) {
527 if (strstr(accept_enc
, "gzip") != NULL
)
528 conn
->accept_gzip
= 1;
534 /* FIXME: maybe we need a smarter way of doing static pages: */
536 /* ---------------------------------------------------------------------------
537 * Web interface: static stylesheet.
540 static_style_css(struct connection
*conn
)
542 #include "stylecss.h"
544 conn
->reply
= style_css
;
545 conn
->reply_length
= style_css_len
;
546 conn
->reply_dont_free
= 1;
547 conn
->mime_type
= mime_type_css
;
550 /* ---------------------------------------------------------------------------
551 * Web interface: static JavaScript.
554 static_graph_js(struct connection
*conn
)
558 conn
->reply
= graph_js
;
559 conn
->reply_length
= graph_js_len
;
560 conn
->reply_dont_free
= 1;
561 conn
->mime_type
= mime_type_js
;
564 /* ---------------------------------------------------------------------------
565 * gzip a reply, if requested and possible. Don't bother with a minimum
566 * length requirement, I've never seen a page fail to compress.
569 process_gzip(struct connection
*conn
)
575 if (!conn
->accept_gzip
)
578 buf
= xmalloc(conn
->reply_length
);
579 len
= conn
->reply_length
;
585 if (deflateInit2(&zs
,
588 15+16, /* 15 = biggest window,
589 16 = add gzip header+trailer */
591 Z_DEFAULT_STRATEGY
) != Z_OK
) {
596 zs
.avail_in
= conn
->reply_length
;
597 zs
.next_in
= (unsigned char *)conn
->reply
;
599 zs
.avail_out
= conn
->reply_length
;
600 zs
.next_out
= (unsigned char *)buf
;
602 if (deflate(&zs
, Z_FINISH
) != Z_STREAM_END
) {
605 verbosef("failed to compress %zu bytes", len
);
609 if (conn
->reply_dont_free
)
610 conn
->reply_dont_free
= 0;
614 conn
->reply_length
-= zs
.avail_out
;
615 conn
->encoding
= encoding_gzip
;
619 /* ---------------------------------------------------------------------------
620 * Process a GET/HEAD request
622 static void process_get(struct connection
*conn
)
626 verbosef("http: %s \"%s\" %s", conn
->method
, conn
->uri
,
627 (conn
->query
== NULL
)?"":conn
->query
);
630 /* Decode the URL being requested. */
632 char *decoded_url_offset
;
634 decoded_url
= urldecode(conn
->uri
);
636 /* Optionally strip the base. */
637 decoded_url_offset
= decoded_url
;
638 if (str_starts_with(decoded_url
, http_base_url
)) {
639 decoded_url_offset
+= http_base_len
- 1;
642 /* Make sure it's safe. */
643 safe_url
= make_safe_uri(decoded_url_offset
);
645 if (safe_url
== NULL
) {
646 default_reply(conn
, 400, "Bad Request",
647 "You requested an invalid URI: %s", conn
->uri
);
652 if (strcmp(safe_url
, "/") == 0) {
653 struct str
*buf
= html_front_page();
654 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
655 conn
->mime_type
= mime_type_html
;
657 else if (str_starts_with(safe_url
, "/hosts/")) {
658 /* FIXME here - make this saner */
659 struct str
*buf
= html_hosts(safe_url
, conn
->query
);
661 default_reply(conn
, 404, "Not Found",
662 "The page you requested could not be found.");
666 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
667 conn
->mime_type
= mime_type_html
;
669 else if (str_starts_with(safe_url
, "/graphs.xml")) {
670 struct str
*buf
= xml_graphs();
671 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
672 conn
->mime_type
= mime_type_xml
;
673 /* hack around Opera caching the XML */
674 conn
->header_extra
= "Pragma: no-cache\r\n";
676 else if (strcmp(safe_url
, "/style.css") == 0)
677 static_style_css(conn
);
678 else if (strcmp(safe_url
, "/graph.js") == 0)
679 static_graph_js(conn
);
681 default_reply(conn
, 404, "Not Found",
682 "The page you requested could not be found.");
689 assert(conn
->mime_type
!= NULL
);
690 generate_header(conn
, 200, "OK");
695 /* ---------------------------------------------------------------------------
696 * Process a request: build the header and reply, advance state.
698 static void process_request(struct connection
*conn
)
700 if (!parse_request(conn
))
702 default_reply(conn
, 400, "Bad Request",
703 "You sent a request that the server couldn't understand.");
705 else if (strcmp(conn
->method
, "GET") == 0)
709 else if (strcmp(conn
->method
, "HEAD") == 0)
712 conn
->header_only
= 1;
716 default_reply(conn
, 501, "Not Implemented",
717 "The method you specified (%s) is not implemented.",
722 if (conn
->header_only
)
723 conn
->state
= SEND_HEADER
;
725 conn
->state
= SEND_HEADER_AND_REPLY
;
730 /* ---------------------------------------------------------------------------
733 static void poll_recv_request(struct connection
*conn
)
738 recvd
= recv(conn
->socket
, buf
, sizeof(buf
), 0);
739 dverbosef("poll_recv_request(%d) got %d bytes", conn
->socket
, (int)recvd
);
743 verbosef("recv(%d) error: %s", conn
->socket
, strerror(errno
));
747 conn
->last_active_mono
= now_mono();
749 /* append to conn->request */
750 conn
->request
= xrealloc(conn
->request
, conn
->request_length
+recvd
+1);
751 memcpy(conn
->request
+conn
->request_length
, buf
, (size_t)recvd
);
752 conn
->request_length
+= recvd
;
753 conn
->request
[conn
->request_length
] = 0;
755 /* die if it's too long */
756 if (conn
->request_length
> MAX_REQUEST_LENGTH
)
758 default_reply(conn
, 413, "Request Entity Too Large",
759 "Your request was dropped because it was too long.");
760 conn
->state
= SEND_HEADER
;
764 /* process request if we have all of it */
765 if (conn
->request_length
> 4 &&
766 memcmp(conn
->request
+conn
->request_length
-4, "\r\n\r\n", 4) == 0)
768 process_request(conn
);
770 /* request not needed anymore */
772 conn
->request
= NULL
; /* important: don't free it again later */
778 /* ---------------------------------------------------------------------------
779 * Try to send header and [a part of the] reply in one packet.
781 static void poll_send_header_and_reply(struct connection
*conn
)
786 assert(!conn
->header_only
);
787 assert(conn
->reply_length
> 0);
788 assert(conn
->header_sent
== 0);
790 assert(conn
->reply_sent
== 0);
793 iov
[0].iov_base
= conn
->header
;
794 iov
[0].iov_len
= conn
->header_length
;
796 iov
[1].iov_base
= conn
->reply
;
797 iov
[1].iov_len
= conn
->reply_length
;
799 sent
= writev(conn
->socket
, iov
, 2);
800 conn
->last_active_mono
= now_mono();
802 /* handle any errors (-1) or closure (0) in send() */
805 verbosef("writev(%d) error: %s", conn
->socket
, strerror(errno
));
810 /* Figure out what we've sent. */
811 conn
->total_sent
+= (unsigned int)sent
;
812 if (sent
< (ssize_t
)conn
->header_length
) {
813 verbosef("partially sent header");
814 conn
->header_sent
= sent
;
815 conn
->state
= SEND_HEADER
;
819 conn
->header_sent
= conn
->header_length
;
820 sent
-= conn
->header_length
;
822 if (sent
< (ssize_t
)conn
->reply_length
) {
823 verbosef("partially sent reply");
824 conn
->reply_sent
+= sent
;
825 conn
->state
= SEND_REPLY
;
829 conn
->reply_sent
= conn
->reply_length
;
833 /* ---------------------------------------------------------------------------
834 * Sending header. Assumes conn->header is not NULL.
836 static void poll_send_header(struct connection
*conn
)
840 sent
= send(conn
->socket
, conn
->header
+ conn
->header_sent
,
841 conn
->header_length
- conn
->header_sent
, 0);
842 conn
->last_active_mono
= now_mono();
843 dverbosef("poll_send_header(%d) sent %d bytes", conn
->socket
, (int)sent
);
845 /* handle any errors (-1) or closure (0) in send() */
849 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
853 conn
->header_sent
+= (unsigned int)sent
;
854 conn
->total_sent
+= (unsigned int)sent
;
856 /* check if we're done sending */
857 if (conn
->header_sent
== conn
->header_length
)
859 if (conn
->header_only
)
862 conn
->state
= SEND_REPLY
;
868 /* ---------------------------------------------------------------------------
871 static void poll_send_reply(struct connection
*conn
)
875 sent
= send(conn
->socket
,
876 conn
->reply
+ conn
->reply_sent
,
877 conn
->reply_length
- conn
->reply_sent
, 0);
878 conn
->last_active_mono
= now_mono();
879 dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
880 conn
->socket
, (int)sent
,
881 (int)conn
->reply_sent
,
882 (int)(conn
->reply_sent
+ sent
- 1),
883 (int)conn
->reply_length
);
885 /* handle any errors (-1) or closure (0) in send() */
889 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
891 verbosef("send(%d) closure", conn
->socket
);
895 conn
->reply_sent
+= (unsigned int)sent
;
896 conn
->total_sent
+= (unsigned int)sent
;
898 /* check if we're done sending */
899 if (conn
->reply_sent
== conn
->reply_length
) conn
->state
= DONE
;
904 /* --------------------------------------------------------------------------
905 * Initialize the base url.
907 void http_init_base(const char *url
) {
908 char *slashed_url
, *safe_url
;
912 http_base_url
= strdup("/");
914 /* Make sure that the url has leading and trailing slashes. */
915 urllen
= strlen(url
);
916 slashed_url
= xmalloc(urllen
+3);
917 slashed_url
[0] = '/';
918 memcpy(slashed_url
+1, url
, urllen
); /* don't copy NUL */
919 slashed_url
[urllen
+1] = '/';
920 slashed_url
[urllen
+2] = '\0';
923 safe_url
= make_safe_uri(slashed_url
);
925 if (safe_url
== NULL
) {
926 verbosef("invalid base \"%s\", ignored", url
);
927 http_base_url
= strdup("/"); /* set to default */
929 http_base_url
= safe_url
;
932 http_base_len
= strlen(http_base_url
);
933 verbosef("set base url to \"%s\"", http_base_url
);
936 /* Use getaddrinfo to figure out what type of socket to create and
937 * what to bind it to. "bindaddr" can be NULL. Remember to freeaddrinfo()
940 static struct addrinfo
*get_bind_addr(
941 const char *bindaddr
, const unsigned short bindport
)
943 struct addrinfo hints
, *ai
;
947 memset(&hints
, 0, sizeof(hints
));
948 hints
.ai_family
= AF_UNSPEC
;
949 hints
.ai_socktype
= SOCK_STREAM
;
950 hints
.ai_flags
= AI_PASSIVE
;
952 snprintf(portstr
, sizeof(portstr
), "%u", bindport
);
953 if ((ret
= getaddrinfo(bindaddr
, portstr
, &hints
, &ai
)))
954 err(1, "getaddrinfo(%s, %s) failed: %s",
955 bindaddr
? bindaddr
: "NULL", portstr
, gai_strerror(ret
));
957 err(1, "getaddrinfo() returned NULL pointer");
961 void http_add_bindaddr(const char *bindaddr
)
963 struct bindaddr_entry
*ent
;
965 ent
= xmalloc(sizeof(*ent
));
967 STAILQ_INSERT_TAIL(&bindaddrs
, ent
, entries
);
970 static void http_listen_one(struct addrinfo
*ai
,
971 const unsigned short bindport
)
973 char ipaddr
[INET6_ADDRSTRLEN
];
974 int sockin
, sockopt
, ret
;
976 /* format address into ipaddr string */
977 if ((ret
= getnameinfo(ai
->ai_addr
, ai
->ai_addrlen
, ipaddr
,
978 sizeof(ipaddr
), NULL
, 0, NI_NUMERICHOST
)) != 0)
979 err(1, "getnameinfo failed: %s", gai_strerror(ret
));
981 /* create incoming socket */
982 if ((sockin
= socket(ai
->ai_family
, ai
->ai_socktype
,
983 ai
->ai_protocol
)) == -1) {
984 warn("http_listen_one(%s, %u): socket(%d (%s), %d, %d) failed",
985 ipaddr
, (unsigned int)bindport
,
987 (ai
->ai_family
== AF_INET6
) ? "AF_INET6" :
988 (ai
->ai_family
== AF_INET
) ? "AF_INET" :
990 ai
->ai_socktype
, ai
->ai_protocol
);
994 fd_set_nonblock(sockin
);
998 if (setsockopt(sockin
, SOL_SOCKET
, SO_REUSEADDR
,
999 &sockopt
, sizeof(sockopt
)) == -1)
1000 err(1, "can't set SO_REUSEADDR");
1003 /* explicitly disallow IPv4 mapped addresses since OpenBSD doesn't allow
1004 * dual stack sockets under any circumstances
1006 if (ai
->ai_family
== AF_INET6
) {
1008 if (setsockopt(sockin
, IPPROTO_IPV6
, IPV6_V6ONLY
,
1009 &sockopt
, sizeof(sockopt
)) == -1)
1010 err(1, "can't set IPV6_V6ONLY");
1015 if (bind(sockin
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
1016 warn("bind(\"%s\") failed", ipaddr
);
1021 /* listen on socket */
1022 if (listen(sockin
, 128) == -1)
1023 err(1, "listen() failed");
1025 verbosef("listening on http://%s%s%s:%u%s",
1026 (ai
->ai_family
== AF_INET6
) ? "[" : "",
1028 (ai
->ai_family
== AF_INET6
) ? "]" : "",
1032 /* add to insocks */
1033 insocks
= xrealloc(insocks
, sizeof(*insocks
) * (insock_num
+ 1));
1034 insocks
[insock_num
++] = sockin
;
1037 /* Initialize the http sockets and listen on them. */
1038 void http_listen(const unsigned short bindport
)
1040 /* If the user didn't specify any bind addresses, add a NULL.
1041 * This will become a wildcard.
1043 if (STAILQ_EMPTY(&bindaddrs
))
1044 http_add_bindaddr(NULL
);
1046 /* Listen on every specified interface. */
1047 while (!STAILQ_EMPTY(&bindaddrs
)) {
1048 struct bindaddr_entry
*bindaddr
= STAILQ_FIRST(&bindaddrs
);
1049 struct addrinfo
*ai
, *ais
= get_bind_addr(bindaddr
->s
, bindport
);
1051 /* There could be multiple addresses returned, handle them all. */
1052 for (ai
= ais
; ai
; ai
= ai
->ai_next
)
1053 http_listen_one(ai
, bindport
);
1057 STAILQ_REMOVE_HEAD(&bindaddrs
, entries
);
1061 if (insocks
== NULL
)
1062 errx(1, "was not able to bind any ports for http interface");
1064 /* ignore SIGPIPE */
1065 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
)
1066 err(1, "can't ignore SIGPIPE");
1071 /* ---------------------------------------------------------------------------
1072 * Set recv/send fd_sets and calculate timeout length.
1075 http_fd_set(fd_set
*recv_set
, fd_set
*send_set
, int *max_fd
,
1076 struct timeval
*timeout
, int *need_timeout
)
1078 struct connection
*conn
, *next
;
1079 int minidle
= idletime
+ 1;
1082 #define MAX_FD_SET(sock, fdset) do { \
1083 FD_SET(sock, fdset); *max_fd = MAX(*max_fd, sock); } while(0)
1085 for (i
=0; i
<insock_num
; i
++)
1086 MAX_FD_SET(insocks
[i
], recv_set
);
1088 LIST_FOREACH_SAFE(conn
, &connlist
, entries
, next
)
1090 int idlefor
= now_mono() - conn
->last_active_mono
;
1092 /* Time out dead connections. */
1093 if (idlefor
>= idletime
) {
1094 char ipaddr
[INET6_ADDRSTRLEN
];
1095 /* FIXME: this is too late on FreeBSD, socket is invalid */
1096 int ret
= getnameinfo((struct sockaddr
*)&conn
->client
,
1097 sizeof(conn
->client
), ipaddr
, sizeof(ipaddr
),
1098 NULL
, 0, NI_NUMERICHOST
);
1100 verbosef("http socket timeout from %s (fd %d)",
1101 ipaddr
, conn
->socket
);
1103 warn("http socket timeout: getnameinfo error: %s",
1108 /* Connections that need a timeout. */
1109 if (conn
->state
!= DONE
)
1110 minidle
= MIN(minidle
, (idletime
- idlefor
));
1112 switch (conn
->state
)
1115 /* clean out stale connection */
1116 LIST_REMOVE(conn
, entries
);
1117 free_connection(conn
);
1122 MAX_FD_SET(conn
->socket
, recv_set
);
1125 case SEND_HEADER_AND_REPLY
:
1128 MAX_FD_SET(conn
->socket
, send_set
);
1131 default: errx(1, "invalid state");
1136 /* Only set timeout if cap hasn't already. */
1137 if ((*need_timeout
== 0) && (minidle
<= idletime
)) {
1139 timeout
->tv_sec
= minidle
;
1140 timeout
->tv_usec
= 0;
1146 /* ---------------------------------------------------------------------------
1147 * poll connections that select() says need attention
1149 void http_poll(fd_set
*recv_set
, fd_set
*send_set
)
1151 struct connection
*conn
;
1154 for (i
=0; i
<insock_num
; i
++)
1155 if (FD_ISSET(insocks
[i
], recv_set
))
1156 accept_connection(insocks
[i
]);
1158 LIST_FOREACH(conn
, &connlist
, entries
)
1159 switch (conn
->state
)
1162 if (FD_ISSET(conn
->socket
, recv_set
)) poll_recv_request(conn
);
1165 case SEND_HEADER_AND_REPLY
:
1166 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header_and_reply(conn
);
1170 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header(conn
);
1174 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_reply(conn
);
1177 case DONE
: /* fallthrough */
1178 default: errx(1, "invalid state");
1182 void http_stop(void) {
1183 struct connection
*conn
;
1184 struct connection
*next
;
1187 free(http_base_url
);
1189 /* Close listening sockets. */
1190 for (i
=0; i
<insock_num
; i
++)
1195 /* Close in-flight connections. */
1196 LIST_FOREACH_SAFE(conn
, &connlist
, entries
, next
) {
1197 LIST_REMOVE(conn
, entries
);
1198 free_connection(conn
);
1203 /* vim:set ts=4 sw=4 et tw=78: */