Ejemplo n.º 1
0
void WebSockets::close(WebSocketConnection* conn) {
	if(conn == NULL || conn->ws == NULL) {
		return;
	}
	printf("Close connections...\n");
	libwebsocket_close_and_free_session(ws_context, conn->ws, LWS_CLOSE_STATUS_NORMAL);
}
Ejemplo n.º 2
0
int
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
				     struct libwebsocket *wsi, unsigned int sec)
{
	/*
	 * if extensions want in on it (eg, we are a mux parent)
	 * give them a chance to service child timeouts
	 */
	if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_1HZ, NULL, sec) < 0)
		return 0;

	if (!wsi->pending_timeout)
		return 0;

	/*
	 * if we went beyond the allowed time, kill the
	 * connection
	 */
	if (sec > wsi->pending_timeout_limit) {
		lwsl_info("TIMEDOUT WAITING on %d\n", wsi->pending_timeout);
		libwebsocket_close_and_free_session(context,
						wsi, LWS_CLOSE_STATUS_NOSTATUS);
		return 1;
	}

	return 0;
}
Ejemplo n.º 3
0
int
lws_read(struct libwebsocket_context *context,
         struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	if (libwebsocket_ensure_user_space(wsi)) {
		goto bail;
	}

	int r = context->protocols[0].callback(context, wsi,
		LWS_CALLBACK_SOCKET_READ,
		wsi->user_space, (void *)buf, len);

	if (r < 0) {
		goto bail;
	}
	if (r == 0) {
		return libwebsocket_read(context, wsi, buf, len);
	}

	return 0;

bail:
	libwebsocket_close_and_free_session(context,
		wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
	return -1;
}
Ejemplo n.º 4
0
/**
 * libwebsocket_context_destroy() - Destroy the websocket context
 * @context:	Websocket context
 *
 *	This function closes any active connections and then frees the
 *	context.  After calling this, any further use of the context is
 *	undefined.
 */
LWS_VISIBLE void
libwebsocket_context_destroy(struct libwebsocket_context *context)
{
	int n;
	struct libwebsocket_protocols *protocol = context->protocols;

	lwsl_notice("%s\n", __func__);

#ifdef LWS_LATENCY
	if (context->worst_latency_info[0])
		lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
#endif

	for (n = 0; n < context->fds_count; n++) {
		struct libwebsocket *wsi =
					context->lws_lookup[context->fds[n].fd];
		if (!wsi)
			continue;
		libwebsocket_close_and_free_session(context,
			wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
		n--;
	}

	/*
	 * give all extensions a chance to clean up any per-context
	 * allocations they might have made
	 */
	if (context->listen_port != CONTEXT_PORT_NO_LISTEN) {
		if (lws_ext_callback_for_each_extension_type(context, NULL,
			 LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0)
			return;
	} else
		if (lws_ext_callback_for_each_extension_type(context, NULL,
			 LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0)
			return;

	/*
	 * inform all the protocols that they are done and will have no more
	 * callbacks
	 */

	while (protocol->callback) {
		protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
				NULL, NULL, 0);
		protocol++;
	}

	lws_plat_context_early_destroy(context);

	lws_ssl_context_destroy(context);

	if (context->fds)
		free(context->fds);
	if (context->lws_lookup)
		free(context->lws_lookup);

	lws_plat_context_late_destroy(context);

	free(context);
}
Ejemplo n.º 5
0
int
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
                                   struct libwebsocket *wsi, unsigned int sec)
{
    /*
     * if extensions want in on it (eg, we are a mux parent)
     * give them a chance to service child timeouts
     */
    if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_1HZ, NULL, sec) < 0)
        return 0;

    if (!wsi->pending_timeout)
        return 0;

    /*
     * if we went beyond the allowed time, kill the
     * connection
     */
    if (sec > wsi->pending_timeout_limit) {
        lwsl_info("TIMEDOUT WAITING on %d\n", wsi->pending_timeout);
        /*
         * Since he failed a timeout, he already had a chance to do
         * something and was unable to... that includes situations like
         * half closed connections.  So process this "failed timeout"
         * close as a violent death and don't try to do protocol
         * cleanup like flush partials.
         */
        wsi->socket_is_permanently_unusable = 1;
        libwebsocket_close_and_free_session(context,
                                            wsi, LWS_CLOSE_STATUS_NOSTATUS);
        return 1;
    }

    return 0;
}
Ejemplo n.º 6
0
bool WebSocketClient2::disconnectFromServer()
{

    libwebsocket_close_and_free_session(this->m_lws_ctx,
                                        this->m_wsi, LWS_CLOSE_STATUS_GOINGAWAY);
    this->quit_cli_loop = true;
    this->m_wsi = NULL;

    libwebsocket_context_destroy(this->m_lws_ctx);
    this->m_lws_ctx = NULL;
    
    return true;
}
Ejemplo n.º 7
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;
}
Ejemplo n.º 8
0
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
		     struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	size_t n;
	struct allocated_headers *ah;
	char *uri_ptr;
	int uri_len;

	switch (wsi->state) {
	case WSI_STATE_HTTP_ISSUING_FILE:
	case WSI_STATE_HTTP:
		wsi->state = WSI_STATE_HTTP_HEADERS;
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		/* fallthru */
	case WSI_STATE_HTTP_HEADERS:

		lwsl_parser("issuing %d bytes to parser\n", (int)len);

#ifndef LWS_NO_CLIENT
		switch (wsi->mode) {
		case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
		case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
		case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
		case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
		case LWS_CONNMODE_WS_CLIENT:
			for (n = 0; n < len; n++)
				if (libwebsocket_client_rx_sm(wsi, *buf++)) {
					lwsl_info("client_rx_sm failed\n");
					goto bail;
				}
			return 0;
		default:
			break;
		}
#endif
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		for (n = 0; n < len; n++)
			if (libwebsocket_parse(wsi, *buf++)) {
				lwsl_info("libwebsocket_parse failed\n");
				goto bail_nuke_ah;
			}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			break;

		lwsl_parser("libwebsocket_parse sees parsing complete\n");

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;

		/* is this websocket protocol or normal http 1.0? */

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
			     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

			/* it's not websocket.... shall we accept it as http? */

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
				lwsl_warn("Missing URI in HTTP request\n");
				goto bail_nuke_ah;
			}

			lwsl_info("HTTP request for '%s'\n",
				lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));

			if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

			/*
			 * Hm we still need the headers so the
			 * callback can look at leaders like the URI, but we
			 * need to transition to http union state.... hold a
			 * copy of u.hdr.ah and deallocate afterwards
			 */

			ah = wsi->u.hdr.ah;
			uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
			uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);

			/* union transition */
			memset(&wsi->u, 0, sizeof(wsi->u));

			wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
			wsi->state = WSI_STATE_HTTP;
			n = 0;
			if (wsi->protocol->callback)
				n = wsi->protocol->callback(context, wsi,
				    LWS_CALLBACK_HTTP,
				    wsi->user_space, uri_ptr, uri_len);

			/* now drop the header info we kept a pointer to */
			if (ah)
				free(ah);

			if (n) {
				lwsl_info("LWS_CALLBACK_HTTP closing\n");
				goto bail; /* struct ah ptr already nuked */
			}

			return 0;
		}

		if (!wsi->protocol)
			lwsl_err("NULL protocol at libwebsocket_read\n");

		/*
		 * It's websocket
		 *
		 * Make sure user side is happy about protocol
		 */

		while (wsi->protocol->callback) {

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
				if (wsi->protocol->name == NULL)
					break;
			} else
				if (wsi->protocol->name && strcmp(
					lws_hdr_simple_ptr(wsi,
						WSI_TOKEN_PROTOCOL),
						      wsi->protocol->name) == 0)
					break;

			wsi->protocol++;
		}

		/* we didn't find a protocol he wanted? */

		if (wsi->protocol->callback == NULL) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
									 NULL) {
				lwsl_info("no protocol -> prot 0 handler\n");
				wsi->protocol = &context->protocols[0];
			} else {
				lwsl_err("Req protocol %s not supported\n",
				   lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL));
				goto bail_nuke_ah;
			}
		}

		/* allocate wsi->user storage */
		if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}


		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
						       wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* drop the header info -- no bail_nuke_ah after this */

		if (wsi->u.hdr.ah)
			free(wsi->u.hdr.ah);

		wsi->mode = LWS_CONNMODE_WS_SERVING;

		/* union transition */
		memset(&wsi->u, 0, sizeof(wsi->u));
		wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
		wsi->u.ws.rx_user_buffer = malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			goto bail;
		}
		lwsl_info("Allocating RX buffer %d\n", n);

		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const void*) &n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			goto bail;
		}

		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
#endif
		break;

	case WSI_STATE_AWAITING_CLOSE_ACK:
	case WSI_STATE_ESTABLISHED:
#ifndef LWS_NO_CLIENT
		switch (wsi->mode) {
		case LWS_CONNMODE_WS_CLIENT:
			for (n = 0; n < len; n++)
				if (libwebsocket_client_rx_sm(
							     wsi, *buf++) < 0) {
					lwsl_info("client rx has bailed\n");
					goto bail;
				}

			return 0;
		default:
			break;
		}
#endif
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
			lwsl_info("interpret_incoming_packet has bailed\n");
			goto bail;
		}
#endif
		break;
	default:
		lwsl_err("libwebsocket_read: Unhandled state\n");
		break;
	}

	return 0;

bail_nuke_ah:
	/* drop the header info */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

bail:
	lwsl_info("closing connection at libwebsocket_read bail:\n");

	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);

	return -1;
}
Ejemplo n.º 9
0
int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
		   struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
	int n;
	struct lws_tokens eff_buf;
#ifdef LWS_USE_HTTP2
	struct libwebsocket *wsi2;
#endif
	int ret;
	int m;
	int handled = 0;

	/* pending truncated sends have uber priority */

	if (wsi->truncated_send_len) {
		if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
				wsi->truncated_send_offset,
						wsi->truncated_send_len) < 0) {
			lwsl_info("lws_handle_POLLOUT_event signalling to close\n");
			return -1;
		}
		/* leave POLLOUT active either way */
		return 0;
	} else
		if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
			lwsl_info("***** %x signalling to close in POLLOUT handler\n", wsi);
			return -1; /* retry closing now */
		}
#ifdef LWS_USE_HTTP2
	/* protocol packets are next */
	if (wsi->pps) {
		lwsl_info("servicing pps %d\n", wsi->pps);
		switch (wsi->pps) {
		case LWS_PPS_HTTP2_MY_SETTINGS:
		case LWS_PPS_HTTP2_ACK_SETTINGS:
			lws_http2_do_pps_send(context, wsi);
			break;
		default:
			break;
		}
		wsi->pps = LWS_PPS_NONE;
		libwebsocket_rx_flow_control(wsi, 1);
		
		return 0; /* leave POLLOUT active */
	}
#endif
	/* pending control packets have next priority */
	
	if (wsi->state == WSI_STATE_ESTABLISHED && wsi->u.ws.ping_payload_len) {
		n = libwebsocket_write(wsi, 
				&wsi->u.ws.ping_payload_buf[
					LWS_SEND_BUFFER_PRE_PADDING],
					wsi->u.ws.ping_payload_len,
							       LWS_WRITE_PONG);
		if (n < 0)
			return -1;
		/* well he is sent, mark him done */
		wsi->u.ws.ping_payload_len = 0;
		/* leave POLLOUT active either way */
		return 0;
	}

	/* if nothing critical, user can get the callback */
	
	m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
								       NULL, 0);
#ifndef LWS_NO_EXTENSIONS
	if (!wsi->extension_data_pending || handled == 2)
		goto user_service;
#endif
	/*
	 * check in on the active extensions, see if they
	 * had pending stuff to spill... they need to get the
	 * first look-in otherwise sequence will be disordered
	 *
	 * NULL, zero-length eff_buf means just spill pending
	 */

	ret = 1;
	while (ret == 1) {

		/* default to nobody has more to spill */

		ret = 0;
		eff_buf.token = NULL;
		eff_buf.token_len = 0;

		/* give every extension a chance to spill */
		
		m = lws_ext_callback_for_each_active(wsi,
					LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
							           &eff_buf, 0);
		if (m < 0) {
			lwsl_err("ext reports fatal error\n");
			return -1;
		}
		if (m)
			/*
			 * at least one extension told us he has more
			 * to spill, so we will go around again after
			 */
			ret = 1;

		/* assuming they gave us something to send, send it */

		if (eff_buf.token_len) {
			n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
							     eff_buf.token_len);
			if (n < 0) {
				lwsl_info("closing from POLLOUT spill\n");
				return -1;
			}
			/*
			 * Keep amount spilled small to minimize chance of this
			 */
			if (n != eff_buf.token_len) {
				lwsl_err("Unable to spill ext %d vs %s\n",
							  eff_buf.token_len, n);
				return -1;
			}
		} else
			continue;

		/* no extension has more to spill */

		if (!ret)
			continue;

		/*
		 * There's more to spill from an extension, but we just sent
		 * something... did that leave the pipe choked?
		 */

		if (!lws_send_pipe_choked(wsi))
			/* no we could add more */
			continue;

		lwsl_info("choked in POLLOUT service\n");

		/*
		 * Yes, he's choked.  Leave the POLLOUT masked on so we will
		 * come back here when he is unchoked.  Don't call the user
		 * callback to enforce ordering of spilling, he'll get called
		 * when we come back here and there's nothing more to spill.
		 */

		return 0;
	}
#ifndef LWS_NO_EXTENSIONS
	wsi->extension_data_pending = 0;

user_service:
#endif
	/* one shot */

	if (pollfd) {
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_info("failled at set pollfd\n");
			return 1;
		}

		lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);
	}

#ifdef LWS_USE_HTTP2
	/* 
	 * we are the 'network wsi' for potentially many muxed child wsi with
	 * no network connection of their own, who have to use us for all their
	 * network actions.  So we use a round-robin scheme to share out the
	 * POLLOUT notifications to our children.
	 * 
	 * But because any child could exhaust the socket's ability to take
	 * writes, we can only let one child get notified each time.
	 * 
	 * In addition children may be closed / deleted / added between POLLOUT
	 * notifications, so we can't hold pointers
	 */
	
	if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING) {
		lwsl_info("%s: non http2\n", __func__);
		goto notify;
	}

	wsi->u.http2.requested_POLLOUT = 0;
	if (!wsi->u.http2.initialized) {
		lwsl_info("pollout on uninitialized http2 conn\n");
		return 0;
	}
	
	lwsl_info("%s: doing children\n", __func__);

	wsi2 = wsi;
	do {
		wsi2 = wsi2->u.http2.next_child_wsi;
		lwsl_info("%s: child %p\n", __func__, wsi2);
		if (!wsi2)
			continue;
		if (!wsi2->u.http2.requested_POLLOUT)
			continue;
		wsi2->u.http2.requested_POLLOUT = 0;
		if (lws_calllback_as_writeable(context, wsi2)) {
			lwsl_debug("Closing POLLOUT child\n");
			libwebsocket_close_and_free_session(context, wsi2,
						LWS_CLOSE_STATUS_NOSTATUS);
		}
		wsi2 = wsi;
	} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
	
	lwsl_info("%s: completed\n", __func__);
	
	return 0;
notify:
#endif
	return lws_calllback_as_writeable(context, wsi);
}
Ejemplo n.º 10
0
LWS_VISIBLE int
libwebsocket_service_fd(struct libwebsocket_context *context,
							  struct libwebsocket_pollfd *pollfd)
{
	struct libwebsocket *wsi;
	int n;
	int m;
	int listen_socket_fds_index = 0;
	time_t now;
	int timed_out = 0;
	int our_fd = 0;
	char draining_flow = 0;
	int more;
	struct lws_tokens eff_buf;

	if (context->listen_service_fd)
		listen_socket_fds_index = context->lws_lookup[
			     context->listen_service_fd]->position_in_fds_table;

	/*
	 * you can call us with pollfd = NULL to just allow the once-per-second
	 * global timeout checks; if less than a second since the last check
	 * it returns immediately then.
	 */

	time(&now);

	/* TODO: if using libev, we should probably use timeout watchers... */
	if (context->last_timeout_check_s != now) {
		context->last_timeout_check_s = now;

		lws_plat_service_periodic(context);

		/* global timeout check once per second */

		if (pollfd)
			our_fd = pollfd->fd;

		for (n = 0; n < context->fds_count; n++) {
			m = context->fds[n].fd;
			wsi = context->lws_lookup[m];
			if (!wsi)
				continue;

			if (libwebsocket_service_timeout_check(context, wsi, now))
				/* he did time out... */
				if (m == our_fd) {
					/* it was the guy we came to service! */
					timed_out = 1;
					/* mark as handled */
					if (pollfd)
						pollfd->revents = 0;
				}
		}
	}

	/* the socket we came to service timed out, nothing to do */
	if (timed_out)
		return 0;

	/* just here for timeout management? */
	if (pollfd == NULL)
		return 0;

	/* no, here to service a socket descriptor */
	wsi = context->lws_lookup[pollfd->fd];
	if (wsi == NULL)
		/* not lws connection ... leave revents alone and return */
		return 0;

	/*
	 * so that caller can tell we handled, past here we need to
	 * zero down pollfd->revents after handling
	 */

	/*
	 * deal with listen service piggybacking
	 * every listen_service_modulo services of other fds, we
	 * sneak one in to service the listen socket if there's anything waiting
	 *
	 * To handle connection storms, as found in ab, if we previously saw a
	 * pending connection here, it causes us to check again next time.
	 */

	if (context->listen_service_fd && pollfd !=
				       &context->fds[listen_socket_fds_index]) {
		context->listen_service_count++;
		if (context->listen_service_extraseen ||
				context->listen_service_count ==
					       context->listen_service_modulo) {
			context->listen_service_count = 0;
			m = 1;
			if (context->listen_service_extraseen > 5)
				m = 2;
			while (m--) {
				/*
				 * even with extpoll, we prepared this
				 * internal fds for listen
				 */
				n = lws_poll_listen_fd(&context->fds[listen_socket_fds_index]);
				if (n > 0) { /* there's a conn waiting for us */
					libwebsocket_service_fd(context,
						&context->
						  fds[listen_socket_fds_index]);
					context->listen_service_extraseen++;
				} else {
					if (context->listen_service_extraseen)
						context->
						     listen_service_extraseen--;
					break;
				}
			}
		}

	}

	/* handle session socket closed */

	if ((!(pollfd->revents & LWS_POLLIN)) &&
			(pollfd->revents & LWS_POLLHUP)) {

		lwsl_debug("Session Socket %p (fd=%d) dead\n",
						       (void *)wsi, pollfd->fd);

		goto close_and_handled;
	}

	/* okay, what we came here to do... */

	switch (wsi->mode) {
	case LWS_CONNMODE_HTTP_SERVING:
	case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
	case LWS_CONNMODE_SERVER_LISTENER:
	case LWS_CONNMODE_SSL_ACK_PENDING:
		n = lws_server_socket_service(context, wsi, pollfd);
		if (n < 0)
			goto close_and_handled;
		goto handled;

	case LWS_CONNMODE_WS_SERVING:
	case LWS_CONNMODE_WS_CLIENT:
	case LWS_CONNMODE_HTTP2_SERVING:

		/* the guy requested a callback when it was OK to write */

		if ((pollfd->revents & LWS_POLLOUT) &&
			(wsi->state == WSI_STATE_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS ||
				wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) &&
			   lws_handle_POLLOUT_event(context, wsi, pollfd)) {
			lwsl_info("libwebsocket_service_fd: closing\n");
			goto close_and_handled;
		}

		if (wsi->rxflow_buffer &&
			      (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
			lwsl_info("draining rxflow\n");
			/* well, drain it */
			eff_buf.token = (char *)wsi->rxflow_buffer +
						wsi->rxflow_pos;
			eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos;
			draining_flow = 1;
			goto drain;
		}

		/* any incoming data ready? */

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

		eff_buf.token_len = lws_ssl_capable_read(context, wsi,
				context->service_buffer,
					       sizeof(context->service_buffer));
		switch (eff_buf.token_len) {
		case 0:
			lwsl_info("service_fd: closing due to 0 length read\n");
			goto close_and_handled;
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			lwsl_info("SSL Capable more service\n");
			n = 0;
			goto handled;
		case LWS_SSL_CAPABLE_ERROR:
			lwsl_info("Closing when error\n");
			goto close_and_handled;
		}

		/*
		 * give any active extensions a chance to munge the buffer
		 * before parse.  We pass in a pointer to an lws_tokens struct
		 * prepared with the default buffer and content length that's in
		 * there.  Rather than rewrite the default buffer, extensions
		 * that expect to grow the buffer can adapt .token to
		 * point to their own per-connection buffer in the extension
		 * user allocation.  By default with no extensions or no
		 * extension callback handling, just the normal input buffer is
		 * used then so it is efficient.
		 */

		eff_buf.token = (char *)context->service_buffer;
drain:

		do {

			more = 0;
			
			m = lws_ext_callback_for_each_active(wsi,
				LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, &eff_buf, 0);
			if (m < 0)
				goto close_and_handled;
			if (m)
				more = 1;

			/* service incoming data */

			if (eff_buf.token_len) {
				n = libwebsocket_read(context, wsi,
					(unsigned char *)eff_buf.token,
							    eff_buf.token_len);
				if (n < 0) {
					/* we closed wsi */
					n = 0;
					goto handled;
				}
			}

			eff_buf.token = NULL;
			eff_buf.token_len = 0;
		} while (more);

		if (draining_flow && wsi->rxflow_buffer &&
				 wsi->rxflow_pos == wsi->rxflow_len) {
			lwsl_info("flow buffer: drained\n");
			lws_free2(wsi->rxflow_buffer);
			/* having drained the rxflow buffer, can rearm POLLIN */
			_libwebsocket_rx_flow_control(wsi); /* n ignored, needed for NO_SERVER case */
		}

		break;

	default:
#ifdef LWS_NO_CLIENT
		break;
#else
		n = lws_client_socket_service(context, wsi, pollfd);
		goto handled;
#endif
	}

	n = 0;
	goto handled;

close_and_handled:
	lwsl_debug("Close and handled\n");
	libwebsocket_close_and_free_session(context, wsi,
						LWS_CLOSE_STATUS_NOSTATUS);
	n = 1;

handled:
	pollfd->revents = 0;
	return n;
}
Ejemplo n.º 11
0
/**
 * libwebsocket_context_destroy() - Destroy the websocket context
 * @context:	Websocket context
 *
 *	This function closes any active connections and then frees the
 *	context.  After calling this, any further use of the context is
 *	undefined.
 */
LWS_VISIBLE void
libwebsocket_context_destroy(struct libwebsocket_context *context)
{
	/* Note that this is used for freeing partially allocated structs as well
	 * so make sure you don't try to free something uninitialized */
	int n;
	struct libwebsocket_protocols *protocol = NULL;

	lwsl_notice("%s\n", __func__);

	if (!context)
		return;

#ifdef LWS_LATENCY
	if (context->worst_latency_info[0])
		lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
#endif

	for (n = 0; n < context->fds_count; n++) {
		struct libwebsocket *wsi =
					wsi_from_fd(context, context->fds[n].fd);
		if (!wsi)
			continue;
		libwebsocket_close_and_free_session(context,
			wsi, LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY /* no protocol close */);
		n--;
	}

	/*
	 * give all extensions a chance to clean up any per-context
	 * allocations they might have made
	 */
	// TODO: I am not sure, but are we never supposed to be able to run a server
	//       and client at the same time for a given context?
	//       Otherwise both of these callbacks should always be called!
	if (context->listen_port != CONTEXT_PORT_NO_LISTEN) {
		if (lws_ext_callback_for_each_extension_type(context, NULL,
				LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0) {
			lwsl_err("Got error from server extension callback on cleanup");
		}
	} else {
		if (lws_ext_callback_for_each_extension_type(context, NULL,
				LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0) {
			lwsl_err("Got error from client extension callback on cleanup");
		}
	}

	/*
	 * inform all the protocols that they are done and will have no more
	 * callbacks
	 */
	protocol = context->protocols;
	if (protocol) {
		while (protocol->callback) {
			protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
					NULL, NULL, 0);
			protocol++;
		}
	}

	lws_plat_context_early_destroy(context);

	lws_ssl_context_destroy(context);

	if (context->fds)
		lws_free(context->fds);

	lws_plat_context_late_destroy(context);

	lws_free(context);
}
Ejemplo n.º 12
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;
}
Ejemplo n.º 13
0
int
lws_client_interpret_server_handshake(struct libwebsocket_context *context,
		struct libwebsocket *wsi)
{
	const char *pc;
	int okay = 0;
	char *p;
	int len;
#ifndef LWS_NO_EXTENSIONS
	char ext_name[128];
	struct libwebsocket_extension *ext;
	void *v;
	int more = 1;
	const char *c;
#endif
	int n;
	int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;

	/*
	 * well, what the server sent looked reasonable for syntax.
	 * Now let's confirm it sent all the necessary headers
	 */

	if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
		lwsl_info("no ACCEPT\n");
		goto bail3;
	}

	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
	if (!p) {
		lwsl_info("no URI\n");
		goto bail3;
	}
	if (p && strncmp(p, "101", 3)) {
		lwsl_warn(
		       "lws_client_handshake: got bad HTTP response '%s'\n", p);
		goto bail3;
	}

	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
	if (!p) {
		lwsl_info("no UPGRADE\n");
		goto bail3;
	}
	strtolower(p);
	if (strcmp(p, "websocket")) {
		lwsl_warn(
		      "lws_client_handshake: got bad Upgrade header '%s'\n", p);
		goto bail3;
	}

	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
	if (!p) {
		lwsl_info("no Connection hdr\n");
		goto bail3;
	}
	strtolower(p);
	if (strcmp(p, "upgrade")) {
		lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
		goto bail3;
	}

	pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
	if (pc == NULL)
		lwsl_parser("lws_client_int_s_hs: no protocol list\n");
	else
		lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);

	/*
	 * confirm the protocol the server wants to talk was in the list
	 * of protocols we offered
	 */

	len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
	if (!len) {

		lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n");
		/*
		 * no protocol name to work from,
		 * default to first protocol
		 */
		wsi->protocol = &context->protocols[0];
		goto check_extensions;
	}

	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
	len = strlen(p);

	while (*pc && !okay) {
		if (!strncmp(pc, p, len) &&
					  (pc[len] == ',' || pc[len] == '\0')) {
			okay = 1;
			continue;
		}
		while (*pc && *pc != ',')
			pc++;
		while (*pc && *pc == ' ')
			pc++;
	}

	if (!okay) {
		lwsl_err("lws_client_int_s_hs: got bad protocol '%s'\n", p);
		goto bail2;
	}

	/*
	 * identify the selected protocol struct and set it
	 */
	n = 0;
	wsi->protocol = NULL;
	while (context->protocols[n].callback && !wsi->protocol) {
		if (strcmp(p, context->protocols[n].name) == 0) {
			wsi->protocol = &context->protocols[n];
			break;
		}
		n++;
	}

	if (wsi->protocol == NULL) {
		lwsl_err("lws_client_int_s_hs: fail protocol '%s'\n", p);
		goto bail2;
	}


check_extensions:
#ifndef LWS_NO_EXTENSIONS
	/* instantiate the accepted extensions */

	if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
		lwsl_ext("no client extenstions allowed by server\n");
		goto check_accept;
	}

	/*
	 * break down the list of server accepted extensions
	 * and go through matching them or identifying bogons
	 */

	if (lws_hdr_copy(wsi, (char *)context->service_buffer,
		   sizeof(context->service_buffer), WSI_TOKEN_EXTENSIONS) < 0) {
		lwsl_warn("ext list from server failed to copy\n");
		goto bail2;
	}

	c = (char *)context->service_buffer;
	n = 0;
	while (more) {

		if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
			ext_name[n] = *c++;
			if (n < sizeof(ext_name) - 1)
				n++;
			continue;
		}
		ext_name[n] = '\0';
		if (!*c)
			more = 0;
		else {
			c++;
			if (!n)
				continue;
		}

		/* check we actually support it */

		lwsl_ext("checking client ext %s\n", ext_name);

		n = 0;
		ext = wsi->protocol->owning_server->extensions;
		while (ext && ext->callback) {

			if (strcmp(ext_name, ext->name)) {
				ext++;
				continue;
			}

			n = 1;

			lwsl_ext("instantiating client ext %s\n", ext_name);

			/* instantiate the extension on this conn */

			wsi->active_extensions_user[
				wsi->count_active_extensions] =
					 malloc(ext->per_session_data_size);
			if (wsi->active_extensions_user[
				wsi->count_active_extensions] == NULL) {
				lwsl_err("Out of mem\n");
				goto bail2;
			}
			memset(wsi->active_extensions_user[
				wsi->count_active_extensions], 0,
						    ext->per_session_data_size);
			wsi->active_extensions[
				  wsi->count_active_extensions] = ext;

			/* allow him to construct his context */

			ext->callback(wsi->protocol->owning_server,
				ext, wsi,
				   LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
					wsi->active_extensions_user[
					 wsi->count_active_extensions],
								   NULL, 0);

			wsi->count_active_extensions++;

			ext++;
		}

		if (n == 0) {
			lwsl_warn("Unknown ext '%s'!\n", ext_name);
			goto bail2;
		}

		n = 0;
	}

check_accept:
#endif

	/*
	 * Confirm his accept token is the one we precomputed
	 */

	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
	if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) {
		lwsl_warn("lws_client_int_s_hs: accept %s wrong vs %s\n", p,
				  wsi->u.hdr.ah->initial_handshake_hash_base64);
		goto bail2;
	}

	/* allocate the per-connection user memory (if any) */
	if (libwebsocket_ensure_user_space(wsi)) {
		lwsl_err("Problem allocating wsi user mem\n");
		goto bail2;
	}

	/*
	 * we seem to be good to go, give client last chance to check
	 * headers and OK it
	 */

	wsi->protocol->callback(context, wsi,
				LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
						     wsi->user_space, NULL, 0);

	/* clear his proxy connection timeout */

	libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

	/* free up his parsing allocations */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

	/* mark him as being alive */

	wsi->state = WSI_STATE_ESTABLISHED;
	wsi->mode = LWS_CONNMODE_WS_CLIENT;

	/* union transition */

	memset(&wsi->u, 0, sizeof(wsi->u));

	wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;

	/*
	 * create the frame buffer for this connection according to the
	 * size mentioned in the protocol definition.  If 0 there, then
	 * use a big default for compatibility
	 */

	n = wsi->protocol->rx_buffer_size;
	if (!n)
		n = LWS_MAX_SOCKET_IO_BUF;
	n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
	wsi->u.ws.rx_user_buffer = malloc(n);
	if (!wsi->u.ws.rx_user_buffer) {
		lwsl_err("Out of Mem allocating rx buffer %d\n", n);
		goto bail2;
	}
	lwsl_info("Allocating client RX buffer %d\n", n);

	if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) {
		lwsl_warn("Failed to set SNDBUF to %d", n);
		goto bail3;
	}

	lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);

	/* call him back to inform him he is up */

	wsi->protocol->callback(context, wsi,
				LWS_CALLBACK_CLIENT_ESTABLISHED,
						     wsi->user_space, NULL, 0);
#ifndef LWS_NO_EXTENSIONS
	/*
	 * inform all extensions, not just active ones since they
	 * already know
	 */

	ext = context->extensions;

	while (ext && ext->callback) {
		v = NULL;
		for (n = 0; n < wsi->count_active_extensions; n++)
			if (wsi->active_extensions[n] == ext)
				v = wsi->active_extensions_user[n];

		ext->callback(context, ext, wsi,
			  LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
		ext++;
	}
#endif

	return 0;

bail3:
	free(wsi->u.ws.rx_user_buffer);
	wsi->u.ws.rx_user_buffer = NULL;
	close_reason = LWS_CLOSE_STATUS_NOSTATUS;

bail2:
	if (wsi->protocol)
		wsi->protocol->callback(context, wsi,
			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
						      wsi->user_space, NULL, 0);

	lwsl_info("closing connection due to bail2 connection error\n");

	/* free up his parsing allocations */

	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

	libwebsocket_close_and_free_session(context, wsi, close_reason);

	return 1;
}
Ejemplo n.º 14
0
void lws_conn::onDisconnect(TCPStream *s)
{
	(void)s;
	libwebsocket_close_and_free_session(wsi->protocol->owning_server, wsi,
						LWS_CLOSE_STATUS_NOSTATUS);
}
Ejemplo n.º 15
0
struct libwebsocket *libwebsocket_client_connect_2(
	struct libwebsocket_context *context,
	struct libwebsocket *wsi
) {
	struct libwebsocket_pollfd pfd;
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 server_addr6;
	struct sockaddr_in6 client_addr6;
	struct addrinfo hints, *result;
#endif
	struct sockaddr_in server_addr4;
	struct sockaddr_in client_addr4;

	struct sockaddr *v;
	int n;
	int plen = 0;
	const char *ads;

       lwsl_client("libwebsocket_client_connect_2\n");

	/*
	 * proxy?
	 */

	if (context->http_proxy_port) {
		plen = sprintf((char *)context->service_buffer,
			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
			"User-agent: libwebsockets\x0d\x0a",
			lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
			wsi->u.hdr.ah->c_port);

		if (context->proxy_basic_auth_token[0])
			plen += sprintf((char *)context->service_buffer + plen,
					"Proxy-authorization: basic %s\x0d\x0a",
					context->proxy_basic_auth_token);

		plen += sprintf((char *)context->service_buffer + plen,
				"\x0d\x0a");

		ads = context->http_proxy_address;

#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context))
			server_addr6.sin6_port = htons(context->http_proxy_port);
		else
#endif
			server_addr4.sin_port = htons(context->http_proxy_port);

	} else {
		ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context))
			server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port);
		else
#endif
			server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port);
	}

	/*
	 * prepare the actual connection (to the proxy, if any)
	 */
       lwsl_client("%s: address %s\n", __func__, ads);

#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		memset(&hints, 0, sizeof(struct addrinfo));
		n = getaddrinfo(ads, NULL, &hints, &result);
		if (n) {
#ifdef _WIN32
			lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n));
#else
			lwsl_err("getaddrinfo: %s\n", gai_strerror(n));
#endif
			goto oom4;
		}

		server_addr6.sin6_family = AF_INET6;
		switch (result->ai_family) {
		case AF_INET:
			/* map IPv4 to IPv6 */
			bzero((char *)&server_addr6.sin6_addr,
						sizeof(struct in6_addr));
			server_addr6.sin6_addr.s6_addr[10] = 0xff;
			server_addr6.sin6_addr.s6_addr[11] = 0xff;
			memcpy(&server_addr6.sin6_addr.s6_addr[12],
				&((struct sockaddr_in *)result->ai_addr)->sin_addr,
							sizeof(struct in_addr));
			break;
		case AF_INET6:
			memcpy(&server_addr6.sin6_addr,
			  &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
						sizeof(struct in6_addr));
			break;
		default:
			lwsl_err("Unknown address family\n");
			freeaddrinfo(result);
			goto oom4;
		}

		freeaddrinfo(result);
	} else
#endif
	{
		struct addrinfo ai, *res, *result;
		void *p = NULL;

		memset (&ai, 0, sizeof ai);
		ai.ai_family = PF_UNSPEC;
		ai.ai_socktype = SOCK_STREAM;
		ai.ai_flags = AI_CANONNAME;

		if (getaddrinfo(ads, NULL, &ai, &result))
			goto oom4;

		res = result;
		while (!p && res) {
			switch (res->ai_family) {
			case AF_INET:
				p = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
				break;
			}

			res = res->ai_next;
		}
		
		if (!p) {
			freeaddrinfo(result);
			goto oom4;
		}

		server_addr4.sin_family = AF_INET;
		server_addr4.sin_addr = *((struct in_addr *)p);
		bzero(&server_addr4.sin_zero, 8);
		freeaddrinfo(result);
	}

	if (!lws_socket_is_valid(wsi->sock)) {

#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context))
			wsi->sock = socket(AF_INET6, SOCK_STREAM, 0);
		else
#endif
			wsi->sock = socket(AF_INET, SOCK_STREAM, 0);

		if (!lws_socket_is_valid(wsi->sock)) {
			lwsl_warn("Unable to open socket\n");
			goto oom4;
		}

		if (lws_plat_set_socket_options(context, wsi->sock)) {
			lwsl_err("Failed to set wsi socket options\n");
			compatible_close(wsi->sock);
			goto oom4;
		}

		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT;

		lws_libev_accept(context, wsi, wsi->sock);
		if (insert_wsi_socket_into_fds(context, wsi)) {
			compatible_close(wsi->sock);
			goto oom4;
		}

		/*
		 * past here, we can't simply free the structs as error
		 * handling as oom4 does.  We have to run the whole close flow.
		 */

		libwebsocket_set_timeout(wsi,
			PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
							      AWAITING_TIMEOUT);
#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context)) {
			v = (struct sockaddr *)&client_addr6;
			n = sizeof(client_addr6);
			bzero((char *)v, n);
			client_addr6.sin6_family = AF_INET6;
		} else
#endif
		{
			v = (struct sockaddr *)&client_addr4;
			n = sizeof(client_addr4);
			bzero((char *)v, n);
			client_addr4.sin_family = AF_INET;
		}

		if (context->iface) {
			if (interface_to_sa(context, context->iface,
					(struct sockaddr_in *)v, n) < 0) {
				lwsl_err("Unable to find interface %s\n",
								context->iface);
				goto failed;
			}

			if (bind(wsi->sock, v, n) < 0) {
				lwsl_err("Error binding to interface %s",
								context->iface);
				goto failed;
			}
		}
	}

#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		v = (struct sockaddr *)&server_addr6;
		n = sizeof(struct sockaddr_in6);
	} else
#endif
	{
		v = (struct sockaddr *)&server_addr4;
		n = sizeof(struct sockaddr);
	}

	if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {

		if (LWS_ERRNO == LWS_EALREADY
			|| LWS_ERRNO == LWS_EINPROGRESS
			|| LWS_ERRNO == LWS_EWOULDBLOCK
#ifdef _WIN32
			|| LWS_ERRNO == WSAEINVAL
#endif
			)
		{
			lwsl_client("nonblocking connect retry\n");

			/*
			 * must do specifically a POLLOUT poll to hear
			 * about the connect completion
			 */
			if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
				goto failed;
			lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE);

			return wsi;
		}

		if (LWS_ERRNO != LWS_EISCONN) {
			lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO);
			goto failed;
		}
	}

	lwsl_client("connected\n");

	/* we are connected to server, or proxy */

	if (context->http_proxy_port) {

		/* OK from now on we talk via the proxy, so connect to that */

		/*
		 * (will overwrite existing pointer,
		 * leaving old string/frag there but unreferenced)
		 */
		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
						   context->http_proxy_address))
			goto failed;
		wsi->u.hdr.ah->c_port = context->http_proxy_port;

		n = send(wsi->sock, (char *)context->service_buffer, plen, MSG_NOSIGNAL);
		if (n < 0) {
			lwsl_debug("ERROR writing to proxy socket\n");
			goto failed;
		}

		libwebsocket_set_timeout(wsi,
			PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
							      AWAITING_TIMEOUT);

		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;

		return wsi;
	}

	/*
	 * provoke service to issue the handshake directly
	 * we need to do it this way because in the proxy case, this is the
	 * next state and executed only if and when we get a good proxy
	 * response inside the state machine... but notice in SSL case this
	 * may not have sent anything yet with 0 return, and won't until some
	 * many retries from main loop.  To stop that becoming endless,
	 * cover with a timeout.
	 */

	libwebsocket_set_timeout(wsi,
		PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT);

	wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
	pfd.fd = wsi->sock;
	pfd.revents = LWS_POLLIN;

	n = libwebsocket_service_fd(context, &pfd);

	if (n < 0)
		goto failed;

	if (n) /* returns 1 on failure after closing wsi */
		return NULL;

	return wsi;

oom4:
	lws_free(wsi->u.hdr.ah);
	lws_free(wsi);
	return NULL;

failed:
	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
	return NULL;
}
Ejemplo n.º 16
0
struct libwebsocket *libwebsocket_client_connect_2(
	struct libwebsocket_context *context,
	struct libwebsocket *wsi
) {
	struct pollfd pfd;
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 server_addr6;
	struct sockaddr_in6 client_addr6;
	struct addrinfo hints, *result;
#endif
	struct sockaddr_in server_addr4;
	struct sockaddr_in client_addr4;
	struct hostent *server_hostent;

	struct sockaddr *v;
	int n;
	int plen = 0;
	const char *ads;

       lwsl_client("libwebsocket_client_connect_2\n");

	/*
	 * proxy?
	 */

	if (context->http_proxy_port) {
		plen = sprintf((char *)context->service_buffer,
			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
			"User-agent: libwebsockets\x0d\x0a"
/*Proxy-authorization: basic aGVsbG86d29ybGQ= */
			"\x0d\x0a",
			lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
			wsi->u.hdr.ah->c_port);
		ads = context->http_proxy_address;

#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context))
			server_addr6.sin6_port = htons(context->http_proxy_port);
		else
#endif
			server_addr4.sin_port = htons(context->http_proxy_port);

	} else {
		ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#ifdef LWS_WITH_IPV6
		if (LWS_IPV6_ENABLED(context))
			server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port);
		else
#endif
			server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port);
	}

	/*
	 * prepare the actual connection (to the proxy, if any)
	 */
       lwsl_client("libwebsocket_client_connect_2: address %s\n", ads);

#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		memset(&hints, 0, sizeof(struct addrinfo));
		n = getaddrinfo(ads, NULL, &hints, &result);
		if (n) {
			lwsl_err("getaddrinfo: %s\n", gai_strerror(n));
			goto oom4;
		}

		server_addr6.sin6_family = AF_INET6;
		switch (result->ai_family) {
		case AF_INET:
			/* map IPv4 to IPv6 */
			bzero((char *)&server_addr6.sin6_addr,
						sizeof(struct in6_addr));
			server_addr6.sin6_addr.s6_addr16[5] = 0xffff;
			bcopy(&((struct sockaddr_in *)result->ai_addr)->sin_addr,
				&server_addr6.sin6_addr.s6_addr16[6],
							sizeof(struct in_addr));
			break;
		case AF_INET6:
			memcpy(&server_addr6.sin6_addr,
			  &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
						sizeof(struct in6_addr));
			break;
		default:
			lwsl_err("Unknown address family\n");
			freeaddrinfo(result);
			goto oom4;
		}

		freeaddrinfo(result);
	} else
#endif
	{
		server_hostent = gethostbyname(ads);
		if (!server_hostent) {
			lwsl_err("Unable to get host name from %s\n", ads);
			goto oom4;
		}

		server_addr4.sin_family = AF_INET;
		server_addr4.sin_addr =
				*((struct in_addr *)server_hostent->h_addr);
		bzero(&server_addr4.sin_zero, 8);
	}

	if (wsi->sock < 0) {

#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context))
			wsi->sock = socket(AF_INET6, SOCK_STREAM, 0);
		else
#endif
			wsi->sock = socket(AF_INET, SOCK_STREAM, 0);

		if (wsi->sock < 0) {
			lwsl_warn("Unable to open socket\n");
			goto oom4;
		}

		if (lws_set_socket_options(context, wsi->sock)) {
			lwsl_err("Failed to set wsi socket options\n");
			compatible_close(wsi->sock);
			goto oom4;
		}

		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT;

		insert_wsi_socket_into_fds(context, wsi);

		libwebsocket_set_timeout(wsi,
			PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
							      AWAITING_TIMEOUT);
#ifdef LWS_USE_IPV6
		if (LWS_IPV6_ENABLED(context)) {
			v = (struct sockaddr *)&client_addr6;
			n = sizeof(client_addr6);
			bzero((char *)v, n);
			client_addr6.sin6_family = AF_INET6;
		} else
#endif
		{
			v = (struct sockaddr *)&client_addr4;
			n = sizeof(client_addr4);
			bzero((char *)v, n);
			client_addr4.sin_family = AF_INET;
		}

		if (context->iface) {
			if (interface_to_sa(context, context->iface,
					(struct sockaddr_in *)v, n) < 0) {
				lwsl_err("Unable to find interface %s\n",
								context->iface);
				compatible_close(wsi->sock);
				goto failed;
			}

			if (bind(wsi->sock, v, n) < 0) {
				lwsl_err("Error binding to interface %s",
								context->iface);
				compatible_close(wsi->sock);
				goto failed;
			}
		}
	}

#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		v = (struct sockaddr *)&server_addr6;
		n = sizeof(struct sockaddr_in6);
	} else
#endif
	{
		v = (struct sockaddr *)&server_addr4;
		n = sizeof(struct sockaddr);
	}

	if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {

		if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS) {
			lwsl_client("nonblocking connect retry\n");

			/*
			 * must do specifically a POLLOUT poll to hear
			 * about the connect completion
			 */
			lws_change_pollfd(wsi, 0, POLLOUT);

			return wsi;
		}

		if (LWS_ERRNO != LWS_EISCONN) {
			lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO);
			goto failed;
		}
	}

	lwsl_client("connected\n");

	/* we are connected to server, or proxy */

	if (context->http_proxy_port) {

		/* OK from now on we talk via the proxy, so connect to that */

		/*
		 * (will overwrite existing pointer,
		 * leaving old string/frag there but unreferenced)
		 */
		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
						   context->http_proxy_address))
			goto failed;
		wsi->u.hdr.ah->c_port = context->http_proxy_port;

		n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL);
		if (n < 0) {
			lwsl_debug("ERROR writing to proxy socket\n");
			goto failed;
		}

		libwebsocket_set_timeout(wsi,
			PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
							      AWAITING_TIMEOUT);

		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;

		return wsi;
	}

	/*
	 * provoke service to issue the handshake directly
	 * we need to do it this way because in the proxy case, this is the
	 * next state and executed only if and when we get a good proxy
	 * response inside the state machine... but notice in SSL case this
	 * may not have sent anything yet with 0 return, and won't until some
	 * many retries from main loop.  To stop that becoming endless,
	 * cover with a timeout.
	 */

	libwebsocket_set_timeout(wsi,
		PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT);

	wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
	pfd.fd = wsi->sock;
	pfd.revents = POLLIN;

	n = libwebsocket_service_fd(context, &pfd);

	if (n < 0)
		goto failed;

	if (n) /* returns 1 on failure after closing wsi */
		return NULL;

	return wsi;

oom4:
	free(wsi->u.hdr.ah);
	free(wsi);
	return NULL;

failed:
	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
	return NULL;
}
Ejemplo n.º 17
0
 //--------------------------------------------------------------
 void Reactor::close(Connection* const conn){
     if (conn != NULL && conn->ws != NULL){
         libwebsocket_close_and_free_session(context, conn->ws, LWS_CLOSE_STATUS_NORMAL);
     }
 }
Ejemplo n.º 18
0
static int
callback_dumb_increment(struct libwebsocket_context *context,
			struct libwebsocket *wsi,
			enum libwebsocket_callback_reasons reason,
					       void *user, void *in, size_t len)
{
	int n;
	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
						  LWS_SEND_BUFFER_POST_PADDING];
	unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
	struct per_session_data__dumb_increment *pss = user;

	switch (reason) {

	case LWS_CALLBACK_ESTABLISHED:
		fprintf(stderr, "callback_dumb_increment: "
						 "LWS_CALLBACK_ESTABLISHED\n");
		pss->number = 0;
		break;

	/*
	 * in this protocol, we just use the broadcast action as the chance to
	 * send our own connection-specific data and ignore the broadcast info
	 * that is available in the 'in' parameter
	 */

	case LWS_CALLBACK_BROADCAST:
		n = sprintf((char *)p, "%d", pss->number++);
		n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
		if (n < 0) {
			fprintf(stderr, "ERROR writing to socket");
			return 1;
		}
		if (close_testing && pss->number == 50) {
			fprintf(stderr, "close tesing limit, closing\n");
			libwebsocket_close_and_free_session(context, wsi,
						       LWS_CLOSE_STATUS_NORMAL);
		}
		break;

	case LWS_CALLBACK_RECEIVE:
		fprintf(stderr, "rx %d\n", (int)len);
		if (len < 6)
			break;
		if (strcmp(in, "reset\n") == 0)
			pss->number = 0;
		break;
	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

	case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
		dump_handshake_info((struct lws_tokens *)(long)user);
		/* you could return non-zero here and kill the connection */
		break;

	default:
		break;
	}

	return 0;
}
Ejemplo n.º 19
0
int lws_client_socket_service(struct libwebsocket_context *context,
		struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
	int n;
	char *p = (char *)&context->service_buffer[0];
	int len;
	unsigned char c;

	switch (wsi->mode) {

	case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT:

		/*
		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
		 * timeout protection set in client-handshake.c
		 */

               if (libwebsocket_client_connect_2(context, wsi) == NULL) {
			/* closed */
			lwsl_client("closed\n");
			return -1;
		}

		/* either still pending connection, or changed mode */
		return 0;

	case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:

		/* handle proxy hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {

			lwsl_warn("Proxy connection %p (fd=%d) dead\n",
				(void *)wsi, pollfd->fd);

			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->sock, context->service_buffer,
					sizeof(context->service_buffer), 0);
		if (n < 0) {
			
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug(
						   "Proxy read returned EAGAIN... retrying\n");
				return 0;
			}
			
			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from proxy socket\n");
			return 0;
		}

		context->service_buffer[13] = '\0';
		if (strcmp((char *)context->service_buffer, "HTTP/1.0 200 ")) {
			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR proxy: %s\n", context->service_buffer);
			return 0;
		}

		/* clear his proxy connection timeout */

		libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* fallthru */

	case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:

		/*
		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
		 * timeout protection set in client-handshake.c
		 */

		/*
		 * take care of our libwebsocket_callback_on_writable
		 * happening at a time when there's no real connection yet
		 */
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			return -1;

#ifdef LWS_OPENSSL_SUPPORT
		/* we can retry this... just cook the SSL BIO the first time */

		if (wsi->use_ssl && !wsi->ssl) {
#if defined(CYASSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)
			const char *hostname = lws_hdr_simple_ptr(wsi,
						_WSI_TOKEN_CLIENT_PEER_ADDRESS);
#endif

			wsi->ssl = SSL_new(context->ssl_client_ctx);
#ifndef USE_CYASSL
			SSL_set_mode(wsi->ssl,
					SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
			/*
			 * use server name indication (SNI), if supported,
			 * when establishing connection
			 */
#ifdef USE_CYASSL
#ifdef CYASSL_SNI_HOST_NAME
			CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME,
				hostname, strlen(hostname));
#endif
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
			SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#endif

#ifdef USE_CYASSL
			/*
			 * CyaSSL does certificate verification differently
			 * from OpenSSL.
			 * If we should ignore the certificate, we need to set
			 * this before SSL_new and SSL_connect is called.
			 * Otherwise the connect will simply fail with error
			 * code -155
			 */
			if (wsi->use_ssl == 2)
				CyaSSL_set_verify(wsi->ssl,
							SSL_VERIFY_NONE, NULL);
#endif /* USE_CYASSL */

			wsi->client_bio =
				BIO_new_socket(wsi->sock, BIO_NOCLOSE);
			SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);

#ifdef USE_CYASSL
			CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
			BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif

			SSL_set_ex_data(wsi->ssl,
					openssl_websocket_private_data_index,
								       context);
		}

		if (wsi->use_ssl) {
			lws_latency_pre(context, wsi);
			n = SSL_connect(wsi->ssl);
			lws_latency(context, wsi,
			  "SSL_connect LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE",
								      n, n > 0);

			if (n < 0) {
				n = SSL_get_error(wsi->ssl, n);

				if (n == SSL_ERROR_WANT_READ ||
					n == SSL_ERROR_WANT_WRITE) {
					/*
					 * wants us to retry connect due to
					 * state of the underlying ssl layer...
					 * but since it may be stalled on
					 * blocked write, no incoming data may
					 * arrive to trigger the retry.
					 * Force (possibly many times if the SSL
					 * state persists in returning the
					 * condition code, but other sockets
					 * are getting serviced inbetweentimes)
					 * us to get called back when writable.
					 */

					lwsl_info(
					     "SSL_connect WANT_... retrying\n");
					libwebsocket_callback_on_writable(
								  context, wsi);
					
					wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL;
					
					return 0; /* no error */
				}
				n = -1;
			}

			if (n <= 0) {
				/*
				 * retry if new data comes until we
				 * run into the connection timeout or win
				 */
				
				n = ERR_get_error();
				if (n != SSL_ERROR_NONE) {
					lwsl_err("SSL connect error %lu: %s\n",
						n,
						ERR_error_string(n,
							  (char *)context->service_buffer));
					return 0;
				}
			}
		} else
			wsi->ssl = NULL;

		/* fallthru */
			
	case LWS_CONNMODE_WS_CLIENT_WAITING_SSL:
			
		if (wsi->use_ssl) {
				
			if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SSL) {
				lws_latency_pre(context, wsi);
				n = SSL_connect(wsi->ssl);
				lws_latency(context, wsi,
							"SSL_connect LWS_CONNMODE_WS_CLIENT_WAITING_SSL",
							n, n > 0);
				
				if (n < 0) {
					n = SSL_get_error(wsi->ssl, n);
					
					if (n == SSL_ERROR_WANT_READ ||
						n == SSL_ERROR_WANT_WRITE) {
						/*
						 * wants us to retry connect due to
						 * state of the underlying ssl layer...
						 * but since it may be stalled on
						 * blocked write, no incoming data may
						 * arrive to trigger the retry.
						 * Force (possibly many times if the SSL
						 * state persists in returning the
						 * condition code, but other sockets
						 * are getting serviced inbetweentimes)
						 * us to get called back when writable.
						 */
						
						lwsl_info(
								  "SSL_connect WANT_... retrying\n");
						libwebsocket_callback_on_writable(
														  context, wsi);
						
						wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL;
						
						return 0; /* no error */
					}
					n = -1;
				}
				
				if (n <= 0) {
					/*
					 * retry if new data comes until we
					 * run into the connection timeout or win
					 */
					n = ERR_get_error();
					if (n != SSL_ERROR_NONE) {
						lwsl_err("SSL connect error %lu: %s\n",
								 n,
								 ERR_error_string(n,
												  (char *)context->service_buffer));
						return 0;
					}
				}
			}
			
			#ifndef USE_CYASSL
			/*
			 * See comment above about CyaSSL certificate
			 * verification
			 */
			lws_latency_pre(context, wsi);
			n = SSL_get_verify_result(wsi->ssl);
			lws_latency(context, wsi,
				"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE",
								      n, n > 0);

			if (n != X509_V_OK) {
				if((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && wsi->use_ssl == 2) {
					lwsl_notice("accepting self-signed certificate\n");
				} else {
					lwsl_err(
						"server's cert didn't look good %d\n", n);
					libwebsocket_close_and_free_session(context,
							wsi, LWS_CLOSE_STATUS_NOSTATUS);
					return 0;
				}
			}
#endif /* USE_CYASSL */
		} else
			wsi->ssl = NULL;
#endif
		
		wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2;
		libwebsocket_set_timeout(wsi,
				PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
							      AWAITING_TIMEOUT);

		/* fallthru */

	case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2:
		p = libwebsockets_generate_client_handshake(context, wsi, p);
		if (p == NULL) {
			lwsl_err("Failed to generate handshake for client\n");
			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		/* send our request to the server */

		lws_latency_pre(context, wsi);

		n = lws_ssl_capable_write(wsi, context->service_buffer, p - (char *)context->service_buffer);
		lws_latency(context, wsi, "send lws_issue_raw", n, n == p - (char *)context->service_buffer);
		switch (n) {
		case LWS_SSL_CAPABLE_ERROR:
			lwsl_debug("ERROR writing to client socket\n");
			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY;
		libwebsocket_set_timeout(wsi,
				PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
							      AWAITING_TIMEOUT);
		break;

	case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:

		/* handle server hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {

			lwsl_debug("Server connection %p (fd=%d) dead\n",
				(void *)wsi, pollfd->fd);

			goto bail3;
		}

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

		/* interpret the server response */

		/*
		 *  HTTP/1.1 101 Switching Protocols
		 *  Upgrade: websocket
		 *  Connection: Upgrade
		 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
		 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
		 *  Sec-WebSocket-Protocol: chat
		 */

		/*
		 * we have to take some care here to only take from the
		 * socket bytewise.  The browser may (and has been seen to
		 * in the case that onopen() performs websocket traffic)
		 * coalesce both handshake response and websocket traffic
		 * in one packet, since at that point the connection is
		 * definitively ready from browser pov.
		 */
		len = 1;
		while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE &&
								      len > 0) {
			n = lws_ssl_capable_read(context, wsi, &c, 1);
			lws_latency(context, wsi, "send lws_issue_raw", n, n == 1);
			switch (n) {
			case LWS_SSL_CAPABLE_ERROR:
				goto bail3;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				return 0;
			}

			if (libwebsocket_parse(context, wsi, c)) {
				lwsl_warn("problems parsing header\n");
				goto bail3;
			}
		}

		/*
		 * hs may also be coming in multiple packets, there is a 5-sec
		 * libwebsocket timeout still active here too, so if parsing did
		 * not complete just wait for next packet coming in this state
		 */

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			break;

		/*
		 * otherwise deal with the handshake.  If there's any
		 * packet traffic already arrived we'll trigger poll() again
		 * right away and deal with it that way
		 */

		return lws_client_interpret_server_handshake(context, wsi);

bail3:
		lwsl_info(
			"closing connection at LWS_CONNMODE...SERVER_REPLY\n");
		libwebsocket_close_and_free_session(context, wsi,
						    LWS_CLOSE_STATUS_NOSTATUS);
		return -1;

	case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
		lwsl_ext("LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT\n");
		break;

	case LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD:
		lwsl_ext("LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD\n");
		break;
	default:
		break;
	}

	return 0;
}
Ejemplo n.º 20
0
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
		     struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	size_t n;
	struct allocated_headers *ah;
	char *uri_ptr = NULL;
	int uri_len = 0;
	char content_length_str[32];

	switch (wsi->state) {

	case WSI_STATE_HTTP_BODY:
http_postbody:
		while (len--) {

			if (wsi->u.http.content_length_seen >= wsi->u.http.content_length)
				break;

			wsi->u.http.post_buffer[wsi->u.http.body_index++] = *buf++;
			wsi->u.http.content_length_seen++;
			n = wsi->protocol->rx_buffer_size;
			if (!n)
				n = LWS_MAX_SOCKET_IO_BUF;

			if (wsi->u.http.body_index != n &&
			    wsi->u.http.content_length_seen != wsi->u.http.content_length)
				continue;

			if (wsi->protocol->callback) {
				n = wsi->protocol->callback(
					wsi->protocol->owning_server, wsi,
					    LWS_CALLBACK_HTTP_BODY,
					    wsi->user_space, wsi->u.http.post_buffer,
							wsi->u.http.body_index);
				wsi->u.http.body_index = 0;
				if (n)
					goto bail;
			}

			if (wsi->u.http.content_length_seen == wsi->u.http.content_length) {
				/* he sent the content in time */
				libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
				n = wsi->protocol->callback(
					wsi->protocol->owning_server, wsi,
					    LWS_CALLBACK_HTTP_BODY_COMPLETION,
					    wsi->user_space, NULL, 0);
				wsi->u.http.body_index = 0;
				if (n)
					goto bail;
			}

		}

		/*
		 * we need to spill here so everything is seen in the case
		 * there is no content-length
		 */
		if (wsi->u.http.body_index && wsi->protocol->callback) {
			n = wsi->protocol->callback(
				wsi->protocol->owning_server, wsi,
				    LWS_CALLBACK_HTTP_BODY,
				    wsi->user_space, wsi->u.http.post_buffer,
						wsi->u.http.body_index);
			wsi->u.http.body_index = 0;
			if (n)
				goto bail;
		}
		break;

	case WSI_STATE_HTTP_ISSUING_FILE:
	case WSI_STATE_HTTP:
		wsi->state = WSI_STATE_HTTP_HEADERS;
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		/* fallthru */
	case WSI_STATE_HTTP_HEADERS:

		lwsl_parser("issuing %d bytes to parser\n", (int)len);

#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		while (len--) {
			if (libwebsocket_parse(wsi, *buf++)) {
				lwsl_info("libwebsocket_parse failed\n");
				goto bail_nuke_ah;
			}

			if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
				continue;

			lwsl_parser("libwebsocket_parse sees parsing complete\n");

			wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;

			/* is this websocket protocol or normal http 1.0? */

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
				     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

				/* it's not websocket.... shall we accept it as http? */

				if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
				    !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_warn("Missing URI in HTTP request\n");
					goto bail_nuke_ah;
				}

				if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
				    lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_warn("GET and POST methods?\n");
					goto bail_nuke_ah;
				}

				if (libwebsocket_ensure_user_space(wsi))
					goto bail_nuke_ah;

				if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
					uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
					uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
					lwsl_info("HTTP GET request for '%s'\n",
					    lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));

				}
				if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_info("HTTP POST request for '%s'\n",
					   lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
					uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
					uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
				}

				/*
				 * Hm we still need the headers so the
				 * callback can look at leaders like the URI, but we
				 * need to transition to http union state.... hold a
				 * copy of u.hdr.ah and deallocate afterwards
				 */
				ah = wsi->u.hdr.ah;

				/* union transition */
				memset(&wsi->u, 0, sizeof(wsi->u));
				wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
				wsi->state = WSI_STATE_HTTP;
				wsi->u.http.fd = -1;

				/* expose it at the same offset as u.hdr */
				wsi->u.http.ah = ah;

				/* HTTP header had a content length? */

				wsi->u.http.content_length = 0;
				if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
					wsi->u.http.content_length = 100 * 1024 * 1024;

				if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
					lws_hdr_copy(wsi, content_length_str,
							sizeof(content_length_str) - 1,
									WSI_TOKEN_HTTP_CONTENT_LENGTH);
					wsi->u.http.content_length = atoi(content_length_str);
				}

				if (wsi->u.http.content_length > 0) {
					wsi->u.http.body_index = 0;
					n = wsi->protocol->rx_buffer_size;
					if (!n)
						n = LWS_MAX_SOCKET_IO_BUF;
					wsi->u.http.post_buffer = malloc(n);
					if (!wsi->u.http.post_buffer) {
						lwsl_err("Unable to allocate post buffer\n");
						n = -1;
						goto leave;
					}
				}

				n = 0;
				if (wsi->protocol->callback)
					n = wsi->protocol->callback(context, wsi,
						LWS_CALLBACK_FILTER_HTTP_CONNECTION,
						     wsi->user_space, uri_ptr, uri_len);

				if (!n && wsi->protocol->callback)
					n = wsi->protocol->callback(context, wsi,
					    LWS_CALLBACK_HTTP,
					    wsi->user_space, uri_ptr, uri_len);

leave:
				/* now drop the header info we kept a pointer to */
				if (ah)
					free(ah);
				/* not possible to continue to use past here */
				wsi->u.http.ah = NULL;

				if (n) {
					lwsl_info("LWS_CALLBACK_HTTP closing\n");
					goto bail; /* struct ah ptr already nuked */
				}

				/*
				 * if there is content supposed to be coming,
				 * put a timeout on it having arrived
				 */
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT,
							      AWAITING_TIMEOUT);

				/*
				 * (if callback didn't start sending a file)
				 * deal with anything else as body, whether
				 * there was a content-length or not
				 */

				if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
					wsi->state = WSI_STATE_HTTP_BODY;
				goto http_postbody;
			}

			if (!wsi->protocol)
				lwsl_err("NULL protocol at libwebsocket_read\n");

			/*
			 * It's websocket
			 *
			 * Make sure user side is happy about protocol
			 */

			while (wsi->protocol->callback) {

				if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
					if (wsi->protocol->name == NULL)
						break;
				} else
					if (wsi->protocol->name && strcmp(
						lws_hdr_simple_ptr(wsi,
							WSI_TOKEN_PROTOCOL),
							      wsi->protocol->name) == 0)
						break;

				wsi->protocol++;
			}

			/* we didn't find a protocol he wanted? */

			if (wsi->protocol->callback == NULL) {
				if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
										 NULL) {
					lwsl_info("no protocol -> prot 0 handler\n");
					wsi->protocol = &context->protocols[0];
				} else {
					lwsl_err("Req protocol %s not supported\n",
					   lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL));
					goto bail_nuke_ah;
				}
			}

			/* allocate wsi->user storage */
			if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

			/*
			 * Give the user code a chance to study the request and
			 * have the opportunity to deny it
			 */

			if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
					LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
					wsi->user_space,
				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
				lwsl_warn("User code denied connection\n");
				goto bail_nuke_ah;
			}


			/*
			 * Perform the handshake according to the protocol version the
			 * client announced
			 */

			switch (wsi->ietf_spec_revision) {
			case 13:
				lwsl_parser("lws_parse calling handshake_04\n");
				if (handshake_0405(context, wsi)) {
					lwsl_info("hs0405 has failed the connection\n");
					goto bail_nuke_ah;
				}
				break;

			default:
				lwsl_warn("Unknown client spec version %d\n",
							       wsi->ietf_spec_revision);
				goto bail_nuke_ah;
			}

			/* drop the header info -- no bail_nuke_ah after this */

			if (wsi->u.hdr.ah)
				free(wsi->u.hdr.ah);

			wsi->mode = LWS_CONNMODE_WS_SERVING;

			/* union transition */
			memset(&wsi->u, 0, sizeof(wsi->u));
			wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;

			/*
			 * create the frame buffer for this connection according to the
			 * size mentioned in the protocol definition.  If 0 there, use
			 * a big default for compatibility
			 */

			n = wsi->protocol->rx_buffer_size;
			if (!n)
				n = LWS_MAX_SOCKET_IO_BUF;
			n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
			wsi->u.ws.rx_user_buffer = malloc(n);
			if (!wsi->u.ws.rx_user_buffer) {
				lwsl_err("Out of Mem allocating rx buffer %d\n", n);
				goto bail;
			}
			lwsl_info("Allocating RX buffer %d\n", n);

			if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,  &n, sizeof n)) {
				lwsl_warn("Failed to set SNDBUF to %d", n);
				goto bail;
			}

			lwsl_parser("accepted v%02d connection\n",
							       wsi->ietf_spec_revision);
#endif
		} /* while all chars are handled */
		break;

	case WSI_STATE_AWAITING_CLOSE_ACK:
	case WSI_STATE_ESTABLISHED:
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
			lwsl_info("interpret_incoming_packet has bailed\n");
			goto bail;
		}
#endif
		break;
	default:
		lwsl_err("libwebsocket_read: Unhandled state\n");
		break;
	}

	return 0;

bail_nuke_ah:
	/* drop the header info */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

bail:
	lwsl_info("closing connection at libwebsocket_read bail:\n");

	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);

	return -1;
}
Ejemplo n.º 21
0
char *
libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
		struct libwebsocket *wsi, char *pkt)
{
	char buf[128];
	char hash[20];
	char key_b64[40];
	char *p = pkt;
	int n;
#ifndef LWS_NO_EXTENSIONS
	struct libwebsocket_extension *ext;
	int ext_count = 0;
#endif

	/*
	 * create the random key
	 */

	n = libwebsockets_get_random(context, hash, 16);
	if (n != 16) {
		lwsl_err("Unable to read from random dev %s\n",
						SYSTEM_RANDOM_FILEPATH);
		libwebsocket_close_and_free_session(context, wsi,
					     LWS_CLOSE_STATUS_NOSTATUS);
		return NULL;
	}

	lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));

	/*
	 * 00 example client handshake
	 *
	 * GET /socket.io/websocket HTTP/1.1
	 * Upgrade: WebSocket
	 * Connection: Upgrade
	 * Host: 127.0.0.1:9999
	 * Origin: http://127.0.0.1
	 * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7  92 ^
	 * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
	 * Cookie: socketio=websocket
	 *
	 * (Á®Ä0¶†≥
	 *
	 * 04 example client handshake
	 *
	 * GET /chat HTTP/1.1
	 * Host: server.example.com
	 * Upgrade: websocket
	 * Connection: Upgrade
	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
	 * Sec-WebSocket-Origin: http://example.com
	 * Sec-WebSocket-Protocol: chat, superchat
	 * Sec-WebSocket-Version: 4
	 */

	p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a",
				lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));

	p += sprintf(p,
		"Pragma: no-cache\x0d\x0a""Cache-Control: no-cache\x0d\x0a");

	p += sprintf(p, "Host: %s\x0d\x0a",
			       lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
	p += sprintf(p,
"Upgrade: websocket\x0d\x0a""Connection: Upgrade\x0d\x0a""Sec-WebSocket-Key: ");
	strcpy(p, key_b64);
	p += strlen(key_b64);
	p += sprintf(p, "\x0d\x0a");
	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN))
		p += sprintf(p, "Origin: http://%s\x0d\x0a",
			     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));

	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
		p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));

	/* tell the server what extensions we could support */

	p += sprintf(p, "Sec-WebSocket-Extensions: ");
#ifndef LWS_NO_EXTENSIONS
	ext = context->extensions;
	while (ext && ext->callback) {

		n = lws_ext_callback_for_each_extension_type(context, wsi,
			   LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
							  (char *)ext->name, 0);
		if (n) { /* an extension vetos us */
			lwsl_ext("ext %s vetoed\n", (char *)ext->name);
			ext++;
			continue;
		}

		n = context->protocols[0].callback(context, wsi,
			LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
				wsi->user_space, (char *)ext->name, 0);

		/*
		 * zero return from callback means
		 * go ahead and allow the extension,
		 * it's what we get if the callback is
		 * unhandled
		 */

		if (n) {
			ext++;
			continue;
		}

		/* apply it */

		if (ext_count)
			*p++ = ',';
		p += sprintf(p, "%s", ext->name);
		ext_count++;

		ext++;
	}
#endif
	p += sprintf(p, "\x0d\x0a");

	if (wsi->ietf_spec_revision)
		p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
					       wsi->ietf_spec_revision);

	/* give userland a chance to append, eg, cookies */

	context->protocols[0].callback(context, wsi,
		LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
		NULL, &p, (pkt + sizeof(context->service_buffer)) - p - 12);

	p += sprintf(p, "\x0d\x0a");

	/* prepare the expected server accept response */

	key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
	n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);

	libwebsockets_SHA1((unsigned char *)buf, n, (unsigned char *)hash);

	lws_b64_encode_string(hash, 20,
			wsi->u.hdr.ah->initial_handshake_hash_base64,
			  sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));

	return p;
}
Ejemplo n.º 22
0
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
		     struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	size_t n;
	int body_chunk_len;
	unsigned char *last_char;

	switch (wsi->state) {
#ifdef LWS_USE_HTTP2
	case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
	case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
	case WSI_STATE_HTTP2_ESTABLISHED:
		n = 0;
		while (n < len) {
			/*
			 * we were accepting input but now we stopped doing so
			 */
			if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
				lws_rxflow_cache(wsi, buf, n, len);

				return 1;
			}

			/* account for what we're using in rxflow buffer */
			if (wsi->rxflow_buffer)
				wsi->rxflow_pos++;
			if (lws_http2_parser(context, wsi, buf[n++]))
				goto bail;
		}
		break;
#endif
http_new:
	case WSI_STATE_HTTP:
		wsi->hdr_parsing_completed = 0;
		/* fallthru */
	case WSI_STATE_HTTP_ISSUING_FILE:
		wsi->state = WSI_STATE_HTTP_HEADERS;
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		/* fallthru */
	case WSI_STATE_HTTP_HEADERS:
		lwsl_parser("issuing %d bytes to parser\n", (int)len);

		if (lws_handshake_client(wsi, &buf, len))
			goto bail;

		last_char = buf;
		if (lws_handshake_server(context, wsi, &buf, len))
			/* Handshake indicates this session is done. */
			goto bail;

		/* It's possible that we've exhausted our data already, but
		 * lws_handshake_server doesn't update len for us. Figure out how
		 * much was read, so that we can proceed appropriately: */
		len -= (buf - last_char);

		if (!wsi->hdr_parsing_completed)
			/* More header content on the way */
			goto read_ok;

		switch (wsi->state) {
			case WSI_STATE_HTTP:
			case WSI_STATE_HTTP_HEADERS:
				goto http_complete;
			case WSI_STATE_HTTP_ISSUING_FILE:
				goto read_ok;
			case WSI_STATE_HTTP_BODY:
				wsi->u.http.content_remain = wsi->u.http.content_length;
				if (!wsi->u.http.content_remain) {
					/* there is no POST content */
					libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
					if (wsi->protocol->callback) {
						n = wsi->protocol->callback(
							wsi->protocol->owning_server, wsi,
							LWS_CALLBACK_HTTP_BODY_COMPLETION,
							wsi->user_space, NULL, 0);
						if (n)
							goto bail;
					}
					goto http_complete;
				}
				goto http_postbody;
			default:
				break;
		}
		break;

	case WSI_STATE_HTTP_BODY:
http_postbody:
		while (len && wsi->u.http.content_remain) {
			/* Copy as much as possible, up to the limit of:
			 * what we have in the read buffer (len)
			 * remaining portion of the POST body (content_remain)
			 */
			body_chunk_len = min(wsi->u.http.content_remain,len);
			wsi->u.http.content_remain -= body_chunk_len;
			len -= body_chunk_len;

			if (wsi->protocol->callback) {
				n = wsi->protocol->callback(
					wsi->protocol->owning_server, wsi,
					LWS_CALLBACK_HTTP_BODY, wsi->user_space,
					buf, body_chunk_len);
				if (n)
					goto bail;
			}
			buf += body_chunk_len;

			if (!wsi->u.http.content_remain)  {
				/* he sent the content in time */
				libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
				if (wsi->protocol->callback) {
					n = wsi->protocol->callback(
						wsi->protocol->owning_server, wsi,
						LWS_CALLBACK_HTTP_BODY_COMPLETION,
						wsi->user_space, NULL, 0);
					if (n)
						goto bail;
				}
				goto http_complete;
			} else
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT,
					AWAITING_TIMEOUT);
		}
		break;

	case WSI_STATE_ESTABLISHED:
	case WSI_STATE_AWAITING_CLOSE_ACK:
		if (lws_handshake_client(wsi, &buf, len))
			goto bail;
		switch (wsi->mode) {
		case LWS_CONNMODE_WS_SERVING:

			if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
				lwsl_info("interpret_incoming_packet has bailed\n");
				goto bail;
			}
			break;
		}
		break;
	default:
		lwsl_err("libwebsocket_read: Unhandled state\n");
		break;
	}

read_ok:
	/* Nothing more to do for now. */
	lwsl_debug("libwebsocket_read: read_ok\n");

	return 0;

http_complete:
	lwsl_debug("libwebsocket_read: http_complete\n");

#ifndef LWS_NO_SERVER
	/* Did the client want to keep the HTTP connection going? */
	if (lws_http_transaction_completed(wsi))
		goto bail;
#endif
	/* If we have more data, loop back around: */
	if (len)
		goto http_new;

	return 0;

bail:
	lwsl_debug("closing connection at libwebsocket_read bail:\n");

	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);

	return -1;
}
Ejemplo n.º 23
0
int
libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi,
                        unsigned char * buf, size_t len)
{
    size_t n;

    switch (wsi->state) {
    case WSI_STATE_HTTP:
        wsi->state = WSI_STATE_HTTP_HEADERS;
        wsi->parser_state = WSI_TOKEN_NAME_PART;
        /* fallthru */
    case WSI_STATE_HTTP_HEADERS:

        debug("issuing %d bytes to parser\n", (int)len);
#ifdef DEBUG
        fwrite(buf, 1, len, stderr);
#endif

        switch (wsi->mode) {
        case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
        case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
        case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
        case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
        case LWS_CONNMODE_WS_CLIENT:
            for (n = 0; n < len; n++)
                libwebsocket_client_rx_sm(wsi, *buf++);

            return 0;
        default:
            break;
        }

        /* LWS_CONNMODE_WS_SERVING */

        for (n = 0; n < len; n++)
            libwebsocket_parse(wsi, *buf++);

        if (wsi->parser_state != WSI_PARSING_COMPLETE)
            break;

        fprintf(stderr, "seem to be serving, mode is %d\n", wsi->mode);

        fprintf(stderr, "libwebsocket_parse sees parsing complete\n");

        /* is this websocket protocol or normal http 1.0? */

        if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
                 !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
            if (wsi->protocol->callback)
                (wsi->protocol->callback)(context, wsi,
                   LWS_CALLBACK_HTTP, wsi->user_space,
                   wsi->utf8_token[WSI_TOKEN_GET_URI].token, 0);
            wsi->state = WSI_STATE_HTTP;
            return 0;
        }

        if (!wsi->protocol) {
            fprintf(stderr, "NULL protocol coming on libwebsocket_read\n");
        }

        /*
         * It's websocket
         *
         * Make sure user side is happy about protocol
         */

        while (wsi->protocol->callback) {

            if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
                if (wsi->protocol->name == NULL)
                    break;
            } else
                if (strcmp(
                     wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
                              wsi->protocol->name) == 0)
                    break;

            wsi->protocol++;
        }

        /* we didn't find a protocol he wanted? */

        if (wsi->protocol->callback == NULL) {
            if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
                fprintf(stderr, "[no protocol] "
                    "not supported (use NULL .name)\n");
            else
                fprintf(stderr, "Requested protocol %s "
                        "not supported\n",
                     wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
            goto bail;
        }

        /*
         * find out which spec version the client is using
         * if this header is not given, we default to 00 (aka 76)
         */

        if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
            wsi->ietf_spec_revision =
                 atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);

        /*
         * Give the user code a chance to study the request and
         * have the opportunity to deny it
         */

        if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
                LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
                        &wsi->utf8_token[0], NULL, 0)) {
            fprintf(stderr, "User code denied connection\n");
            goto bail;
        }


        /*
         * Perform the handshake according to the protocol version the
         * client announced
         */

        switch (wsi->ietf_spec_revision) {
        case 0: /* applies to 76 and 00 */
            wsi->xor_mask = xor_no_mask;
            if (handshake_00(context, wsi))
                goto bail;
            break;
        case 4: /* 04 */
            wsi->xor_mask = xor_mask_04;
            debug("libwebsocket_parse calling handshake_04\n");
            if (handshake_0405(context, wsi))
                goto bail;
            break;
        case 5:
        case 6:
        case 7:
        case 8:
        case 13:
            wsi->xor_mask = xor_mask_05;
            debug("libwebsocket_parse calling handshake_04\n");
            if (handshake_0405(context, wsi))
                goto bail;
            break;

        default:
            fprintf(stderr, "Unknown client spec version %d\n",
                               wsi->ietf_spec_revision);
            goto bail;
        }

        fprintf(stderr, "accepted v%02d connection\n",
                               wsi->ietf_spec_revision);

        break;

    case WSI_STATE_AWAITING_CLOSE_ACK:
    case WSI_STATE_ESTABLISHED:
        switch (wsi->mode) {
        case LWS_CONNMODE_WS_CLIENT:
            for (n = 0; n < len; n++)
                if (libwebsocket_client_rx_sm(wsi, *buf++) < 0)
                    goto bail;

            return 0;
        default:
            break;
        }

        /* LWS_CONNMODE_WS_SERVING */

        if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
            goto bail;

        break;
    default:
        break;
    }

    return 0;

bail:
    libwebsocket_close_and_free_session(context, wsi,
                             LWS_CLOSE_STATUS_NOSTATUS);

    return -1;
}
Ejemplo n.º 24
0
LWS_VISIBLE int
lws_server_socket_service_ssl(struct libwebsocket_context *context,
		struct libwebsocket **pwsi, struct libwebsocket *new_wsi,
			int accept_fd, struct libwebsocket_pollfd *pollfd)
{
	int n, m;
	struct libwebsocket *wsi = *pwsi;
#ifndef USE_CYASSL
	BIO *bio;
#endif

	if (!LWS_SSL_ENABLED(context))
		return 0;

	switch (wsi->mode) {
	case LWS_CONNMODE_SERVER_LISTENER:

		if (!new_wsi) {
			lwsl_err("no new_wsi\n");
			return 0;
		}

		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
		SSL_set_mode(new_wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
		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
		 */

		*pwsi = new_wsi;
		wsi = *pwsi;
		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:

		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			goto fail;

		lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);

		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)
			goto accepted;

		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) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
				goto fail;

			lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ);

			lwsl_info("SSL_ERROR_WANT_READ\n");
			break;
		}
		if (m == SSL_ERROR_WANT_WRITE) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
				goto fail;

			lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE);
			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... give him some time to negotiate */
		libwebsocket_set_timeout(wsi,
			PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
							AWAITING_TIMEOUT);

		wsi->mode = LWS_CONNMODE_HTTP_SERVING;

		lws_http2_configure_if_upgraded(wsi);

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

	return 0;
	
fail:
	return 1;
}