listen.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * uhttpd - Tiny single-threaded httpd
  3. *
  4. * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
  5. * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
  6. *
  7. * Permission to use, copy, modify, and/or distribute this software for any
  8. * purpose with or without fee is hereby granted, provided that the above
  9. * copyright notice and this permission notice appear in all copies.
  10. *
  11. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  14. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. */
  19. #include <sys/types.h>
  20. #include <sys/socket.h>
  21. #include <netinet/tcp.h>
  22. #include <netdb.h>
  23. #include "uhttpd.h"
  24. struct listener {
  25. struct list_head list;
  26. struct uloop_fd fd;
  27. int socket;
  28. int n_clients;
  29. struct sockaddr_in6 addr;
  30. bool tls;
  31. bool blocked;
  32. };
  33. static LIST_HEAD(listeners);
  34. static int n_blocked;
  35. void uh_close_listen_fds(void)
  36. {
  37. struct listener *l;
  38. list_for_each_entry(l, &listeners, list)
  39. close(l->fd.fd);
  40. }
  41. static void uh_block_listener(struct listener *l)
  42. {
  43. uloop_fd_delete(&l->fd);
  44. n_blocked++;
  45. l->blocked = true;
  46. }
  47. static void uh_poll_listeners(struct uloop_timeout *timeout)
  48. {
  49. struct listener *l;
  50. if ((!n_blocked && conf.max_connections) ||
  51. n_clients >= conf.max_connections)
  52. return;
  53. list_for_each_entry(l, &listeners, list) {
  54. if (!l->blocked)
  55. continue;
  56. l->fd.cb(&l->fd, ULOOP_READ);
  57. if (n_clients >= conf.max_connections)
  58. break;
  59. n_blocked--;
  60. l->blocked = false;
  61. uloop_fd_add(&l->fd, ULOOP_READ);
  62. }
  63. }
  64. void uh_unblock_listeners(void)
  65. {
  66. static struct uloop_timeout poll_timer = {
  67. .cb = uh_poll_listeners
  68. };
  69. uloop_timeout_set(&poll_timer, 1);
  70. }
  71. static void listener_cb(struct uloop_fd *fd, unsigned int events)
  72. {
  73. struct listener *l = container_of(fd, struct listener, fd);
  74. while (1) {
  75. if (!uh_accept_client(fd->fd, l->tls))
  76. break;
  77. }
  78. if (conf.max_connections && n_clients >= conf.max_connections)
  79. uh_block_listener(l);
  80. }
  81. void uh_setup_listeners(void)
  82. {
  83. struct listener *l;
  84. int yes = 1;
  85. list_for_each_entry(l, &listeners, list) {
  86. int sock = l->fd.fd;
  87. /* TCP keep-alive */
  88. if (conf.tcp_keepalive > 0) {
  89. #ifdef linux
  90. int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt, tcp_fstopn;
  91. tcp_ka_idl = 1;
  92. tcp_ka_cnt = 3;
  93. tcp_ka_int = conf.tcp_keepalive;
  94. tcp_fstopn = 5;
  95. setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl));
  96. setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int));
  97. setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt));
  98. setsockopt(sock, SOL_TCP, TCP_FASTOPEN, &tcp_fstopn, sizeof(tcp_fstopn));
  99. #endif
  100. setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes));
  101. }
  102. l->fd.cb = listener_cb;
  103. uloop_fd_add(&l->fd, ULOOP_READ);
  104. }
  105. }
  106. int uh_socket_bind(const char *host, const char *port, bool tls)
  107. {
  108. int sock = -1;
  109. int yes = 1;
  110. int status;
  111. int bound = 0;
  112. struct listener *l = NULL;
  113. struct addrinfo *addrs = NULL, *p = NULL;
  114. static struct addrinfo hints = {
  115. .ai_family = AF_UNSPEC,
  116. .ai_socktype = SOCK_STREAM,
  117. .ai_flags = AI_PASSIVE,
  118. };
  119. if ((status = getaddrinfo(host, port, &hints, &addrs)) != 0) {
  120. fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status));
  121. return 0;
  122. }
  123. /* try to bind a new socket to each found address */
  124. for (p = addrs; p; p = p->ai_next) {
  125. /* get the socket */
  126. sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
  127. if (sock < 0) {
  128. perror("socket()");
  129. goto error;
  130. }
  131. /* "address already in use" */
  132. if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) {
  133. perror("setsockopt()");
  134. goto error;
  135. }
  136. /* required to get parallel v4 + v6 working */
  137. if (p->ai_family == AF_INET6 &&
  138. setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
  139. perror("setsockopt()");
  140. goto error;
  141. }
  142. /* bind */
  143. if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) {
  144. perror("bind()");
  145. goto error;
  146. }
  147. /* listen */
  148. if (listen(sock, UH_LIMIT_CLIENTS) < 0) {
  149. perror("listen()");
  150. goto error;
  151. }
  152. fd_cloexec(sock);
  153. l = calloc(1, sizeof(*l));
  154. if (!l)
  155. goto error;
  156. l->fd.fd = sock;
  157. l->tls = tls;
  158. l->addr = *(struct sockaddr_in6 *)p->ai_addr;
  159. list_add_tail(&l->list, &listeners);
  160. bound++;
  161. continue;
  162. error:
  163. if (sock > -1)
  164. close(sock);
  165. }
  166. freeaddrinfo(addrs);
  167. return bound;
  168. }
  169. int uh_first_tls_port(int family)
  170. {
  171. struct listener *l;
  172. int tls_port = -1;
  173. list_for_each_entry(l, &listeners, list) {
  174. if (!l->tls || l->addr.sin6_family != family)
  175. continue;
  176. if (tls_port != -1 && ntohs(l->addr.sin6_port) != 443)
  177. continue;
  178. tls_port = ntohs(l->addr.sin6_port);
  179. }
  180. return tls_port;
  181. }