mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	
		
			
	
	
		
			415 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			415 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * libwebsockets - small server side websockets and web server implementation | ||
|  |  * | ||
|  |  * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> | ||
|  |  * | ||
|  |  *  This library is free software; you can redistribute it and/or | ||
|  |  *  modify it under the terms of the GNU Lesser General Public | ||
|  |  *  License as published by the Free Software Foundation: | ||
|  |  *  version 2.1 of the License. | ||
|  |  * | ||
|  |  *  This library is distributed in the hope that it will be useful, | ||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||
|  |  *  Lesser General Public License for more details. | ||
|  |  * | ||
|  |  *  You should have received a copy of the GNU Lesser General Public | ||
|  |  *  License along with this library; if not, write to the Free Software | ||
|  |  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
|  |  *  MA  02110-1301  USA | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "core/private.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | lws_get_idlest_tsi(struct lws_context *context) | ||
|  | { | ||
|  | 	unsigned int lowest = ~0; | ||
|  | 	int n = 0, hit = -1; | ||
|  | 
 | ||
|  | 	for (; n < context->count_threads; n++) { | ||
|  | 		if ((unsigned int)context->pt[n].fds_count != | ||
|  | 		    context->fd_limit_per_thread - 1 && | ||
|  | 		    (unsigned int)context->pt[n].fds_count < lowest) { | ||
|  | 			lowest = context->pt[n].fds_count; | ||
|  | 			hit = n; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return hit; | ||
|  | } | ||
|  | 
 | ||
|  | struct lws * | ||
|  | lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi) | ||
|  | { | ||
|  | 	struct lws *new_wsi; | ||
|  | 	int n = fixed_tsi; | ||
|  | 
 | ||
|  | 	if (n < 0) | ||
|  | 		n = lws_get_idlest_tsi(vhost->context); | ||
|  | 
 | ||
|  | 	if (n < 0) { | ||
|  | 		lwsl_err("no space for new conn\n"); | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); | ||
|  | 	if (new_wsi == NULL) { | ||
|  | 		lwsl_err("Out of memory for new connection\n"); | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	new_wsi->tsi = n; | ||
|  | 	lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, | ||
|  | 		   vhost->name, new_wsi->tsi); | ||
|  | 
 | ||
|  | 	lws_vhost_bind_wsi(vhost, new_wsi); | ||
|  | 	new_wsi->context = vhost->context; | ||
|  | 	new_wsi->pending_timeout = NO_PENDING_TIMEOUT; | ||
|  | 	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; | ||
|  | 
 | ||
|  | 	/* initialize the instance struct */ | ||
|  | 
 | ||
|  | 	lwsi_set_state(new_wsi, LRS_UNCONNECTED); | ||
|  | 	new_wsi->hdr_parsing_completed = 0; | ||
|  | 
 | ||
|  | #ifdef LWS_WITH_TLS
 | ||
|  | 	new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	 * these can only be set once the protocol is known | ||
|  | 	 * we set an un-established connection's protocol pointer | ||
|  | 	 * to the start of the supported list, so it can look | ||
|  | 	 * for matching ones during the handshake | ||
|  | 	 */ | ||
|  | 	new_wsi->protocol = vhost->protocols; | ||
|  | 	new_wsi->user_space = NULL; | ||
|  | 	new_wsi->desc.sockfd = LWS_SOCK_INVALID; | ||
|  | 	new_wsi->position_in_fds_table = LWS_NO_FDS_POS; | ||
|  | 
 | ||
|  | 	vhost->context->count_wsi_allocated++; | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	 * outermost create notification for wsi | ||
|  | 	 * no user_space because no protocol selection | ||
|  | 	 */ | ||
|  | 	vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL, | ||
|  | 				     NULL, 0); | ||
|  | 
 | ||
|  | 	return new_wsi; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* if not a socket, it's a raw, non-ssl file descriptor */ | ||
|  | 
 | ||
|  | LWS_VISIBLE struct lws * | ||
|  | lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, | ||
|  | 			   lws_sock_file_fd_type fd, const char *vh_prot_name, | ||
|  | 			   struct lws *parent) | ||
|  | { | ||
|  | 	struct lws_context *context = vh->context; | ||
|  | 	struct lws *new_wsi; | ||
|  | 	struct lws_context_per_thread *pt; | ||
|  | 	int n; | ||
|  | 
 | ||
|  | #if defined(LWS_WITH_PEER_LIMITS)
 | ||
|  | 	struct lws_peer *peer = NULL; | ||
|  | 
 | ||
|  | 	if (type & LWS_ADOPT_SOCKET) { | ||
|  | 		peer = lws_get_or_create_peer(vh, fd.sockfd); | ||
|  | 
 | ||
|  | 		if (peer && context->ip_limit_wsi && | ||
|  | 		    peer->count_wsi >= context->ip_limit_wsi) { | ||
|  | 			lwsl_notice("Peer reached wsi limit %d\n", | ||
|  | 					context->ip_limit_wsi); | ||
|  | 			lws_stats_atomic_bump(context, &context->pt[0], | ||
|  | 					      LWSSTATS_C_PEER_LIMIT_WSI_DENIED, | ||
|  | 					      1); | ||
|  | 			return NULL; | ||
|  | 		} | ||
|  | 	} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	n = -1; | ||
|  | 	if (parent) | ||
|  | 		n = parent->tsi; | ||
|  | 	new_wsi = lws_create_new_server_wsi(vh, n); | ||
|  | 	if (!new_wsi) { | ||
|  | 		if (type & LWS_ADOPT_SOCKET) | ||
|  | 			compatible_close(fd.sockfd); | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | #if defined(LWS_WITH_PEER_LIMITS)
 | ||
|  | 	if (peer) | ||
|  | 		lws_peer_add_wsi(context, peer, new_wsi); | ||
|  | #endif
 | ||
|  | 	pt = &context->pt[(int)new_wsi->tsi]; | ||
|  | 	lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1); | ||
|  | 
 | ||
|  | 	if (parent) { | ||
|  | 		new_wsi->parent = parent; | ||
|  | 		new_wsi->sibling_list = parent->child_list; | ||
|  | 		parent->child_list = new_wsi; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	new_wsi->desc = fd; | ||
|  | 
 | ||
|  | 	if (vh_prot_name) { | ||
|  | 		new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, | ||
|  | 							       vh_prot_name); | ||
|  | 		if (!new_wsi->protocol) { | ||
|  | 			lwsl_err("Protocol %s not enabled on vhost %s\n", | ||
|  | 				 vh_prot_name, new_wsi->vhost->name); | ||
|  | 			goto bail; | ||
|  | 		} | ||
|  | 		if (lws_ensure_user_space(new_wsi)) { | ||
|  | 		       lwsl_notice("OOM trying to get user_space\n"); | ||
|  | 			goto bail; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_SOCKET)) | ||
|  | 		type &= ~LWS_ADOPT_ALLOW_SSL; | ||
|  | 
 | ||
|  | 	if (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) { | ||
|  | 		lwsl_err("Unable to find a role that can adopt descriptor\n"); | ||
|  | 		goto bail; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	 * A new connection was accepted. Give the user a chance to | ||
|  | 	 * set properties of the newly created wsi. There's no protocol | ||
|  | 	 * selected yet so we issue this to the vhosts's default protocol, | ||
|  | 	 * itself by default protocols[0] | ||
|  | 	 */ | ||
|  | 	n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; | ||
|  | 	if (!(type & LWS_ADOPT_HTTP)) { | ||
|  | 		if (!(type & LWS_ADOPT_SOCKET)) | ||
|  | 			n = LWS_CALLBACK_RAW_ADOPT_FILE; | ||
|  | 		else | ||
|  | 			n = LWS_CALLBACK_RAW_ADOPT; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); | ||
|  | 
 | ||
|  | 	if (context->event_loop_ops->accept) | ||
|  | 		if (context->event_loop_ops->accept(new_wsi)) | ||
|  | 			goto fail; | ||
|  | 
 | ||
|  | 	if (!(type & LWS_ADOPT_ALLOW_SSL)) { | ||
|  | 		lws_pt_lock(pt, __func__); | ||
|  | 		if (__insert_wsi_socket_into_fds(context, new_wsi)) { | ||
|  | 			lws_pt_unlock(pt); | ||
|  | 			lwsl_err("%s: fail inserting socket\n", __func__); | ||
|  | 			goto fail; | ||
|  | 		} | ||
|  | 		lws_pt_unlock(pt); | ||
|  | 	} else | ||
|  | 		if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { | ||
|  | 			lwsl_info("%s: fail ssl negotiation\n", __func__); | ||
|  | 			goto fail; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	 *  by deferring callback to this point, after insertion to fds, | ||
|  | 	 * lws_callback_on_writable() can work from the callback | ||
|  | 	 */ | ||
|  | 	if ((new_wsi->protocol->callback)(new_wsi, n, new_wsi->user_space, | ||
|  | 					  NULL, 0)) | ||
|  | 		goto fail; | ||
|  | 
 | ||
|  | 	/* role may need to do something after all adoption completed */ | ||
|  | 
 | ||
|  | 	lws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH, | ||
|  | 				    vh_prot_name); | ||
|  | 
 | ||
|  | 	lws_cancel_service_pt(new_wsi); | ||
|  | 
 | ||
|  | 	return new_wsi; | ||
|  | 
 | ||
|  | fail: | ||
|  | 	if (type & LWS_ADOPT_SOCKET) | ||
|  | 		lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, | ||
|  | 				   "adopt skt fail"); | ||
|  | 
 | ||
|  | 	return NULL; | ||
|  | 
 | ||
|  | bail: | ||
|  |        lwsl_notice("%s: exiting on bail\n", __func__); | ||
|  | 	if (parent) | ||
|  | 		parent->child_list = new_wsi->sibling_list; | ||
|  | 	if (new_wsi->user_space) | ||
|  | 		lws_free(new_wsi->user_space); | ||
|  | 
 | ||
|  | 	vh->context->count_wsi_allocated--; | ||
|  | 
 | ||
|  | 	lws_vhost_unbind_wsi(new_wsi); | ||
|  | 	lws_free(new_wsi); | ||
|  | 
 | ||
|  | 	compatible_close(fd.sockfd); | ||
|  | 
 | ||
|  | 	return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | LWS_VISIBLE struct lws * | ||
|  | lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) | ||
|  | { | ||
|  | 	lws_sock_file_fd_type fd; | ||
|  | 
 | ||
|  | 	fd.sockfd = accept_fd; | ||
|  | 	return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | | ||
|  | 			LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); | ||
|  | } | ||
|  | 
 | ||
|  | LWS_VISIBLE struct lws * | ||
|  | lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) | ||
|  | { | ||
|  | 	return lws_adopt_socket_vhost(context->vhost_list, accept_fd); | ||
|  | } | ||
|  | 
 | ||
|  | /* Common read-buffer adoption for lws_adopt_*_readbuf */ | ||
|  | static struct lws* | ||
|  | adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) | ||
|  | { | ||
|  | 	struct lws_context_per_thread *pt; | ||
|  | 	struct lws_pollfd *pfd; | ||
|  | 	int n; | ||
|  | 
 | ||
|  | 	if (!wsi) | ||
|  | 		return NULL; | ||
|  | 
 | ||
|  | 	if (!readbuf || len == 0) | ||
|  | 		return wsi; | ||
|  | 
 | ||
|  | 	if (wsi->position_in_fds_table == LWS_NO_FDS_POS) | ||
|  | 		return wsi; | ||
|  | 
 | ||
|  | 	pt = &wsi->context->pt[(int)wsi->tsi]; | ||
|  | 
 | ||
|  | 	n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, | ||
|  | 				       len); | ||
|  | 	if (n < 0) | ||
|  | 		goto bail; | ||
|  | 	if (n) | ||
|  | 		lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); | ||
|  | 
 | ||
|  | 	/*
 | ||
|  | 	 * we can't process the initial read data until we can attach an ah. | ||
|  | 	 * | ||
|  | 	 * if one is available, get it and place the data in his ah rxbuf... | ||
|  | 	 * wsi with ah that have pending rxbuf get auto-POLLIN service. | ||
|  | 	 * | ||
|  | 	 * no autoservice because we didn't get a chance to attach the | ||
|  | 	 * readbuf data to wsi or ah yet, and we will do it next if we get | ||
|  | 	 * the ah. | ||
|  | 	 */ | ||
|  | 	if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { | ||
|  | 
 | ||
|  | 		lwsl_notice("%s: calling service on readbuf ah\n", __func__); | ||
|  | 
 | ||
|  | 		/*
 | ||
|  | 		 * unlike a normal connect, we have the headers already | ||
|  | 		 * (or the first part of them anyway). | ||
|  | 		 * libuv won't come back and service us without a network | ||
|  | 		 * event, so we need to do the header service right here. | ||
|  | 		 */ | ||
|  | 		pfd = &pt->fds[wsi->position_in_fds_table]; | ||
|  | 		pfd->revents |= LWS_POLLIN; | ||
|  | 		lwsl_err("%s: calling service\n", __func__); | ||
|  | 		if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) | ||
|  | 			/* service closed us */ | ||
|  | 			return NULL; | ||
|  | 
 | ||
|  | 		return wsi; | ||
|  | 	} | ||
|  | 	lwsl_err("%s: deferring handling ah\n", __func__); | ||
|  | 
 | ||
|  | 	return wsi; | ||
|  | 
 | ||
|  | bail: | ||
|  | 	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, | ||
|  | 			   "adopt skt readbuf fail"); | ||
|  | 
 | ||
|  | 	return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | LWS_EXTERN struct lws * | ||
|  | lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags, | ||
|  | 		     const char *protocol_name, struct lws *parent_wsi) | ||
|  | { | ||
|  | 	lws_sock_file_fd_type sock; | ||
|  | 	struct addrinfo h, *r, *rp; | ||
|  | 	struct lws *wsi = NULL; | ||
|  | 	char buf[16]; | ||
|  | 	int n; | ||
|  | 
 | ||
|  | 	memset(&h, 0, sizeof(h)); | ||
|  | 	h.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */ | ||
|  | 	h.ai_socktype = SOCK_DGRAM; | ||
|  | 	h.ai_protocol = IPPROTO_UDP; | ||
|  | 	h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; | ||
|  | 
 | ||
|  | 	lws_snprintf(buf, sizeof(buf), "%u", port); | ||
|  | 	n = getaddrinfo(NULL, buf, &h, &r); | ||
|  | 	if (n) { | ||
|  | 		lwsl_info("%s: getaddrinfo error: %s\n", __func__, | ||
|  | 			  gai_strerror(n)); | ||
|  | 		goto bail; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for (rp = r; rp; rp = rp->ai_next) { | ||
|  | 		sock.sockfd = socket(rp->ai_family, rp->ai_socktype, | ||
|  | 				     rp->ai_protocol); | ||
|  | 		if (sock.sockfd != LWS_SOCK_INVALID) | ||
|  | 			break; | ||
|  | 	} | ||
|  | 	if (!rp) { | ||
|  | 		lwsl_err("%s: unable to create INET socket\n", __func__); | ||
|  | 		goto bail1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr, | ||
|  | #if defined(_WIN32)
 | ||
|  | 			    (int)rp->ai_addrlen | ||
|  | #else
 | ||
|  | 			    rp->ai_addrlen | ||
|  | #endif
 | ||
|  | 	   ) == -1) { | ||
|  | 		lwsl_err("%s: bind failed\n", __func__); | ||
|  | 		goto bail2; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock, | ||
|  | 				        protocol_name, parent_wsi); | ||
|  | 	if (!wsi) | ||
|  | 		lwsl_err("%s: udp adoption failed\n", __func__); | ||
|  | 
 | ||
|  | bail2: | ||
|  | 	if (!wsi) | ||
|  | 		compatible_close((int)sock.sockfd); | ||
|  | bail1: | ||
|  | 	freeaddrinfo(r); | ||
|  | 
 | ||
|  | bail: | ||
|  | 	return wsi; | ||
|  | } | ||
|  | 
 | ||
|  | LWS_VISIBLE struct lws * | ||
|  | lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, | ||
|  | 			 const char *readbuf, size_t len) | ||
|  | { | ||
|  |         return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), | ||
|  | 				    readbuf, len); | ||
|  | } | ||
|  | 
 | ||
|  | LWS_VISIBLE struct lws * | ||
|  | lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, | ||
|  | 			       lws_sockfd_type accept_fd, | ||
|  | 			       const char *readbuf, size_t len) | ||
|  | { | ||
|  |         return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), | ||
|  | 				    readbuf, len); | ||
|  | } |