예제 #1
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;
}
예제 #2
0
파일: client.c 프로젝트: 93i/godot
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;
}