Ejemplo n.º 1
0
LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
{
#ifdef LWS_USE_HTTP2
	struct lws *network_wsi, *wsi2;
	int already;

	lwsl_info("%s: %p\n", __func__, wsi);

	if (wsi->mode != LWSCM_HTTP2_SERVING)
		goto network_sock;

	if (wsi->u.http2.requested_POLLOUT) {
		lwsl_info("already pending writable\n");
		return 1;
	}

	if (wsi->u.http2.tx_credit <= 0) {
		/*
		 * other side is not able to cope with us sending
		 * anything so no matter if we have POLLOUT on our side.
		 *
		 * Delay waiting for our POLLOUT until peer indicates he has
		 * space for more using tx window command in http2 layer
		 */
		lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi,
			  wsi->u.http2.tx_credit);
		wsi->u.http2.waiting_tx_credit = 1;
		return 0;
	}

	network_wsi = lws_http2_get_network_wsi(wsi);
	already = network_wsi->u.http2.requested_POLLOUT;

	/* mark everybody above him as requesting pollout */

	wsi2 = wsi;
	while (wsi2) {
		wsi2->u.http2.requested_POLLOUT = 1;
		lwsl_info("mark %p pending writable\n", wsi2);
		wsi2 = wsi2->u.http2.parent_wsi;
	}

	/* for network action, act only on the network wsi */

	wsi = network_wsi;
	if (already)
		return 1;
network_sock:
#endif

	if (lws_ext_cb_wsi_active_exts(wsi,
				LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
		return 1;

	if (wsi->position_in_fds_table < 0) {
		lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock);
		return -1;
	}

	if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
		return -1;

	lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);

	return 1;
}
Ejemplo n.º 2
0
LWS_VISIBLE int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
{
	int n;
	int i;
	DWORD ev;
	WSANETWORKEVENTS networkevents;
	struct libwebsocket_pollfd *pfd;

	/* stay dead once we are dead */

	if (context == NULL)
		return 1;

	context->service_tid = context->protocols[0].callback(context, NULL,
				     LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);

	for (i = 0; i < context->fds_count; ++i) {
		pfd = &context->fds[i];
		if (pfd->fd == context->listen_service_fd)
			continue;

		if (pfd->events & LWS_POLLOUT) {
			if (context->lws_lookup[pfd->fd]->sock_send_blocking)
				continue;
			pfd->revents = LWS_POLLOUT;
			n = libwebsocket_service_fd(context, pfd);
			if (n < 0)
				return n;
		}
	}

	ev = WSAWaitForMultipleEvents(context->fds_count + 1,
				     context->events, FALSE, timeout_ms, FALSE);
	context->service_tid = 0;

	if (ev == WSA_WAIT_TIMEOUT) {
		libwebsocket_service_fd(context, NULL);
		return 0;
	}

	if (ev == WSA_WAIT_EVENT_0) {
		WSAResetEvent(context->events[0]);
		return 0;
	}

	if (ev < WSA_WAIT_EVENT_0 || ev > WSA_WAIT_EVENT_0 + context->fds_count)
		return -1;

	pfd = &context->fds[ev - WSA_WAIT_EVENT_0 - 1];

	if (WSAEnumNetworkEvents(pfd->fd,
			context->events[ev - WSA_WAIT_EVENT_0],
					      &networkevents) == SOCKET_ERROR) {
		lwsl_err("WSAEnumNetworkEvents() failed with error %d\n",
								     LWS_ERRNO);
		return -1;
	}

	pfd->revents = networkevents.lNetworkEvents;

	if (pfd->revents & LWS_POLLOUT)
		context->lws_lookup[pfd->fd]->sock_send_blocking = FALSE;

	return libwebsocket_service_fd(context, pfd);
}
Ejemplo n.º 3
0
LWS_VISIBLE LWS_EXTERN int
lws_cgi_write_split_stdout_headers(struct lws *wsi)
{
	int n, m, cmd;
	unsigned char buf[LWS_PRE + 4096], *start = &buf[LWS_PRE], *p = start,
			*end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
			*value = NULL;
	char c, hrs;

	if (!wsi->http.cgi)
		return -1;

	while (wsi->hdr_state != LHCS_PAYLOAD) {
		/*
		 * We have to separate header / finalize and payload chunks,
		 * since they need to be handled separately
		 */
		switch (wsi->hdr_state) {
		case LHCS_RESPONSE:
			lwsl_debug("LHCS_RESPONSE: issuing response %d\n",
				   wsi->http.cgi->response_code);
			if (lws_add_http_header_status(wsi,
						   wsi->http.cgi->response_code,
						       &p, end))
				return 1;
			if (!wsi->http.cgi->explicitly_chunked &&
			    !wsi->http.cgi->content_length &&
				lws_add_http_header_by_token(wsi,
					WSI_TOKEN_HTTP_TRANSFER_ENCODING,
					(unsigned char *)"chunked", 7, &p, end))
				return 1;
			if (!(wsi->http2_substream))
				if (lws_add_http_header_by_token(wsi,
						WSI_TOKEN_CONNECTION,
						(unsigned char *)"close", 5,
						&p, end))
					return 1;
			n = lws_write(wsi, start, p - start,
				      LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);

			/*
			 * so we have a bunch of http/1 style ascii headers
			 * starting from wsi->http.cgi->headers_buf through
			 * wsi->http.cgi->headers_pos.  These are OK for http/1
			 * connections, but they're no good for http/2 conns.
			 *
			 * Let's redo them at headers_pos forward using the
			 * correct coding for http/1 or http/2
			 */
			if (!wsi->http2_substream)
				goto post_hpack_recode;

			p = wsi->http.cgi->headers_start;
			wsi->http.cgi->headers_start =
					wsi->http.cgi->headers_pos;
			wsi->http.cgi->headers_dumped =
					wsi->http.cgi->headers_start;
			hrs = HR_NAME;
			name = buf;

			while (p < wsi->http.cgi->headers_start) {
				switch (hrs) {
				case HR_NAME:
					/*
					 * in http/2 upper-case header names
					 * are illegal.  So convert to lower-
					 * case.
					 */
					if (name - buf > 64)
						return -1;
					if (*p != ':') {
						if (*p >= 'A' && *p <= 'Z')
							*name++ = (*p++) +
								  ('a' - 'A');
						else
							*name++ = *p++;
					} else {
						p++;
						*name++ = '\0';
						value = name;
						hrs = HR_WHITESPACE;
					}
					break;
				case HR_WHITESPACE:
					if (*p == ' ') {
						p++;
						break;
					}
					hrs = HR_ARG;
					/* fallthru */
				case HR_ARG:
					if (name > end - 64)
						return -1;

					if (*p != '\x0a' && *p != '\x0d') {
						*name++ = *p++;
						break;
					}
					hrs = HR_CRLF;
					/* fallthru */
				case HR_CRLF:
					if ((*p != '\x0a' && *p != '\x0d') ||
					    p + 1 == wsi->http.cgi->headers_start) {
						*name = '\0';
						if ((strcmp((const char *)buf,
							    "transfer-encoding")
						)) {
							lwsl_debug("+ %s: %s\n",
								   buf, value);
							if (
					lws_add_http_header_by_name(wsi, buf,
					(unsigned char *)value, name - value,
					(unsigned char **)&wsi->http.cgi->headers_pos,
					(unsigned char *)wsi->http.cgi->headers_end))
								return 1;
							hrs = HR_NAME;
							name = buf;
							break;
						}
					}
					p++;
					break;
				}
			}
post_hpack_recode:
			/* finalize cached headers before dumping them */
			if (lws_finalize_http_header(wsi,
			      (unsigned char **)&wsi->http.cgi->headers_pos,
			      (unsigned char *)wsi->http.cgi->headers_end)) {

				lwsl_notice("finalize failed\n");
				return -1;
			}

			wsi->hdr_state = LHCS_DUMP_HEADERS;
			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS;
			lws_callback_on_writable(wsi);
			/* back to the loop for writeability again */
			return 0;

		case LHCS_DUMP_HEADERS:

			n = wsi->http.cgi->headers_pos -
			    wsi->http.cgi->headers_dumped;
			if (n > 512)
				n = 512;

			lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);

			cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
			if (wsi->http.cgi->headers_dumped + n !=
			    wsi->http.cgi->headers_pos) {
				lwsl_notice("adding no fin flag\n");
				cmd |= LWS_WRITE_NO_FIN;
			}

			m = lws_write(wsi,
				 (unsigned char *)wsi->http.cgi->headers_dumped,
				      n, cmd);
			if (m < 0) {
				lwsl_debug("%s: write says %d\n", __func__, m);
				return -1;
			}
			wsi->http.cgi->headers_dumped += n;
			if (wsi->http.cgi->headers_dumped ==
			    wsi->http.cgi->headers_pos) {
				wsi->hdr_state = LHCS_PAYLOAD;
				lws_free_set_NULL(wsi->http.cgi->headers_buf);
				lwsl_debug("freed cgi headers\n");
			} else {
				wsi->reason_bf |=
					LWS_CB_REASON_AUX_BF__CGI_HEADERS;
				lws_callback_on_writable(wsi);
			}

			/* writeability becomes uncertain now we wrote
			 * something, we must return to the event loop
			 */
			return 0;
		}

		if (!wsi->http.cgi->headers_buf) {
			/* if we don't already have a headers buf, cook one */
			n = 2048;
			if (wsi->http2_substream)
				n = 4096;
			wsi->http.cgi->headers_buf = lws_malloc(n + LWS_PRE,
							   "cgi hdr buf");
			if (!wsi->http.cgi->headers_buf) {
				lwsl_err("OOM\n");
				return -1;
			}

			lwsl_debug("allocated cgi hdrs\n");
			wsi->http.cgi->headers_start =
					wsi->http.cgi->headers_buf + LWS_PRE;
			wsi->http.cgi->headers_pos = wsi->http.cgi->headers_start;
			wsi->http.cgi->headers_dumped = wsi->http.cgi->headers_pos;
			wsi->http.cgi->headers_end =
					wsi->http.cgi->headers_buf + n - 1;

			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
				wsi->http.cgi->match[n] = 0;
				wsi->http.cgi->lp = 0;
			}
		}

		n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]);
		if (n < 0)
			return -1;
		n = read(n, &c, 1);
		if (n < 0) {
			if (errno != EAGAIN) {
				lwsl_debug("%s: read says %d\n", __func__, n);
				return -1;
			}
			else
				n = 0;

			if (wsi->http.cgi->headers_pos >=
					wsi->http.cgi->headers_end - 4) {
				lwsl_notice("CGI hdrs > buf size\n");

				return -1;
			}
		}
		if (!n)
			goto agin;

		lwsl_debug("-- 0x%02X %c %d %d\n", (unsigned char)c, c,
			   wsi->http.cgi->match[1], wsi->hdr_state);
		if (!c)
			return -1;
		switch (wsi->hdr_state) {
		case LCHS_HEADER:
			hdr:
			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++) {
				/*
				 * significant headers with
				 * numeric decimal payloads
				 */
				if (!significant_hdr[n][wsi->http.cgi->match[n]] &&
				    (c >= '0' && c <= '9') &&
				    wsi->http.cgi->lp < (int)sizeof(wsi->http.cgi->l) - 1) {
					wsi->http.cgi->l[wsi->http.cgi->lp++] = c;
					wsi->http.cgi->l[wsi->http.cgi->lp] = '\0';
					switch (n) {
					case SIGNIFICANT_HDR_CONTENT_LENGTH:
						wsi->http.cgi->content_length =
							atoll(wsi->http.cgi->l);
						break;
					case SIGNIFICANT_HDR_STATUS:
						wsi->http.cgi->response_code =
							atol(wsi->http.cgi->l);
						lwsl_debug("Status set to %d\n",
						   wsi->http.cgi->response_code);
						break;
					default:
						break;
					}
				}
				/* hits up to the NUL are sticky until next hdr */
				if (significant_hdr[n][wsi->http.cgi->match[n]]) {
					if (tolower(c) ==
					    significant_hdr[n][wsi->http.cgi->match[n]])
						wsi->http.cgi->match[n]++;
					else
						wsi->http.cgi->match[n] = 0;
				}
			}

			/* some cgi only send us \x0a for EOL */
			if (c == '\x0a') {
				wsi->hdr_state = LCHS_SINGLE_0A;
				*wsi->http.cgi->headers_pos++ = '\x0d';
			}
			*wsi->http.cgi->headers_pos++ = c;
			if (c == '\x0d')
				wsi->hdr_state = LCHS_LF1;

			if (wsi->hdr_state != LCHS_HEADER &&
			    !significant_hdr[SIGNIFICANT_HDR_TRANSFER_ENCODING]
				    [wsi->http.cgi->match[
					 SIGNIFICANT_HDR_TRANSFER_ENCODING]]) {
				lwsl_info("cgi produced chunked\n");
				wsi->http.cgi->explicitly_chunked = 1;
			}

			/* presence of Location: mandates 302 retcode */
			if (wsi->hdr_state != LCHS_HEADER &&
			    !significant_hdr[SIGNIFICANT_HDR_LOCATION][
			      wsi->http.cgi->match[SIGNIFICANT_HDR_LOCATION]]) {
				lwsl_debug("CGI: Location hdr seen\n");
				wsi->http.cgi->response_code = 302;
			}
			break;
		case LCHS_LF1:
			*wsi->http.cgi->headers_pos++ = c;
			if (c == '\x0a') {
				wsi->hdr_state = LCHS_CR2;
				break;
			}
			/* we got \r[^\n]... it's unreasonable */
			lwsl_debug("%s: funny CRLF 0x%02X\n", __func__,
				   (unsigned char)c);
			return -1;

		case LCHS_CR2:
			if (c == '\x0d') {
				/* drop the \x0d */
				wsi->hdr_state = LCHS_LF2;
				break;
			}
			wsi->hdr_state = LCHS_HEADER;
			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
				wsi->http.cgi->match[n] = 0;
			wsi->http.cgi->lp = 0;
			goto hdr;

		case LCHS_LF2:
		case LCHS_SINGLE_0A:
			m = wsi->hdr_state;
			if (c == '\x0a') {
				lwsl_debug("Content-Length: %lld\n",
					(unsigned long long)
					wsi->http.cgi->content_length);
				wsi->hdr_state = LHCS_RESPONSE;
				/*
				 * drop the \0xa ... finalize
				 * will add it if needed (HTTP/1)
				 */
				break;
			}
			if (m == LCHS_LF2)
				/* we got \r\n\r[^\n]... unreasonable */
				return -1;
			/* we got \x0anext header, it's reasonable */
			*wsi->http.cgi->headers_pos++ = c;
			wsi->hdr_state = LCHS_HEADER;
			for (n = 0; n < SIGNIFICANT_HDR_COUNT; n++)
				wsi->http.cgi->match[n] = 0;
			wsi->http.cgi->lp = 0;
			break;
		case LHCS_PAYLOAD:
			break;
		}

agin:
		/* ran out of input, ended the hdrs, or filled up the hdrs buf */
		if (!n || wsi->hdr_state == LHCS_PAYLOAD)
			return 0;
	}

	/* payload processing */

	m = !wsi->http.cgi->implied_chunked && !wsi->http2_substream &&
	    !wsi->http.cgi->explicitly_chunked &&
	    !wsi->http.cgi->content_length;
	n = lws_get_socket_fd(wsi->http.cgi->stdwsi[LWS_STDOUT]);
	if (n < 0)
		return -1;
	if (m) {
		uint8_t term[LWS_PRE + 6];

		lwsl_info("%s: zero chunk\n", __func__);

		memcpy(term + LWS_PRE, (uint8_t *)"0\x0d\x0a\x0d\x0a", 5);

		if (lws_write(wsi, term + LWS_PRE, 5,
			      LWS_WRITE_HTTP_FINAL) != 5)
			return -1;

		wsi->http.cgi->cgi_transaction_over = 1;

		return 0;
	}

	n = read(n, start, sizeof(buf) - LWS_PRE);

	if (n < 0 && errno != EAGAIN) {
		lwsl_debug("%s: stdout read says %d\n", __func__, n);
		return -1;
	}
	if (n > 0) {
/*
		if (!wsi->http2_substream && m) {
			char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
			m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
					 "%X\x0d\x0a", n);
			memmove(start + m, start, n);
			memcpy(start, chdr, m);
			memcpy(start + m + n, "\x0d\x0a", 2);
			n += m + 2;
		}
		*/

#if defined(LWS_WITH_HTTP2)
		if (wsi->http2_substream) {
			struct lws *nwsi = lws_get_network_wsi(wsi);

			__lws_set_timeout(wsi,
				PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);

			if (!nwsi->immortal_substream_count)
				__lws_set_timeout(nwsi,
					PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
		}
#endif

		cmd = LWS_WRITE_HTTP;
		if (wsi->http.cgi->content_length_seen + n ==
						wsi->http.cgi->content_length)
			cmd = LWS_WRITE_HTTP_FINAL;

		m = lws_write(wsi, (unsigned char *)start, n, cmd);
		//lwsl_notice("write %d\n", m);
		if (m < 0) {
			lwsl_debug("%s: stdout write says %d\n", __func__, m);
			return -1;
		}
		wsi->http.cgi->content_length_seen += n;
	} else {
		if (wsi->cgi_stdout_zero_length) {
			lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
			if (wsi->http2_substream)
				m = lws_write(wsi, (unsigned char *)start, 0,
					      LWS_WRITE_HTTP_FINAL);
			else
				return -1;
			return 1;
		}
		wsi->cgi_stdout_zero_length = 1;
	}
	return 0;
}
Ejemplo n.º 4
0
int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
		   struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
	int n;
	struct lws_tokens eff_buf;
#ifdef LWS_USE_HTTP2
	struct libwebsocket *wsi2;
#endif
	int ret;
	int m;
	int write_type = LWS_WRITE_PONG;

	/* pending truncated sends have uber priority */

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

		if (wsi->u.ws.payload_is_close)
			write_type = LWS_WRITE_CLOSE;

		n = libwebsocket_write(wsi, 
				&wsi->u.ws.ping_payload_buf[
					LWS_SEND_BUFFER_PRE_PADDING],
					wsi->u.ws.ping_payload_len,
							       write_type);
		if (n < 0)
			return -1;

		/* well he is sent, mark him done */
		wsi->u.ws.ping_pending_flag = 0;
		if (wsi->u.ws.payload_is_close)
			/* oh... a close frame was it... then we are done */
			return -1;

		/* otherwise for PING, leave POLLOUT active either way */
		return 0;
	}

	/* if we are closing, don't confuse the user with writeable cb */

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

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

		/* default to nobody has more to spill */

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

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

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

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

		/* no extension has more to spill */

		if (!ret)
			continue;

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

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

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

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

		return 0;
	}
#ifndef LWS_NO_EXTENSIONS
	wsi->extension_data_pending = 0;
#endif
user_service:
	/* one shot */

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

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

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

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

	wsi2 = wsi;
	do {
		wsi2 = wsi2->u.http2.next_child_wsi;
		lwsl_info("%s: child %p\n", __func__, wsi2);
		if (!wsi2)
			continue;
		if (!wsi2->u.http2.requested_POLLOUT)
			continue;
		wsi2->u.http2.requested_POLLOUT = 0;
		if (lws_calllback_as_writeable(context, wsi2)) {
			lwsl_debug("Closing POLLOUT child\n");
			libwebsocket_close_and_free_session(context, wsi2,
						LWS_CLOSE_STATUS_NOSTATUS);
		}
		wsi2 = wsi;
	} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
	
	lwsl_info("%s: completed\n", __func__);
	
	return 0;
notify:
#endif
	return lws_calllback_as_writeable(context, wsi);
}
Ejemplo n.º 5
0
int
lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len,
			 const char *passphrase)
{
	BIO* bio = BIO_new(BIO_s_mem());
	BIGNUM *mpi, *dummy[6];
	EVP_PKEY *pkey = NULL;
	EC_KEY *ecpriv = NULL;
	RSA *rsapriv = NULL;
	const BIGNUM *cmpi;
	int n, m, ret = -1;

	BIO_write(bio, pem, len);
	PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb,
				(void *)passphrase);
	BIO_free(bio);
	lws_explicit_bzero((void *)pem, len);
	if (!pkey) {
		lwsl_err("%s: unable to parse PEM privkey\n", __func__);
		lws_tls_err_describe();

		return -1;
	}

	/* confirm the key type matches the existing jwk situation */

	switch (jwk->kty) {
	case LWS_GENCRYPTO_KTY_EC:
		if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
			lwsl_err("%s: jwk is EC but privkey isn't\n", __func__);

			goto bail;
		}
		ecpriv = EVP_PKEY_get1_EC_KEY(pkey);
		if (!ecpriv) {
			lwsl_notice("%s: missing EC key\n", __func__);

			goto bail;
		}

		cmpi = EC_KEY_get0_private_key(ecpriv);

		/* quick size check first */

		n = BN_num_bytes(cmpi);
		if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) {
			lwsl_err("%s: jwk key size doesn't match\n", __func__);

			goto bail1;
		}

		/* TODO.. check public curve / group + point */

		jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = n;
		jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc(n, "ec");
		if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
			goto bail1;

		m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
				      jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
		if (m != BN_num_bytes(cmpi))
			goto bail1;

		break;

	case LWS_GENCRYPTO_KTY_RSA:
		if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) {
			lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__);

			goto bail;
		}
		rsapriv = EVP_PKEY_get1_RSA(pkey);
		if (!rsapriv) {
			lwsl_notice("%s: missing RSA key\n", __func__);

			goto bail;
		}

#if defined(LWS_HAVE_RSA_SET0_KEY)
		RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */
				      (const BIGNUM **)&dummy[1], /* e */
				      (const BIGNUM **)&mpi);	  /* d */
		RSA_get0_factors(rsapriv, (const BIGNUM **)&dummy[4],  /* p */
					  (const BIGNUM **)&dummy[5]); /* q */
#else
		dummy[0] = rsapriv->n;
		dummy[1] = rsapriv->e;
		dummy[4] = rsapriv->p;
		dummy[5] = rsapriv->q;
		mpi = rsapriv->d;
#endif

		/* quick size check first */

		n = BN_num_bytes(mpi);
		if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) {
			lwsl_err("%s: jwk key size doesn't match\n", __func__);

			goto bail1;
		}

		/* then check that n & e match what we got from the cert */

		dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf,
				     jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len,
				     NULL);
		dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
				     jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len,
				     NULL);

		m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]);
		BN_clear_free(dummy[2]);
		BN_clear_free(dummy[3]);
		if (m) {
			lwsl_err("%s: privkey doesn't match jwk pubkey\n",
				 __func__);

			goto bail1;
		}

		/* accept d from the PEM privkey into the JWK */

		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = n;
		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc(n, "privjk");
		if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
			goto bail1;

		BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);

		/* accept p and q from the PEM privkey into the JWK */

		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].len = BN_num_bytes(dummy[4]);
		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc(n, "privjk");
		if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) {
			lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
			goto bail1;
		}
		BN_bn2bin(dummy[4], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf);

		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].len = BN_num_bytes(dummy[5]);
		jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc(n, "privjk");
		if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf) {
			lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
			lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf);
			goto bail1;
		}
		BN_bn2bin(dummy[5], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf);
		break;
	default:
		lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty);
		return -1;
	}

	ret = 0;

bail1:
	if (jwk->kty == LWS_GENCRYPTO_KTY_EC)
		EC_KEY_free(ecpriv);
	else
		RSA_free(rsapriv);

bail:
	EVP_PKEY_free(pkey);

	return ret;
}
Ejemplo n.º 6
0
int lws_handshake_server(struct libwebsocket_context *context,
		struct libwebsocket *wsi, unsigned char **buf, size_t len)
{
	struct allocated_headers *ah;
	int protocol_len;
	char protocol_list[128];
	char protocol_name[32];
	char *p;
	int n, hit;

	/* LWS_CONNMODE_WS_SERVING */

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

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

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

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
		libwebsocket_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 libwebsocket_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 (libwebsocket_ensure_user_space(wsi))
			goto bail_nuke_ah;

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

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


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

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

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

		/* drop the header info -- no bail_nuke_ah after this */
		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;
}
Ejemplo n.º 7
0
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);
}
Ejemplo n.º 8
0
int main(int argc, char **argv)
{
	char cert_path[1024];
	char key_path[1024];
	int n = 0;
	int use_ssl = 0;
	int opts = 0;
	char interface_name[128] = "";
	const char *iface = NULL;
#ifndef WIN32
	int syslog_options = LOG_PID | LOG_PERROR;
#endif
	unsigned int oldus = 0;
	struct lws_context_creation_info info;

	int debug_level = 7;
#ifndef LWS_NO_DAEMONIZE
	int daemonize = 0;
#endif

	memset(&info, 0, sizeof info);
	info.port = 7681;

	while (n >= 0) {
		n = getopt_long(argc, argv, "ci:hsap:d:Dr:", options, NULL);
		if (n < 0)
			continue;
		switch (n) {
#ifndef LWS_NO_DAEMONIZE
		case 'D':
			daemonize = 1;
			#ifndef WIN32
			syslog_options &= ~LOG_PERROR;
			#endif
			break;
#endif
		case 'd':
			debug_level = atoi(optarg);
			break;
		case 's':
			use_ssl = 1;
			break;
		case 'a':
			opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
			break;
		case 'p':
			info.port = atoi(optarg);
			break;
		case 'i':
			strncpy(interface_name, optarg, sizeof interface_name);
			interface_name[(sizeof interface_name) - 1] = '\0';
			iface = interface_name;
			break;
		case 'c':
			close_testing = 1;
			fprintf(stderr, " Close testing mode -- closes on "
					   "client after 50 dumb increments"
					   "and suppresses lws_mirror spam\n");
			break;
		case 'r':
			resource_path = optarg;
			printf("Setting resource path to \"%s\"\n", resource_path);
			break;
		case 'h':
			fprintf(stderr, "Usage: test-server "
					"[--port=<p>] [--ssl] "
					"[-d <log bitfield>] "
					"[--resource_path <path>]\n");
			exit(1);
		}
	}

#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
	/* 
	 * normally lock path would be /var/lock/lwsts or similar, to
	 * simplify getting started without having to take care about
	 * permissions or running as root, set to /tmp/.lwsts-lock
	 */
	if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
		fprintf(stderr, "Failed to daemonize\n");
		return 1;
	}
#endif

	signal(SIGINT, sighandler);

#ifndef WIN32
	/* we will only try to log things according to our debug_level */
	setlogmask(LOG_UPTO (LOG_DEBUG));
	openlog("lwsts", syslog_options, LOG_DAEMON);
#endif

	/* tell the library what debug level to emit and to send it to syslog */
	lws_set_log_level(debug_level, lwsl_emit_syslog);

	lwsl_notice("libwebsockets test server - "
			"(C) Copyright 2010-2013 Andy Green <*****@*****.**> - "
						    "licensed under LGPL2.1\n");
#ifdef EXTERNAL_POLL
	max_poll_elements = getdtablesize();
	pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
	fd_lookup = malloc(max_poll_elements * sizeof (int));
	if (pollfds == NULL || fd_lookup == NULL) {
		lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
		return -1;
	}
#endif

	info.iface = iface;
	info.protocols = protocols;
#ifndef LWS_NO_EXTENSIONS
	info.extensions = libwebsocket_get_internal_extensions();
#endif
	if (!use_ssl) {
		info.ssl_cert_filepath = NULL;
		info.ssl_private_key_filepath = NULL;
	} else {
		if (strlen(resource_path) > sizeof(cert_path) - 32) {
			lwsl_err("resource path too long\n");
			return -1;
		}
		sprintf(cert_path, "%s/libwebsockets-test-server.pem",
								resource_path);
		if (strlen(resource_path) > sizeof(key_path) - 32) {
			lwsl_err("resource path too long\n");
			return -1;
		}
		sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
								resource_path);

		info.ssl_cert_filepath = cert_path;
		info.ssl_private_key_filepath = key_path;
	}
	info.gid = -1;
	info.uid = -1;
	info.options = opts;

	context = libwebsocket_create_context(&info);
	if (context == NULL) {
		lwsl_err("libwebsocket init failed\n");
		return -1;
	}

	n = 0;
	while (n >= 0 && !force_exit) {
		struct timeval tv;

		gettimeofday(&tv, NULL);

		/*
		 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
		 * live websocket connection using the DUMB_INCREMENT protocol,
		 * as soon as it can take more packets (usually immediately)
		 */

		if (((unsigned int)tv.tv_usec - oldus) > 50000) {
			libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
			oldus = tv.tv_usec;
		}

#ifdef EXTERNAL_POLL

		/*
		 * this represents an existing server's single poll action
		 * which also includes libwebsocket sockets
		 */

		n = poll(pollfds, count_pollfds, 50);
		if (n < 0)
			continue;


		if (n)
			for (n = 0; n < count_pollfds; n++)
				if (pollfds[n].revents)
					/*
					* returns immediately if the fd does not
					* match anything under libwebsockets
					* control
					*/
					if (libwebsocket_service_fd(context,
								  &pollfds[n]) < 0)
						goto done;
#else
		/*
		 * If libwebsockets sockets are all we care about,
		 * you can use this api which takes care of the poll()
		 * and looping through finding who needed service.
		 *
		 * If no socket needs service, it'll return anyway after
		 * the number of ms in the second argument.
		 */

		n = libwebsocket_service(context, 50);
#endif
	}

#ifdef EXTERNAL_POLL
done:
#endif

	libwebsocket_context_destroy(context);

	lwsl_notice("libwebsockets-test-server exited cleanly\n");

#ifndef WIN32
	closelog();
#endif

	return 0;
}
Ejemplo n.º 9
0
LWS_VISIBLE int
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
{
	struct lws_context_per_thread *pt = &context->pt[tsi];
	struct lws_vhost *vh = context->vhost_list;
	int status = 0, n, ns, first = 1;

	if (!pt->io_loop_uv) {
		if (!loop) {
			loop = lws_malloc(sizeof(*loop));
			if (!loop) {
				lwsl_err("OOM\n");
				return -1;
			}
	#if UV_VERSION_MAJOR > 0
			uv_loop_init(loop);
	#else
			lwsl_err("This libuv is too old to work...\n");
			return 1;
	#endif
			pt->ev_loop_foreign = 0;
		} else {
			lwsl_notice(" Using foreign event loop...\n");
			pt->ev_loop_foreign = 1;
		}

		pt->io_loop_uv = loop;
		uv_idle_init(loop, &pt->uv_idle);

		ns = ARRAY_SIZE(sigs);
		if (lws_check_opt(context->options,
				  LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
			ns = 2;

		if (pt->context->use_ev_sigint) {
			assert(ns <= ARRAY_SIZE(pt->signals));
			for (n = 0; n < ns; n++) {
				uv_signal_init(loop, &pt->signals[n]);
				pt->signals[n].data = pt->context;
				uv_signal_start(&pt->signals[n],
						context->lws_uv_sigint_cb, sigs[n]);
			}
		}
	} else
		first = 0;

	/*
	 * Initialize the accept wsi read watcher with all the listening sockets
	 * and register a callback for read operations
	 *
	 * We have to do it here because the uv loop(s) are not
	 * initialized until after context creation.
	 */
	while (vh) {
		if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1)
			return -1;
		vh = vh->vhost_next;
	}

	if (first) {
		uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
		uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb,
			       10, 1000);
	}

	return status;
}
Ejemplo n.º 10
0
static int callback_http(struct libwebsocket_context *context,
		struct libwebsocket *wsi,
		enum libwebsocket_callback_reasons reason, void *user,
							   void *in, size_t len)
{
#if 0
	char client_name[128];
	char client_ip[128];
#endif
	char buf[256];
	char leaf_path[1024];
	char b64[64];
	struct timeval tv;
	int n, m;
	unsigned char *p;
	char *other_headers;
	static unsigned char buffer[4096];
	struct stat stat_buf;
	struct per_session_data__http *pss =
			(struct per_session_data__http *)user;
	const char *mimetype;
#ifdef EXTERNAL_POLL
	struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
#endif

	switch (reason) {
	case LWS_CALLBACK_HTTP:

		dump_handshake_info(wsi);

		if (len < 1) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_BAD_REQUEST, NULL);
			return -1;
		}

		/* this server has no concept of directories */
		if (strchr((const char *)in + 1, '/')) {
			libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_FORBIDDEN, NULL);
			return -1;
		}

		/* if a legal POST URL, let it continue and accept data */
		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
			return 0;

		/* check for the "send a big file by hand" example case */

		if (!strcmp((const char *)in, "/leaf.jpg")) {
			if (strlen(resource_path) > sizeof(leaf_path) - 10)
				return -1;
			sprintf(leaf_path, "%s/leaf.jpg", resource_path);

			/* well, let's demonstrate how to send the hard way */

			p = buffer;

#ifdef WIN32
			pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
#else
			pss->fd = open(leaf_path, O_RDONLY);
#endif

			if (pss->fd < 0)
				return -1;

			fstat(pss->fd, &stat_buf);

			/*
			 * we will send a big jpeg file, but it could be
			 * anything.  Set the Content-Type: appropriately
			 * so the browser knows what to do with it.
			 */

			p += sprintf((char *)p,
				"HTTP/1.0 200 OK\x0d\x0a"
				"Server: libwebsockets\x0d\x0a"
				"Content-Type: image/jpeg\x0d\x0a"
					"Content-Length: %u\x0d\x0a\x0d\x0a",
					(unsigned int)stat_buf.st_size);

			/*
			 * send the http headers...
			 * this won't block since it's the first payload sent
			 * on the connection since it was established
			 * (too small for partial)
			 */

			n = libwebsocket_write(wsi, buffer,
				   p - buffer, LWS_WRITE_HTTP);

			if (n < 0) {
				close(pss->fd);
				return -1;
			}
			/*
			 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
			 */
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

		/* if not, send a file the easy way */
		strcpy(buf, resource_path);
		if (strcmp(in, "/")) {
			if (*((const char *)in) != '/')
				strcat(buf, "/");
			strncat(buf, in, sizeof(buf) - strlen(resource_path));
		} else /* default file to serve */
			strcat(buf, "/test.html");
		buf[sizeof(buf) - 1] = '\0';

		/* refuse to serve files we don't understand */
		mimetype = get_mimetype(buf);
		if (!mimetype) {
			lwsl_err("Unknown mimetype for %s\n", buf);
			libwebsockets_return_http_status(context, wsi,
				      HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
			return -1;
		}

		/* demostrates how to set a cookie on / */

		other_headers = NULL;
		if (!strcmp((const char *)in, "/") &&
			   !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
			/* this isn't very unguessable but it'll do for us */
			gettimeofday(&tv, NULL);
			sprintf(b64, "LWS_%u_%u_COOKIE",
				(unsigned int)tv.tv_sec,
				(unsigned int)tv.tv_usec);

			sprintf(leaf_path,
				"Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a",
			    (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
			other_headers = leaf_path;
			lwsl_err(other_headers);
		}

		if (libwebsockets_serve_http_file(context, wsi, buf,
						mimetype, other_headers))
			return -1; /* through completion or error, close the socket */

		/*
		 * notice that the sending of the file completes asynchronously,
		 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
		 * it's done
		 */

		break;

	case LWS_CALLBACK_HTTP_BODY:
		strncpy(buf, in, 20);
		buf[20] = '\0';
		if (len < 20)
			buf[len] = '\0';

		lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
				(const char *)buf, (int)len);

		break;

	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
		lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
		/* the whole of the sent body arried, close the connection */
		libwebsockets_return_http_status(context, wsi,
						HTTP_STATUS_OK, NULL);

		return -1;

	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
//		lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
		/* kill the connection after we sent one file */
		return -1;

	case LWS_CALLBACK_HTTP_WRITEABLE:
		/*
		 * we can send more of whatever it is we were sending
		 */

		do {
			n = read(pss->fd, buffer, sizeof buffer);
			/* problem reading, close conn */
			if (n < 0)
				goto bail;
			/* sent it all, close conn */
			if (n == 0)
				goto flush_bail;
			/*
			 * because it's HTTP and not websocket, don't need to take
			 * care about pre and postamble
			 */
			m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
			if (m < 0)
				/* write failed, close conn */
				goto bail;
			if (m != n)
				/* partial write, adjust */
				lseek(pss->fd, m - n, SEEK_CUR);

		} while (!lws_send_pipe_choked(wsi));
		libwebsocket_callback_on_writable(context, wsi);
		break;
flush_bail:
		/* true if still partial pending */
		if (lws_send_pipe_choked(wsi)) {
			libwebsocket_callback_on_writable(context, wsi);
			break;
		}

bail:
		close(pss->fd);
		return -1;

	/*
	 * callback for confirming to continue with client IP appear in
	 * protocol 0 callback since no websocket protocol has been agreed
	 * yet.  You can just ignore this if you won't filter on client IP
	 * since the default uhandled callback return is 0 meaning let the
	 * connection continue.
	 */

	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
#if 0
		libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
			     sizeof(client_name), client_ip, sizeof(client_ip));

		fprintf(stderr, "Received network connect from %s (%s)\n",
							client_name, client_ip);
#endif
		/* if we returned non-zero from here, we kill the connection */
		break;

#ifdef EXTERNAL_POLL
	/*
	 * callbacks for managing the external poll() array appear in
	 * protocol 0 callback
	 */

	case LWS_CALLBACK_LOCK_POLL:
		/*
		 * lock mutex to protect pollfd state
		 * called before any other POLL related callback
		 */
		break;

	case LWS_CALLBACK_UNLOCK_POLL:
		/*
		 * unlock mutex to protect pollfd state when
		 * called after any other POLL related callback
		 */
		break;

	case LWS_CALLBACK_ADD_POLL_FD:

		if (count_pollfds >= max_poll_elements) {
			lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
			return 1;
		}

		fd_lookup[pa->fd] = count_pollfds;
		pollfds[count_pollfds].fd = pa->fd;
		pollfds[count_pollfds].events = pa->events;
		pollfds[count_pollfds++].revents = 0;
		break;

	case LWS_CALLBACK_DEL_POLL_FD:
		if (!--count_pollfds)
			break;
		m = fd_lookup[pa->fd];
		/* have the last guy take up the vacant slot */
		pollfds[m] = pollfds[count_pollfds];
		fd_lookup[pollfds[count_pollfds].fd] = m;
		break;

	case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
	        pollfds[fd_lookup[pa->fd]].events = pa->events;
		break;

#endif

	case LWS_CALLBACK_GET_THREAD_ID:
		/*
		 * if you will call "libwebsocket_callback_on_writable"
		 * from a different thread, return the caller thread ID
		 * here so lws can use this information to work out if it
		 * should signal the poll() loop to exit and restart early
		 */

		/* return pthread_getthreadid_np(); */

		break;

	default:
		break;
	}

	return 0;
}
Ejemplo n.º 11
0
static int
callback_lws_mirror(struct libwebsocket_context *context,
			struct libwebsocket *wsi,
			enum libwebsocket_callback_reasons reason,
					       void *user, void *in, size_t len)
{
	int n;
	struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;

	switch (reason) {

	case LWS_CALLBACK_ESTABLISHED:
		lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
		pss->ringbuffer_tail = ringbuffer_head;
		pss->wsi = wsi;
		break;

	case LWS_CALLBACK_PROTOCOL_DESTROY:
		lwsl_notice("mirror protocol cleaning up\n");
		for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
			if (ringbuffer[n].payload)
				free(ringbuffer[n].payload);
		break;

	case LWS_CALLBACK_SERVER_WRITEABLE:
		if (close_testing)
			break;
		while (pss->ringbuffer_tail != ringbuffer_head) {

			n = libwebsocket_write(wsi, (unsigned char *)
				   ringbuffer[pss->ringbuffer_tail].payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   ringbuffer[pss->ringbuffer_tail].len,
								LWS_WRITE_TEXT);
			if (n < 0) {
				lwsl_err("ERROR %d writing to mirror socket\n", n);
				return -1;
			}
			if (n < ringbuffer[pss->ringbuffer_tail].len)
				lwsl_err("mirror partial write %d vs %d\n",
				       n, ringbuffer[pss->ringbuffer_tail].len);

			if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
				pss->ringbuffer_tail = 0;
			else
				pss->ringbuffer_tail++;

			if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
				libwebsocket_rx_flow_allow_all_protocol(
					       libwebsockets_get_protocol(wsi));

			// lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));

			if (lws_send_pipe_choked(wsi)) {
				libwebsocket_callback_on_writable(context, wsi);
				break;
			}
			/*
			 * for tests with chrome on same machine as client and
			 * server, this is needed to stop chrome choking
			 */
#ifdef _WIN32
			Sleep(1);
#else
			usleep(1);
#endif
		}
		break;

	case LWS_CALLBACK_RECEIVE:

		if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
			lwsl_err("dropping!\n");
			goto choke;
		}

		if (ringbuffer[ringbuffer_head].payload)
			free(ringbuffer[ringbuffer_head].payload);

		ringbuffer[ringbuffer_head].payload =
				malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
						  LWS_SEND_BUFFER_POST_PADDING);
		ringbuffer[ringbuffer_head].len = len;
		memcpy((char *)ringbuffer[ringbuffer_head].payload +
					  LWS_SEND_BUFFER_PRE_PADDING, in, len);
		if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
			ringbuffer_head = 0;
		else
			ringbuffer_head++;

		if (((ringbuffer_head - pss->ringbuffer_tail) &
				  (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
			goto done;

choke:
		lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
		libwebsocket_rx_flow_control(wsi, 0);

//		lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
done:
		libwebsocket_callback_on_writable_all_protocol(
					       libwebsockets_get_protocol(wsi));
		break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

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

	default:
		break;
	}

	return 0;
}
Ejemplo n.º 12
0
static void pssl_debug(void *ctx, int level, const char *str)
{
    lwsl_err("PolarSSL [level %d]: %s", level, str);
}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
0
LWS_VISIBLE struct lws_context *
lws_create_context(struct lws_context_creation_info *info)
{
    struct lws_context *context = NULL;
    struct lws wsi;
#ifndef LWS_NO_DAEMONIZE
    int pid_daemon = get_daemonize_pid();
#endif
    char *p;
    int n;

    lwsl_notice("Initial logging level %d\n", log_level);

    lwsl_notice("Libwebsockets version: %s\n", library_version);
#if LWS_POSIX
#ifdef LWS_USE_IPV6
    if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6))
        lwsl_notice("IPV6 compiled in and enabled\n");
    else
        lwsl_notice("IPV6 compiled in but disabled\n");
#else
    lwsl_notice("IPV6 not compiled in\n");
#endif
    lws_feature_status_libev(info);
#endif
    lwsl_info(" LWS_MAX_HEADER_LEN    : %u\n", LWS_MAX_HEADER_LEN);
    lwsl_info(" LWS_MAX_PROTOCOLS     : %u\n", LWS_MAX_PROTOCOLS);
    lwsl_info(" LWS_MAX_SMP           : %u\n", LWS_MAX_SMP);
    lwsl_info(" SPEC_LATEST_SUPPORTED : %u\n", SPEC_LATEST_SUPPORTED);
    lwsl_info(" AWAITING_TIMEOUT      : %u\n", AWAITING_TIMEOUT);
    lwsl_info(" sizeof (*info)        : %u\n", sizeof(*info));
#if LWS_POSIX
    lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
#endif
    if (lws_plat_context_early_init())
        return NULL;

    context = lws_zalloc(sizeof(struct lws_context));
    if (!context) {
        lwsl_err("No memory for websocket context\n");
        return NULL;
    }
#ifndef LWS_NO_DAEMONIZE
    if (pid_daemon) {
        context->started_with_parent = pid_daemon;
        lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
    }
#endif
    context->max_fds = getdtablesize();

    if (info->count_threads)
        context->count_threads = info->count_threads;
    else
        context->count_threads = 1;

    if (context->count_threads > LWS_MAX_SMP)
        context->count_threads = LWS_MAX_SMP;

    context->lserv_seen = 0;
    context->protocols = info->protocols;
    context->token_limits = info->token_limits;
    context->listen_port = info->port;
    context->http_proxy_port = 0;
    context->http_proxy_address[0] = '\0';
    context->options = info->options;
    context->iface = info->iface;
    context->ka_time = info->ka_time;
    context->ka_interval = info->ka_interval;
    context->ka_probes = info->ka_probes;

    /* we zalloc only the used ones, so the memory is not wasted
     * allocating for unused threads
     */
    for (n = 0; n < context->count_threads; n++) {
        context->pt[n].serv_buf = lws_zalloc(LWS_MAX_SOCKET_IO_BUF);
        if (!context->pt[n].serv_buf) {
            lwsl_err("OOM\n");
            return NULL;
        }
    }

    if (info->fd_limit_per_thread)
        context->fd_limit_per_thread = info->fd_limit_per_thread;
    else
        context->fd_limit_per_thread = context->max_fds /
                                       context->count_threads;

    lwsl_notice(" Threads: %d each %d fds\n", context->count_threads,
                context->fd_limit_per_thread);

    memset(&wsi, 0, sizeof(wsi));
    wsi.context = context;

    if (!info->ka_interval && info->ka_time > 0) {
        lwsl_err("info->ka_interval can't be 0 if ka_time used\n");
        return NULL;
    }

#ifdef LWS_USE_LIBEV
    /* (Issue #264) In order to *avoid breaking backwards compatibility*, we
     * enable libev mediated SIGINT handling with a default handler of
     * lws_sigint_cb. The handler can be overridden or disabled
     * by invoking lws_sigint_cfg after creating the context, but
     * before invoking lws_initloop:
     */
    context->use_ev_sigint = 1;
    context->lws_ev_sigint_cb = &lws_sigint_cb;
#endif /* LWS_USE_LIBEV */

    lwsl_info(" mem: context:         %5u bytes (%d + (%d x %d))\n",
              sizeof(struct lws_context) +
              (context->count_threads * LWS_MAX_SOCKET_IO_BUF),
              sizeof(struct lws_context),
              context->count_threads,
              LWS_MAX_SOCKET_IO_BUF);

    /*
     * allocate and initialize the pool of
     * allocated_header structs + data
     */
    if (info->max_http_header_data)
        context->max_http_header_data = info->max_http_header_data;
    else
        context->max_http_header_data = LWS_MAX_HEADER_LEN;
    if (info->max_http_header_pool)
        context->max_http_header_pool = info->max_http_header_pool;
    else
        context->max_http_header_pool = LWS_MAX_HEADER_POOL;

    context->http_header_data = lws_malloc(context->max_http_header_data *
                                           context->max_http_header_pool);
    if (!context->http_header_data)
        goto bail;
    context->ah_pool = lws_zalloc(sizeof(struct allocated_headers) *
                                  context->max_http_header_pool);
    if (!context->ah_pool)
        goto bail;

    for (n = 0; n < context->max_http_header_pool; n++)
        context->ah_pool[n].data = (char *)context->http_header_data +
                                   (n * context->max_http_header_data);

    /* this is per context */
    lwsl_info(" mem: http hdr rsvd:   %5u bytes ((%u + %u) x %u)\n",
              (context->max_http_header_data +
               sizeof(struct allocated_headers)) *
              context->max_http_header_pool,
              context->max_http_header_data,
              sizeof(struct allocated_headers),
              context->max_http_header_pool);
    n = sizeof(struct lws_pollfd) * context->count_threads *
        context->fd_limit_per_thread;
    context->pt[0].fds = lws_zalloc(n);
    if (context->pt[0].fds == NULL) {
        lwsl_err("OOM allocating %d fds\n", context->max_fds);
        goto bail;
    }
    lwsl_info(" mem: pollfd map:      %5u\n", n);
    /* each thread serves his own chunk of fds */
    for (n = 1; n < (int)info->count_threads; n++)
        context->pt[n].fds = context->pt[0].fds +
                             (n * context->fd_limit_per_thread);

    if (lws_plat_init(context, info))
        goto bail;

    lws_context_init_extensions(info, context);

    context->user_space = info->user;

    lwsl_notice(" mem: per-conn:        %5u bytes + protocol rx buf\n",
                sizeof(struct lws));

    strcpy(context->canonical_hostname, "unknown");
    lws_server_get_canonical_hostname(context, info);

    /* either use proxy from info, or try get it from env var */

    if (info->http_proxy_address) {
        /* override for backwards compatibility */
        if (info->http_proxy_port)
            context->http_proxy_port = info->http_proxy_port;
        lws_set_proxy(context, info->http_proxy_address);
    } else {
#ifdef LWS_HAVE_GETENV
        p = getenv("http_proxy");
        if (p)
            lws_set_proxy(context, p);
#endif
    }

    if (lws_context_init_server_ssl(info, context))
        goto bail;

    if (lws_context_init_client_ssl(info, context))
        goto bail;

    if (lws_context_init_server(info, context))
        goto bail;

    /*
     * drop any root privs for this process
     * to listen on port < 1023 we would have needed root, but now we are
     * listening, we don't want the power for anything else
     */
    lws_plat_drop_app_privileges(info);

    /* initialize supported protocols */

    for (context->count_protocols = 0;
            info->protocols[context->count_protocols].callback;
            context->count_protocols++)
        /*
         * inform all the protocols that they are doing their one-time
         * initialization if they want to.
         *
         * NOTE the wsi is all zeros except for the context pointer
         * so lws_get_context(wsi) can work in the callback.
         */
        info->protocols[context->count_protocols].callback(&wsi,
                LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);

    /*
     * give all extensions a chance to create any per-context
     * allocations they need
     */
    if (info->port != CONTEXT_PORT_NO_LISTEN) {
        if (lws_ext_cb_all_exts(context, NULL,
                                LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
            goto bail;
    } else if (lws_ext_cb_all_exts(context, NULL,
                                   LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
        goto bail;

    return context;

bail:
    lws_context_destroy(context);
    return NULL;
}
Ejemplo n.º 15
0
LWS_VISIBLE int
lws_plat_plugins_init(struct lws_context * context, const char * const *d)
{
	struct lws_plugin_capability lcaps;
	struct lws_plugin *plugin;
	lws_plugin_init_func initfunc;
	int m, ret = 0;
	void *v;
	uv_dirent_t dent;
	uv_fs_t req;
	char path[256];
	uv_loop_t loop;
	uv_lib_t lib;

	lib.errmsg = NULL;
	lib.handle = NULL;

	uv_loop_init(&loop);

	lwsl_notice("  Plugins:\n");

	while (d && *d) {

		lwsl_notice("  Scanning %s\n", *d);
		m =uv_fs_scandir(&loop, &req, *d, 0, NULL);
		if (m < 1) {
			lwsl_err("Scandir on %s failed\n", *d);
			return 1;
		}

		while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
			if (strlen(dent.name) < 7)
				continue;

			lwsl_notice("   %s\n", dent.name);

			snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name);
			if (uv_dlopen(path, &lib)) {
				uv_dlerror(&lib);
				lwsl_err("Error loading DSO: %s\n", lib.errmsg);
				goto bail;
			}
			/* we could open it, can we get his init function? */
			m = snprintf(path, sizeof(path) - 1, "init_%s",
				     dent.name + 3 /* snip lib... */);
			path[m - 3] = '\0'; /* snip the .so */
			if (uv_dlsym(&lib, path, &v)) {
				uv_dlerror(&lib);
				lwsl_err("Failed to get init on %s: %s",
						dent.name, lib.errmsg);
				goto bail;
			}
			initfunc = (lws_plugin_init_func)v;
			lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
			m = initfunc(context, &lcaps);
			if (m) {
				lwsl_err("Initializing %s failed %d\n", dent.name, m);
				goto skip;
			}

			plugin = lws_malloc(sizeof(*plugin));
			if (!plugin) {
				lwsl_err("OOM\n");
				goto bail;
			}
			plugin->list = context->plugin_list;
			context->plugin_list = plugin;
			strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1);
			plugin->name[sizeof(plugin->name) - 1] = '\0';
			plugin->lib = lib;
			plugin->caps = lcaps;
			context->plugin_protocol_count += lcaps.count_protocols;
			context->plugin_extension_count += lcaps.count_extensions;

			continue;

skip:
			uv_dlclose(&lib);
		}
bail:
		uv_fs_req_cleanup(&req);
		d++;
	}

	uv_loop_close(&loop);

	return ret;

}
Ejemplo n.º 16
0
int
lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len)
{
	struct cached_file_info *info = NULL;
	lwsac_cached_file_t old = *cache;
	struct lwsac *lac = NULL;
	time_t t = time(NULL);
	unsigned char *a;
	struct stat s;
	size_t all;
	ssize_t rd;
	int fd;

	if (old) { /* we already have a cached copy of it */

		info = (struct cached_file_info *)((*cache) - sizeof(*info));

		if (t - info->last_confirm < 5)
			/* we checked it as fresh less than 5s ago, use old */
			return 0;
	}

	/*
	 * ...it's been 5s, we should check again on the filesystem
	 * that the file hasn't changed
	 */

	fd = open(filepath, O_RDONLY);
	if (fd < 0) {
		lwsl_err("%s: cannot open %s\n", __func__, filepath);

		return 1;
	}

	if (fstat(fd, &s)) {
		lwsl_err("%s: cannot stat %s\n", __func__, filepath);

		goto bail;
	}

	if (old && s.st_mtime == info->s.st_mtime) {
		/* it still seems to be the same as our cached one */
		info->last_confirm = t;

		close(fd);

		return 0;
	}

	/*
	 * we either didn't cache it yet, or it has changed since we cached
	 * it... reload in a new lac and then detach the old lac.
	 */

	all = sizeof(*info) + s.st_size + 1;

	info = lwsac_use(&lac, all, all);
	if (!info)
		goto bail;

	info->s = s;
	info->last_confirm = t;

	a = (unsigned char *)(info + 1);

	*len = s.st_size;
	a[s.st_size] = '\0';

	rd = read(fd, a, s.st_size);
	if (rd != s.st_size) {
		lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath,
			 (int)rd);
		goto bail1;
	}

	close(fd);

	*cache = (lwsac_cached_file_t)a;
	if (old)
		lwsac_use_cached_file_detach(&old);

	return 0;

bail1:
	lwsac_free(&lac);

bail:
	close(fd);

	return 1;
}
Ejemplo n.º 17
0
int lws_context_init_server(struct lws_context_creation_info *info,
			    struct libwebsocket_context *context)
{
	lws_sockfd_type sockfd;
#if LWS_POSIX
	int n;
	struct sockaddr_in sin;
	socklen_t len = sizeof(sin);
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 serv_addr6;
#endif
	struct sockaddr_in serv_addr4;
	struct sockaddr *v;
	int opt = 1;
#endif
	struct libwebsocket *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 libwebsocket));
	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
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
{
	struct libwebsocket_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 */
		libwebsocket_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 libwebsocket_context *context,
		    struct libwebsocket *wsi)
{
	char *uri_ptr = NULL;
	int uri_len = 0;
	enum http_version request_version;
	enum http_connection_type connection_type;
	int http_version_len;
	char content_length_str[32];
	char http_version_str[10];
	char http_conn_str[20];
	unsigned int n, count = 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 (libwebsocket_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 = 0;
	if (wsi->protocol->callback)
		n = wsi->protocol->callback(context, wsi,
					LWS_CALLBACK_FILTER_HTTP_CONNECTION,
					     wsi->user_space, uri_ptr, uri_len);

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

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

	/* now drop the header info we kept a pointer to */
	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 libwebsocket_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;
}
Ejemplo n.º 18
0
LWS_VISIBLE int
lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
	struct lws_context_per_thread *pt = &context->pt[tsi];
	int n = -1, m, c;
	char buf;

	/* stay dead once we are dead */

	if (!context || !context->vhost_list)
		return 1;

	if (timeout_ms < 0)
		goto faked_service;

	lws_libev_run(context, tsi);
	lws_libuv_run(context, tsi);

	if (!context->service_tid_detected) {
		struct lws _lws;

		memset(&_lws, 0, sizeof(_lws));
		_lws.context = context;

		context->service_tid_detected =
			context->vhost_list->protocols[0].callback(
			&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
	}
	context->service_tid = context->service_tid_detected;

	timeout_ms = lws_service_adjust_timeout(context, timeout_ms, tsi);

	n = poll(pt->fds, pt->fds_count, timeout_ms);

#ifdef LWS_OPENSSL_SUPPORT
	if (!pt->rx_draining_ext_list &&
	    !lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
#else
	if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
#endif
		lws_service_fd_tsi(context, NULL, tsi);
		return 0;
	}

faked_service:
	m = lws_service_flag_pending(context, tsi);
	if (m)
		c = -1; /* unknown limit */
	else
		if (n < 0) {
			if (LWS_ERRNO != LWS_EINTR)
				return -1;
			return 0;
		} else
			c = n;

	/* any socket with events to service? */
	for (n = 0; n < pt->fds_count && c; n++) {
		if (!pt->fds[n].revents)
			continue;

		c--;

		if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
			if (read(pt->fds[n].fd, &buf, 1) != 1)
				lwsl_err("Cannot read from dummy pipe.");
			continue;
		}

		m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
		if (m < 0)
			return -1;
		/* if something closed, retry this slot */
		if (m)
			n--;
	}

	return 0;
}

LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
	return 0;
}

LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
	return lws_plat_service_tsi(context, timeout_ms, 0);
}

LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
{
	int optval = 1;
	socklen_t optlen = sizeof(optval);

#if defined(__APPLE__) || \
    defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
    defined(__NetBSD__) || \
    defined(__OpenBSD__)
	struct protoent *tcp_proto;
#endif

	if (vhost->ka_time) {
		/* enable keepalive on this socket */
		optval = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
			       (const void *)&optval, optlen) < 0)
			return 1;

#if defined(__APPLE__) || \
    defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
    defined(__NetBSD__) || \
        defined(__CYGWIN__) || defined(__OpenBSD__)

		/*
		 * didn't find a way to set these per-socket, need to
		 * tune kernel systemwide values
		 */
#else
		/* set the keepalive conditions we want on it too */
		optval = vhost->ka_time;
		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
			       (const void *)&optval, optlen) < 0)
			return 1;

		optval = vhost->ka_interval;
		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
			       (const void *)&optval, optlen) < 0)
			return 1;

		optval = vhost->ka_probes;
		if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
			       (const void *)&optval, optlen) < 0)
			return 1;
#endif
	}

	/* Disable Nagle */
	optval = 1;
#if !defined(__APPLE__) && \
    !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \
    !defined(__NetBSD__) && \
    !defined(__OpenBSD__)
	if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
		return 1;
#else
	tcp_proto = getprotobyname("TCP");
	if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
		return 1;
#endif

	/* We are nonblocking... */
	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
		return 1;

	return 0;
}
Ejemplo n.º 19
0
int
lws_ssl_client_bio_create(struct lws *wsi)
{
	X509_VERIFY_PARAM *param;
	char hostname[128], *p;
	const char *alpn_comma = wsi->context->tls.alpn_default;
	struct alpn_ctx protos;

	if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
			 _WSI_TOKEN_CLIENT_HOST) <= 0) {
		lwsl_err("%s: Unable to get hostname\n", __func__);

		return -1;
	}

	/*
	 * remove any :port part on the hostname... necessary for network
	 * connection but typical certificates do not contain it
	 */
	p = hostname;
	while (*p) {
		if (*p == ':') {
			*p = '\0';
			break;
		}
		p++;
	}

	wsi->tls.ssl = SSL_new(wsi->vhost->tls.ssl_client_ctx);
	if (!wsi->tls.ssl)
		return -1;

	if (wsi->vhost->tls.ssl_info_event_mask)
		SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback);

	if (!(wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
		param = SSL_get0_param(wsi->tls.ssl);
		/* Enable automatic hostname checks */
	//	X509_VERIFY_PARAM_set_hostflags(param,
	//				X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
		X509_VERIFY_PARAM_set1_host(param, hostname, 0);
	}

	if (wsi->vhost->tls.alpn)
		alpn_comma = wsi->vhost->tls.alpn;

	if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
			 _WSI_TOKEN_CLIENT_ALPN) > 0)
		alpn_comma = hostname;

	lwsl_info("%s: %p: client conn sending ALPN list '%s'\n",
		  __func__, wsi, alpn_comma);

	protos.len = lws_alpn_comma_to_openssl(alpn_comma, protos.data,
					       sizeof(protos.data) - 1);

	/* with mbedtls, protos is not pointed to after exit from this call */
	SSL_set_alpn_select_cb(wsi->tls.ssl, &protos);

	/*
	 * use server name indication (SNI), if supported,
	 * when establishing connection
	 */
	SSL_set_verify(wsi->tls.ssl, SSL_VERIFY_PEER,
		       OpenSSL_client_verify_callback);

	SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd);

	return 0;
}
Ejemplo n.º 20
0
LWS_VISIBLE int
lws_plat_plugins_init(struct lws_context * context, const char * const *d)
{
	struct lws_plugin_capability lcaps;
	struct lws_plugin *plugin;
	lws_plugin_init_func initfunc;
	struct dirent **namelist;
	int n, i, m, ret = 0;
	char path[256];
	void *l;

	lwsl_notice("  Plugins:\n");

	while (d && *d) {
		n = scandir(*d, &namelist, filter, alphasort);
		if (n < 0) {
			lwsl_err("Scandir on %s failed\n", *d);
			return 1;
		}

		for (i = 0; i < n; i++) {
			if (strlen(namelist[i]->d_name) < 7)
				goto inval;

			lwsl_notice("   %s\n", namelist[i]->d_name);

			snprintf(path, sizeof(path) - 1, "%s/%s", *d,
				 namelist[i]->d_name);
			l = dlopen(path, RTLD_NOW);
			if (!l) {
				lwsl_err("Error loading DSO: %s\n", dlerror());
				while (i++ < n)
					free(namelist[i]);
				goto bail;
			}
			/* we could open it, can we get his init function? */
			m = snprintf(path, sizeof(path) - 1, "init_%s",
				     namelist[i]->d_name + 3 /* snip lib... */);
			path[m - 3] = '\0'; /* snip the .so */
			initfunc = dlsym(l, path);
			if (!initfunc) {
				lwsl_err("Failed to get init on %s: %s",
						namelist[i]->d_name, dlerror());
				dlclose(l);
			}
			lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
			m = initfunc(context, &lcaps);
			if (m) {
				lwsl_err("Initializing %s failed %d\n",
					namelist[i]->d_name, m);
				dlclose(l);
				goto skip;
			}

			plugin = lws_malloc(sizeof(*plugin));
			if (!plugin) {
				lwsl_err("OOM\n");
				goto bail;
			}
			plugin->list = context->plugin_list;
			context->plugin_list = plugin;
			strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
			plugin->name[sizeof(plugin->name) - 1] = '\0';
			plugin->l = l;
			plugin->caps = lcaps;
			context->plugin_protocol_count += lcaps.count_protocols;
			context->plugin_extension_count += lcaps.count_extensions;

			free(namelist[i]);
			continue;

	skip:
			dlclose(l);
	inval:
			free(namelist[i]);
		}
		free(namelist);
		d++;
	}

bail:
	free(namelist);

	return ret;
}
Ejemplo n.º 21
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 (!lws_socket_is_valid(wsi->sock))
		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, (unsigned int)n == len);

	switch (n) {
	case LWS_SSL_CAPABLE_ERROR:
		lwsl_err("%s: wsi %p: LWS_SSL_CAPABLE_ERROR\n", __func__, (void *)wsi);
		/* 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 ((unsigned int)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;
}
Ejemplo n.º 22
0
static char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
	struct jpargs *a = (struct jpargs *)ctx->user;
	struct lws_protocol_vhost_options *rej;
	int n;

	/* we only match on the prepared path strings */
	if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
		return 0;

	/* this catches, eg, vhosts[].headers[].xxx */
	if (reason == LEJPCB_VAL_STR_END &&
	    ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
		rej = lwsws_align(a);
		a->p += sizeof(*rej);

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		rej->next = a->info->reject_service_keywords;
		a->info->reject_service_keywords = rej;
		rej->name = a->p;
		 lwsl_notice("  adding rej %s=%s\n", a->p, ctx->buf);
		a->p += n - 1;
		*(a->p++) = '\0';
		rej->value = a->p;
		rej->options = NULL;
		goto dostring;
	}

	switch (ctx->path_match - 1) {
	case LEJPGP_UID:
		a->info->uid = atoi(ctx->buf);
		return 0;
	case LEJPGP_GID:
		a->info->gid = atoi(ctx->buf);
		return 0;
	case LEJPGP_COUNT_THREADS:
		a->info->count_threads = atoi(ctx->buf);
		return 0;
	case LWJPGP_INIT_SSL:
		if (arg_to_bool(ctx->buf))
			a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
		return 0;
	case LEJPGP_SERVER_STRING:
		a->info->server_string = a->p;
		break;
	case LEJPGP_PLUGIN_DIR:
		if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
			lwsl_err("Too many plugin dirs\n");
			return -1;
		}
		a->plugin_dirs[a->count_plugin_dirs++] = a->p;
		break;

	case LWJPGP_PINGPONG_SECS:
		a->info->ws_ping_pong_interval = atoi(ctx->buf);
		return 0;

	case LWJPGP_TIMEOUT_SECS:
		a->info->timeout_secs = atoi(ctx->buf);
		return 0;

	default:
		return 0;
	}

dostring:
	a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
	*(a->p)++ = '\0';

	return 0;
}
Ejemplo n.º 23
0
int
lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
		       const char *curves, int rsa_min_bits)
{
	int id, n, ret = -1, count;
	ASN1_OBJECT *obj = NULL;
	const EC_POINT *ecpoint;
	const EC_GROUP *ecgroup;
	EC_KEY *ecpub = NULL;
	X509_PUBKEY *pubkey;
	RSA *rsapub = NULL;
	BIGNUM *mpi[4];
	EVP_PKEY *pkey;

	memset(jwk, 0, sizeof(*jwk));

	pubkey = X509_get_X509_PUBKEY(x509->cert);
	if (!pubkey) {
		lwsl_err("%s: missing pubkey alg in cert\n", __func__);

		goto bail;
	}

	if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) {
		lwsl_err("%s: missing pubkey alg in cert\n", __func__);

		goto bail;
	}

	id = OBJ_obj2nid(obj);
	if (id == NID_undef) {
		lwsl_err("%s: missing pubkey alg in cert\n", __func__);

		goto bail;
	}

	lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id));

	pkey = X509_get_pubkey(x509->cert);
	if (!pkey) {
		lwsl_notice("%s: unable to extract pubkey", __func__);

		goto bail;
	}

	switch (id) {
	case NID_X9_62_id_ecPublicKey:
		lwsl_debug("%s: EC key\n", __func__);
		jwk->kty = LWS_GENCRYPTO_KTY_EC;

		if (!curves) {
			lwsl_err("%s: ec curves not allowed\n", __func__);

			goto bail1;
		}

		ecpub = EVP_PKEY_get1_EC_KEY(pkey);
		if (!ecpub) {
			lwsl_notice("%s: missing EC pubkey\n", __func__);

			goto bail1;
		}

		ecpoint = EC_KEY_get0_public_key(ecpub);
		if (!ecpoint) {
			lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
			goto bail2;
		}

		ecgroup = EC_KEY_get0_group(ecpub);
		if (!ecgroup) {
			lwsl_err("%s: EC_KEY_get0_group failed\n", __func__);
			goto bail2;
		}

		/* validate the curve against ones we allow */

		if (lws_genec_confirm_curve_allowed_by_tls_id(curves,
				EC_GROUP_get_curve_name(ecgroup), jwk))
			/* already logged */
			goto bail2;

		mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL;
		mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */
		mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL;
		mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */

#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates)
		if (EC_POINT_get_affine_coordinates(ecgroup, ecpoint,
#else
		if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint,
#endif
						  mpi[LWS_GENCRYPTO_EC_KEYEL_X],
						  mpi[LWS_GENCRYPTO_EC_KEYEL_Y],
							  NULL) != 1) {
			BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
			BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
			lwsl_err("%s: EC_POINT_get_aff failed\n", __func__);
			goto bail2;
		}
		count = LWS_GENCRYPTO_EC_KEYEL_COUNT;
		n = LWS_GENCRYPTO_EC_KEYEL_X;
		break;

	case NID_rsaEncryption:
		lwsl_debug("%s: rsa key\n", __func__);
		jwk->kty = LWS_GENCRYPTO_KTY_RSA;

		rsapub = EVP_PKEY_get1_RSA(pkey);
		if (!rsapub) {
			lwsl_notice("%s: missing RSA pubkey\n", __func__);

			goto bail1;
		}

		if (RSA_size(rsapub) * 8 < rsa_min_bits) {
			lwsl_err("%s: key bits %d less than minimum %d\n",
				 __func__, RSA_size(rsapub) * 8, rsa_min_bits);

			goto bail2;
		}

#if defined(LWS_HAVE_RSA_SET0_KEY)
		/* we don't need d... but the api wants to write it */
		RSA_get0_key(rsapub,
			    (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N],
			    (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E],
			    (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]);
#else
		mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e;
		mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n;
		mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL;
#endif
		count = LWS_GENCRYPTO_RSA_KEYEL_D;
		n = LWS_GENCRYPTO_RSA_KEYEL_E;
		break;
	default:
		lwsl_err("%s: unknown NID\n", __func__);
		goto bail2;
	}

	for (; n < count; n++) {
		if (!mpi[n])
			continue;
		jwk->e[n].len = BN_num_bytes(mpi[n]);
		jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp");
		if (!jwk->e[n].buf) {
			if (id == NID_X9_62_id_ecPublicKey) {
				BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
				BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
			}
			goto bail2;
		}
		BN_bn2bin(mpi[n], jwk->e[n].buf);
	}

	if (id == NID_X9_62_id_ecPublicKey) {
		BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
		BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
	}

	ret = 0;

bail2:
	if (id == NID_X9_62_id_ecPublicKey)
		EC_KEY_free(ecpub);
	else
		RSA_free(rsapub);

bail1:
	EVP_PKEY_free(pkey);
bail:
	/* jwk destroy will clean any partial state */
	if (ret)
		lws_jwk_destroy(jwk);

	return ret;
}
Ejemplo n.º 24
0
static char
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
{
	struct jpargs *a = (struct jpargs *)ctx->user;
	struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
	struct lws_http_mount *m;
	char *p, *p1;
	int n;

#if 0
	lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
	for (n = 0; n < ctx->wildcount; n++)
		lwsl_notice("    %d\n", ctx->wild[n]);
#endif

	if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
		/* set the defaults for this vhost */
		a->valid = 1;
		a->head = NULL;
		a->last = NULL;
		a->info->port = 0;
		a->info->iface = NULL;
		a->info->protocols = a->protocols;
		a->info->extensions = a->extensions;
		a->info->ssl_cert_filepath = NULL;
		a->info->ssl_private_key_filepath = NULL;
		a->info->ssl_ca_filepath = NULL;
		a->info->client_ssl_cert_filepath = NULL;
		a->info->client_ssl_private_key_filepath = NULL;
		a->info->client_ssl_ca_filepath = NULL;
		a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
			"ECDHE-RSA-AES256-GCM-SHA384:"
			"DHE-RSA-AES256-GCM-SHA384:"
			"ECDHE-RSA-AES256-SHA384:"
			"HIGH:!aNULL:!eNULL:!EXPORT:"
			"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
			"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
			"!DHE-RSA-AES128-SHA256:"
			"!AES128-GCM-SHA256:"
			"!AES128-SHA256:"
			"!DHE-RSA-AES256-SHA256:"
			"!AES256-GCM-SHA384:"
			"!AES256-SHA256";
		a->info->timeout_secs = 5;
		a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
				       "ECDHE-RSA-AES256-GCM-SHA384:"
				       "DHE-RSA-AES256-GCM-SHA384:"
				       "ECDHE-RSA-AES256-SHA384:"
				       "HIGH:!aNULL:!eNULL:!EXPORT:"
				       "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
				       "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
				       "!DHE-RSA-AES128-SHA256:"
				       "!AES128-GCM-SHA256:"
				       "!AES128-SHA256:"
				       "!DHE-RSA-AES256-SHA256:"
				       "!AES256-GCM-SHA384:"
				       "!AES256-SHA256";
		a->info->pvo = NULL;
		a->info->headers = NULL;
		a->info->keepalive_timeout = 5;
		a->info->log_filepath = NULL;
		a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
				      LWS_SERVER_OPTION_STS);
		a->enable_client_ssl = 0;
	}

	if (reason == LEJPCB_OBJECT_START &&
	    ctx->path_match == LEJPVP_MOUNTS + 1) {
		a->fresh_mount = 1;
		memset(&a->m, 0, sizeof(a->m));
	}

	/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
	if (reason == LEJPCB_OBJECT_START &&
	    ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
		a->pvo = lwsws_align(a);
		a->p += sizeof(*a->pvo);

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		/* ie, enable this protocol, no options yet */
		a->pvo->next = a->info->pvo;
		a->info->pvo = a->pvo;
		a->pvo->name = a->p;
		lwsl_notice("  adding protocol %s\n", a->p);
		a->p += n;
		a->pvo->value = a->p;
		a->pvo->options = NULL;
		goto dostring;
	}

	/* this catches, eg, vhosts[].headers[].xxx */
	if (reason == LEJPCB_VAL_STR_END &&
	    ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
		headers = lwsws_align(a);
		a->p += sizeof(*headers);

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		/* ie, enable this protocol, no options yet */
		headers->next = a->info->headers;
		a->info->headers = headers;
		headers->name = a->p;
		// lwsl_notice("  adding header %s=%s\n", a->p, ctx->buf);
		a->p += n - 1;
		*(a->p++) = ':';
		if (a->p < a->end)
			*(a->p++) = '\0';
		else
			*(a->p - 1) = '\0';
		headers->value = a->p;
		headers->options = NULL;
		goto dostring;
	}

	if (reason == LEJPCB_OBJECT_END &&
	    (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
	    a->valid) {

		struct lws_vhost *vhost;

		//lwsl_notice("%s\n", ctx->path);
		if (!a->info->port) {
			lwsl_err("Port required (eg, 443)");
			return 1;
		}
		a->valid = 0;
		a->info->mounts = a->head;

		vhost = lws_create_vhost(a->context, a->info);
		if (!vhost) {
			lwsl_err("Failed to create vhost %s\n",
				 a->info->vhost_name);
			return 1;
		}
		a->any_vhosts = 1;

		if (a->enable_client_ssl) {
			const char *cert_filepath = a->info->client_ssl_cert_filepath;
			const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
			const char *ca_filepath = a->info->client_ssl_ca_filepath;
			const char *cipher_list = a->info->client_ssl_cipher_list;
			memset(a->info, 0, sizeof(*a->info));
			a->info->client_ssl_cert_filepath = cert_filepath;
			a->info->client_ssl_private_key_filepath = private_key_filepath;
			a->info->client_ssl_ca_filepath = ca_filepath;
			a->info->client_ssl_cipher_list = cipher_list;
			a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
			lws_init_vhost_client_ssl(a->info, vhost);
		}

		return 0;
	}

	if (reason == LEJPCB_OBJECT_END &&
	    ctx->path_match == LEJPVP_MOUNTS + 1) {
		static const char * const mount_protocols[] = {
			"http://",
			"https://",
			"file://",
			"cgi://",
			">http://",
			">https://",
			"callback://",
			"gzip://",
		};

		if (!a->fresh_mount)
			return 0;

		if (!a->m.mountpoint || !a->m.origin) {
			lwsl_err("mountpoint and origin required\n");
			return 1;
		}
		lwsl_debug("adding mount %s\n", a->m.mountpoint);
		m = lwsws_align(a);
		memcpy(m, &a->m, sizeof(*m));
		if (a->last)
			a->last->mount_next = m;

		for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
			if (!strncmp(a->m.origin, mount_protocols[n],
			     strlen(mount_protocols[n]))) {
				lwsl_err("----%s\n", a->m.origin);
				m->origin_protocol = n;
				m->origin = a->m.origin +
					    strlen(mount_protocols[n]);
				break;
			}

		if (n == ARRAY_SIZE(mount_protocols)) {
			lwsl_err("unsupported protocol:// %s\n", a->m.origin);
			return 1;
		}

		a->p += sizeof(*m);
		if (!a->head)
			a->head = m;

		a->last = m;
		a->fresh_mount = 0;
	}

	/* we only match on the prepared path strings */
	if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
		return 0;

	switch (ctx->path_match - 1) {
	case LEJPVP_NAME:
		a->info->vhost_name = a->p;
		break;
	case LEJPVP_PORT:
		a->info->port = atoi(ctx->buf);
		return 0;
	case LEJPVP_INTERFACE:
		a->info->iface = a->p;
		break;
	case LEJPVP_UNIXSKT:
		if (arg_to_bool(ctx->buf))
			a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
		else
			a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
		return 0;
	case LEJPVP_STS:
		if (arg_to_bool(ctx->buf))
			a->info->options |= LWS_SERVER_OPTION_STS;
		else
			a->info->options &= ~(LWS_SERVER_OPTION_STS);
		return 0;
	case LEJPVP_HOST_SSL_KEY:
		a->info->ssl_private_key_filepath = a->p;
		break;
	case LEJPVP_HOST_SSL_CERT:
		a->info->ssl_cert_filepath = a->p;
		break;
	case LEJPVP_HOST_SSL_CA:
		a->info->ssl_ca_filepath = a->p;
		break;
	case LEJPVP_ACCESS_LOG:
		a->info->log_filepath = a->p;
		break;
	case LEJPVP_MOUNTPOINT:
		a->m.mountpoint = a->p;
		a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
		break;
	case LEJPVP_ORIGIN:
		if (!strncmp(ctx->buf, "callback://", 11))
			a->m.protocol = a->p + 11;

		if (!a->m.origin)
			a->m.origin = a->p;
		break;
	case LEJPVP_DEFAULT:
		a->m.def = a->p;
		break;
	case LEJPVP_DEFAULT_AUTH_MASK:
		a->m.auth_mask = atoi(ctx->buf);
		return 0;
	case LEJPVP_MOUNT_CACHE_MAX_AGE:
		a->m.cache_max_age = atoi(ctx->buf);
		return 0;
	case LEJPVP_MOUNT_CACHE_REUSE:
		a->m.cache_reusable = arg_to_bool(ctx->buf);
		return 0;
	case LEJPVP_MOUNT_CACHE_REVALIDATE:
		a->m.cache_revalidate = arg_to_bool(ctx->buf);
		return 0;
	case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
		a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
		return 0;
	case LEJPVP_MOUNT_BASIC_AUTH:
		a->m.basic_auth_login_file = a->p;
		break;
	case LEJPVP_CGI_TIMEOUT:
		a->m.cgi_timeout = atoi(ctx->buf);
		return 0;
	case LEJPVP_KEEPALIVE_TIMEOUT:
		a->info->keepalive_timeout = atoi(ctx->buf);
		return 0;
	case LEJPVP_CLIENT_CIPHERS:
		a->info->client_ssl_cipher_list = a->p;
		break;
	case LEJPVP_CIPHERS:
		a->info->ssl_cipher_list = a->p;
		break;
	case LEJPVP_ECDH_CURVE:
		a->info->ecdh_curve = a->p;
		break;
	case LEJPVP_PMO:
	case LEJPVP_CGI_ENV:
		mp_cgienv = lwsws_align(a);
		a->p += sizeof(*a->m.cgienv);

		mp_cgienv->next = a->m.cgienv;
		a->m.cgienv = mp_cgienv;

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		mp_cgienv->name = a->p;
		a->p += n;
		mp_cgienv->value = a->p;
		mp_cgienv->options = NULL;
		//lwsl_notice("    adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
		//		mp_cgienv->value);
		goto dostring;

	case LEJPVP_PROTOCOL_NAME_OPT:
		/* this catches, eg,
		 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
		 * ie, these are options attached to a protocol with { }
		 */
		pvo = lwsws_align(a);
		a->p += sizeof(*a->pvo);

		n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
		/* ie, enable this protocol, no options yet */
		pvo->next = a->pvo->options;
		a->pvo->options = pvo;
		pvo->name = a->p;
		a->p += n;
		pvo->value = a->p;
		pvo->options = NULL;
		break;

	case LEJPVP_MOUNT_EXTRA_MIMETYPES:
		a->pvo_em = lwsws_align(a);
		a->p += sizeof(*a->pvo_em);

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		/* ie, enable this protocol, no options yet */
		a->pvo_em->next = a->m.extra_mimetypes;
		a->m.extra_mimetypes = a->pvo_em;
		a->pvo_em->name = a->p;
		lwsl_notice("  adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
		a->p += n;
		a->pvo_em->value = a->p;
		a->pvo_em->options = NULL;
		break;

	case LEJPVP_MOUNT_INTERPRET:
		a->pvo_int = lwsws_align(a);
		a->p += sizeof(*a->pvo_int);

		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
		/* ie, enable this protocol, no options yet */
		a->pvo_int->next = a->m.interpret;
		a->m.interpret = a->pvo_int;
		a->pvo_int->name = a->p;
		lwsl_notice("  adding interpret %s -> %s\n", a->p,
			    ctx->buf);
		a->p += n;
		a->pvo_int->value = a->p;
		a->pvo_int->options = NULL;
		break;

	case LEJPVP_ENABLE_CLIENT_SSL:
		a->enable_client_ssl = arg_to_bool(ctx->buf);
		return 0;
	case LEJPVP_CLIENT_SSL_KEY:
		a->info->client_ssl_private_key_filepath = a->p;
		break;
	case LEJPVP_CLIENT_SSL_CERT:
		a->info->client_ssl_cert_filepath = a->p;
		break;
	case LEJPVP_CLIENT_SSL_CA:
		a->info->client_ssl_ca_filepath = a->p;
		break;

	case LEJPVP_NOIPV6:
		if (arg_to_bool(ctx->buf))
			a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
		else
			a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
		return 0;

	case LEJPVP_IPV6ONLY:
		a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
		if (arg_to_bool(ctx->buf))
			a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
		else
			a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
		return 0;

	case LEJPVP_SSL_OPTION_SET:
		a->info->ssl_options_set |= atol(ctx->buf);
		return 0;
	case LEJPVP_SSL_OPTION_CLEAR:
		a->info->ssl_options_clear |= atol(ctx->buf);
		return 0;

	default:
		return 0;
	}

dostring:
	p = ctx->buf;
	p1 = strstr(p, ESC_INSTALL_DATADIR);
	if (p1) {
		n = p1 - p;
		if (n > a->end - a->p)
			n = a->end - a->p;
		strncpy(a->p, p, n);
		a->p += n;
		a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
		p += n + strlen(ESC_INSTALL_DATADIR);
	}

	a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
	*(a->p)++ = '\0';

	return 0;
}
Ejemplo n.º 25
0
int
callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
		    void *user, void *in, size_t len)
{
	struct per_session_data__lws_mirror *pss =
			(struct per_session_data__lws_mirror *)user;
	int n, m;
int client_sockfd=lws_get_socket_fd(wsi);
	switch (reason) {

	case LWS_CALLBACK_ESTABLISHED:
		lwsl_notice("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
		pss->ringbuffer_tail = ringbuffer_head;
		pss->wsi = wsi;
		break;

	case LWS_CALLBACK_PROTOCOL_DESTROY:
		lwsl_notice("%s: mirror protocol cleaning up\n", __func__);
		for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
			if (ringbuffer[n].payload)
				free(ringbuffer[n].payload);
		break;

	case LWS_CALLBACK_SERVER_WRITEABLE:
		//if (close_testing)
		//	break;

	printf("服务器开始发送数据\n");
	websocket_write_back(wsi, msg,-1);
	// n = lws_write(wsi, (char*)in + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT);

		// while (pss->ringbuffer_tail != ringbuffer_head) {
		// 	m = ringbuffer[pss->ringbuffer_tail].len;
		// 	n = lws_write(wsi, (unsigned char *)
		// 		   ringbuffer[pss->ringbuffer_tail].payload +
		// 		   LWS_PRE, m, LWS_WRITE_TEXT);
			
			
		// 	printf("the n is %d, the cilent fd is %d\n",n,client_sockfd);
		// 	if (n < 0) {
		// 		lwsl_err("ERROR %d writing to mirror socket\n", n);
		// 		return -1;
		// 	}
		// 	if (n < m)
		// 		lwsl_err("mirror partial write %d vs %d\n", n, m);

		// 	if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
		// 		pss->ringbuffer_tail = 0;
		// 	else
		// 		pss->ringbuffer_tail++;

		// 	// if (((ringbuffer_head - pss->ringbuffer_tail) &
		// 	//     (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
		// 	// 	lws_rx_flow_allow_all_protocol(lws_get_context(wsi),
		// 	// 		       lws_get_protocol(wsi));

		// 	// if (lws_send_pipe_choked(wsi)) {
		// 	// 	lws_callback_on_writable(wsi);
		// 	// 	break;
		// 	// }
		// }
		break;

	case LWS_CALLBACK_RECEIVE:
	client_sockfd=lws_get_socket_fd(wsi);
	lwsl_notice("接收到客户端 %d 的信息=[%s]\n",client_sockfd, (const char*)in);
	memcpy(msg,in,strlen(in));
		if (((ringbuffer_head - pss->ringbuffer_tail) &
		    (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
			lwsl_err("dropping!\n");
			goto choke;
		}

		if (ringbuffer[ringbuffer_head].payload)
			free(ringbuffer[ringbuffer_head].payload);

		ringbuffer[ringbuffer_head].payload = malloc(LWS_PRE + len);
		ringbuffer[ringbuffer_head].len = len;
		memcpy((char *)ringbuffer[ringbuffer_head].payload +
		       LWS_PRE, in, len);
		if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
			ringbuffer_head = 0;
		else
			ringbuffer_head++;

		if (((ringbuffer_head - pss->ringbuffer_tail) &
		    (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
			goto done;

choke:
		lwsl_notice("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
		lws_rx_flow_control(wsi, 0);

done:
	
	printf("lws_callback_on_writable_all_protocol, the client fd is %d\n", client_sockfd);
		lws_callback_on_writable_all_protocol(lws_get_context(wsi),
						      lws_get_protocol(wsi));
		break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

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

	default:
		break;
	}

	return 0;
}
Ejemplo n.º 26
0
LWS_VISIBLE void
lws_uv_sigint_cb(uv_signal_t *watcher, int signum)
{
	lwsl_err("internal signal handler caught signal %d\n", signum);
	lws_libuv_stop(watcher->data);
}
Ejemplo n.º 27
0
LWS_VISIBLE LWS_EXTERN int
lws_cgi(struct lws *wsi, const char * const *exec_array,
	int script_uri_path_len, int timeout_secs,
	const struct lws_protocol_vhost_options *mp_cgienv)
{
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	char *env_array[30], cgi_path[500], e[1024], *p = e,
	     *end = p + sizeof(e) - 1, tok[256], *t, *sum, *sumend;
	struct lws_cgi *cgi;
	int n, m = 0, i, uritok = -1, c;

	/*
	 * give the master wsi a cgi struct
	 */

	wsi->http.cgi = lws_zalloc(sizeof(*wsi->http.cgi), "new cgi");
	if (!wsi->http.cgi) {
		lwsl_err("%s: OOM\n", __func__);
		return -1;
	}

	wsi->http.cgi->response_code = HTTP_STATUS_OK;

	cgi = wsi->http.cgi;
	cgi->wsi = wsi; /* set cgi's owning wsi */
	sum = cgi->summary;
	sumend = sum + strlen(cgi->summary) - 1;

	for (n = 0; n < 3; n++) {
		cgi->pipe_fds[n][0] = -1;
		cgi->pipe_fds[n][1] = -1;
	}

	/* create pipes for [stdin|stdout] and [stderr] */

	for (n = 0; n < 3; n++)
		if (pipe(cgi->pipe_fds[n]) == -1)
			goto bail1;

	/* create cgi wsis for each stdin/out/err fd */

	for (n = 0; n < 3; n++) {
		cgi->stdwsi[n] = lws_create_basic_wsi(wsi->context, wsi->tsi);
		if (!cgi->stdwsi[n]) {
			lwsl_err("%s: unable to create cgi stdwsi\n", __func__);
			goto bail2;
		}
		cgi->stdwsi[n]->cgi_channel = n;
		lws_vhost_bind_wsi(wsi->vhost, cgi->stdwsi[n]);

		lwsl_debug("%s: cgi stdwsi %p: pipe idx %d -> fd %d / %d\n", __func__,
			   cgi->stdwsi[n], n, cgi->pipe_fds[n][!!(n == 0)],
			   cgi->pipe_fds[n][!(n == 0)]);

		/* read side is 0, stdin we want the write side, others read */
		cgi->stdwsi[n]->desc.sockfd = cgi->pipe_fds[n][!!(n == 0)];
		if (fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL,
		    O_NONBLOCK) < 0) {
			lwsl_err("%s: setting NONBLOCK failed\n", __func__);
			goto bail2;
		}
	}

	for (n = 0; n < 3; n++) {
		if (wsi->context->event_loop_ops->accept)
			if (wsi->context->event_loop_ops->accept(cgi->stdwsi[n]))
				goto bail3;

		if (__insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
			goto bail3;
		cgi->stdwsi[n]->parent = wsi;
		cgi->stdwsi[n]->sibling_list = wsi->child_list;
		wsi->child_list = cgi->stdwsi[n];
	}

	lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT);
	lws_change_pollfd(cgi->stdwsi[LWS_STDOUT], LWS_POLLOUT, LWS_POLLIN);
	lws_change_pollfd(cgi->stdwsi[LWS_STDERR], LWS_POLLOUT, LWS_POLLIN);

	lwsl_debug("%s: fds in %d, out %d, err %d\n", __func__,
		   cgi->stdwsi[LWS_STDIN]->desc.sockfd,
		   cgi->stdwsi[LWS_STDOUT]->desc.sockfd,
		   cgi->stdwsi[LWS_STDERR]->desc.sockfd);

	if (timeout_secs)
		lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs);

	/* the cgi stdout is always sending us http1.x header data first */
	wsi->hdr_state = LCHS_HEADER;

	/* add us to the pt list of active cgis */
	lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->http.cgi);
	cgi->cgi_list = pt->http.cgi_list;
	pt->http.cgi_list = cgi;

	sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]);

	if (0) {
		char *pct = lws_hdr_simple_ptr(wsi,
				WSI_TOKEN_HTTP_CONTENT_ENCODING);

		if (pct && !strcmp(pct, "gzip"))
			wsi->http.cgi->gzip_inflate = 1;
	}

	/* prepare his CGI env */

	n = 0;

	if (lws_is_ssl(wsi))
		env_array[n++] = "HTTPS=ON";
	if (wsi->http.ah) {
		static const unsigned char meths[] = {
			WSI_TOKEN_GET_URI,
			WSI_TOKEN_POST_URI,
			WSI_TOKEN_OPTIONS_URI,
			WSI_TOKEN_PUT_URI,
			WSI_TOKEN_PATCH_URI,
			WSI_TOKEN_DELETE_URI,
			WSI_TOKEN_CONNECT,
			WSI_TOKEN_HEAD_URI,
		#ifdef LWS_WITH_HTTP2
			WSI_TOKEN_HTTP_COLON_PATH,
		#endif
		};
		static const char * const meth_names[] = {
			"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
			"CONNECT", "HEAD", ":path"
		};

		if (script_uri_path_len >= 0)
			for (m = 0; m < (int)LWS_ARRAY_SIZE(meths); m++)
				if (lws_hdr_total_length(wsi, meths[m]) >=
						script_uri_path_len) {
					uritok = meths[m];
					break;
				}

		if (script_uri_path_len < 0 && uritok < 0)
			goto bail3;
//		if (script_uri_path_len < 0)
//			uritok = 0;

		if (m >= 0) {
			env_array[n++] = p;
			if (m < 8) {
				p += lws_snprintf(p, end - p,
						  "REQUEST_METHOD=%s",
						  meth_names[m]);
				sum += lws_snprintf(sum, sumend - sum, "%s ",
						    meth_names[m]);
			} else {
				p += lws_snprintf(p, end - p,
						  "REQUEST_METHOD=%s",
			  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
				sum += lws_snprintf(sum, sumend - sum, "%s ",
					lws_hdr_simple_ptr(wsi,
						  WSI_TOKEN_HTTP_COLON_METHOD));
			}
			p++;
		}

		if (uritok >= 0)
			sum += lws_snprintf(sum, sumend - sum, "%s ",
					    lws_hdr_simple_ptr(wsi, uritok));

		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "QUERY_STRING=");
		/* dump the individual URI Arg parameters */
		m = 0;
		while (script_uri_path_len >= 0) {
			i = lws_hdr_copy_fragment(wsi, tok, sizeof(tok),
					     WSI_TOKEN_HTTP_URI_ARGS, m);
			if (i < 0)
				break;
			t = tok;
			while (*t && *t != '=' && p < end - 4)
				*p++ = *t++;
			if (*t == '=')
				*p++ = *t++;
			i = urlencode(t, i- (t - tok), p, end - p);
			if (i > 0) {
				p += i;
				*p++ = '&';
			}
			m++;
		}
		if (m)
			p--;
		*p++ = '\0';

		if (uritok >= 0) {
			strcpy(cgi_path, "REQUEST_URI=");
			c = lws_hdr_copy(wsi, cgi_path + 12,
					 sizeof(cgi_path) - 12, uritok);
			if (c < 0)
				goto bail3;

			cgi_path[sizeof(cgi_path) - 1] = '\0';
			env_array[n++] = cgi_path;
		}

		sum += lws_snprintf(sum, sumend - sum, "%s", env_array[n - 1]);

		if (script_uri_path_len >= 0) {
			env_array[n++] = p;
			p += lws_snprintf(p, end - p, "PATH_INFO=%s",
				      cgi_path + 12 + script_uri_path_len);
			p++;
		}
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_COOKIE=");
		m = lws_hdr_copy(wsi, p, end - p, WSI_TOKEN_HTTP_COOKIE);
		if (m > 0)
			p += lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE);
		*p++ = '\0';
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_USER_AGENT=%s",
			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_CONTENT_ENCODING=%s",
		      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_ACCEPT=%s",
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING)) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "HTTP_ACCEPT_ENCODING=%s",
		      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING));
		p++;
	}
	if (script_uri_path_len >= 0 &&
	    uritok == WSI_TOKEN_POST_URI) {
		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
			env_array[n++] = p;
			p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
			  lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
			p++;
		}
		if (!wsi->http.cgi->gzip_inflate &&
		    lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
			env_array[n++] = p;
			p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
					  lws_hdr_simple_ptr(wsi,
					  WSI_TOKEN_HTTP_CONTENT_LENGTH));
			p++;
		}

		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
			wsi->http.cgi->post_in_expected =
				atoll(lws_hdr_simple_ptr(wsi,
						WSI_TOKEN_HTTP_CONTENT_LENGTH));
	}


	env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";

	env_array[n++] = p;
	p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;

	while (mp_cgienv) {
		env_array[n++] = p;
		p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
			      mp_cgienv->value);
		if (!strcmp(mp_cgienv->name, "GIT_PROJECT_ROOT")) {
			wsi->http.cgi->implied_chunked = 1;
			wsi->http.cgi->explicitly_chunked = 1;
		}
		lwsl_info("   Applying mount-specific cgi env '%s'\n",
			   env_array[n - 1]);
		p++;
		mp_cgienv = mp_cgienv->next;
	}

	env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
	env_array[n] = NULL;

#if 0
	for (m = 0; m < n; m++)
		lwsl_notice("    %s\n", env_array[m]);
#endif

	/*
	 * Actually having made the env, as a cgi we don't need the ah
	 * any more
	 */
	if (script_uri_path_len >= 0)
		lws_header_table_detach(wsi, 0);

	/* we are ready with the redirection pipes... run the thing */
#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
	cgi->pid = fork();
#else
	cgi->pid = vfork();
#endif
	if (cgi->pid < 0) {
		lwsl_err("fork failed, errno %d", errno);
		goto bail3;
	}

#if defined(__linux__)
	prctl(PR_SET_PDEATHSIG, SIGTERM);
#endif
	if (script_uri_path_len >= 0)
		/* stops non-daemonized main processess getting SIGINT
		 * from TTY */
		setpgrp();

	if (cgi->pid) {
		/* we are the parent process */
		wsi->context->count_cgi_spawned++;
		lwsl_info("%s: cgi %p spawned PID %d\n", __func__,
			   cgi, cgi->pid);

		/*
		 *  close:                stdin:r, stdout:w, stderr:w
		 * hide from other forks: stdin:w, stdout:r, stderr:r
		 */
		for (n = 0; n < 3; n++) {
			lws_plat_apply_FD_CLOEXEC(cgi->pipe_fds[n][!!(n == 0)]);
			close(cgi->pipe_fds[n][!(n == 0)]);
		}

		/* inform cgi owner of the child PID */
		n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
					    LWS_CALLBACK_CGI_PROCESS_ATTACH,
					    wsi->user_space, NULL, cgi->pid);
		(void)n;

		return 0;
	}

	/* somewhere we can at least read things and enter it */
	if (chdir("/tmp"))
		lwsl_notice("%s: Failed to chdir\n", __func__);

	/* We are the forked process, redirect and kill inherited things.
	 *
	 * Because of vfork(), we cannot do anything that changes pages in
	 * the parent environment.  Stuff that changes kernel state for the
	 * process is OK.  Stuff that happens after the execvpe() is OK.
	 */

	for (n = 0; n < 3; n++) {
		if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
			lwsl_err("%s: stdin dup2 failed\n", __func__);
			goto bail3;
		}
		close(cgi->pipe_fds[n][0]);
		close(cgi->pipe_fds[n][1]);
	}

#if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
	for (m = 0; m < n; m++) {
		p = strchr(env_array[m], '=');
		*p++ = '\0';
		setenv(env_array[m], p, 1);
	}
	execvp(exec_array[0], (char * const *)&exec_array[0]);
#else
	execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]);
#endif

	exit(1);

bail3:
	/* drop us from the pt cgi list */
	pt->http.cgi_list = cgi->cgi_list;

	while (--n >= 0)
		__remove_wsi_socket_from_fds(wsi->http.cgi->stdwsi[n]);
bail2:
	for (n = 0; n < 3; n++)
		if (wsi->http.cgi->stdwsi[n])
			__lws_free_wsi(cgi->stdwsi[n]);

bail1:
	for (n = 0; n < 3; n++) {
		if (cgi->pipe_fds[n][0] >= 0)
			close(cgi->pipe_fds[n][0]);
		if (cgi->pipe_fds[n][1] >= 0)
			close(cgi->pipe_fds[n][1]);
	}

	lws_free_set_NULL(wsi->http.cgi);

	lwsl_err("%s: failed\n", __func__);

	return -1;
}
Ejemplo n.º 28
0
LWS_VISIBLE int
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
{
	struct lws_context_per_thread *pt = &context->pt[tsi];
	struct lws_vhost *vh = context->vhost_list;
	int status = 0, n;

	if (!loop) {
		loop = lws_malloc(sizeof(*loop));
#if UV_VERSION_MAJOR > 0
		uv_loop_init(loop);
#else
		lwsl_err("This libuv is too old to work...\n");
		return 1;
#endif
		pt->ev_loop_foreign = 0;
	} else {
		lwsl_notice(" Using foreign event loop...\n");
		pt->ev_loop_foreign = 1;
	}

	pt->io_loop_uv = loop;
	uv_idle_init(loop, &pt->uv_idle);

	if (pt->context->use_ev_sigint) {
		assert(ARRAY_SIZE(sigs) <= ARRAY_SIZE(pt->signals));
		for (n = 0; n < ARRAY_SIZE(sigs); n++) {
			uv_signal_init(loop, &pt->signals[n]);
			pt->signals[n].data = pt->context;
			uv_signal_start(&pt->signals[n],
					context->lws_uv_sigint_cb, sigs[n]);
		}
	}

	/*
	 * Initialize the accept wsi read watcher with all the listening sockets
	 * and register a callback for read operations
	 *
	 * We have to do it here because the uv loop(s) are not
	 * initialized until after context creation.
	 */
	while (vh) {
		if (vh->lserv_wsi) {
			vh->lserv_wsi->w_read.context = context;
			n = uv_poll_init_socket(pt->io_loop_uv,
						&vh->lserv_wsi->w_read.uv_watcher,
						vh->lserv_wsi->sock);
			if (n) {
				lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
					n, (void *)(long)vh->lserv_wsi->sock);

				return -1;
			}
			lws_libuv_io(vh->lserv_wsi, LWS_EV_START | LWS_EV_READ);
		}
		vh = vh->vhost_next;
	}

	uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
	uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb, 1000, 1000);

	return status;
}
Ejemplo n.º 29
0
int
lws_ssl_client_connect2(struct lws *wsi)
{
	struct lws_context *context = wsi->context;
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	char *p = (char *)&pt->serv_buf[0];
	char *sb = p;
	int n;

	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) {
				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
			 */
			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, x509: %s\n",
				 n, ERR_error_string(n, sb),  X509_verify_cert_error_string(n));
			lws_close_free_wsi(wsi,
				LWS_CLOSE_STATUS_NOSTATUS);
			return 0;
		}
	}
#endif /* USE_WOLFSSL */

	return 1;
}
Ejemplo n.º 30
0
static int
callback_maos_monitor(struct libwebsocket_context *context,
		      struct libwebsocket *wsi,
		      enum libwebsocket_callback_reasons reason,
		      void *user, void *in, size_t len)
{
    int n;
    struct per_session_data__maos_monitor *pss = (struct per_session_data__maos_monitor *)user;

    switch (reason) {

    case LWS_CALLBACK_ESTABLISHED:
	lwsl_info("callback_maos_monitor: LWS_CALLBACK_ESTABLISHED\n");
	pss->ringbuffer_tail = ringbuffer_head;
	pss->wsi = wsi;
	pss->head=pss->tail=0;
	html_convert_all(&pss->head, &pss->tail, 
			 LWS_SEND_BUFFER_PRE_PADDING, LWS_SEND_BUFFER_POST_PADDING);
	libwebsocket_callback_on_writable(context, wsi);
	lwsl_notice("head=%p, tail=%p\n", pss->head, pss->tail);
	break;

    case LWS_CALLBACK_PROTOCOL_DESTROY:
	lwsl_notice("mirror protocol cleaning up\n");
	for (n = 0; n < (int)(sizeof ringbuffer / sizeof ringbuffer[0]); n++)
	    if (ringbuffer[n].payload)
		free(ringbuffer[n].payload);
	break;

    case LWS_CALLBACK_SERVER_WRITEABLE:
	while(pss->head){/*initialization*/
	    n = libwebsocket_write(wsi, (unsigned char *)
				   pss->head->payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   pss->head->len,
				   LWS_WRITE_TEXT);
	    if(n<0){
		lwsl_err("ERROR %d writing to mirror socket\n", n);
		return -1;
	    }else if(n<(int)pss->head->len){
		lwsl_err("mirror partial write %d vs %d\n", n, pss->head->len);
	    }
	    l_message *tmp=pss->head;
	    pss->head=pss->head->next;
	    free(tmp->payload);
	    free(tmp);
	    if(lws_send_pipe_choked(wsi)){
		libwebsocket_callback_on_writable(context, wsi);
		break;
	    }
#ifdef _WIN32
	    Sleep(1);
#else
	    usleep(1);
#endif
	}
	while (pss->ringbuffer_tail != ringbuffer_head) {
	    n = libwebsocket_write(wsi, (unsigned char *)
				   ringbuffer[pss->ringbuffer_tail].payload +
				   LWS_SEND_BUFFER_PRE_PADDING,
				   ringbuffer[pss->ringbuffer_tail].len,
				   LWS_WRITE_TEXT);
	    if (n < 0) {
		lwsl_err("ERROR %d writing to mirror socket\n", n);
		return -1;
	    }
	    if (n < (int)ringbuffer[pss->ringbuffer_tail].len)
		lwsl_err("mirror partial write %d vs %d\n",
			 n, ringbuffer[pss->ringbuffer_tail].len);

	    if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
		pss->ringbuffer_tail = 0;
	    else
		pss->ringbuffer_tail++;


	    if (lws_send_pipe_choked(wsi)) {
		libwebsocket_callback_on_writable(context, wsi);
		break;
	    }
	    /*
	     * for tests with chrome on same machine as client and
	     * server, this is needed to stop chrome choking
	     */
#ifdef _WIN32
	    Sleep(1);
#else
	    usleep(1);
#endif
	}
	break;

    case LWS_CALLBACK_RECEIVE:
	scheduler_handle_ws((char*)in, len);
	//ws_push(in, len);
	break;

	/*
	 * this just demonstrates how to use the protocol filter. If you won't
	 * study and reject connections based on header content, you don't need
	 * to handle this callback
	 */

    case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
	/* you could return non-zero here and kill the connection */
	break;

    default:
	break;
    }

    return 0;
}