xref: /PHP-7.3/sapi/fpm/fpm/fpm_sockets.c (revision 1c850bfc)
1 	/* (c) 2007,2008 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #ifdef HAVE_ALLOCA_H
6 #include <alloca.h>
7 #endif
8 #include <sys/types.h>
9 #include <sys/stat.h> /* for chmod(2) */
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <sys/un.h>
14 #include <netdb.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 
21 #include "zlog.h"
22 #include "fpm_arrays.h"
23 #include "fpm_sockets.h"
24 #include "fpm_worker_pool.h"
25 #include "fpm_unix.h"
26 #include "fpm_str.h"
27 #include "fpm_env.h"
28 #include "fpm_cleanup.h"
29 #include "fpm_scoreboard.h"
30 
31 struct listening_socket_s {
32 	int refcount;
33 	int sock;
34 	int type;
35 	char *key;
36 };
37 
38 static struct fpm_array_s sockets_list;
39 
40 enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
41 
fpm_sockets_cleanup(int which,void * arg)42 static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
43 {
44 	unsigned i;
45 	unsigned socket_set_count = 0;
46 	unsigned socket_set[FPM_ENV_SOCKET_SET_MAX];
47 	unsigned socket_set_buf = 0;
48 	char envname[32];
49 	char *env_value = 0;
50 	int p = 0;
51 	struct listening_socket_s *ls = sockets_list.data;
52 
53 	for (i = 0; i < sockets_list.used; i++, ls++) {
54 		if (which != FPM_CLEANUP_PARENT_EXEC) {
55 			close(ls->sock);
56 		} else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
57 			char fd[32];
58 			sprintf(fd, "%d", ls->sock);
59 
60 			socket_set_buf = (i % FPM_ENV_SOCKET_SET_SIZE == 0 && i) ? 1 : 0;
61 			env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + socket_set_buf + 1);
62 
63 			if (i % FPM_ENV_SOCKET_SET_SIZE == 0) {
64 				socket_set[socket_set_count] = p + socket_set_buf;
65 				socket_set_count++;
66 				if (i) {
67 					*(env_value + p + 1) = 0;
68 				}
69 			}
70 
71 			p += sprintf(env_value + p + socket_set_buf, "%s%s=%s", (p && !socket_set_buf) ? "," : "", ls->key, fd);
72 			p += socket_set_buf;
73 		}
74 
75 		if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
76 			if (ls->type == FPM_AF_UNIX) {
77 				unlink(ls->key);
78 			}
79 		}
80 		free(ls->key);
81 	}
82 
83 	if (env_value) {
84 		for (i = 0; i < socket_set_count; i++) {
85 			if (!i) {
86 				strcpy(envname, "FPM_SOCKETS");
87 			} else {
88 				sprintf(envname, "FPM_SOCKETS_%d", i);
89 			}
90 			setenv(envname, env_value + socket_set[i], 1);
91 		}
92 		free(env_value);
93 	}
94 
95 	fpm_array_free(&sockets_list);
96 }
97 /* }}} */
98 
fpm_get_in_addr(struct sockaddr * sa)99 static void *fpm_get_in_addr(struct sockaddr *sa) /* {{{ */
100 {
101     if (sa->sa_family == AF_INET) {
102         return &(((struct sockaddr_in*)sa)->sin_addr);
103     }
104 
105     return &(((struct sockaddr_in6*)sa)->sin6_addr);
106 }
107 /* }}} */
108 
fpm_get_in_port(struct sockaddr * sa)109 static int fpm_get_in_port(struct sockaddr *sa) /* {{{ */
110 {
111     if (sa->sa_family == AF_INET) {
112         return ntohs(((struct sockaddr_in*)sa)->sin_port);
113     }
114 
115     return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
116 }
117 /* }}} */
118 
fpm_sockets_hash_op(int sock,struct sockaddr * sa,char * key,int type,int op)119 static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
120 {
121 	if (key == NULL) {
122 		switch (type) {
123 			case FPM_AF_INET : {
124 				key = alloca(INET6_ADDRSTRLEN+10);
125 				inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, INET6_ADDRSTRLEN);
126 				sprintf(key+strlen(key), ":%d", fpm_get_in_port(sa));
127 				break;
128 			}
129 
130 			case FPM_AF_UNIX : {
131 				struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
132 				key = alloca(strlen(sa_un->sun_path) + 1);
133 				strcpy(key, sa_un->sun_path);
134 				break;
135 			}
136 
137 			default :
138 				return -1;
139 		}
140 	}
141 
142 	switch (op) {
143 
144 		case FPM_GET_USE_SOCKET :
145 		{
146 			unsigned i;
147 			struct listening_socket_s *ls = sockets_list.data;
148 
149 			for (i = 0; i < sockets_list.used; i++, ls++) {
150 				if (!strcmp(ls->key, key)) {
151 					++ls->refcount;
152 					return ls->sock;
153 				}
154 			}
155 			break;
156 		}
157 
158 		case FPM_STORE_SOCKET :			/* inherited socket */
159 		case FPM_STORE_USE_SOCKET :		/* just created */
160 		{
161 			struct listening_socket_s *ls;
162 
163 			ls = fpm_array_push(&sockets_list);
164 			if (!ls) {
165 				break;
166 			}
167 
168 			if (op == FPM_STORE_SOCKET) {
169 				ls->refcount = 0;
170 			} else {
171 				ls->refcount = 1;
172 			}
173 			ls->type = type;
174 			ls->sock = sock;
175 			ls->key = strdup(key);
176 
177 			return 0;
178 		}
179 	}
180 	return -1;
181 }
182 /* }}} */
183 
fpm_sockets_new_listening_socket(struct fpm_worker_pool_s * wp,struct sockaddr * sa,int socklen)184 static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
185 {
186 	int flags = 1;
187 	int sock;
188 	mode_t saved_umask = 0;
189 
190 	sock = socket(sa->sa_family, SOCK_STREAM, 0);
191 
192 	if (0 > sock) {
193 		zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
194 		return -1;
195 	}
196 
197 	if (0 > setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags))) {
198 		zlog(ZLOG_WARNING, "failed to change socket attribute");
199 	}
200 
201 	if (wp->listen_address_domain == FPM_AF_UNIX) {
202 		if (fpm_socket_unix_test_connect((struct sockaddr_un *)sa, socklen) == 0) {
203 			zlog(ZLOG_ERROR, "Another FPM instance seems to already listen on %s", ((struct sockaddr_un *) sa)->sun_path);
204 			close(sock);
205 			return -1;
206 		}
207 		unlink( ((struct sockaddr_un *) sa)->sun_path);
208 		saved_umask = umask(0777 ^ wp->socket_mode);
209 	}
210 
211 	if (0 > bind(sock, sa, socklen)) {
212 		zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
213 		if (wp->listen_address_domain == FPM_AF_UNIX) {
214 			umask(saved_umask);
215 		}
216 		close(sock);
217 		return -1;
218 	}
219 
220 	if (wp->listen_address_domain == FPM_AF_UNIX) {
221 		char *path = ((struct sockaddr_un *) sa)->sun_path;
222 
223 		umask(saved_umask);
224 
225 		if (0 > fpm_unix_set_socket_premissions(wp, path)) {
226 			close(sock);
227 			return -1;
228 		}
229 	}
230 
231 	if (0 > listen(sock, wp->config->listen_backlog)) {
232 		zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
233 		close(sock);
234 		return -1;
235 	}
236 
237 	return sock;
238 }
239 /* }}} */
240 
fpm_sockets_get_listening_socket(struct fpm_worker_pool_s * wp,struct sockaddr * sa,int socklen)241 static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
242 {
243 	int sock;
244 
245 	sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
246 	if (sock >= 0) {
247 		return sock;
248 	}
249 
250 	sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
251 	fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
252 
253 	return sock;
254 }
255 /* }}} */
256 
fpm_sockets_domain_from_address(char * address)257 enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
258 {
259 	if (strchr(address, ':')) {
260 		return FPM_AF_INET;
261 	}
262 
263 	if (strlen(address) == strspn(address, "0123456789")) {
264 		return FPM_AF_INET;
265 	}
266 	return FPM_AF_UNIX;
267 }
268 /* }}} */
269 
fpm_socket_af_inet_socket_by_addr(struct fpm_worker_pool_s * wp,const char * addr,const char * port)270 static int fpm_socket_af_inet_socket_by_addr(struct fpm_worker_pool_s *wp, const char *addr, const char *port) /* {{{ */
271 {
272 	struct addrinfo hints, *servinfo, *p;
273 	char tmpbuf[INET6_ADDRSTRLEN];
274 	int status;
275 	int sock = -1;
276 
277 	memset(&hints, 0, sizeof hints);
278 	hints.ai_family = AF_UNSPEC;
279 	hints.ai_socktype = SOCK_STREAM;
280 
281 	if ((status = getaddrinfo(addr, port, &hints, &servinfo)) != 0) {
282 		zlog(ZLOG_ERROR, "getaddrinfo: %s\n", gai_strerror(status));
283 		return -1;
284 	}
285 
286 	for (p = servinfo; p != NULL; p = p->ai_next) {
287 		inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN);
288 		if (sock < 0) {
289 			if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) {
290 				zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", addr, tmpbuf);
291 			}
292 		} else {
293 			zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", addr, tmpbuf);
294 		}
295 	}
296 
297 	freeaddrinfo(servinfo);
298 
299 	return sock;
300 }
301 /* }}} */
302 
fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s * wp)303 static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
304 {
305 	char *dup_address = strdup(wp->config->listen_address);
306 	char *port_str = strrchr(dup_address, ':');
307 	char *addr = NULL;
308 	int addr_len;
309 	int port = 0;
310 	int sock = -1;
311 
312 	if (port_str) { /* this is host:port pair */
313 		*port_str++ = '\0';
314 		port = atoi(port_str);
315 		addr = dup_address;
316 
317 		/* strip brackets from address for getaddrinfo */
318 		addr_len = strlen(addr);
319 		if (addr[0] == '[' && addr[addr_len - 1] == ']') {
320 			addr[addr_len - 1] = '\0';
321 			addr++;
322 		}
323 
324 	} else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
325 		port = atoi(dup_address);
326 		port_str = dup_address;
327 	}
328 
329 	if (port == 0) {
330 		zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
331 		return -1;
332 	}
333 
334 	if (addr) {
335 		/* Bind a specific address */
336 		sock = fpm_socket_af_inet_socket_by_addr(wp, addr, port_str);
337 	} else {
338 		/* Bind ANYADDR
339 		 *
340 		 * Try "::" first as that covers IPv6 ANYADDR and mapped IPv4 ANYADDR
341 		 * silencing warnings since failure is an option
342 		 *
343 		 * If that fails (because AF_INET6 is unsupported) retry with 0.0.0.0
344 		 */
345 		int old_level = zlog_set_level(ZLOG_ALERT);
346 		sock = fpm_socket_af_inet_socket_by_addr(wp, "::", port_str);
347 		zlog_set_level(old_level);
348 
349 		if (sock < 0) {
350 			zlog(ZLOG_NOTICE, "Failed implicitly binding to ::, retrying with 0.0.0.0");
351 			sock = fpm_socket_af_inet_socket_by_addr(wp, "0.0.0.0", port_str);
352 		}
353 	}
354 
355 	free(dup_address);
356 
357 	return sock;
358 }
359 /* }}} */
360 
fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s * wp)361 static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
362 {
363 	struct sockaddr_un sa_un;
364 
365 	memset(&sa_un, 0, sizeof(sa_un));
366 	strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
367 	sa_un.sun_family = AF_UNIX;
368 	return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
369 }
370 /* }}} */
371 
fpm_sockets_init_main()372 int fpm_sockets_init_main() /* {{{ */
373 {
374 	unsigned i, lq_len;
375 	struct fpm_worker_pool_s *wp;
376 	char sockname[32];
377 	char sockpath[256];
378 	char *inherited;
379 	struct listening_socket_s *ls;
380 
381 	if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
382 		return -1;
383 	}
384 
385 	/* import inherited sockets */
386 	for (i = 0; i < FPM_ENV_SOCKET_SET_MAX; i++) {
387 		if (!i) {
388 			strcpy(sockname, "FPM_SOCKETS");
389 		} else {
390 			sprintf(sockname, "FPM_SOCKETS_%d", i);
391 		}
392 		inherited = getenv(sockname);
393 		if (!inherited) {
394 			break;
395 		}
396 
397 		while (inherited && *inherited) {
398 			char *comma = strchr(inherited, ',');
399 			int type, fd_no;
400 			char *eq;
401 
402 			if (comma) {
403 				*comma = '\0';
404 			}
405 
406 			eq = strchr(inherited, '=');
407 			if (eq) {
408 				int sockpath_len = eq - inherited;
409 				if (sockpath_len > 255) {
410 					/* this should never happen as UDS limit is lower */
411 					sockpath_len = 255;
412 				}
413 				memcpy(sockpath, inherited, sockpath_len);
414 				sockpath[sockpath_len] = '\0';
415 				fd_no = atoi(eq + 1);
416 				type = fpm_sockets_domain_from_address(sockpath);
417 				zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, sockpath);
418 				fpm_sockets_hash_op(fd_no, 0, sockpath, type, FPM_STORE_SOCKET);
419 			}
420 
421 			if (comma) {
422 				inherited = comma + 1;
423 			} else {
424 				inherited = 0;
425 			}
426 		}
427 	}
428 
429 	/* create all required sockets */
430 	for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
431 		switch (wp->listen_address_domain) {
432 			case FPM_AF_INET :
433 				wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
434 				break;
435 
436 			case FPM_AF_UNIX :
437 				if (0 > fpm_unix_resolve_socket_premissions(wp)) {
438 					return -1;
439 				}
440 				wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
441 				break;
442 		}
443 
444 		if (wp->listening_socket == -1) {
445 			return -1;
446 		}
447 
448 	if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
449 			fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
450 		}
451 	}
452 
453 	/* close unused sockets that was inherited */
454 	ls = sockets_list.data;
455 
456 	for (i = 0; i < sockets_list.used; ) {
457 		if (ls->refcount == 0) {
458 			close(ls->sock);
459 			if (ls->type == FPM_AF_UNIX) {
460 				unlink(ls->key);
461 			}
462 			free(ls->key);
463 			fpm_array_item_remove(&sockets_list, i);
464 		} else {
465 			++i;
466 			++ls;
467 		}
468 	}
469 
470 	if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
471 		return -1;
472 	}
473 	return 0;
474 }
475 /* }}} */
476 
477 #if HAVE_FPM_LQ
478 
479 #ifdef HAVE_LQ_TCP_INFO
480 
481 #include <netinet/tcp.h>
482 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)483 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
484 {
485 	struct tcp_info info;
486 	socklen_t len = sizeof(info);
487 
488 	if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
489 		zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
490 		return -1;
491 	}
492 #if defined(__FreeBSD__) || defined(__NetBSD__)
493 	if (info.__tcpi_sacked == 0) {
494 		return -1;
495 	}
496 
497 	if (cur_lq) {
498 		*cur_lq = info.__tcpi_unacked;
499 	}
500 
501 	if (max_lq) {
502 		*max_lq = info.__tcpi_sacked;
503 	}
504 #else
505 	/* kernel >= 2.6.24 return non-zero here, that means operation is supported */
506 	if (info.tcpi_sacked == 0) {
507 		return -1;
508 	}
509 
510 	if (cur_lq) {
511 		*cur_lq = info.tcpi_unacked;
512 	}
513 
514 	if (max_lq) {
515 		*max_lq = info.tcpi_sacked;
516 	}
517 #endif
518 
519 	return 0;
520 }
521 
522 #endif
523 
524 #ifdef HAVE_LQ_SO_LISTENQ
525 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)526 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
527 {
528 	int val;
529 	socklen_t len = sizeof(val);
530 
531 	if (cur_lq) {
532 		if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
533 			return -1;
534 		}
535 
536 		*cur_lq = val;
537 	}
538 
539 	if (max_lq) {
540 		if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
541 			return -1;
542 		}
543 
544 		*max_lq = val;
545 	}
546 
547 	return 0;
548 }
549 
550 #endif
551 
552 #else
553 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)554 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
555 {
556 	return -1;
557 }
558 
559 #endif
560 
fpm_socket_unix_test_connect(struct sockaddr_un * sock,size_t socklen)561 int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
562 {
563 	int fd;
564 
565 	if (!sock || sock->sun_family != AF_UNIX) {
566 		return -1;
567 	}
568 
569 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
570 		return -1;
571 	}
572 
573 	if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
574 		close(fd);
575 		return -1;
576 	}
577 
578 	close(fd);
579 	return 0;
580 }
581 /* }}} */
582