Example #1
0
struct libwebsocket *
lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *parent_wsi, unsigned int sid)
{
	struct libwebsocket *wsi = libwebsocket_create_new_server_wsi(context);
	
	if (!wsi)
		return NULL;
	
	/* no more children allowed by parent */
	if (parent_wsi->u.http2.child_count + 1 == parent_wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS])
		return NULL;
	
	lws_http2_init(&wsi->u.http2.peer_settings);
	lws_http2_init(&wsi->u.http2.my_settings);
	wsi->u.http2.stream_id = sid;
	wsi->u.http2.my_stream_id = sid;

	wsi->u.http2.parent_wsi = parent_wsi;
	wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi;
	parent_wsi->u.http2.next_child_wsi = wsi;
	parent_wsi->u.http2.child_count++;
	
	wsi->u.http2.my_priority = 16;
	
	wsi->state = WSI_STATE_HTTP2_ESTABLISHED;
	wsi->mode = parent_wsi->mode;
	
	wsi->protocol = &context->protocols[0];
	libwebsocket_ensure_user_space(wsi);

	lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space);
	
	return wsi;
}
Example #2
0
int lws_server_socket_service(struct libwebsocket_context *context,
                              struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
    struct libwebsocket *new_wsi = NULL;
    int accept_fd = 0;
    socklen_t clilen;
    struct sockaddr_in cli_addr;
    int n;
    int len;

    switch (wsi->mode) {

    case LWS_CONNMODE_HTTP_SERVING:
    case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:

        /* handle http headers coming in */

        /* pending truncated sends have uber priority */

        if (wsi->truncated_send_malloc) {
            if (pollfd->revents & LWS_POLLOUT)
                if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
                                  wsi->truncated_send_offset,
                                  wsi->truncated_send_len) < 0) {
                    lwsl_info("closing from socket service\n");
                    return -1;
                }
            /*
             * we can't afford to allow input processing send
             * something new, so spin around he event loop until
             * he doesn't have any partials
             */
            break;
        }

        /* any incoming data ready? */

        if (pollfd->revents & LWS_POLLIN) {
            len = lws_ssl_capable_read(wsi,
                                       context->service_buffer,
                                       sizeof(context->service_buffer));
            switch (len) {
            case 0:
                lwsl_info("lws_server_skt_srv: read 0 len\n");
                /* lwsl_info("   state=%d\n", wsi->state); */
                if (!wsi->hdr_parsing_completed)
                    free(wsi->u.hdr.ah);
            /* fallthru */
            case LWS_SSL_CAPABLE_ERROR:
                libwebsocket_close_and_free_session(
                    context, wsi,
                    LWS_CLOSE_STATUS_NOSTATUS);
                return 0;
            case LWS_SSL_CAPABLE_MORE_SERVICE:
                break;
            }

            /* just ignore incoming if waiting for close */
            if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {

                /* hm this may want to send (via HTTP callback for example) */
                n = libwebsocket_read(context, wsi,
                                      context->service_buffer, len);
                if (n < 0)
                    /* we closed wsi */
                    return 0;

                /* hum he may have used up the writability above */
                break;
            }
        }

        /* this handles POLLOUT for http serving fragments */

        if (!(pollfd->revents & LWS_POLLOUT))
            break;

        /* one shot */
        if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
            goto fail;

        lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);

        if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) {
            n = user_callback_handle_rxflow(
                    wsi->protocol->callback,
                    wsi->protocol->owning_server,
                    wsi, LWS_CALLBACK_HTTP_WRITEABLE,
                    wsi->user_space,
                    NULL,
                    0);
            if (n < 0)
                libwebsocket_close_and_free_session(
                    context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
            break;
        }

        /* nonzero for completion or error */
        if (libwebsockets_serve_http_file_fragment(context, wsi))
            libwebsocket_close_and_free_session(context, wsi,
                                                LWS_CLOSE_STATUS_NOSTATUS);
        break;

    case LWS_CONNMODE_SERVER_LISTENER:

        /* pollin means a client has connected to us then */

        if (!(pollfd->revents & LWS_POLLIN))
            break;

        /* listen socket got an unencrypted connection... */

        clilen = sizeof(cli_addr);
        lws_latency_pre(context, wsi);
        accept_fd  = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
                            &clilen);
        lws_latency(context, wsi,
                    "unencrypted accept LWS_CONNMODE_SERVER_LISTENER",
                    accept_fd, accept_fd >= 0);
        if (accept_fd < 0) {
            if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) {
                lwsl_debug("accept asks to try again\n");
                break;
            }
            lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO));
            break;
        }

        lws_plat_set_socket_options(context, accept_fd);

        /*
         * look at who we connected to and give user code a chance
         * to reject based on client IP.  There's no protocol selected
         * yet so we issue this to protocols[0]
         */

        if ((context->protocols[0].callback)(context, wsi,
                                             LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
                                             NULL, (void *)(long)accept_fd, 0)) {
            lwsl_debug("Callback denied network connection\n");
            compatible_close(accept_fd);
            break;
        }

        new_wsi = libwebsocket_create_new_server_wsi(context);
        if (new_wsi == NULL) {
            compatible_close(accept_fd);
            break;
        }

        new_wsi->sock = accept_fd;

        /* the transport is accepted... give him time to negotiate */
        libwebsocket_set_timeout(new_wsi,
                                 PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
                                 AWAITING_TIMEOUT);

        /*
         * 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 protocols[0]
         */

        (context->protocols[0].callback)(context, new_wsi,
                                         LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0);

        lws_libev_accept(context, new_wsi, accept_fd);

        if (!LWS_SSL_ENABLED(context)) {
            lwsl_debug("accepted new conn  port %u on fd=%d\n",
                       ntohs(cli_addr.sin_port), accept_fd);

            insert_wsi_socket_into_fds(context, new_wsi);
        }
        break;

    default:
        break;
    }

    if (lws_server_socket_service_ssl(context, &wsi, new_wsi,
                                      accept_fd, pollfd))
        goto fail;

    return 0;

fail:
    libwebsocket_close_and_free_session(context, wsi,
                                        LWS_CLOSE_STATUS_NOSTATUS);
    return 1;
}
Example #3
0
int lws_server_socket_service(struct libwebsocket_context *context,
			struct libwebsocket *wsi, struct pollfd *pollfd)
{
	struct libwebsocket *new_wsi;
	int accept_fd;
	socklen_t clilen;
	struct sockaddr_in cli_addr;
	int n;
	ssize_t len;
#ifdef LWS_OPENSSL_SUPPORT
	int m;
#ifndef USE_CYASSL
	BIO *bio;
#endif
#endif

	switch (wsi->mode) {

	case LWS_CONNMODE_HTTP_SERVING:
	case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:

		/* handle http headers coming in */

		/* pending truncated sends have uber priority */

		if (wsi->truncated_send_malloc) {
			if (pollfd->revents & POLLOUT)
				lws_issue_raw(wsi, wsi->truncated_send_malloc +
					wsi->truncated_send_offset,
							wsi->truncated_send_len);
			/*
			 * we can't afford to allow input processing send
			 * something new, so spin around he event loop until
			 * he doesn't have any partials
			 */
			break;
		}

		/* any incoming data ready? */

		if (pollfd->revents & POLLIN) {

	#ifdef LWS_OPENSSL_SUPPORT
			if (wsi->ssl)
				len = SSL_read(wsi->ssl,
					context->service_buffer,
					       sizeof(context->service_buffer));
			else
	#endif
				len = recv(pollfd->fd,
					context->service_buffer,
					sizeof(context->service_buffer), 0);

			if (len < 0) {
				lwsl_debug("Socket read returned %d\n", len);
				if (errno != EINTR && errno != EAGAIN)
					libwebsocket_close_and_free_session(
						context, wsi,
						LWS_CLOSE_STATUS_NOSTATUS);
				return 0;
			}
			if (!len) {
				lwsl_info("lws_server_skt_srv: read 0 len\n");
				/* lwsl_info("   state=%d\n", wsi->state); */
				if (!wsi->hdr_parsing_completed)
					free(wsi->u.hdr.ah);
				libwebsocket_close_and_free_session(
				       context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
				return 0;
			}

			/* hm this may want to send (via HTTP callback for example) */

			n = libwebsocket_read(context, wsi,
						context->service_buffer, len);
			if (n < 0)
				/* we closed wsi */
				return 0;

			/* hum he may have used up the writability above */
			break;
		}

		/* this handles POLLOUT for http serving fragments */

		if (!(pollfd->revents & POLLOUT))
			break;

		/* one shot */
		lws_change_pollfd(wsi, POLLOUT, 0);

		if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) {
			n = user_callback_handle_rxflow(
					wsi->protocol->callback,
					wsi->protocol->owning_server,
					wsi, LWS_CALLBACK_HTTP_WRITEABLE,
					wsi->user_space,
					NULL,
					0);
			if (n < 0)
				libwebsocket_close_and_free_session(
				       context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
			break;
		}

		/* nonzero for completion or error */
		if (libwebsockets_serve_http_file_fragment(context, wsi))
			libwebsocket_close_and_free_session(context, wsi,
					       LWS_CLOSE_STATUS_NOSTATUS);
		break;

	case LWS_CONNMODE_SERVER_LISTENER:

		/* pollin means a client has connected to us then */

		if (!(pollfd->revents & POLLIN))
			break;

		/* listen socket got an unencrypted connection... */

		clilen = sizeof(cli_addr);
		lws_latency_pre(context, wsi);
		accept_fd  = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
								       &clilen);
		lws_latency(context, wsi,
			"unencrypted accept LWS_CONNMODE_SERVER_LISTENER",
						     accept_fd, accept_fd >= 0);
		if (accept_fd < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK) {
				lwsl_debug("accept asks to try again\n");
				break;
			}
			lwsl_warn("ERROR on accept: %s\n", strerror(errno));
			break;
		}

		lws_set_socket_options(context, accept_fd);

		/*
		 * look at who we connected to and give user code a chance
		 * to reject based on client IP.  There's no protocol selected
		 * yet so we issue this to protocols[0]
		 */

		if ((context->protocols[0].callback)(context, wsi,
				LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
					   NULL, (void *)(long)accept_fd, 0)) {
			lwsl_debug("Callback denied network connection\n");
			compatible_close(accept_fd);
			break;
		}

		new_wsi = libwebsocket_create_new_server_wsi(context);
		if (new_wsi == NULL) {
			compatible_close(accept_fd);
			break;
		}

		new_wsi->sock = accept_fd;

#ifdef LWS_OPENSSL_SUPPORT
		new_wsi->ssl = NULL;
		if (!context->use_ssl) {
#endif

			lwsl_debug("accepted new conn  port %u on fd=%d\n",
					  ntohs(cli_addr.sin_port), accept_fd);

			insert_wsi_socket_into_fds(context, new_wsi);
			break;
#ifdef LWS_OPENSSL_SUPPORT
		}

		new_wsi->ssl = SSL_new(context->ssl_ctx);
		if (new_wsi->ssl == NULL) {
			lwsl_err("SSL_new failed: %s\n",
			    ERR_error_string(SSL_get_error(
			    new_wsi->ssl, 0), NULL));
			    libwebsockets_decode_ssl_error();
			free(new_wsi);
			compatible_close(accept_fd);
			break;
		}

		SSL_set_ex_data(new_wsi->ssl,
			openssl_websocket_private_data_index, context);

		SSL_set_fd(new_wsi->ssl, accept_fd);

		#ifdef USE_CYASSL
		CyaSSL_set_using_nonblock(new_wsi->ssl, 1);
		#else
		bio = SSL_get_rbio(new_wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
		bio = SSL_get_wbio(new_wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
		#endif

		/*
		 * we are not accepted yet, but we need to enter ourselves
		 * as a live connection.  That way we can retry when more
		 * pieces come if we're not sorted yet
		 */

		wsi = new_wsi;
		wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING;
		insert_wsi_socket_into_fds(context, wsi);

		libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
							AWAITING_TIMEOUT);

		lwsl_info("inserted SSL accept into fds, trying SSL_accept\n");

		/* fallthru */

	case LWS_CONNMODE_SSL_ACK_PENDING:

		lws_change_pollfd(wsi, POLLOUT, 0);

		lws_latency_pre(context, wsi);

		n = recv(wsi->sock, context->service_buffer,
			sizeof(context->service_buffer), MSG_PEEK);

		/*
		 * optionally allow non-SSL connect on SSL listening socket
		 * This is disabled by default, if enabled it goes around any
		 * SSL-level access control (eg, client-side certs) so leave
		 * it disabled unless you know it's not a problem for you
		 */

		if (context->allow_non_ssl_on_ssl_port && n >= 1 &&
					context->service_buffer[0] >= ' ') {
			/*
			 * TLS content-type for Handshake is 0x16
			 * TLS content-type for ChangeCipherSpec Record is 0x14
			 *
			 * A non-ssl session will start with the HTTP method in
			 * ASCII.  If we see it's not a legit SSL handshake
			 * kill the SSL for this connection and try to handle
			 * as a HTTP connection upgrade directly.
			 */
			wsi->use_ssl = 0;
			SSL_shutdown(wsi->ssl);
			SSL_free(wsi->ssl);
			wsi->ssl = NULL;
			goto accepted;
		}

		/* normal SSL connection processing path */

		n = SSL_accept(wsi->ssl);
		lws_latency(context, wsi,
			"SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1);

		if (n != 1) {
			m = SSL_get_error(wsi->ssl, n);
			lwsl_debug("SSL_accept failed %d / %s\n",
						  m, ERR_error_string(m, NULL));

			if (m == SSL_ERROR_WANT_READ) {
				lws_change_pollfd(wsi, 0, POLLIN);
				lwsl_info("SSL_ERROR_WANT_READ\n");
				break;
			}
			if (m == SSL_ERROR_WANT_WRITE) {
				lws_change_pollfd(wsi, 0, POLLOUT);
				break;
			}
			lwsl_debug("SSL_accept failed skt %u: %s\n",
				  pollfd->fd,
				  ERR_error_string(m, NULL));
			libwebsocket_close_and_free_session(context, wsi,
						 LWS_CLOSE_STATUS_NOSTATUS);
			break;
		}

accepted:
		/* OK, we are accepted */

		libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		wsi->mode = LWS_CONNMODE_HTTP_SERVING;

		lwsl_debug("accepted new SSL conn\n");
		break;
#endif

	default:
		break;
	}
	return 0;
}