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 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <netinet/tcp.h> 23 #include <netdb.h> 24 25 #include "uhttpd.h" 26 27 struct listener { 28 struct list_head list; 29 struct uloop_fd fd; 30 int socket; 31 int n_clients; 32 struct sockaddr_in6 addr; 33 bool tls; 34 bool blocked; 35 }; 36 37 static LIST_HEAD(listeners); 38 static int n_blocked; 39 40 void uh_close_listen_fds(void) 41 { 42 struct listener *l; 43 44 list_for_each_entry(l, &listeners, list) 45 close(l->fd.fd); 46 } 47 48 static void uh_block_listener(struct listener *l) 49 { 50 uloop_fd_delete(&l->fd); 51 n_blocked++; 52 l->blocked = true; 53 } 54 55 static void uh_poll_listeners(struct uloop_timeout *timeout) 56 { 57 struct listener *l; 58 59 if ((!n_blocked && conf.max_connections) || 60 n_clients >= conf.max_connections) 61 return; 62 63 list_for_each_entry(l, &listeners, list) { 64 if (!l->blocked) 65 continue; 66 67 l->fd.cb(&l->fd, ULOOP_READ); 68 if (n_clients >= conf.max_connections) 69 break; 70 71 n_blocked--; 72 l->blocked = false; 73 uloop_fd_add(&l->fd, ULOOP_READ); 74 } 75 } 76 77 void uh_unblock_listeners(void) 78 { 79 static struct uloop_timeout poll_timer = { 80 .cb = uh_poll_listeners 81 }; 82 83 uloop_timeout_set(&poll_timer, 1); 84 } 85 86 static void listener_cb(struct uloop_fd *fd, unsigned int events) 87 { 88 struct listener *l = container_of(fd, struct listener, fd); 89 90 while (1) { 91 if (!uh_accept_client(fd->fd, l->tls)) 92 break; 93 } 94 95 if (conf.max_connections && n_clients >= conf.max_connections) 96 uh_block_listener(l); 97 } 98 99 void uh_setup_listeners(void) 100 { 101 struct listener *l; 102 int yes = 1; 103 104 list_for_each_entry(l, &listeners, list) { 105 int sock = l->fd.fd; 106 107 /* TCP keep-alive */ 108 if (conf.tcp_keepalive > 0) { 109 #ifdef linux 110 int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt, tcp_fstopn; 111 112 tcp_ka_idl = 1; 113 tcp_ka_cnt = 3; 114 tcp_ka_int = conf.tcp_keepalive; 115 tcp_fstopn = 5; 116 117 setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl)); 118 setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)); 119 setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt)); 120 setsockopt(sock, SOL_TCP, TCP_FASTOPEN, &tcp_fstopn, sizeof(tcp_fstopn)); 121 #endif 122 123 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); 124 } 125 126 l->fd.cb = listener_cb; 127 uloop_fd_add(&l->fd, ULOOP_READ); 128 } 129 } 130 131 int uh_socket_bind(const char *host, const char *port, bool tls) 132 { 133 int sock = -1; 134 int yes = 1; 135 int status; 136 int bound = 0; 137 struct listener *l = NULL; 138 struct addrinfo *addrs = NULL, *p = NULL; 139 static struct addrinfo hints = { 140 .ai_family = AF_UNSPEC, 141 .ai_socktype = SOCK_STREAM, 142 .ai_flags = AI_PASSIVE, 143 }; 144 145 if ((status = getaddrinfo(host, port, &hints, &addrs)) != 0) { 146 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status)); 147 return 0; 148 } 149 150 /* try to bind a new socket to each found address */ 151 for (p = addrs; p; p = p->ai_next) { 152 /* get the socket */ 153 sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 154 if (sock < 0) { 155 perror("socket()"); 156 goto error; 157 } 158 159 /* "address already in use" */ 160 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) { 161 perror("setsockopt()"); 162 goto error; 163 } 164 165 /* required to get parallel v4 + v6 working */ 166 if (p->ai_family == AF_INET6 && 167 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { 168 perror("setsockopt()"); 169 goto error; 170 } 171 172 /* bind */ 173 if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { 174 perror("bind()"); 175 goto error; 176 } 177 178 /* listen */ 179 if (listen(sock, UH_LIMIT_CLIENTS) < 0) { 180 perror("listen()"); 181 goto error; 182 } 183 184 fd_cloexec(sock); 185 186 l = calloc(1, sizeof(*l)); 187 if (!l) 188 goto error; 189 190 l->fd.fd = sock; 191 l->tls = tls; 192 memcpy(&l->addr, p->ai_addr, p->ai_addrlen); 193 list_add_tail(&l->list, &listeners); 194 bound++; 195 196 continue; 197 198 error: 199 if (sock > -1) 200 close(sock); 201 } 202 203 freeaddrinfo(addrs); 204 205 return bound; 206 } 207 208 int uh_first_tls_port(int family) 209 { 210 struct listener *l; 211 int tls_port = -1; 212 213 list_for_each_entry(l, &listeners, list) { 214 if (!l->tls || l->addr.sin6_family != family) 215 continue; 216 217 if (tls_port != -1 && ntohs(l->addr.sin6_port) != 443) 218 continue; 219 220 tls_port = ntohs(l->addr.sin6_port); 221 } 222 223 return tls_port; 224 } 225
This page was automatically generated by LXR 0.3.1. • OpenWrt