Beispiel #1
0
int
lws_ssl_client_connect1(struct lws *wsi)
{
	struct lws_context *context = wsi->context;
	int n = 0;

	lws_latency_pre(context, wsi);
	n = lws_tls_client_connect(wsi);
	lws_latency(context, wsi, "SSL_connect hs", n, n > 0);

	switch (n) {
	case LWS_SSL_CAPABLE_ERROR:
		return -1;
	case LWS_SSL_CAPABLE_DONE:
		return 1; /* connected */
	case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
		lws_callback_on_writable(wsi);
		/* fallthru */
	case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
		lwsi_set_state(wsi, LRS_WAITING_SSL);
		break;
	case LWS_SSL_CAPABLE_MORE_SERVICE:
		break;
	}

	return 0; /* retry */
}
Beispiel #2
0
int
lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
{
	int n = 0;

	if (lwsi_state(wsi) == LRS_WAITING_SSL) {
		lws_latency_pre(wsi->context, wsi);

		n = lws_tls_client_connect(wsi);
		lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
		lws_latency(wsi->context, wsi,
			    "SSL_connect LRS_WAITING_SSL", n, n > 0);

		switch (n) {
		case LWS_SSL_CAPABLE_ERROR:
			lws_snprintf(errbuf, len, "client connect failed");
			return -1;
		case LWS_SSL_CAPABLE_DONE:
			break; /* connected */
		case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
			lws_callback_on_writable(wsi);
			/* fallthru */
		case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
			lwsi_set_state(wsi, LRS_WAITING_SSL);
			/* fallthru */
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			return 0;
		}
	}

	if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len))
		return -1;

	return 1;
}
Beispiel #3
0
int
lws_ssl_client_connect1(struct lws *wsi)
{
	struct lws_context *context = wsi->context;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	char *sb = p;
	int n;

	lws_latency_pre(context, wsi);
	n = SSL_connect(wsi->ssl);
	lws_latency(context, wsi,
	  "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);

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

		if (n == SSL_ERROR_WANT_READ)
			goto some_wait;

		if (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("%s: WANT_WRITE... retrying\n", __func__);
			lws_callback_on_writable(wsi);
some_wait:
			wsi->mode = LWSCM_WSCL_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, sb));
			return 0;
		}
	}

	return 1;
}
Beispiel #4
0
LWS_VISIBLE void
lws_get_peer_addresses(struct lws_context *context, struct lws *wsi,
               lws_sockfd_type fd, char *name, int name_len,
               char *rip, int rip_len)
{
#if LWS_POSIX
    socklen_t len;
#ifdef LWS_USE_IPV6
    struct sockaddr_in6 sin6;
#endif
    struct sockaddr_in sin4;
    int ret = -1;
    void *p;

    rip[0] = '\0';
    name[0] = '\0';

    lws_latency_pre(context, wsi);

#ifdef LWS_USE_IPV6
    if (LWS_IPV6_ENABLED(context)) {
        len = sizeof(sin6);
        p = &sin6;
    } else
#endif
    {
        len = sizeof(sin4);
        p = &sin4;
    }

    if (getpeername(fd, p, &len) < 0) {
        lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
        goto bail;
    }
    
    ret = lws_get_addresses(context, p, name, name_len, rip, rip_len);

bail:
    lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1);
#else
    (void)context;
    (void)wsi;
    (void)fd;
    (void)name;
    (void)name_len;
    (void)rip;
    (void)rip_len;
#endif
}
Beispiel #5
0
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
	struct lws_context *context = wsi->context;
	struct lws_vhost *vh;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	int n;
        char buf[256];

        (void)buf;

	if (!LWS_SSL_ENABLED(wsi->vhost))
		return 0;

	switch (lwsi_state(wsi)) {
	case LRS_SSL_INIT:

		if (wsi->tls.ssl)
			lwsl_err("%s: leaking ssl\n", __func__);
		if (accept_fd == LWS_SOCK_INVALID)
			assert(0);
		if (context->simultaneous_ssl_restriction &&
		    context->simultaneous_ssl >=
		    	    context->simultaneous_ssl_restriction) {
			lwsl_notice("unable to deal with SSL connection\n");
			return 1;
		}

		if (lws_tls_server_new_nonblocking(wsi, accept_fd)) {
			if (accept_fd != LWS_SOCK_INVALID)
				compatible_close(accept_fd);
			goto fail;
		}

		if (context->simultaneous_ssl_restriction &&
		    ++context->simultaneous_ssl ==
				    context->simultaneous_ssl_restriction)
			/* that was the last allowed SSL connection */
			lws_gate_accepts(context, 0);

#if defined(LWS_WITH_STATS)
		context->updated = 1;
#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
		 */
		lwsi_set_state(wsi, LRS_SSL_ACK_PENDING);

		lws_pt_lock(pt, __func__);
		if (__insert_wsi_socket_into_fds(context, wsi)) {
			lwsl_err("%s: failed to insert into fds\n", __func__);
			goto fail;
		}
		lws_pt_unlock(pt);

		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
				context->timeout_secs);

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

		/* fallthru */

	case LRS_SSL_ACK_PENDING:

		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
			goto fail;
		}

		lws_latency_pre(context, wsi);

		if (wsi->vhost->tls.allow_non_ssl_on_ssl_port) {

			n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
				 context->pt_serv_buf_size, 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 (n >= 1 && pt->serv_buf[0] >= ' ') {
				/*
				* TLS content-type for Handshake is 0x16, and
				* for ChangeCipherSpec Record, it's 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->tls.use_ssl = 0;

				lws_tls_server_abort_connection(wsi);
				/*
				 * care... this creates wsi with no ssl
				 * when ssl is enabled and normally
				 * mandatory
				 */
				wsi->tls.ssl = NULL;
				if (lws_check_opt(context->options,
				    LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
					wsi->tls.redirect_to_https = 1;
				lwsl_debug("accepted as non-ssl\n");
				goto accepted;
			}
			if (!n) {
				/*
				 * connection is gone, fail out
				 */
				lwsl_debug("PEEKed 0\n");
				goto fail;
			}
			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
				/*
				 * well, we get no way to know ssl or not
				 * so go around again waiting for something
				 * to come and give us a hint, or timeout the
				 * connection.
				 */
				if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
					lwsl_info("%s: change_pollfd failed\n",
						  __func__);
					return -1;
				}

				lwsl_info("SSL_ERROR_WANT_READ\n");
				return 0;
			}
		}

		/* normal SSL connection processing path */

#if defined(LWS_WITH_STATS)
		/* only set this the first time around */
		if (!wsi->accept_start_us)
			wsi->accept_start_us = lws_time_in_microseconds();
#endif
		errno = 0;
		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
		n = lws_tls_server_accept(wsi);
		lws_latency(context, wsi,
			"SSL_accept LRS_SSL_ACK_PENDING\n", n, n == 1);
		lwsl_info("SSL_accept says %d\n", n);
		switch (n) {
		case LWS_SSL_CAPABLE_DONE:
			break;
		case LWS_SSL_CAPABLE_ERROR:
			lws_stats_atomic_bump(wsi->context, pt,
					      LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
	                lwsl_info("SSL_accept failed socket %u: %d\n",
	                		wsi->desc.sockfd, n);
			wsi->socket_is_permanently_unusable = 1;
			goto fail;

		default: /* MORE_SERVICE */
			return 0;
		}

		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
#if defined(LWS_WITH_STATS)
		if (wsi->accept_start_us)
			lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
				      lws_time_in_microseconds() - wsi->accept_start_us);
		wsi->accept_start_us = lws_time_in_microseconds();
#endif

accepted:

		/* adapt our vhost to match the SNI SSL_CTX that was chosen */
		vh = context->vhost_list;
		while (vh) {
			if (!vh->being_destroyed && wsi->tls.ssl &&
			    vh->tls.ssl_ctx == lws_tls_ctx_from_wsi(wsi)) {
				lwsl_info("setting wsi to vh %s\n", vh->name);
				lws_vhost_bind_wsi(vh, wsi);
				break;
			}
			vh = vh->vhost_next;
		}

		/* OK, we are accepted... give him some time to negotiate */
		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				context->timeout_secs);

		lwsi_set_state(wsi, LRS_ESTABLISHED);
		if (lws_tls_server_conn_alpn(wsi))
			goto fail;
		lwsl_debug("accepted new SSL conn\n");
		break;

	default:
		break;
	}

	return 0;

fail:
	return 1;
}
int
lws_ssl_client_connect2(struct lws *wsi)
{
	struct lws_context *context = wsi->context;
#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	char *sb = p;
#endif
#endif
	int n = 0;

	if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
		lws_latency_pre(context, wsi);
#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
		n = SSL_connect(wsi->ssl);
#endif
#endif
		lws_latency(context, wsi,
			    "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);

		if (n < 0) {
			n = lws_ssl_get_error(wsi, n);

			if (n == SSL_ERROR_WANT_READ) {
				wsi->mode = LWSCM_WSCL_WAITING_SSL;

				return 0; /* no error */
			}

			if (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_WRITE... retrying\n");
				lws_callback_on_writable(wsi);

				wsi->mode = LWSCM_WSCL_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
			 */
#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
			n = ERR_get_error();
			if (n != SSL_ERROR_NONE) {
				lwsl_err("SSL connect error %lu: %s\n",
					 n, ERR_error_string(n, sb));
				return -1;
			}
#endif
#endif
		}
	}

#if defined(LWS_USE_POLARSSL)
#else
#if defined(LWS_USE_MBEDTLS)
#else
#ifndef USE_WOLFSSL
	/*
	 * See comment above about wolfSSL 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 & LCCSCF_ALLOW_SELFSIGNED) {
			lwsl_notice("accepting self-signed certificate\n");
		} else {
			lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
				 n, ERR_error_string(n, sb));
			lws_ssl_elaborate_error();
			return -1;
		}
	}
#endif /* USE_WOLFSSL */
#endif
#endif

	return 1;
}
LWS_VISIBLE void
libwebsockets_get_peer_addresses(struct libwebsocket_context *context,
	struct libwebsocket *wsi, int fd, char *name, int name_len,
					char *rip, int rip_len)
{
	socklen_t len;
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 sin6;
#endif
	struct sockaddr_in sin4;
	struct hostent *host;
	struct hostent *host1;
	char ip[128];
	unsigned char *p;
	int n;
#ifdef AF_LOCAL
	struct sockaddr_un *un;
#endif
	int ret = -1;

	rip[0] = '\0';
	name[0] = '\0';

	lws_latency_pre(context, wsi);

#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {

		len = sizeof(sin6);
		if (getpeername(fd, (struct sockaddr *) &sin6, &len) < 0) {
			lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
			goto bail;
		}

		if (!lws_plat_inet_ntop(AF_INET6, &sin6.sin6_addr, rip, rip_len)) {
			lwsl_err("inet_ntop", strerror(LWS_ERRNO));
			goto bail;
		}

		// Strip off the IPv4 to IPv6 header if one exists
		if (strncmp(rip, "::ffff:", 7) == 0)
			memmove(rip, rip + 7, strlen(rip) - 6);

		getnameinfo((struct sockaddr *)&sin6,
				sizeof(struct sockaddr_in6), name,
							name_len, NULL, 0, 0);

	} else
#endif
	{
		len = sizeof(sin4);
		if (getpeername(fd, (struct sockaddr *) &sin4, &len) < 0) {
			lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
			goto bail;
		}
		host = gethostbyaddr((char *) &sin4.sin_addr,
						sizeof(sin4.sin_addr), AF_INET);
		if (host == NULL) {
			lwsl_warn("gethostbyaddr: %s\n", strerror(LWS_ERRNO));
			goto bail;
		}

		strncpy(name, host->h_name, name_len);
		name[name_len - 1] = '\0';

		host1 = gethostbyname(host->h_name);
		if (host1 == NULL)
			goto bail;
		p = (unsigned char *)host1;
		n = 0;
		while (p != NULL) {
			p = (unsigned char *)host1->h_addr_list[n++];
			if (p == NULL)
				continue;
			if ((host1->h_addrtype != AF_INET)
#ifdef AF_LOCAL
				&& (host1->h_addrtype != AF_LOCAL)
#endif
				)
				continue;

			if (host1->h_addrtype == AF_INET)
				sprintf(ip, "%u.%u.%u.%u",
						p[0], p[1], p[2], p[3]);
#ifdef AF_LOCAL
			else {
				un = (struct sockaddr_un *)p;
				strncpy(ip, un->sun_path, sizeof(ip) - 1);
				ip[sizeof(ip) - 1] = '\0';
			}
#endif
			p = NULL;
			strncpy(rip, ip, rip_len);
			rip[rip_len - 1] = '\0';
		}
	}

	ret = 0;
bail:
	lws_latency(context, wsi, "libwebsockets_get_peer_addresses", ret, 1);
}
Beispiel #8
0
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
	struct lws_context *context = wsi->context;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	int n, m;
#if !defined(USE_WOLFSSL) && !defined(LWS_USE_POLARSSL) && !defined(LWS_USE_MBEDTLS)
	BIO *bio;
#endif

	if (!LWS_SSL_ENABLED(wsi->vhost))
		return 0;

	switch (wsi->mode) {
	case LWSCM_SSL_INIT:

		if (wsi->ssl)
			lwsl_err("%s: leaking ssl\n", __func__);
		if (accept_fd == LWS_SOCK_INVALID)
			assert(0);

#if defined(LWS_USE_POLARSSL)
	{
		ssl_session *ssn;
		int rc;

		wsi->ssl = lws_zalloc(sizeof(ssl_context));
		ssn = lws_zalloc(sizeof(ssl_session));

		rc = ssl_init(wsi->ssl);
		if (rc) {
			lwsl_err("ssl_init failed\n");
			goto fail;
		}

		ssl_set_endpoint(wsi->ssl, SSL_IS_SERVER);
		ssl_set_authmode(wsi->ssl, SSL_VERIFY_OPTIONAL);
		ssl_set_rng(wsi->ssl, urandom_bytes, NULL);
		ssl_set_dbg(wsi->ssl, pssl_debug, NULL);
		ssl_set_bio(wsi->ssl, net_recv, &wsi->sock, net_send, &wsi->sock);

		ssl_set_ciphersuites(wsi->ssl, ciphers);

		ssl_set_session(wsi->ssl, ssn);

		ssl_set_ca_chain(wsi->ssl, &wsi->vhost->ssl_ctx->ca,
				 NULL, NULL);

		ssl_set_own_cert_rsa(wsi->ssl,
				&wsi->vhost->ssl_ctx->certificate,
				&wsi->vhost->ssl_ctx->key);

//		ssl_set_dh_param(wsi->ssl, my_dhm_P, my_dhm_G);

		lwsl_err("%s: polarssl init done\n", __func__);
	}
#else
#if defined(LWS_USE_MBEDTLS)
#else
		wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
		if (wsi->ssl == NULL) {
			lwsl_err("SSL_new failed: %s\n",
				 ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
			lws_decode_ssl_error();
			if (accept_fd != LWS_SOCK_INVALID)
				compatible_close(accept_fd);
			goto fail;
		}

		SSL_set_ex_data(wsi->ssl,
			openssl_websocket_private_data_index, wsi->vhost);

		SSL_set_fd(wsi->ssl, accept_fd);
#endif
#endif

#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
		CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
		wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if defined(LWS_USE_POLARSSL)

#else
#if defined(LWS_USE_MBEDTLS)
#else
		SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
		bio = SSL_get_rbio(wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
		bio = SSL_get_wbio(wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
#endif
#endif
#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->mode = LWSCM_SSL_ACK_PENDING;
		if (insert_wsi_socket_into_fds(context, wsi)) {
			lwsl_err("%s: failed to insert into fds\n", __func__);
			goto fail;
		}

		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
				context->timeout_secs);

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

		/* fallthru */

	case LWSCM_SSL_ACK_PENDING:

		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
			goto fail;
		}

		lws_latency_pre(context, wsi);

		n = recv(wsi->sock, (char *)pt->serv_buf, context->pt_serv_buf_size,
			 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 (wsi->vhost->allow_non_ssl_on_ssl_port) {
			if (n >= 1 && pt->serv_buf[0] >= ' ') {
				/*
				* TLS content-type for Handshake is 0x16, and
				* for ChangeCipherSpec Record, it's 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;
#if defined(LWS_USE_POLARSSL)
				ssl_close_notify(wsi->ssl);
				ssl_free(wsi->ssl);
#else
#if defined(LWS_USE_MBEDTLS)
#else
				SSL_shutdown(wsi->ssl);
				SSL_free(wsi->ssl);
#endif
#endif
				wsi->ssl = NULL;
				if (lws_check_opt(context->options,
				    LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
					wsi->redirect_to_https = 1;
				goto accepted;
			}
			if (!n) /*
				 * connection is gone, or nothing to read
				 * if it's gone, we will timeout on
				 * PENDING_TIMEOUT_SSL_ACCEPT
				 */
				break;
			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
				/*
				 * well, we get no way to know ssl or not
				 * so go around again waiting for something
				 * to come and give us a hint, or timeout the
				 * connection.
				 */
				m = SSL_ERROR_WANT_READ;
				goto go_again;
			}
		}

		/* normal SSL connection processing path */
#if defined(LWS_USE_POLARSSL)
		n = ssl_handshake(wsi->ssl);
#else
#if defined(LWS_USE_MBEDTLS)
#else
		n = SSL_accept(wsi->ssl);
#endif
#endif
		lws_latency(context, wsi,
			"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);

		if (n == 1)
			goto accepted;

		m = lws_ssl_get_error(wsi, n);
		lwsl_debug("SSL_accept failed %d / %s\n",
			   m, ERR_error_string(m, NULL));
go_again:
		if (m == SSL_ERROR_WANT_READ) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
				lwsl_err("%s: WANT_READ change_pollfd failed\n", __func__);
				goto fail;
			}

			lwsl_info("SSL_ERROR_WANT_READ\n");
			break;
		}
		if (m == SSL_ERROR_WANT_WRITE) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
				lwsl_err("%s: WANT_WRITE change_pollfd failed\n", __func__);
				goto fail;
			}

			break;
		}
		lwsl_err("SSL_accept failed skt %u: %s\n",
			   wsi->sock, ERR_error_string(m, NULL));

		lws_ssl_elaborate_error();
		goto fail;

accepted:
		/* OK, we are accepted... give him some time to negotiate */
		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				context->timeout_secs);

		wsi->mode = LWSCM_HTTP_SERVING;

		lws_http2_configure_if_upgraded(wsi);

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

	return 0;

fail:
	return 1;
}
Beispiel #9
0
LWS_VISIBLE
int lws_server_socket_service(struct lws_context *context,
			      struct lws *wsi, struct lws_pollfd *pollfd)
{
	lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
#if LWS_POSIX
	struct sockaddr_in cli_addr;
	socklen_t clilen;
#endif
	struct lws *new_wsi = NULL;
	int n, len;

	switch (wsi->mode) {

	case LWSCM_HTTP_SERVING:
	case LWSCM_HTTP_SERVING_ACCEPTED:
	case LWSCM_HTTP2_SERVING:

		/* handle http headers coming in */

		/* pending truncated sends have uber priority */

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

			if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
					  wsi->trunc_len) < 0)
				goto fail;
			/*
			 * 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 & pollfd->events && LWS_POLLIN))
			goto try_pollout;

		len = lws_ssl_capable_read(wsi, context->serv_buf,
					   sizeof(context->serv_buf));
		lwsl_debug("%s: read %d\r\n", __func__, len);
		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)
				lws_free_header_table(wsi);
			/* fallthru */
		case LWS_SSL_CAPABLE_ERROR:
			goto fail;
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			goto try_pollout;
		}

		/* just ignore incoming if waiting for close */
		if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
			/*
			 * hm this may want to send
			 * (via HTTP callback for example)
			 */
			n = lws_read(wsi, context->serv_buf, len);
			if (n < 0) /* we closed wsi */
				return 1;

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

try_pollout:
		/* 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(wsi, LWS_EV_STOP | LWS_EV_WRITE);

		if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
			n = user_callback_handle_rxflow(
					wsi->protocol->callback,
					wsi, LWS_CALLBACK_HTTP_WRITEABLE,
					wsi->user_space, NULL, 0);
			if (n < 0)
				goto fail;
			break;
		}

		/* >0 == completion, <0 == error */
		n = lws_serve_http_file_fragment(wsi);
		if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi)))
			goto fail;
		break;

	case LWSCM_SERVER_LISTENER:

#if LWS_POSIX
		/* 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 LWSCM_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);
#else
		/* not very beautiful... */
		accept_fd = (lws_sockfd_type)pollfd;
#endif
		/*
		 * 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)(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 = lws_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 */
		lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				AWAITING_TIMEOUT);

#if LWS_POSIX == 0
		mbed3_tcp_stream_accept(accept_fd, new_wsi);
#endif

		/*
		 * 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)(new_wsi,
			LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
			NULL, NULL, 0);

		lws_libev_accept(new_wsi, accept_fd);

		if (!LWS_SSL_ENABLED(context)) {
#if LWS_POSIX
			lwsl_debug("accepted new conn  port %u on fd=%d\n",
					  ntohs(cli_addr.sin_port), accept_fd);
#endif
			if (insert_wsi_socket_into_fds(context, new_wsi))
				goto fail;
		}
		break;

	default:
		break;
	}

	if (!lws_server_socket_service_ssl(&wsi, new_wsi, accept_fd, pollfd))
		return 0;

fail:
	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);

	return 1;
}
Beispiel #10
0
int lws_context_init_server(struct lws_context_creation_info *info,
			    struct lws_context *context)
{
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 serv_addr6;
#endif
#if LWS_POSIX
	struct sockaddr_in serv_addr4;
	socklen_t len = sizeof(struct sockaddr);
	struct sockaddr_in sin;
	struct sockaddr *v;
	int n, opt = 1;
#endif
	lws_sockfd_type sockfd;
	struct lws *wsi;

	/* set up our external listening socket we serve on */

	if (info->port == CONTEXT_PORT_NO_LISTEN)
		return 0;

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context))
		sockfd = socket(AF_INET6, SOCK_STREAM, 0);
	else
#endif
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		
	if (sockfd == -1) {
#else
	sockfd = mbed3_create_tcp_stream_socket();
	if (!lws_sockfd_valid(sockfd)) {
#endif
		lwsl_err("ERROR opening socket\n");
		return 1;
	}

#if LWS_POSIX
	/*
	 * allow us to restart even if old sockets in TIME_WAIT
	 */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (const void *)&opt, sizeof(opt)) < 0) {
		compatible_close(sockfd);
		return 1;
	}
#endif
	lws_plat_set_socket_options(context, sockfd);

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		v = (struct sockaddr *)&serv_addr6;
		n = sizeof(struct sockaddr_in6);
		bzero((char *) &serv_addr6, sizeof(serv_addr6));
		serv_addr6.sin6_addr = in6addr_any;
		serv_addr6.sin6_family = AF_INET6;
		serv_addr6.sin6_port = htons(info->port);
	} else
#endif
	{
		v = (struct sockaddr *)&serv_addr4;
		n = sizeof(serv_addr4);
		bzero((char *) &serv_addr4, sizeof(serv_addr4));
		serv_addr4.sin_addr.s_addr = INADDR_ANY;
		serv_addr4.sin_family = AF_INET;

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

		serv_addr4.sin_port = htons(info->port);
	} /* ipv4 */

	n = bind(sockfd, v, n);
	if (n < 0) {
		lwsl_err("ERROR on binding to port %d (%d %d)\n",
					      info->port, n, LWS_ERRNO);
		compatible_close(sockfd);
		return 1;
	}

	if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1)
		lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
	else
		info->port = ntohs(sin.sin_port);
#endif

	context->listen_port = info->port;

	wsi = lws_zalloc(sizeof(struct lws));
	if (wsi == NULL) {
		lwsl_err("Out of mem\n");
		compatible_close(sockfd);
		return 1;
	}
	wsi->sock = sockfd;
	wsi->mode = LWS_CONNMODE_SERVER_LISTENER;
	wsi->protocol = context->protocols;

	if (insert_wsi_socket_into_fds(context, wsi)) {
		compatible_close(sockfd);
		return 1;
	}

	context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO;
	context->listen_service_count = 0;
	context->listen_service_fd = sockfd;

#if LWS_POSIX
	listen(sockfd, LWS_SOMAXCONN);
#else
	mbed3_tcp_stream_bind(sockfd, info->port, wsi);
#endif
	lwsl_notice(" Listening on port %d\n", info->port);

	return 0;
}

int
_lws_rx_flow_control(struct lws *wsi)
{
	struct lws_context *context = wsi->protocol->owning_server;

	/* there is no pending change */
	if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
		return 0;

	/* stuff is still buffered, not ready to really accept new input */
	if (wsi->rxflow_buffer) {
		/* get ourselves called back to deal with stashed buffer */
		lws_callback_on_writable(context, wsi);
		return 0;
	}

	/* pending is cleared, we can change rxflow state */

	wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;

	lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
			      wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);

	/* adjust the pollfd for this wsi */

	if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
		if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
			lwsl_info("%s: fail\n", __func__);
			return -1;
		}
	} else
		if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
			return -1;

	return 0;
}

int lws_http_action(struct lws_context *context, struct lws *wsi)
{
	enum http_connection_type connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	enum http_version request_version;
	char content_length_str[32];
	unsigned int n, count = 0;
	char http_version_str[10];
	char http_conn_str[20];
	int http_version_len;
	char *uri_ptr = NULL;
	int uri_len = 0;

	static const unsigned char methods[] = {
		WSI_TOKEN_GET_URI,
		WSI_TOKEN_POST_URI,
		WSI_TOKEN_OPTIONS_URI,
		WSI_TOKEN_PUT_URI,
		WSI_TOKEN_PATCH_URI,
		WSI_TOKEN_DELETE_URI,
#ifdef LWS_USE_HTTP2
		WSI_TOKEN_HTTP_COLON_PATH,
#endif
	};
#ifdef _DEBUG
	static const char * const method_names[] = {
		"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
#ifdef LWS_USE_HTTP2
		":path",
#endif
	};
#endif
	
	/* it's not websocket.... shall we accept it as http? */

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n]))
			count++;
	if (!count) {
		lwsl_warn("Missing URI in HTTP request\n");
		goto bail_nuke_ah;
	}

	if (count != 1) {
		lwsl_warn("multiple methods?\n");
		goto bail_nuke_ah;
	}

	if (lws_ensure_user_space(wsi))
		goto bail_nuke_ah;

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n])) {
			uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
			uri_len = lws_hdr_total_length(wsi, methods[n]);
			lwsl_info("Method: %s request for '%s'\n",
				  	method_names[n], uri_ptr);
			break;
		}

	/* HTTP header had a content length? */

	wsi->u.http.content_length = 0;
	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PUT_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);
	}

	/* http_version? Default to 1.0, override with token: */
	request_version = HTTP_VERSION_1_0;

	/* Works for single digit HTTP versions. : */
	http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
	if (http_version_len > 7) {
		lws_hdr_copy(wsi, http_version_str,
				sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
		if (http_version_str[5] == '1' && http_version_str[7] == '1')
			request_version = HTTP_VERSION_1_1;
	}
	wsi->u.http.request_version = request_version;

	/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
	if (request_version == HTTP_VERSION_1_1)
		connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	else
		connection_type = HTTP_CONNECTION_CLOSE;

	/* Override default if http "Connection:" header: */
	if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
		lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
			     WSI_TOKEN_CONNECTION);
		http_conn_str[sizeof(http_conn_str) - 1] = '\0';
		if (!strcasecmp(http_conn_str, "keep-alive"))
			connection_type = HTTP_CONNECTION_KEEP_ALIVE;
		else
			if (!strcasecmp(http_conn_str, "close"))
				connection_type = HTTP_CONNECTION_CLOSE;
	}
	wsi->u.http.connection_type = connection_type;

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

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

		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 */
	lws_free2(wsi->u.http.ah);

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

	/* 
	 * If we're not issuing a file, check for content_length or
	 * HTTP keep-alive. No keep-alive header allocation for
	 * ISSUING_FILE, as this uses HTTP/1.0. 
	 * 
	 * In any case, return 0 and let lws_read decide how to
	 * proceed based on state
	 */
	if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
		/* Prepare to read body if we have a content length: */
		if (wsi->u.http.content_length > 0)
			wsi->state = WSI_STATE_HTTP_BODY;

	return 0;

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

	return 1;
}


int lws_handshake_server(struct lws_context *context, struct lws *wsi,
			 unsigned char **buf, size_t len)
{
	struct allocated_headers *ah;
	int protocol_len, n, hit;
	char protocol_list[128];
	char protocol_name[32];
	char *p;

	/* LWS_CONNMODE_WS_SERVING */

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

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

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

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* 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)) {
			
			ah = wsi->u.hdr.ah;
			
			lws_union_transition(wsi, LWS_CONNMODE_HTTP_SERVING_ACCEPTED);
			wsi->state = WSI_STATE_HTTP;
			wsi->u.http.fd = LWS_INVALID_FILE;

			/* expose it at the same offset as u.hdr */
			wsi->u.http.ah = ah;
			
			n = lws_http_action(context, wsi);

			return n;
		}

		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
				"websocket"))
			goto upgrade_ws;
#ifdef LWS_USE_HTTP2
		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
								"h2c-14"))
			goto upgrade_h2c;
#endif
		/* dunno what he wanted to upgrade to */
		goto bail_nuke_ah;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list,
					  sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWS_CONNMODE_HTTP2_SERVING);
		
		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;
		
		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);
		
		/* HTTP2 union */
		
		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
				(unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
					strlen(protocol_list));
		if (n != strlen(protocol_list)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}
		
		wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
		
		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at lws_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			unsigned int n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (wsi->protocol && context->protocols[n].callback) {
				if (!wsi->protocol->name) {
					n++;
					continue;
				}
				if (!strcmp(context->protocols[n].name,
					    protocol_name)) {
					lwsl_info("prot match %d\n", n);
					wsi->protocol = &context->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

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

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
									 NULL) {
				/*
				 * some clients only have one protocol and
				 * do not sent the protocol list header...
				 * allow it and match to protocol 0
				 */
				lwsl_info("defaulting to prot 0 handler\n");
				wsi->protocol = &context->protocols[0];
			} else {
				lwsl_err("No protocol from list \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
		}

		/* allocate wsi->user storage */
		if (lws_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 */
		lws_free_header_table(wsi);

		lws_union_transition(wsi, LWS_CONNMODE_WS_SERVING);

		/*
		 * 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 = lws_malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		lwsl_info("Allocating RX buffer %d\n", n);
#if LWS_POSIX
		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
			       (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}
#endif
		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	lws_free_header_table(wsi);
	return 1;
}

struct lws *
lws_create_new_server_wsi(struct lws_context *context)
{
	struct lws *new_wsi;

	new_wsi = lws_zalloc(sizeof(struct lws));
	if (new_wsi == NULL) {
		lwsl_err("Out of memory for new connection\n");
		return NULL;
	}

	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;

	/* intialize the instance struct */

	new_wsi->state = WSI_STATE_HTTP;
	new_wsi->mode = LWS_CONNMODE_HTTP_SERVING;
	new_wsi->hdr_parsing_completed = 0;

#ifdef LWS_OPENSSL_SUPPORT
	new_wsi->use_ssl = LWS_SSL_ENABLED(context);
#endif

	if (lws_allocate_header_table(new_wsi)) {
		lws_free(new_wsi);
		return NULL;
	}

	/*
	 * these can only be set once the protocol is known
	 * we set an unestablished connection's protocol pointer
	 * to the start of the supported list, so it can look
	 * for matching ones during the handshake
	 */
	new_wsi->protocol = context->protocols;
	new_wsi->user_space = NULL;
	new_wsi->ietf_spec_revision = 0;
	new_wsi->sock = LWS_SOCK_INVALID;
	
	/*
	 * outermost create notification for wsi
	 * no user_space because no protocol selection
	 */
	context->protocols[0].callback(context, new_wsi,
			LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0);

	return new_wsi;
}

/**
 * lws_http_transaction_completed() - wait for new http transaction or close
 * @wsi:	websocket connection
 *
 *	Returns 1 if the HTTP connection must close now
 *	Returns 0 and resets connection to wait for new HTTP header /
 *	  transaction if possible
 */

LWS_VISIBLE
int lws_http_transaction_completed(struct lws *wsi)
{
	/* if we can't go back to accept new headers, drop the connection */
	if (wsi->u.http.connection_type == HTTP_CONNECTION_CLOSE) {
		lwsl_info("%s: close connection\n", __func__);
		return 1;
	}

	/* otherwise set ourselves up ready to go again */
	wsi->state = WSI_STATE_HTTP;
	wsi->mode = LWS_CONNMODE_HTTP_SERVING;
	wsi->u.http.content_length = 0;

	/* He asked for it to stay alive indefinitely */
	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

	if (lws_allocate_header_table(wsi))
		return 1;

	/* If we're (re)starting on headers, need other implied init */
	wsi->u.hdr.ues = URIES_IDLE;
	
	lwsl_info("%s: keep-alive await new transaction\n", __func__);
	
	return 0;
}

LWS_VISIBLE
int lws_server_socket_service(struct lws_context *context,
			      struct lws *wsi, struct lws_pollfd *pollfd)
{
	lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
#if LWS_POSIX
	struct sockaddr_in cli_addr;
	socklen_t clilen;
#endif
	struct lws *new_wsi = NULL;
	int n, len;

	switch (wsi->mode) {

	case LWS_CONNMODE_HTTP_SERVING:
	case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
	case LWS_CONNMODE_HTTP2_SERVING:

		/* handle http headers coming in */

		/* pending truncated sends have uber priority */

		if (wsi->truncated_send_len) {
			if (pollfd->revents & LWS_POLLOUT)
				if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
						       wsi->truncated_send_offset,
						  wsi->truncated_send_len) < 0) {
					goto fail;
				}
			/*
			 * 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(context, wsi,
						   context->service_buffer,
						   sizeof(context->service_buffer));
			lwsl_debug("%s: read %d\r\n", __func__, len);
			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)
					lws_free_header_table(wsi);
				/* fallthru */
			case LWS_SSL_CAPABLE_ERROR:
				goto fail;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				goto try_pollout;
			}

			/* 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 = lws_read(context, wsi,
					     context->service_buffer, len);
				if (n < 0) /* we closed wsi */
					return 1;

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

try_pollout:
		/* 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)
				goto fail;
			break;
		}

		/* >0 == completion, <0 == error */
		n = lws_serve_http_file_fragment(context, wsi);
		if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi)))
			goto fail;
		break;

	case LWS_CONNMODE_SERVER_LISTENER:

#if LWS_POSIX
		/* 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);
#else
		/* not very beautiful... */
		accept_fd = (lws_sockfd_type)pollfd;
#endif
		/*
		 * 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 = lws_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 */
		lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				AWAITING_TIMEOUT);

#if LWS_POSIX == 0
		mbed3_tcp_stream_accept(accept_fd, new_wsi);
#endif
		
		/*
		 * 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)) {
#if LWS_POSIX
			lwsl_debug("accepted new conn  port %u on fd=%d\n",
					  ntohs(cli_addr.sin_port), accept_fd);
#endif
			if (insert_wsi_socket_into_fds(context, new_wsi))
				goto fail;
		}
		break;

	default:
		break;
	}

	if (!lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd,
					   pollfd))
		return 0;

fail:
	lws_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);

	return 1;
}
Beispiel #11
0
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
	struct lws_context *context = wsi->context;
	struct lws_vhost *vh;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	int n, m;
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
	BIO *bio;
#endif
        char buf[256];

        (void)buf;

	if (!LWS_SSL_ENABLED(wsi->vhost))
		return 0;

	switch (wsi->mode) {
	case LWSCM_SSL_INIT:
	case LWSCM_SSL_INIT_RAW:
		if (wsi->ssl)
			lwsl_err("%s: leaking ssl\n", __func__);
		if (accept_fd == LWS_SOCK_INVALID)
			assert(0);
		if (context->simultaneous_ssl_restriction &&
		    context->simultaneous_ssl >= context->simultaneous_ssl_restriction) {
			lwsl_notice("unable to deal with SSL connection\n");
			return 1;
		}
		errno = 0;
		wsi->ssl = SSL_new(wsi->vhost->ssl_ctx);
		if (wsi->ssl == NULL) {
			lwsl_err("SSL_new failed: %d (errno %d)\n",
				 lws_ssl_get_error(wsi, 0), errno);

			lws_ssl_elaborate_error();
			if (accept_fd != LWS_SOCK_INVALID)
				compatible_close(accept_fd);
			goto fail;
		}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
		if (wsi->vhost->ssl_info_event_mask)
			SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
		if (context->simultaneous_ssl_restriction &&
		    ++context->simultaneous_ssl == context->simultaneous_ssl_restriction)
			/* that was the last allowed SSL connection */
			lws_gate_accepts(context, 0);
#if defined(LWS_WITH_STATS)
	context->updated = 1;
#endif

#if !defined(LWS_WITH_MBEDTLS)
		SSL_set_ex_data(wsi->ssl,
			openssl_websocket_private_data_index, wsi);
#endif
		SSL_set_fd(wsi->ssl, accept_fd);

#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
		CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
		wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
		lws_plat_set_socket_options(wsi->vhost, accept_fd);
#else
		SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
		bio = SSL_get_rbio(wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
		bio = SSL_get_wbio(wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
#endif
#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
		 */

		if (wsi->mode == LWSCM_SSL_INIT)
			wsi->mode = LWSCM_SSL_ACK_PENDING;
		else
			wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;

		if (insert_wsi_socket_into_fds(context, wsi)) {
			lwsl_err("%s: failed to insert into fds\n", __func__);
			goto fail;
		}

		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
				context->timeout_secs);

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

		/* fallthru */

	case LWSCM_SSL_ACK_PENDING:
	case LWSCM_SSL_ACK_PENDING_RAW:
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
			goto fail;
		}

		lws_latency_pre(context, wsi);

		if (wsi->vhost->allow_non_ssl_on_ssl_port) {

			n = recv(wsi->desc.sockfd, (char *)pt->serv_buf,
				 context->pt_serv_buf_size, 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 (n >= 1 && pt->serv_buf[0] >= ' ') {
				/*
				* TLS content-type for Handshake is 0x16, and
				* for ChangeCipherSpec Record, it's 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;
				if (lws_check_opt(context->options,
				    LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS))
					wsi->redirect_to_https = 1;
				goto accepted;
			}
			if (!n) /*
				 * connection is gone, or nothing to read
				 * if it's gone, we will timeout on
				 * PENDING_TIMEOUT_SSL_ACCEPT
				 */
				break;
			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
				/*
				 * well, we get no way to know ssl or not
				 * so go around again waiting for something
				 * to come and give us a hint, or timeout the
				 * connection.
				 */
				m = SSL_ERROR_WANT_READ;
				goto go_again;
			}
		}

		/* normal SSL connection processing path */

#if defined(LWS_WITH_STATS)
		if (!wsi->accept_start_us)
			wsi->accept_start_us = time_in_microseconds();
#endif
		errno = 0;
		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
		n = SSL_accept(wsi->ssl);
		lws_latency(context, wsi,
			"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
		lwsl_info("SSL_accept says %d\n", n);
		if (n == 1)
			goto accepted;

		m = lws_ssl_get_error(wsi, n);

#if defined(LWS_WITH_MBEDTLS)
		if (m == SSL_ERROR_SYSCALL && errno == 11)
			m = SSL_ERROR_WANT_READ;
#endif
		if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
			goto failed;

go_again:
		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
				lwsl_info("%s: WANT_READ change_pollfd failed\n", __func__);
				goto fail;
			}

			lwsl_info("SSL_ERROR_WANT_READ\n");
			break;
		}
		if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
			lwsl_debug("%s: WANT_WRITE\n", __func__);

			if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
				lwsl_info("%s: WANT_WRITE change_pollfd failed\n", __func__);
				goto fail;
			}

			break;
		}
failed:
		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
		wsi->socket_is_permanently_unusable = 1;
                lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
                         lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
		lws_ssl_elaborate_error();
		goto fail;

accepted:
		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, 1);
#if defined(LWS_WITH_STATS)
		lws_stats_atomic_bump(wsi->context, pt,
				      LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY,
				      time_in_microseconds() - wsi->accept_start_us);
		wsi->accept_start_us = time_in_microseconds();
#endif

		/* adapt our vhost to match the SNI SSL_CTX that was chosen */
		vh = context->vhost_list;
		while (vh) {
			if (!vh->being_destroyed && wsi->ssl &&
			    vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
				lwsl_info("setting wsi to vh %s\n", vh->name);
				wsi->vhost = vh;
				break;
			}
			vh = vh->vhost_next;
		}

		/* OK, we are accepted... give him some time to negotiate */
		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				context->timeout_secs);

		if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
			wsi->mode = LWSCM_RAW;
		else
			wsi->mode = LWSCM_HTTP_SERVING;
#if defined(LWS_WITH_HTTP2)
		if (lws_h2_configure_if_upgraded(wsi))
			goto fail;
#endif
		lwsl_debug("accepted new SSL conn\n");
		break;
	}

	return 0;

fail:
	return 1;
}
Beispiel #12
0
int
lws_client_socket_service(struct lws_context *context, struct lws *wsi,
			  struct lws_pollfd *pollfd)
{
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	const char *cce = NULL;
	unsigned char c;
	char *sb = p;
	int n = 0, len = 0;
#if defined(LWS_WITH_SOCKS5)
	char conn_mode = 0, pending_timeout = 0;
#endif

	switch (wsi->mode) {

	case LWSCM_WSCL_WAITING_CONNECT:

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

		if (!lws_client_connect_2(wsi)) {
			/* closed */
			lwsl_client("closed\n");
			return -1;
		}

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

#if defined(LWS_WITH_SOCKS5)
	/* SOCKS Greeting Reply */
	case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:

		/* handle proxy hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {

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

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("SOCKS read returned EAGAIN..."
					"retrying\n");
				return 0;
			}

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from SOCKS socket\n");
			return 0;
		}

		/* processing greeting reply */
		if (pt->serv_buf[0] == SOCKS_VERSION_5
			&& pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH)
		{
			lwsl_client("%s\n", "SOCKS greeting reply received "
				"- No Authentication Method");
			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len);

			conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
			pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
			lwsl_client("%s\n", "Sending SOCKS connect command");
		}
		else if (pt->serv_buf[0] == SOCKS_VERSION_5
				&& pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD)
		{
			lwsl_client("%s\n", "SOCKS greeting reply received "
				"- User Name Password Method");
			socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD,
				(size_t *)&len);

			conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY;
			pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
			lwsl_client("%s\n", "Sending SOCKS user/password");
		}
		else
		{
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR SOCKS greeting reply failed, method "
				"code: %d\n", pt->serv_buf[1]);
			return 0;
		}

		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
			 MSG_NOSIGNAL);
		if (n < 0) {
			lwsl_debug("ERROR writing socks command to socks proxy "
				"socket\n");
			return 0;
		}

		lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
		wsi->mode = conn_mode;

		break;
	/* SOCKS auth Reply */
	case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:

		/* handle proxy hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {

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

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("SOCKS read returned EAGAIN... "
					"retrying\n");
				return 0;
			}

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from socks socket\n");
			return 0;
		}

		/* processing auth reply */
		if (pt->serv_buf[0] == SOCKS_SUBNEGOTIATION_VERSION_1
			&& pt->serv_buf[1] == SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
		{
			lwsl_client("%s\n", "SOCKS password reply recieved - "
				"successful");
			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len);

			conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
			pending_timeout =
				PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
			lwsl_client("%s\n", "Sending SOCKS connect command");
		}
		else
		{
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR : SOCKS user/password reply failed, "
				"error code: %d\n", pt->serv_buf[1]);
			return 0;
		}

		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
			 MSG_NOSIGNAL);
		if (n < 0) {
			lwsl_debug("ERROR writing connect command to SOCKS "
				"socket\n");
			return 0;
		}

		lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
		wsi->mode = conn_mode;

		break;

	/* SOCKS connect command Reply */
	case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:

		/* handle proxy hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {

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

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("SOCKS read returned EAGAIN... "
					"retrying\n");
				return 0;
			}

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from socks socket\n");
			return 0;
		}

		/* processing connect reply */
		if (pt->serv_buf[0] == SOCKS_VERSION_5
			&& pt->serv_buf[1] == SOCKS_REQUEST_REPLY_SUCCESS)
		{
			lwsl_client("%s\n", "SOCKS connect reply recieved - "
				"successful");
		}
		else
		{
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR SOCKS connect reply failed, error "
				"code: %d\n", pt->serv_buf[1]);
			return 0;
		}

		/* free stash since we are done with it */
		lws_free_set_NULL(wsi->u.hdr.stash);

		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
			wsi->vhost->socks_proxy_address))
			goto bail3;
		wsi->c_port = wsi->vhost->socks_proxy_port;

		/* clear his proxy connection timeout */

		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		goto start_ws_hanshake;
#endif
	case LWSCM_WSCL_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);

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("Proxy read returned EAGAIN... retrying\n");
				return 0;
			}

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from proxy socket\n");
			return 0;
		}

		pt->serv_buf[13] = '\0';
		if (strcmp(sb, "HTTP/1.0 200 ") &&
		    strcmp(sb, "HTTP/1.1 200 ")) {
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR proxy: %s\n", sb);
			return 0;
		}

		/* clear his proxy connection timeout */

		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* fallthru */

	case LWSCM_WSCL_ISSUE_HANDSHAKE:

		/*
		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
		 * timeout protection set in client-handshake.c
		 *
		 * take care of our lws_callback_on_writable
		 * happening at a time when there's no real connection yet
		 */
#if defined(LWS_WITH_SOCKS5)
start_ws_hanshake:
#endif
		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 (lws_ssl_client_bio_create(wsi))
				return -1;
		}

		if (wsi->use_ssl) {
			n = lws_ssl_client_connect1(wsi);
			if (!n)
				return 0;
			if (n < 0) {
				cce = "lws_ssl_client_connect1 failed";
				goto bail3;
			}
		} else
			wsi->ssl = NULL;

		/* fallthru */

	case LWSCM_WSCL_WAITING_SSL:

		if (wsi->use_ssl) {
			n = lws_ssl_client_connect2(wsi);
			if (!n)
				return 0;
			if (n < 0) {
				cce = "lws_ssl_client_connect2 failed";
				goto bail3;
			}
		} else
			wsi->ssl = NULL;
#endif

		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
				context->timeout_secs);

		/* fallthru */

	case LWSCM_WSCL_ISSUE_HANDSHAKE2:
		p = lws_generate_client_handshake(wsi, p);
		if (p == NULL) {
			if (wsi->mode == LWSCM_RAW)
				return 0;

			lwsl_err("Failed to generate handshake for client\n");
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		/* send our request to the server */

		lws_latency_pre(context, wsi);

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

		if (wsi->client_http_body_pending) {
			wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY;
			lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
					context->timeout_secs);
			/* user code must ask for writable callback */
			break;
		}

		goto client_http_body_sent;

	case LWSCM_WSCL_ISSUE_HTTP_BODY:
		if (wsi->client_http_body_pending) {
			lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
					context->timeout_secs);
			/* user code must ask for writable callback */
			break;
		}
client_http_body_sent:
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY;
		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
				context->timeout_secs);
		break;

	case LWSCM_WSCL_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);
			cce = "Peer hung up";
			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(wsi, &c, 1);
			lws_latency(context, wsi, "send lws_issue_raw", n,
				    n == 1);
			switch (n) {
			case 0:
			case LWS_SSL_CAPABLE_ERROR:
				cce = "read failed";
				goto bail3;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				return 0;
			}

			if (lws_parse(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(wsi);

bail3:
		lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
		if (cce)
			lwsl_info("reason: %s\n", cce);
		wsi->protocol->callback(wsi,
			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
			wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
		wsi->already_did_cce = 1;
		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
		return -1;

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

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

	return 0;
}
Beispiel #13
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;
}
Beispiel #14
0
int lws_client_socket_service(struct libwebsocket_context *context,
				struct libwebsocket *wsi, struct pollfd *pollfd)
{
	int n;
	char *p = (char *)&context->service_buffer[0];
	int len;
	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 & (POLLERR | 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) {
			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
		 */

	#ifdef LWS_OPENSSL_SUPPORT

		/*
		 * take care of our libwebsocket_callback_on_writable
		 * happening at a time when there's no real connection yet
		 */

		pollfd->events &= ~POLLOUT;

		/* external POLL support via protocol 0 */
		context->protocols[0].callback(context, wsi,
			LWS_CALLBACK_CLEAR_MODE_POLL_FD,
			wsi->user_space, (void *)(long)wsi->sock, POLLOUT);

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

		if (wsi->use_ssl && !wsi->ssl) {

			wsi->ssl = SSL_new(context->ssl_client_ctx);

#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);

					return 0; /* no error */
				}
				n = -1;
			}

			if (n <= 0) {
				/*
				 * retry if new data comes until we
				 * run into the connection timeout or win
				 */

				lwsl_err("SSL connect error %lu: %s\n", 
					ERR_get_error(),
					ERR_error_string(ERR_get_error(),
					      (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) && (
				n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
							   wsi->use_ssl != 2)) {

				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

		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);
#ifdef LWS_OPENSSL_SUPPORT
		if (wsi->use_ssl)
			n = SSL_write(wsi->ssl, context->service_buffer,
					   p - (char *)context->service_buffer);
		else
#endif
			n = send(wsi->sock, context->service_buffer,
					p - (char *)context->service_buffer, MSG_NOSIGNAL);
		lws_latency(context, wsi,
			"send or SSL_write LWS_CONNMODE...HANDSHAKE",
								     n, n >= 0);

		if (n < 0) {
			lwsl_debug("ERROR writing to client socket\n");
			libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		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 & (POLLERR | POLLHUP)) {

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

			goto bail3;
		}

		if (!(pollfd->revents & 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) {
#ifdef LWS_OPENSSL_SUPPORT
			if (wsi->use_ssl) {
				len = SSL_read(wsi->ssl, &c, 1);
				if (len < 0) {
					n = SSL_get_error(wsi->ssl, len);
					if (n ==  SSL_ERROR_WANT_READ ||
						     n ==  SSL_ERROR_WANT_WRITE)
						return 0;
				}
			} else
#endif
				len = recv(wsi->sock, &c, 1, 0);

			if (len < 0) {
				lwsl_warn("error on parsing recv\n");
				goto bail3;
			}

			if (libwebsocket_parse(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;
}
Beispiel #15
0
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	struct libwebsocket_context *context = wsi->protocol->owning_server;
	int n;
	size_t real_len = len;
	int m;
	
	if (!len)
		return 0;
	/* just ignore sends after we cleared the truncation buffer */
	if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
						!wsi->truncated_send_len)
		return len;

	if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc ||
			buf > (wsi->truncated_send_malloc +
				wsi->truncated_send_len +
				wsi->truncated_send_offset))) {
		lwsl_err("****** %x Sending new, pending truncated ...\n", wsi);
		assert(0);
	}

	m = lws_ext_callback_for_each_active(wsi,
			LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len);
	if (m < 0)
		return -1;
	if (m) /* handled */ {
		n = m;
		goto handle_truncated_send;
	}
	if (wsi->sock < 0)
		lwsl_warn("** error invalid sock but expected to send\n");

	/*
	 * nope, send it on the socket directly
	 */
	lws_latency_pre(context, wsi);
	n = lws_ssl_capable_write(wsi, buf, len);
	lws_latency(context, wsi, "send lws_issue_raw", n, n == len);

	switch (n) {
	case LWS_SSL_CAPABLE_ERROR:
		/* we're going to close, let close know sends aren't possible */
		wsi->socket_is_permanently_unusable = 1;
		return -1;
	case LWS_SSL_CAPABLE_MORE_SERVICE:
		/* nothing got sent, not fatal, retry the whole thing later */
		n = 0;
		break;
	}

handle_truncated_send:
	/*
	 * we were already handling a truncated send?
	 */
	if (wsi->truncated_send_len) {
		lwsl_info("***** %x partial send moved on by %d (vs %d)\n",
							     wsi, n, real_len);
		wsi->truncated_send_offset += n;
		wsi->truncated_send_len -= n;

		if (!wsi->truncated_send_len) {
			lwsl_info("***** %x partial send completed\n", wsi);
			/* done with it, but don't free it */
			n = real_len;
			if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
				lwsl_info("***** %x signalling to close now\n", wsi);
				return -1; /* retry closing now */
			}
		}
		/* always callback on writeable */
		libwebsocket_callback_on_writable(
					     wsi->protocol->owning_server, wsi);

		return n;
	}

	if (n == real_len)
		/* what we just sent went out cleanly */
		return n;

	if (n && wsi->u.ws.clean_buffer)
		/*
		 * This buffer unaffected by extension rewriting.
		 * It means the user code is expected to deal with
		 * partial sends.  (lws knows the header was already
		 * sent, so on next send will just resume sending
		 * payload)
		 */
		 return n;

	/*
	 * Newly truncated send.  Buffer the remainder (it will get
	 * first priority next time the socket is writable)
	 */
	lwsl_info("***** %x new partial sent %d from %d total\n",
						      wsi, n, real_len);

	/*
	 *  - if we still have a suitable malloc lying around, use it
	 *  - or, if too small, reallocate it
	 *  - or, if no buffer, create it
	 */
	if (!wsi->truncated_send_malloc ||
			real_len - n > wsi->truncated_send_allocation) {
		lws_free(wsi->truncated_send_malloc);

		wsi->truncated_send_allocation = real_len - n;
		wsi->truncated_send_malloc = lws_malloc(real_len - n);
		if (!wsi->truncated_send_malloc) {
			lwsl_err("truncated send: unable to malloc %d\n",
							  real_len - n);
			return -1;
		}
	}
	wsi->truncated_send_offset = 0;
	wsi->truncated_send_len = real_len - n;
	memcpy(wsi->truncated_send_malloc, buf + n, real_len - n);

	/* since something buffered, force it to get another chance to send */
	libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi);

	return real_len;
}
Beispiel #16
0
/*
 * notice this returns number of bytes consumed, or -1
 */
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
	struct lws_context *context = lws_get_context(wsi);
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	size_t real_len = len;
	unsigned int n;

	// lwsl_hexdump_err(buf, len);

	/*
	 * Detect if we got called twice without going through the
	 * event loop to handle pending.  This would be caused by either
	 * back-to-back writes in one WRITABLE (illegal) or calling lws_write()
	 * from outside the WRITABLE callback (illegal).
	 */
	if (wsi->could_have_pending) {
		lwsl_hexdump_level(LLL_ERR, buf, len);
		lwsl_err("** %p: vh: %s, prot: %s, role %s: "
			 "Illegal back-to-back write of %lu detected...\n",
			 wsi, wsi->vhost->name, wsi->protocol->name,
			 wsi->role_ops->name,
			 (unsigned long)len);
		// assert(0);

		return -1;
	}

	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);

	if (!len)
		return 0;
	/* just ignore sends after we cleared the truncation buffer */
	if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
		return (int)len;

	if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
	    buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
		lwsl_hexdump_level(LLL_ERR, buf, len);
		lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n"
			 "   It's illegal to do an lws_write outside of\n"
			 "   the writable callback: fix your code\n",
			 wsi, wsi->vhost->name, wsi->protocol->name,
			 (unsigned long)len);
		assert(0);

		return -1;
	}

	if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
		lwsl_warn("** error invalid sock but expected to send\n");

	/* limit sending */
	if (wsi->protocol->tx_packet_size)
		n = (int)wsi->protocol->tx_packet_size;
	else {
		n = (int)wsi->protocol->rx_buffer_size;
		if (!n)
			n = context->pt_serv_buf_size;
	}
	n += LWS_PRE + 4;
	if (n > len)
		n = (int)len;

	/* nope, send it on the socket directly */
	lws_latency_pre(context, wsi);
	n = lws_ssl_capable_write(wsi, buf, n);
	lws_latency(context, wsi, "send lws_issue_raw", n, n == len);

	/* something got written, it can have been truncated now */
	wsi->could_have_pending = 1;

	switch (n) {
	case LWS_SSL_CAPABLE_ERROR:
		/* we're going to close, let close know sends aren't possible */
		wsi->socket_is_permanently_unusable = 1;
		return -1;
	case LWS_SSL_CAPABLE_MORE_SERVICE:
		/*
		 * nothing got sent, not fatal.  Retry the whole thing later,
		 * ie, implying treat it was a truncated send so it gets
		 * retried
		 */
		n = 0;
		break;
	}

	/*
	 * we were already handling a truncated send?
	 */
	if (wsi->trunc_len) {
		lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
		wsi->trunc_offset += n;
		wsi->trunc_len -= n;

		if (!wsi->trunc_len) {
			lwsl_info("** %p partial send completed\n", wsi);
			/* done with it, but don't free it */
			n = (int)real_len;
			if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
				lwsl_info("** %p signalling to close now\n", wsi);
				return -1; /* retry closing now */
			}
		}
		/* always callback on writeable */
		lws_callback_on_writable(wsi);

		return n;
	}

	if ((unsigned int)n == real_len)
		/* what we just sent went out cleanly */
		return n;

	/*
	 * Newly truncated send.  Buffer the remainder (it will get
	 * first priority next time the socket is writable).
	 */
	lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
		    (unsigned long)real_len);

	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
	lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);

	/*
	 *  - if we still have a suitable malloc lying around, use it
	 *  - or, if too small, reallocate it
	 *  - or, if no buffer, create it
	 */
	if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
		lws_free(wsi->trunc_alloc);

		wsi->trunc_alloc_len = (unsigned int)(real_len - n);
		wsi->trunc_alloc = lws_malloc(real_len - n,
					      "truncated send alloc");
		if (!wsi->trunc_alloc) {
			lwsl_err("truncated send: unable to malloc %lu\n",
				 (unsigned long)(real_len - n));
			return -1;
		}
	}
	wsi->trunc_offset = 0;
	wsi->trunc_len = (unsigned int)(real_len - n);
	memcpy(wsi->trunc_alloc, buf + n, real_len - n);

#if !defined(LWS_WITH_ESP32)
	if (lws_wsi_is_udp(wsi)) {
		/* stash original destination for fulfilling UDP partials */
		wsi->udp->sa_pending = wsi->udp->sa;
		wsi->udp->salen_pending = wsi->udp->salen;
	}
#endif

	/* since something buffered, force it to get another chance to send */
	lws_callback_on_writable(wsi);

	return (int)real_len;
}
Beispiel #17
0
int
lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
			  struct lws *wsi_conn)
{
	struct lws_context *context = wsi->context;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	struct lws *w;
#if defined(LWS_WITH_TLS)
	char ebuf[128];
#endif
	const char *cce = NULL;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
	ssize_t len = 0;
	unsigned char c;
#endif
	char *sb = p;
	int n = 0;
#if defined(LWS_WITH_SOCKS5)
	char conn_mode = 0, pending_timeout = 0;
#endif

	if ((pollfd->revents & LWS_POLLOUT) &&
	     wsi->keepalive_active &&
	     wsi->dll_client_transaction_queue_head.next) {
		struct lws *wfound = NULL;

		lwsl_debug("%s: pollout HANDSHAKE2\n", __func__);

		/*
		 * We have a transaction queued that wants to pipeline.
		 *
		 * We have to allow it to send headers strictly in the order
		 * that it was queued, ie, tail-first.
		 */
		lws_vhost_lock(wsi->vhost);
		lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
					   wsi->dll_client_transaction_queue_head.next) {
			struct lws *w = lws_container_of(d, struct lws,
						  dll_client_transaction_queue);

			lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate);
			if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2)
				wfound = w;
		} lws_end_foreach_dll_safe(d, d1);

		if (wfound) {
			/*
			 * pollfd has the master sockfd in it... we
			 * need to use that in HANDSHAKE2 to understand
			 * which wsi to actually write on
			 */
			lws_client_socket_service(wfound, pollfd, wsi);
			lws_callback_on_writable(wsi);
		} else
			lwsl_debug("%s: didn't find anything in txn q in HS2\n",
							   __func__);

		lws_vhost_unlock(wsi->vhost);

		return 0;
	}

	switch (lwsi_state(wsi)) {

	case LRS_WAITING_CONNECT:

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

		if (!lws_client_connect_2(wsi)) {
			/* closed */
			lwsl_client("closed\n");
			return -1;
		}

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

#if defined(LWS_WITH_SOCKS5)
	/* SOCKS Greeting Reply */
	case LRS_WAITING_SOCKS_GREETING_REPLY:
	case LRS_WAITING_SOCKS_AUTH_REPLY:
	case LRS_WAITING_SOCKS_CONNECT_REPLY:

		/* handle proxy hung up on us */

		if (pollfd->revents & LWS_POLLHUP) {
			lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
				  (void *)wsi, pollfd->fd);
			goto bail3;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("SOCKS read EAGAIN, retrying\n");
				return 0;
			}
			lwsl_err("ERROR reading from SOCKS socket\n");
			goto bail3;
		}

		switch (lwsi_state(wsi)) {

		case LRS_WAITING_SOCKS_GREETING_REPLY:
			if (pt->serv_buf[0] != SOCKS_VERSION_5)
				goto socks_reply_fail;

			if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
				lwsl_client("SOCKS GR: No Auth Method\n");
				socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
				conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
				pending_timeout =
				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
				goto socks_send;
			}

			if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) {
				lwsl_client("SOCKS GR: User/Pw Method\n");
				socks_generate_msg(wsi,
						   SOCKS_MSG_USERNAME_PASSWORD,
						   &len);
				conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
				pending_timeout =
				      PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
				goto socks_send;
			}
			goto socks_reply_fail;

		case LRS_WAITING_SOCKS_AUTH_REPLY:
			if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
			    pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
				goto socks_reply_fail;

			lwsl_client("SOCKS password OK, sending connect\n");
			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
			conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
			pending_timeout =
				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
socks_send:
			n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
				 MSG_NOSIGNAL);
			if (n < 0) {
				lwsl_debug("ERROR writing to socks proxy\n");
				goto bail3;
			}

			lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
			lwsi_set_state(wsi, conn_mode);
			break;

socks_reply_fail:
			lwsl_notice("socks reply: v%d, err %d\n",
				    pt->serv_buf[0], pt->serv_buf[1]);
			goto bail3;

		case LRS_WAITING_SOCKS_CONNECT_REPLY:
			if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
			    pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
				goto socks_reply_fail;

			lwsl_client("socks connect OK\n");

			/* free stash since we are done with it */
			lws_client_stash_destroy(wsi);
			if (lws_hdr_simple_create(wsi,
						  _WSI_TOKEN_CLIENT_PEER_ADDRESS,
						  wsi->vhost->socks_proxy_address))
				goto bail3;

			wsi->c_port = wsi->vhost->socks_proxy_port;

			/* clear his proxy connection timeout */
			lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
			goto start_ws_handshake;
		}
		break;
#endif

	case LRS_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);

			goto bail3;
		}

		n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("Proxy read EAGAIN... retrying\n");
				return 0;
			}
			lwsl_err("ERROR reading from proxy socket\n");
			goto bail3;
		}

		pt->serv_buf[13] = '\0';
		if (strcmp(sb, "HTTP/1.0 200 ") &&
		    strcmp(sb, "HTTP/1.1 200 ")) {
			lwsl_err("ERROR proxy: %s\n", sb);
			goto bail3;
		}

		/* clear his proxy connection timeout */

		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* fallthru */

	case LRS_H1C_ISSUE_HANDSHAKE:

		/*
		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
		 * timeout protection set in client-handshake.c
		 *
		 * take care of our lws_callback_on_writable
		 * happening at a time when there's no real connection yet
		 */
#if defined(LWS_WITH_SOCKS5)
start_ws_handshake:
#endif
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			return -1;

#if defined(LWS_WITH_TLS)
		/* we can retry this... just cook the SSL BIO the first time */

		if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !wsi->tls.ssl &&
		    lws_ssl_client_bio_create(wsi) < 0) {
			cce = "bio_create failed";
			goto bail3;
		}

		if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
			n = lws_ssl_client_connect1(wsi);
			if (!n)
				return 0;
			if (n < 0) {
				cce = "lws_ssl_client_connect1 failed";
				goto bail3;
			}
		} else
			wsi->tls.ssl = NULL;

		/* fallthru */

	case LRS_WAITING_SSL:

		if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
			n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
			if (!n)
				return 0;
			if (n < 0) {
				cce = ebuf;
				goto bail3;
			}
		} else
			wsi->tls.ssl = NULL;
#endif
#if defined (LWS_WITH_HTTP2)
		if (wsi->client_h2_alpn) {
			/*
			 * We connected to the server and set up tls, and
			 * negotiated "h2".
			 *
			 * So this is it, we are an h2 master client connection
			 * now, not an h1 client connection.
			 */
			lws_tls_server_conn_alpn(wsi);

			/* send the H2 preface to legitimize the connection */
			if (lws_h2_issue_preface(wsi)) {
				cce = "error sending h2 preface";
				goto bail3;
			}

			break;
		}
#endif
		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
				context->timeout_secs);

		/* fallthru */

	case LRS_H1C_ISSUE_HANDSHAKE2:
		p = lws_generate_client_handshake(wsi, p);
		if (p == NULL) {
			if (wsi->role_ops == &role_ops_raw_skt ||
			    wsi->role_ops == &role_ops_raw_file)
				return 0;

			lwsl_err("Failed to generate handshake for client\n");
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "chs");
			return 0;
		}

		/* send our request to the server */
		lws_latency_pre(context, wsi);

		w = _lws_client_wsi_master(wsi);
		lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p (wsistate 0x%x 0x%x)\n",
				__func__, wsi, w, wsi->wsistate, w->wsistate);

		n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb));
		lws_latency(context, wsi, "send lws_issue_raw", n,
			    n == p - sb);
		switch (n) {
		case LWS_SSL_CAPABLE_ERROR:
			lwsl_debug("ERROR writing to client socket\n");
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cws");
			return 0;
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			lws_callback_on_writable(wsi);
			break;
		}

		if (wsi->client_http_body_pending) {
			lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
			lws_set_timeout(wsi,
					PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
					context->timeout_secs);
			/* user code must ask for writable callback */
			break;
		}

		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
		wsi->hdr_parsing_completed = 0;

		if (lwsi_state(w) == LRS_IDLING) {
			lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
			w->hdr_parsing_completed = 0;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
			w->http.ah->parser_state = WSI_TOKEN_NAME_PART;
			w->http.ah->lextable_pos = 0;
			/* If we're (re)starting on headers, need other implied init */
			wsi->http.ah->ues = URIES_IDLE;
#endif
		}

		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
				wsi->context->timeout_secs);

		lws_callback_on_writable(w);

		goto client_http_body_sent;

	case LRS_ISSUE_HTTP_BODY:
		if (wsi->client_http_body_pending) {
			//lws_set_timeout(wsi,
			//		PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
			//		context->timeout_secs);
			/* user code must ask for writable callback */
			break;
		}
client_http_body_sent:
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
		/* prepare ourselves to do the parsing */
		wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
		wsi->http.ah->lextable_pos = 0;
#endif
		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
				context->timeout_secs);
		break;

	case LRS_WAITING_SERVER_REPLY:
		/*
		 * handle server hanging up on us...
		 * but if there is POLLIN waiting, handle that first
		 */
		if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
								LWS_POLLHUP) {

			lwsl_debug("Server connection %p (fd=%d) dead\n",
				(void *)wsi, pollfd->fd);
			cce = "Peer hung up";
			goto bail3;
		}

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

#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
		/* 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->http.ah->parser_state != WSI_PARSING_COMPLETE &&
		       len > 0) {
			int plen = 1;

			n = lws_ssl_capable_read(wsi, &c, 1);
			lws_latency(context, wsi, "send lws_issue_raw", n,
				    n == 1);
			switch (n) {
			case 0:
			case LWS_SSL_CAPABLE_ERROR:
				cce = "read failed";
				goto bail3;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				return 0;
			}

			if (lws_parse(wsi, &c, &plen)) {
				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->http.ah->parser_state != WSI_PARSING_COMPLETE)
			break;

#endif

		/*
		 * 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(wsi);

bail3:
		lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
		if (cce)
			lwsl_info("reason: %s\n", cce);
		wsi->protocol->callback(wsi,
			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
			wsi->user_space, (void *)cce, cce ? strlen(cce) : 0);
		wsi->already_did_cce = 1;
		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
		return -1;

	default:
		break;
	}

	return 0;
}
Beispiel #18
0
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	struct libwebsocket_context *context = wsi->protocol->owning_server;
	int n;
#ifndef LWS_NO_EXTENSIONS
	int m;

	/*
	 * one of the extensions is carrying our data itself?  Like mux?
	 */

	for (n = 0; n < wsi->count_active_extensions; n++) {
		/*
		 * there can only be active extensions after handshake completed
		 * so we can rely on protocol being set already in here
		 */
		m = wsi->active_extensions[n]->callback(
				wsi->protocol->owning_server,
				wsi->active_extensions[n], wsi,
				LWS_EXT_CALLBACK_PACKET_TX_DO_SEND,
				     wsi->active_extensions_user[n], &buf, len);
		if (m < 0) {
			lwsl_ext("Extension reports fatal error\n");
			return -1;
		}
		if (m) /* handled */ {
/*			lwsl_ext("ext sent it\n"); */
			return m;
		}
	}
#endif
	if (!wsi->sock)
		lwsl_warn("** error 0 sock but expected to send\n");

	/*
	 * nope, send it on the socket directly
	 */

#if 0
	lwsl_debug("  TX: ");
	lws_hexdump(buf, len);
#endif

	lws_latency_pre(context, wsi);
#ifdef LWS_OPENSSL_SUPPORT
	if (wsi->ssl) {
		n = SSL_write(wsi->ssl, buf, len);
		lws_latency(context, wsi, "SSL_write lws_issue_raw", n, n >= 0);
		if (n < 0) {
			lwsl_debug("ERROR writing to socket\n");
			return -1;
		}
	} else {
#endif
		n = send(wsi->sock, buf, len, MSG_NOSIGNAL);
		lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
		if (n < 0) {
			lwsl_debug("ERROR writing len %d to skt %d\n", len, n);
			return -1;
		}
#ifdef LWS_OPENSSL_SUPPORT
	}
#endif
	return n;
}
Beispiel #19
0
LWS_VISIBLE int
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
{
	struct lws_context *context = wsi->context;
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	int n, m;
#ifndef USE_WOLFSSL
	BIO *bio;
#endif

	if (!LWS_SSL_ENABLED(context))
		return 0;
lwsl_err("%s: mode %d, state %d\n", __func__, wsi->mode, wsi->state);
	switch (wsi->mode) {
	case LWSCM_SSL_INIT:

		if (!wsi)
			return 0;

		wsi->ssl = SSL_new(context->ssl_ctx);
		if (wsi->ssl == NULL) {
			lwsl_err("SSL_new failed: %s\n",
				 ERR_error_string(SSL_get_error(wsi->ssl, 0), NULL));
			lws_decode_ssl_error();
			compatible_close(accept_fd);
			goto fail;
		}

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

		SSL_set_fd(wsi->ssl, accept_fd);

#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
		CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
		wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
		SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
		bio = SSL_get_rbio(wsi->ssl);
		if (bio)
			BIO_set_nbio(bio, 1); /* nonblocking */
		else
			lwsl_notice("NULL rbio\n");
		bio = SSL_get_wbio(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->mode = LWSCM_SSL_ACK_PENDING;
		if (insert_wsi_socket_into_fds(context, wsi))
			goto fail;

		lws_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
							AWAITING_TIMEOUT);

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

		/* fallthru */

	case LWSCM_SSL_ACK_PENDING:

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

		lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);

		lws_latency_pre(context, wsi);

		n = recv(wsi->sock, (char *)pt->serv_buf, LWS_MAX_SOCKET_IO_BUF,
			 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) {
			if (n >= 1 && pt->serv_buf[0] >= ' ') {
				/*
				* TLS content-type for Handshake is 0x16, and
				* for ChangeCipherSpec Record, it's 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;
			}
			if (!n) /*
				 * connection is gone, or nothing to read
				 * if it's gone, we will timeout on
				 * PENDING_TIMEOUT_SSL_ACCEPT
				 */
				break;
			if (n < 0 && (LWS_ERRNO == LWS_EAGAIN ||
				      LWS_ERRNO == LWS_EWOULDBLOCK)) {
				/*
				 * well, we get no way to know ssl or not
				 * so go around again waiting for something
				 * to come and give us a hint, or timeout the
				 * connection.
				 */
				m = SSL_ERROR_WANT_READ;
				goto go_again;
			}
		}

		/* normal SSL connection processing path */

		n = SSL_accept(wsi->ssl);
		lws_latency(context, wsi,
			"SSL_accept LWSCM_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));
go_again:
		if (m == SSL_ERROR_WANT_READ) {
			if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
				goto fail;

			lws_libev_io(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(wsi, LWS_EV_START | LWS_EV_WRITE);
			break;
		}
		lwsl_debug("SSL_accept failed skt %u: %s\n",
			   wsi->sock, ERR_error_string(m, NULL));
		goto fail;

accepted:
		/* OK, we are accepted... give him some time to negotiate */
		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				AWAITING_TIMEOUT);

		wsi->mode = LWSCM_HTTP_SERVING;

		lws_http2_configure_if_upgraded(wsi);

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

	return 0;

fail:
	return 1;
}
Beispiel #20
0
LWS_VISIBLE int
lws_server_socket_service(struct lws_context *context, struct lws *wsi,
			  struct lws_pollfd *pollfd)
{
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
	struct allocated_headers *ah;
#if LWS_POSIX
	struct sockaddr_in cli_addr;
	socklen_t clilen;
#endif
	int n, len;

	switch (wsi->mode) {

	case LWSCM_HTTP_SERVING:
	case LWSCM_HTTP_SERVING_ACCEPTED:
	case LWSCM_HTTP2_SERVING:

		/* handle http headers coming in */

		/* pending truncated sends have uber priority */

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

			if (lws_issue_raw(wsi, wsi->trunc_alloc +
					       wsi->trunc_offset,
					  wsi->trunc_len) < 0)
				goto fail;
			/*
			 * we can't afford to allow input processing to send
			 * something new, so spin around he event loop until
			 * he doesn't have any partials
			 */
			break;
		}

		/* any incoming data ready? */

		if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
			goto try_pollout;

		/* these states imply we MUST have an ah attached */

		if (wsi->state == LWSS_HTTP ||
		    wsi->state == LWSS_HTTP_ISSUING_FILE ||
		    wsi->state == LWSS_HTTP_HEADERS) {
			if (!wsi->u.hdr.ah)
				/* no autoservice beacuse we will do it next */
				if (lws_header_table_attach(wsi, 0))
					goto try_pollout;

			ah = wsi->u.hdr.ah;

			lwsl_debug("%s: %p: rxpos:%d rxlen:%d\n", __func__, wsi,
				   ah->rxpos, ah->rxlen);

			/* if nothing in ah rx buffer, get some fresh rx */
			if (ah->rxpos == ah->rxlen) {
				ah->rxlen = lws_ssl_capable_read(wsi, ah->rx,
						   sizeof(ah->rx));
				ah->rxpos = 0;
				lwsl_debug("%s: wsi %p, ah->rxlen = %d\r\n",
					   __func__, wsi, ah->rxlen);
				switch (ah->rxlen) {
				case 0:
					lwsl_info("%s: read 0 len\n", __func__);
					/* lwsl_info("   state=%d\n", wsi->state); */
//					if (!wsi->hdr_parsing_completed)
//						lws_header_table_detach(wsi);
					/* fallthru */
				case LWS_SSL_CAPABLE_ERROR:
					goto fail;
				case LWS_SSL_CAPABLE_MORE_SERVICE:
					ah->rxlen = ah->rxpos = 0;
					goto try_pollout;
				}
			}
			assert(ah->rxpos != ah->rxlen && ah->rxlen);
			/* just ignore incoming if waiting for close */
			if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
				n = lws_read(wsi, ah->rx + ah->rxpos,
					     ah->rxlen - ah->rxpos);
				if (n < 0) /* we closed wsi */
					return 1;
				if (wsi->u.hdr.ah) {
					if ( wsi->u.hdr.ah->rxlen)
						 wsi->u.hdr.ah->rxpos += n;

					if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen &&
					    (wsi->mode != LWSCM_HTTP_SERVING &&
					     wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
					     wsi->mode != LWSCM_HTTP2_SERVING))
						lws_header_table_detach(wsi, 1);
				}
				break;
			}

			goto try_pollout;
		}

		len = lws_ssl_capable_read(wsi, pt->serv_buf,
					   LWS_MAX_SOCKET_IO_BUF);
		lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
		switch (len) {
		case 0:
			lwsl_info("%s: read 0 len\n", __func__);
			/* lwsl_info("   state=%d\n", wsi->state); */
//			if (!wsi->hdr_parsing_completed)
//				lws_header_table_detach(wsi);
			/* fallthru */
		case LWS_SSL_CAPABLE_ERROR:
			goto fail;
		case LWS_SSL_CAPABLE_MORE_SERVICE:
			goto try_pollout;
		}

		/* just ignore incoming if waiting for close */
		if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
			/*
			 * hm this may want to send
			 * (via HTTP callback for example)
			 */
			n = lws_read(wsi, pt->serv_buf, len);
			if (n < 0) /* we closed wsi */
				return 1;
			/* hum he may have used up the
			 * writability above */
			break;
		}

try_pollout:
		/* this handles POLLOUT for http serving fragments */

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

		/* one shot */
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
			lwsl_notice("%s a\n", __func__);
			goto fail;
		}

		if (!wsi->hdr_parsing_completed)
			break;

		if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
			n = user_callback_handle_rxflow(wsi->protocol->callback,
					wsi, LWS_CALLBACK_HTTP_WRITEABLE,
					wsi->user_space, NULL, 0);
			if (n < 0) {
				lwsl_info("writeable_fail\n");
				goto fail;
			}
			break;
		}

		/* >0 == completion, <0 == error */
		n = lws_serve_http_file_fragment(wsi);
		if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) {
			lwsl_info("completed\n");
			goto fail;
		}
		break;

	case LWSCM_SERVER_LISTENER:

#if LWS_POSIX
		/* pollin means a client has connected to us then */

		do {
			if (!(pollfd->revents & LWS_POLLIN) || !(pollfd->events & 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, "listener accept", accept_fd,
				    accept_fd >= 0);
			if (accept_fd < 0) {
				if (LWS_ERRNO == LWS_EAGAIN ||
				    LWS_ERRNO == LWS_EWOULDBLOCK) {
					lwsl_err("accept asks to try again\n");
					break;
				}
				lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO));
				break;
			}

			lws_plat_set_socket_options(wsi->vhost, accept_fd);

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

#else
			/* not very beautiful... */
			accept_fd = (lws_sockfd_type)pollfd;
#endif
			/*
			 * 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 ((wsi->vhost->protocols[0].callback)(wsi,
					LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
					NULL, (void *)(long)accept_fd, 0)) {
				lwsl_debug("Callback denied network connection\n");
				compatible_close(accept_fd);
				break;
			}

			if (!lws_adopt_socket_vhost(wsi->vhost, accept_fd))
				/* already closed cleanly as necessary */
				return 1;

#if LWS_POSIX
		} while (pt->fds_count < context->fd_limit_per_thread - 1 &&
			 lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0);
#endif
		return 0;

	default:
		break;
	}

	if (!lws_server_socket_service_ssl(wsi, accept_fd))
		return 0;

fail:
	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);

	return 1;
}
Beispiel #21
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();
			lws_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));
		goto fail;

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;
}
Beispiel #22
0
int
lws_client_socket_service(struct lws_context *context, struct lws *wsi,
			  struct lws_pollfd *pollfd)
{
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	char *sb = p;
	unsigned char c;
	int n, len;

	switch (wsi->mode) {

	case LWSCM_WSCL_WAITING_CONNECT:

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

		if (!lws_client_connect_2(wsi)) {
			/* closed */
			lwsl_client("closed\n");
			return -1;
		}

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

	case LWSCM_WSCL_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);

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		n = recv(wsi->sock, sb, LWS_MAX_SOCKET_IO_BUF, 0);
		if (n < 0) {
			if (LWS_ERRNO == LWS_EAGAIN) {
				lwsl_debug("Proxy read returned EAGAIN... retrying\n");
				return 0;
			}

			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR reading from proxy socket\n");
			return 0;
		}

		pt->serv_buf[13] = '\0';
		if (strcmp(sb, "HTTP/1.0 200 ") &&
		    strcmp(sb, "HTTP/1.1 200 ")) {
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			lwsl_err("ERROR proxy: %s\n", sb);
			return 0;
		}

		/* clear his proxy connection timeout */

		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* fallthru */

	case LWSCM_WSCL_ISSUE_HANDSHAKE:

		/*
		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
		 * timeout protection set in client-handshake.c
		 *
		 * take care of our lws_callback_on_writable
		 * happening at a time when there's no real connection yet
		 */
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			return -1;
		lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);

#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(WOLFSSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)
			const char *hostname = lws_hdr_simple_ptr(wsi,
						_WSI_TOKEN_CLIENT_HOST);
#endif

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

#ifdef USE_WOLFSSL
			/*
			 * wolfSSL/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
			 */
#ifdef USE_OLD_CYASSL
			if (wsi->use_ssl == 2)
				CyaSSL_set_verify(wsi->ssl,
							SSL_VERIFY_NONE, NULL);
#else
			if (wsi->use_ssl == 2)
				wolfSSL_set_verify(wsi->ssl,
							SSL_VERIFY_NONE, NULL);
#endif
#endif /* USE_WOLFSSL */

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

#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
			CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
			wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#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 LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);

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

				if (n == SSL_ERROR_WANT_READ)
					goto some_wait;

				if (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("%s: WANT_WRITE... retrying\n", __func__);
					lws_callback_on_writable(wsi);
some_wait:
					wsi->mode = LWSCM_WSCL_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, sb));
					return 0;
				}
			}
		} else
			wsi->ssl = NULL;

		/* fallthru */

	case LWSCM_WSCL_WAITING_SSL:

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

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

					if (n == SSL_ERROR_WANT_READ)
						goto some_wait;

					if (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_WRITE... retrying\n");
						lws_callback_on_writable(wsi);

						goto some_wait;
					}
					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, sb));
						return 0;
					}
				}
			}

			#ifndef USE_WOLFSSL
			/*
			 * See comment above about wolfSSL 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, X509_V_ERR = %d: %s\n",
						 n, ERR_error_string(n, sb));
					lws_close_free_wsi(wsi,
						LWS_CLOSE_STATUS_NOSTATUS);
					return 0;
				}
			}
#endif /* USE_WOLFSSL */
		} else
			wsi->ssl = NULL;
#endif

		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
				AWAITING_TIMEOUT);

		/* fallthru */

	case LWSCM_WSCL_ISSUE_HANDSHAKE2:
		p = lws_generate_client_handshake(wsi, p);
		if (p == NULL) {
			lwsl_err("Failed to generate handshake for client\n");
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}

		/* send our request to the server */

		lws_latency_pre(context, wsi);

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

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

	case LWSCM_WSCL_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(wsi, &c, 1);
			lws_latency(context, wsi, "send lws_issue_raw", n,
				    n == 1);
			switch (n) {
			case 0:
			case LWS_SSL_CAPABLE_ERROR:
				goto bail3;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				return 0;
			}

			if (lws_parse(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(wsi);

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

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

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

	return 0;
}
Beispiel #23
0
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	struct libwebsocket_context *context = wsi->protocol->owning_server;
	int n;
	size_t real_len = len;
	int m;
	
	if (!len)
		return 0;

	if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc ||
			buf > (wsi->truncated_send_malloc +
				wsi->truncated_send_len +
				wsi->truncated_send_offset))) {
		lwsl_err("****** %x Sending new, pending truncated ...\n", wsi);
		assert(0);
	}

	m = lws_ext_callback_for_each_active(wsi,
			LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len);
	if (m < 0)
		return -1;
	if (m) /* handled */ {
		n = m;
		goto handle_truncated_send;
	}
	if (wsi->sock < 0)
		lwsl_warn("** error invalid sock but expected to send\n");

	/*
	 * nope, send it on the socket directly
	 */
	lws_latency_pre(context, wsi);

	n = lws_ssl_capable_write(wsi, buf, len);
	lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
	switch (n) {
	case LWS_SSL_CAPABLE_ERROR:
		return -1;
	case LWS_SSL_CAPABLE_MORE_SERVICE:
		n = 0;
		goto handle_truncated_send;
	}

handle_truncated_send:
	/*
	 * already handling a truncated send?
	 */
	if (wsi->truncated_send_len) {
		lwsl_info("***** %x partial send moved on by %d (vs %d)\n",
							     wsi, n, real_len);
		wsi->truncated_send_offset += n;
		wsi->truncated_send_len -= n;

		if (!wsi->truncated_send_len) {
			lwsl_info("***** %x partial send completed\n", wsi);
			/* done with it, but don't free it */
			n = real_len;
		} else
			libwebsocket_callback_on_writable(
					     wsi->protocol->owning_server, wsi);

		return n;
	}

	if (n < real_len) {
		if (n && wsi->u.ws.clean_buffer)
			/*
			 * This buffer unaffected by extension rewriting.
			 * It means the user code is expected to deal with
			 * partial sends.  (lws knows the header was already
			 * sent, so on next send will just resume sending
			 * payload)
			 */
			 return n;

		/*
		 * Newly truncated send.  Buffer the remainder (it will get
		 * first priority next time the socket is writable)
		 */
		lwsl_info("***** %x new partial sent %d from %d total\n",
							      wsi, n, real_len);

		/*
		 *  - if we still have a suitable malloc lying around, use it
		 *  - or, if too small, reallocate it
		 *  - or, if no buffer, create it
		 */
		if (!wsi->truncated_send_malloc ||
				real_len - n > wsi->truncated_send_allocation) {
			if (wsi->truncated_send_malloc)
				free(wsi->truncated_send_malloc);

			wsi->truncated_send_allocation = real_len - n;
			wsi->truncated_send_malloc = malloc(real_len - n);
			if (!wsi->truncated_send_malloc) {
				lwsl_err(
				   "truncated send: unable to malloc %d\n",
								  real_len - n);
				return -1;
			}
		}
		wsi->truncated_send_offset = 0;
		wsi->truncated_send_len = real_len - n;
		memcpy(wsi->truncated_send_malloc, buf + n, real_len - n);

		libwebsocket_callback_on_writable(
					     wsi->protocol->owning_server, wsi);

		return real_len;
	}

	return n;
}