diff options
Diffstat (limited to 'thirdparty/lws/client')
-rw-r--r-- | thirdparty/lws/client/client-handshake.c | 1051 | ||||
-rw-r--r-- | thirdparty/lws/client/client-parser.c | 598 | ||||
-rw-r--r-- | thirdparty/lws/client/client.c | 1304 | ||||
-rw-r--r-- | thirdparty/lws/client/ssl-client.c | 625 |
4 files changed, 0 insertions, 3578 deletions
diff --git a/thirdparty/lws/client/client-handshake.c b/thirdparty/lws/client/client-handshake.c deleted file mode 100644 index c2720d9283..0000000000 --- a/thirdparty/lws/client/client-handshake.c +++ /dev/null @@ -1,1051 +0,0 @@ -#include "private-libwebsockets.h" - -static int -lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) -{ - struct addrinfo hints; - - memset(&hints, 0, sizeof(hints)); - *result = NULL; - -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) { - -#if !defined(__ANDROID__) - hints.ai_family = AF_INET6; - hints.ai_flags = AI_V4MAPPED; -#endif - } else -#endif - { - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - } - - return getaddrinfo(ads, NULL, &hints, result); -} - -struct lws * -lws_client_connect_2(struct lws *wsi) -{ - sockaddr46 sa46; - struct addrinfo *result; - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws_pollfd pfd; - const char *cce = "", *iface; - int n, port; - ssize_t plen = 0; - const char *ads; -#ifdef LWS_WITH_IPV6 - char ipv6only = lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY | - LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); - -#if defined(__ANDROID__) - ipv6only = 0; -#endif -#endif - - lwsl_client("%s\n", __func__); - - if (!wsi->u.hdr.ah) { - cce = "ah was NULL at cc2"; - lwsl_err("%s\n", cce); - goto oom4; - } - - /* - * start off allowing ipv6 on connection if vhost allows it - */ - wsi->ipv6 = LWS_IPV6_ENABLED(wsi->vhost); - - /* Decide what it is we need to connect to: - * - * Priority 1: connect to http proxy */ - - if (wsi->vhost->http_proxy_port) { - plen = sprintf((char *)pt->serv_buf, - "CONNECT %s:%u HTTP/1.0\x0d\x0a" - "User-agent: libwebsockets\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), - wsi->c_port); - - if (wsi->vhost->proxy_basic_auth_token[0]) - plen += sprintf((char *)pt->serv_buf + plen, - "Proxy-authorization: basic %s\x0d\x0a", - wsi->vhost->proxy_basic_auth_token); - - plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); - ads = wsi->vhost->http_proxy_address; - port = wsi->vhost->http_proxy_port; - -#if defined(LWS_WITH_SOCKS5) - - /* Priority 2: Connect to SOCK5 Proxy */ - - } else if (wsi->vhost->socks_proxy_port) { - socks_generate_msg(wsi, SOCKS_MSG_GREETING, &plen); - lwsl_client("Sending SOCKS Greeting\n"); - ads = wsi->vhost->socks_proxy_address; - port = wsi->vhost->socks_proxy_port; -#endif - } else { - - /* Priority 3: Connect directly */ - - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = wsi->c_port; - } - - /* - * prepare the actual connection - * to whatever we decided to connect to - */ - - lwsl_notice("%s: %p: address %s\n", __func__, wsi, ads); - - n = lws_getaddrinfo46(wsi, ads, &result); - -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) { - - if (n) { - /* lws_getaddrinfo46 failed, there is no usable result */ - lwsl_notice("%s: lws_getaddrinfo46 failed %d\n", - __func__, n); - cce = "ipv6 lws_getaddrinfo46 failed"; - goto oom4; - } - - memset(&sa46, 0, sizeof(sa46)); - - sa46.sa6.sin6_family = AF_INET6; - switch (result->ai_family) { - case AF_INET: - if (ipv6only) - break; - /* map IPv4 to IPv6 */ - bzero((char *)&sa46.sa6.sin6_addr, - sizeof(sa46.sa6.sin6_addr)); - sa46.sa6.sin6_addr.s6_addr[10] = 0xff; - sa46.sa6.sin6_addr.s6_addr[11] = 0xff; - memcpy(&sa46.sa6.sin6_addr.s6_addr[12], - &((struct sockaddr_in *)result->ai_addr)->sin_addr, - sizeof(struct in_addr)); - lwsl_notice("uplevelling AF_INET to AF_INET6\n"); - break; - - case AF_INET6: - memcpy(&sa46.sa6.sin6_addr, - &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, - sizeof(struct in6_addr)); - sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id; - sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo; - break; - default: - lwsl_err("Unknown address family\n"); - freeaddrinfo(result); - cce = "unknown address family"; - goto oom4; - } - } else -#endif /* use ipv6 */ - - /* use ipv4 */ - { - void *p = NULL; - - if (!n) { - struct addrinfo *res = result; - - /* pick the first AF_INET (IPv4) result */ - - while (!p && res) { - switch (res->ai_family) { - case AF_INET: - p = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - break; - } - - res = res->ai_next; - } -#if defined(LWS_FALLBACK_GETHOSTBYNAME) - } else if (n == EAI_SYSTEM) { - struct hostent *host; - - lwsl_info("getaddrinfo (ipv4) failed, trying gethostbyname\n"); - host = gethostbyname(ads); - if (host) { - p = host->h_addr; - } else { - lwsl_err("gethostbyname failed\n"); - cce = "gethostbyname (ipv4) failed"; - goto oom4; - } -#endif - } else { - lwsl_err("getaddrinfo failed\n"); - cce = "getaddrinfo failed"; - goto oom4; - } - - if (!p) { - if (result) - freeaddrinfo(result); - lwsl_err("Couldn't identify address\n"); - cce = "unable to lookup address"; - goto oom4; - } - - sa46.sa4.sin_family = AF_INET; - sa46.sa4.sin_addr = *((struct in_addr *)p); - bzero(&sa46.sa4.sin_zero, 8); - } - - if (result) - freeaddrinfo(result); - - /* now we decided on ipv4 or ipv6, set the port */ - - if (!lws_socket_is_valid(wsi->desc.sockfd)) { - -#if defined(LWS_WITH_LIBUV) - if (LWS_LIBUV_ENABLED(context)) - if (lws_libuv_check_watcher_active(wsi)) { - lwsl_warn("Waiting for libuv watcher to close\n"); - cce = "waiting for libuv watcher to close"; - goto oom4; - } -#endif - -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) - wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0); - else -#endif - wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (!lws_socket_is_valid(wsi->desc.sockfd)) { - lwsl_warn("Unable to open socket\n"); - cce = "unable to open socket"; - goto oom4; - } - - if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) { - lwsl_err("Failed to set wsi socket options\n"); - compatible_close(wsi->desc.sockfd); - cce = "set socket opts failed"; - goto oom4; - } - - wsi->mode = LWSCM_WSCL_WAITING_CONNECT; - - lws_libev_accept(wsi, wsi->desc); - lws_libuv_accept(wsi, wsi->desc); - lws_libevent_accept(wsi, wsi->desc); - - if (insert_wsi_socket_into_fds(context, wsi)) { - compatible_close(wsi->desc.sockfd); - cce = "insert wsi failed"; - goto oom4; - } - - lws_change_pollfd(wsi, 0, LWS_POLLIN); - - /* - * past here, we can't simply free the structs as error - * handling as oom4 does. We have to run the whole close flow. - */ - - if (!wsi->protocol) - wsi->protocol = &wsi->vhost->protocols[0]; - - wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, - wsi->user_space, NULL, 0); - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, - AWAITING_TIMEOUT); - - iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); - - if (iface) { - n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, iface); - if (n < 0) { - cce = "unable to bind socket"; - goto failed; - } - } - } - -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) { - sa46.sa6.sin6_port = htons(port); - n = sizeof(struct sockaddr_in6); - } else -#endif - { - sa46.sa4.sin_port = htons(port); - n = sizeof(struct sockaddr); - } - - if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 || - LWS_ERRNO == LWS_EISCONN) { - if (LWS_ERRNO == LWS_EALREADY || - LWS_ERRNO == LWS_EINPROGRESS || - LWS_ERRNO == LWS_EWOULDBLOCK -#ifdef _WIN32 - || LWS_ERRNO == WSAEINVAL -#endif - ) { - lwsl_client("nonblocking connect retry (errno = %d)\n", - LWS_ERRNO); - - if (lws_plat_check_connection_error(wsi)) { - cce = "socket connect failed"; - goto failed; - } - - /* - * must do specifically a POLLOUT poll to hear - * about the connect completion - */ - if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { - cce = "POLLOUT set failed"; - goto failed; - } - - return wsi; - } - - if (LWS_ERRNO != LWS_EISCONN) { - lwsl_notice("Connect failed errno=%d\n", LWS_ERRNO); - cce = "connect failed"; - goto failed; - } - } - - lwsl_client("connected\n"); - - /* we are connected to server, or proxy */ - - /* http proxy */ - if (wsi->vhost->http_proxy_port) { - - /* - * OK from now on we talk via the proxy, so connect to that - * - * (will overwrite existing pointer, - * leaving old string/frag there but unreferenced) - */ - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - wsi->vhost->http_proxy_address)) - goto failed; - wsi->c_port = wsi->vhost->http_proxy_port; - - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing to proxy socket\n"); - cce = "proxy write failed"; - goto failed; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, - AWAITING_TIMEOUT); - - wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY; - - return wsi; - } -#if defined(LWS_WITH_SOCKS5) - /* socks proxy */ - else if (wsi->vhost->socks_proxy_port) { - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing socks greeting\n"); - cce = "socks write failed"; - goto failed; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, - AWAITING_TIMEOUT); - - wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY; - - return wsi; - } -#endif - - /* - * provoke service to issue the handshake directly - * we need to do it this way because in the proxy case, this is the - * next state and executed only if and when we get a good proxy - * response inside the state machine... but notice in SSL case this - * may not have sent anything yet with 0 return, and won't until some - * many retries from main loop. To stop that becoming endless, - * cover with a timeout. - */ - - lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, - AWAITING_TIMEOUT); - - wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE; - pfd.fd = wsi->desc.sockfd; - pfd.events = LWS_POLLIN; - pfd.revents = LWS_POLLIN; - - n = lws_service_fd(context, &pfd); - if (n < 0) { - cce = "first service failed"; - goto failed; - } - if (n) /* returns 1 on failure after closing wsi */ - return NULL; - - return wsi; - -oom4: - /* we're closing, losing some rx is OK */ - lws_header_table_force_to_detachable_state(wsi); - - if (wsi->mode == LWSCM_HTTP_CLIENT || - wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED || - wsi->mode == LWSCM_WSCL_WAITING_CONNECT) { - wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, (void *)cce, strlen(cce)); - wsi->already_did_cce = 1; - } - /* take care that we might be inserted in fds already */ - if (wsi->position_in_fds_table != -1) - goto failed1; - lws_remove_from_timeout_list(wsi); - lws_header_table_detach(wsi, 0); - lws_free(wsi); - - return NULL; - -failed: - wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, (void *)cce, strlen(cce)); - wsi->already_did_cce = 1; -failed1: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); - - return NULL; -} - -/** - * lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect) - * this only works if still in HTTP, ie, not upgraded yet - * wsi: connection to reset - * address: network address of the new server - * port: port to connect to - * path: uri path to connect to on the new server - * host: host header to send to the new server - */ -LWS_VISIBLE struct lws * -lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, - const char *path, const char *host) -{ - char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p; - struct lws *wsi = *pwsi; - - if (wsi->redirects == 3) { - lwsl_err("%s: Too many redirects\n", __func__); - return NULL; - } - wsi->redirects++; - - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); - if (p) - strncpy(origin, p, sizeof(origin) - 1); - - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - if (p) - strncpy(protocol, p, sizeof(protocol) - 1); - - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); - if (p) - strncpy(method, p, sizeof(method) - 1); - - p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); - if (p) - strncpy(method, p, sizeof(iface) - 1); - - lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n", - address, port, path, ssl); - - /* close the connection by hand */ - -#ifdef LWS_OPENSSL_SUPPORT - lws_ssl_close(wsi); -#endif - -#ifdef LWS_WITH_LIBUV - if (LWS_LIBUV_ENABLED(wsi->context)) { - lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); - /* - * libuv has to do his own close handle processing asynchronously - * but once it starts we can do everything else synchronously, - * including trash wsi->desc.sockfd since it took a copy. - * - * When it completes it will call compatible_close() - */ - lws_libuv_closehandle_manually(wsi); - } else -#else - compatible_close(wsi->desc.sockfd); -#endif - - remove_wsi_socket_from_fds(wsi); - -#ifdef LWS_OPENSSL_SUPPORT - wsi->use_ssl = ssl; -#else - if (ssl) { - lwsl_err("%s: not configured for ssl\n", __func__); - return NULL; - } -#endif - - wsi->desc.sockfd = LWS_SOCK_INVALID; - wsi->state = LWSS_CLIENT_UNCONNECTED; - wsi->protocol = NULL; - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->c_port = port; - wsi->hdr_parsing_completed = 0; - _lws_header_table_reset(wsi->u.hdr.ah); - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) - return NULL; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) - return NULL; - - if (origin[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, - origin)) - return NULL; - if (protocol[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - protocol)) - return NULL; - if (method[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, - method)) - return NULL; - - if (iface[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, - iface)) - return NULL; - - origin[0] = '/'; - strncpy(&origin[1], path, sizeof(origin) - 2); - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin)) - return NULL; - - *pwsi = lws_client_connect_2(wsi); - - return *pwsi; -} - -#ifdef LWS_WITH_HTTP_PROXY -static hubbub_error -html_parser_cb(const hubbub_token *token, void *pw) -{ - struct lws_rewrite *r = (struct lws_rewrite *)pw; - char buf[1024], *start = buf + LWS_PRE, *p = start, - *end = &buf[sizeof(buf) - 1]; - size_t i; - - switch (token->type) { - case HUBBUB_TOKEN_DOCTYPE: - - p += lws_snprintf(p, end - p, "<!DOCTYPE %.*s %s ", - (int) token->data.doctype.name.len, - token->data.doctype.name.ptr, - token->data.doctype.force_quirks ? - "(force-quirks) " : ""); - - if (token->data.doctype.public_missing) - lwsl_debug("\tpublic: missing\n"); - else - p += lws_snprintf(p, end - p, "PUBLIC \"%.*s\"\n", - (int) token->data.doctype.public_id.len, - token->data.doctype.public_id.ptr); - - if (token->data.doctype.system_missing) - lwsl_debug("\tsystem: missing\n"); - else - p += lws_snprintf(p, end - p, " \"%.*s\">\n", - (int) token->data.doctype.system_id.len, - token->data.doctype.system_id.ptr); - - break; - case HUBBUB_TOKEN_START_TAG: - p += lws_snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len, - token->data.tag.name.ptr); - -/* (token->data.tag.self_closing) ? - "(self-closing) " : "", - (token->data.tag.n_attributes > 0) ? - "attributes:" : ""); -*/ - for (i = 0; i < token->data.tag.n_attributes; i++) { - if (!hstrcmp(&token->data.tag.attributes[i].name, "href", 4) || - !hstrcmp(&token->data.tag.attributes[i].name, "action", 6) || - !hstrcmp(&token->data.tag.attributes[i].name, "src", 3)) { - const char *pp = (const char *)token->data.tag.attributes[i].value.ptr; - int plen = (int) token->data.tag.attributes[i].value.len; - - if (strncmp(pp, "http:", 5) && strncmp(pp, "https:", 6)) { - - if (!hstrcmp(&token->data.tag.attributes[i].value, - r->from, r->from_len)) { - pp += r->from_len; - plen -= r->from_len; - } - p += lws_snprintf(p, end - p, " %.*s=\"%s/%.*s\"", - (int) token->data.tag.attributes[i].name.len, - token->data.tag.attributes[i].name.ptr, - r->to, plen, pp); - continue; - } - } - - p += lws_snprintf(p, end - p, " %.*s=\"%.*s\"", - (int) token->data.tag.attributes[i].name.len, - token->data.tag.attributes[i].name.ptr, - (int) token->data.tag.attributes[i].value.len, - token->data.tag.attributes[i].value.ptr); - } - p += lws_snprintf(p, end - p, ">"); - break; - case HUBBUB_TOKEN_END_TAG: - p += lws_snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len, - token->data.tag.name.ptr); -/* - (token->data.tag.self_closing) ? - "(self-closing) " : "", - (token->data.tag.n_attributes > 0) ? - "attributes:" : ""); -*/ - for (i = 0; i < token->data.tag.n_attributes; i++) { - p += lws_snprintf(p, end - p, " %.*s='%.*s'\n", - (int) token->data.tag.attributes[i].name.len, - token->data.tag.attributes[i].name.ptr, - (int) token->data.tag.attributes[i].value.len, - token->data.tag.attributes[i].value.ptr); - } - p += lws_snprintf(p, end - p, ">"); - break; - case HUBBUB_TOKEN_COMMENT: - p += lws_snprintf(p, end - p, "<!-- %.*s -->\n", - (int) token->data.comment.len, - token->data.comment.ptr); - break; - case HUBBUB_TOKEN_CHARACTER: - if (token->data.character.len == 1) { - if (*token->data.character.ptr == '<') { - p += lws_snprintf(p, end - p, "<"); - break; - } - if (*token->data.character.ptr == '>') { - p += lws_snprintf(p, end - p, ">"); - break; - } - if (*token->data.character.ptr == '&') { - p += lws_snprintf(p, end - p, "&"); - break; - } - } - - p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len, - token->data.character.ptr); - break; - case HUBBUB_TOKEN_EOF: - p += lws_snprintf(p, end - p, "\n"); - break; - } - - if (user_callback_handle_rxflow(r->wsi->protocol->callback, - r->wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, - r->wsi->user_space, start, p - start)) - return -1; - - return HUBBUB_OK; -} -#endif - -LWS_VISIBLE struct lws * -lws_client_connect_via_info(struct lws_client_connect_info *i) -{ - struct lws *wsi; - int v = SPEC_LATEST_SUPPORTED; - const struct lws_protocols *p; - - if (i->context->requested_kill) - return NULL; - - if (!i->context->protocol_init_done) - lws_protocol_init(i->context); - - wsi = lws_zalloc(sizeof(struct lws), "client wsi"); - if (wsi == NULL) - goto bail; - - wsi->context = i->context; - /* assert the mode and union status (hdr) clearly */ - lws_union_transition(wsi, LWSCM_HTTP_CLIENT); - wsi->desc.sockfd = LWS_SOCK_INVALID; - - /* 1) fill up the wsi with stuff from the connect_info as far as it - * can go. It's because not only is our connection async, we might - * not even be able to get ahold of an ah at this point. - */ - - /* -1 means just use latest supported */ - if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) - v = i->ietf_version_or_minus_one; - - wsi->ietf_spec_revision = v; - wsi->user_space = NULL; - wsi->state = LWSS_CLIENT_UNCONNECTED; - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->position_in_fds_table = -1; - wsi->c_port = i->port; - wsi->vhost = i->vhost; - if (!wsi->vhost) - wsi->vhost = i->context->vhost_list; - - wsi->protocol = &wsi->vhost->protocols[0]; - - /* for http[s] connection, allow protocol selection by name */ - - if (i->method && i->vhost && i->protocol) { - p = lws_vhost_name_to_protocol(i->vhost, i->protocol); - if (p) - wsi->protocol = p; - } - - if (wsi && !wsi->user_space && i->userdata) { - wsi->user_space_externally_allocated = 1; - wsi->user_space = i->userdata; - } else - /* if we stay in http, we can assign the user space now, - * otherwise do it after the protocol negotiated - */ - if (i->method) - if (lws_ensure_user_space(wsi)) - goto bail; - -#ifdef LWS_OPENSSL_SUPPORT - wsi->use_ssl = i->ssl_connection; -#else - if (i->ssl_connection) { - lwsl_err("libwebsockets not configured for ssl\n"); - goto bail; - } -#endif - - /* 2) stash the things from connect_info that we can't process without - * an ah. Because if no ah, we will go on the ah waiting list and - * process those things later (after the connect_info and maybe the - * things pointed to have gone out of scope. - */ - - wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash), "client stash"); - if (!wsi->u.hdr.stash) { - lwsl_err("%s: OOM\n", __func__); - goto bail; - } - - wsi->u.hdr.stash->origin[0] = '\0'; - wsi->u.hdr.stash->protocol[0] = '\0'; - wsi->u.hdr.stash->method[0] = '\0'; - wsi->u.hdr.stash->iface[0] = '\0'; - - strncpy(wsi->u.hdr.stash->address, i->address, - sizeof(wsi->u.hdr.stash->address) - 1); - strncpy(wsi->u.hdr.stash->path, i->path, - sizeof(wsi->u.hdr.stash->path) - 1); - strncpy(wsi->u.hdr.stash->host, i->host, - sizeof(wsi->u.hdr.stash->host) - 1); - if (i->origin) - strncpy(wsi->u.hdr.stash->origin, i->origin, - sizeof(wsi->u.hdr.stash->origin) - 1); - if (i->protocol) - strncpy(wsi->u.hdr.stash->protocol, i->protocol, - sizeof(wsi->u.hdr.stash->protocol) - 1); - if (i->method) - strncpy(wsi->u.hdr.stash->method, i->method, - sizeof(wsi->u.hdr.stash->method) - 1); - if (i->iface) - strncpy(wsi->u.hdr.stash->iface, i->iface, - sizeof(wsi->u.hdr.stash->iface) - 1); - - wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; - wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; - wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; - wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; - wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; - wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; - wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0'; - - if (i->pwsi) - *i->pwsi = wsi; - - /* if we went on the waiting list, no probs just return the wsi - * when we get the ah, now or later, he will call - * lws_client_connect_via_info2() below. - */ - if (lws_header_table_attach(wsi, 0) < 0) { - /* - * if we failed here, the connection is already closed - * and freed. - */ - goto bail1; - } - - if (i->parent_wsi) { - lwsl_info("%s: created child %p of parent %p\n", __func__, - wsi, i->parent_wsi); - wsi->parent = i->parent_wsi; - wsi->sibling_list = i->parent_wsi->child_list; - i->parent_wsi->child_list = wsi; - } -#ifdef LWS_WITH_HTTP_PROXY - if (i->uri_replace_to) - wsi->rw = lws_rewrite_create(wsi, html_parser_cb, - i->uri_replace_from, - i->uri_replace_to); -#endif - - return wsi; - -bail: - lws_free(wsi); - -bail1: - if (i->pwsi) - *i->pwsi = NULL; - - return NULL; -} - -struct lws * -lws_client_connect_via_info2(struct lws *wsi) -{ - struct client_info_stash *stash = wsi->u.hdr.stash; - - if (!stash) - return wsi; - - /* - * we're not necessarily in a position to action these right away, - * stash them... we only need during connect phase so u.hdr is fine - */ - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - stash->address)) - goto bail1; - - /* these only need u.hdr lifetime as well */ - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, stash->path)) - goto bail1; - - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, stash->host)) - goto bail1; - - if (stash->origin[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, - stash->origin)) - goto bail1; - /* - * this is a list of protocols we tell the server we're okay with - * stash it for later when we compare server response with it - */ - if (stash->protocol[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - stash->protocol)) - goto bail1; - if (stash->method[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, - stash->method)) - goto bail1; - if (stash->iface[0]) - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, - stash->iface)) - goto bail1; - -#if defined(LWS_WITH_SOCKS5) - if (!wsi->vhost->socks_proxy_port) - lws_free_set_NULL(wsi->u.hdr.stash); -#endif - - /* - * Check with each extension if it is able to route and proxy this - * connection for us. For example, an extension like x-google-mux - * can handle this and then we don't need an actual socket for this - * connection. - */ - - if (lws_ext_cb_all_exts(wsi->context, wsi, - LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION, - (void *)stash->address, - wsi->c_port) > 0) { - lwsl_client("lws_client_connect: ext handling conn\n"); - - lws_set_timeout(wsi, - PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, - AWAITING_TIMEOUT); - - wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT; - return wsi; - } - lwsl_client("lws_client_connect: direct conn\n"); - wsi->context->count_wsi_allocated++; - - return lws_client_connect_2(wsi); - -bail1: -#if defined(LWS_WITH_SOCKS5) - if (!wsi->vhost->socks_proxy_port) - lws_free_set_NULL(wsi->u.hdr.stash); -#endif - - return NULL; -} - -LWS_VISIBLE struct lws * -lws_client_connect_extended(struct lws_context *context, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, - const char *protocol, int ietf_version_or_minus_one, - void *userdata) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.address = address; - i.port = port; - i.ssl_connection = ssl_connection; - i.path = path; - i.host = host; - i.origin = origin; - i.protocol = protocol; - i.ietf_version_or_minus_one = ietf_version_or_minus_one; - i.userdata = userdata; - - return lws_client_connect_via_info(&i); -} - -LWS_VISIBLE struct lws * -lws_client_connect(struct lws_context *context, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, - const char *protocol, int ietf_version_or_minus_one) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.address = address; - i.port = port; - i.ssl_connection = ssl_connection; - i.path = path; - i.host = host; - i.origin = origin; - i.protocol = protocol; - i.ietf_version_or_minus_one = ietf_version_or_minus_one; - i.userdata = NULL; - - return lws_client_connect_via_info(&i); -} - -#if defined(LWS_WITH_SOCKS5) -void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, - ssize_t *msg_len) -{ - struct lws_context *context = wsi->context; - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - ssize_t len = 0, n, passwd_len; - short net_num; - char *p; - - switch (type) { - case SOCKS_MSG_GREETING: - /* socks version, version 5 only */ - pt->serv_buf[len++] = SOCKS_VERSION_5; - /* number of methods */ - pt->serv_buf[len++] = 2; - /* username password method */ - pt->serv_buf[len++] = SOCKS_AUTH_USERNAME_PASSWORD; - /* no authentication method */ - pt->serv_buf[len++] = SOCKS_AUTH_NO_AUTH; - break; - - case SOCKS_MSG_USERNAME_PASSWORD: - n = strlen(wsi->vhost->socks_user); - passwd_len = strlen(wsi->vhost->socks_password); - - /* the subnegotiation version */ - pt->serv_buf[len++] = SOCKS_SUBNEGOTIATION_VERSION_1; - /* length of the user name */ - pt->serv_buf[len++] = n; - /* user name */ - strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user, - context->pt_serv_buf_size - len); - len += n; - /* length of the password */ - pt->serv_buf[len++] = passwd_len; - /* password */ - strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password, - context->pt_serv_buf_size - len); - len += passwd_len; - break; - - case SOCKS_MSG_CONNECT: - p = (char*)&net_num; - - /* socks version */ - pt->serv_buf[len++] = SOCKS_VERSION_5; - /* socks command */ - pt->serv_buf[len++] = SOCKS_COMMAND_CONNECT; - /* reserved */ - pt->serv_buf[len++] = 0; - /* address type */ - pt->serv_buf[len++] = SOCKS_ATYP_DOMAINNAME; - /* skip length, we fill it in at the end */ - n = len++; - - /* the address we tell SOCKS proxy to connect to */ - strncpy((char *)&(pt->serv_buf[len]), wsi->u.hdr.stash->address, - context->pt_serv_buf_size - len); - len += strlen(wsi->u.hdr.stash->address); - net_num = htons(wsi->c_port); - - /* the port we tell SOCKS proxy to connect to */ - pt->serv_buf[len++] = p[0]; - pt->serv_buf[len++] = p[1]; - - /* the length of the address, excluding port */ - pt->serv_buf[n] = strlen(wsi->u.hdr.stash->address); - break; - - default: - return; - } - - *msg_len = len; -} -#endif diff --git a/thirdparty/lws/client/client-parser.c b/thirdparty/lws/client/client-parser.c deleted file mode 100644 index 0e42dac362..0000000000 --- a/thirdparty/lws/client/client-parser.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "private-libwebsockets.h" - -/* - * parsers.c: lws_rx_sm() needs to be roughly kept in - * sync with changes here, esp related to ext draining - */ - -int lws_client_rx_sm(struct lws *wsi, unsigned char c) -{ - int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; - int handled, n, m, rx_draining_ext = 0; - unsigned short close_code; - struct lws_tokens eff_buf; - unsigned char *pp; - - if (wsi->u.ws.rx_draining_ext) { - assert(!c); - eff_buf.token = NULL; - eff_buf.token_len = 0; - lws_remove_wsi_from_draining_ext_list(wsi); - rx_draining_ext = 1; - lwsl_debug("%s: doing draining flow\n", __func__); - - goto drain_extension; - } - - if (wsi->socket_is_permanently_unusable) - return -1; - - switch (wsi->lws_rx_parse_state) { - case LWS_RXPS_NEW: - /* control frames (PING) may interrupt checkable sequences */ - wsi->u.ws.defeat_check_utf8 = 0; - - switch (wsi->ietf_spec_revision) { - case 13: - wsi->u.ws.opcode = c & 0xf; - /* revisit if an extension wants them... */ - switch (wsi->u.ws.opcode) { - case LWSWSOPC_TEXT_FRAME: - wsi->u.ws.rsv_first_msg = (c & 0x70); - wsi->u.ws.continuation_possible = 1; - wsi->u.ws.check_utf8 = lws_check_opt( - wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8); - wsi->u.ws.utf8 = 0; - break; - case LWSWSOPC_BINARY_FRAME: - wsi->u.ws.rsv_first_msg = (c & 0x70); - wsi->u.ws.check_utf8 = 0; - wsi->u.ws.continuation_possible = 1; - break; - case LWSWSOPC_CONTINUATION: - if (!wsi->u.ws.continuation_possible) { - lwsl_info("disordered continuation\n"); - return -1; - } - break; - case LWSWSOPC_CLOSE: - wsi->u.ws.check_utf8 = 0; - wsi->u.ws.utf8 = 0; - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 0xb: - case 0xc: - case 0xd: - case 0xe: - case 0xf: - lwsl_info("illegal opcode\n"); - return -1; - default: - wsi->u.ws.defeat_check_utf8 = 1; - break; - } - wsi->u.ws.rsv = (c & 0x70); - /* revisit if an extension wants them... */ - if ( -#ifndef LWS_NO_EXTENSIONS - !wsi->count_act_ext && -#endif - wsi->u.ws.rsv) { - lwsl_info("illegal rsv bits set\n"); - return -1; - } - wsi->u.ws.final = !!((c >> 7) & 1); - lwsl_ext("%s: This RX frame Final %d\n", __func__, - wsi->u.ws.final); - - if (wsi->u.ws.owed_a_fin && - (wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME || - wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) { - lwsl_info("hey you owed us a FIN\n"); - return -1; - } - if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) { - wsi->u.ws.continuation_possible = 0; - wsi->u.ws.owed_a_fin = 0; - } - - if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) { - lwsl_info("control msg can't be fragmented\n"); - return -1; - } - if (!wsi->u.ws.final) - wsi->u.ws.owed_a_fin = 1; - - switch (wsi->u.ws.opcode) { - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == - LWSWSOPC_BINARY_FRAME; - break; - } - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN; - break; - - default: - lwsl_err("unknown spec version %02d\n", - wsi->ietf_spec_revision); - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN: - - wsi->u.ws.this_frame_masked = !!(c & 0x80); - - switch (c & 0x7f) { - case 126: - /* control frames are not allowed to have big lengths */ - if (wsi->u.ws.opcode & 8) - goto illegal_ctl_length; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2; - break; - case 127: - /* control frames are not allowed to have big lengths */ - if (wsi->u.ws.opcode & 8) - goto illegal_ctl_length; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8; - break; - default: - wsi->u.ws.rx_packet_length = c; - if (wsi->u.ws.this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (c) - wsi->lws_rx_parse_state = - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_2: - wsi->u.ws.rx_packet_length = c << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN16_1: - wsi->u.ws.rx_packet_length |= c; - if (wsi->u.ws.this_frame_masked) - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (wsi->u.ws.rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_8: - if (c & 0x80) { - lwsl_warn("b63 of length must be zero\n"); - /* kill the connection */ - return -1; - } -#if defined __LP64__ - wsi->u.ws.rx_packet_length = ((size_t)c) << 56; -#else - wsi->u.ws.rx_packet_length = 0; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_7: -#if defined __LP64__ - wsi->u.ws.rx_packet_length |= ((size_t)c) << 48; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_6: -#if defined __LP64__ - wsi->u.ws.rx_packet_length |= ((size_t)c) << 40; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_5: -#if defined __LP64__ - wsi->u.ws.rx_packet_length |= ((size_t)c) << 32; -#endif - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_4: - wsi->u.ws.rx_packet_length |= ((size_t)c) << 24; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_3: - wsi->u.ws.rx_packet_length |= ((size_t)c) << 16; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_2: - wsi->u.ws.rx_packet_length |= ((size_t)c) << 8; - wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1; - break; - - case LWS_RXPS_04_FRAME_HDR_LEN64_1: - wsi->u.ws.rx_packet_length |= (size_t)c; - if (wsi->u.ws.this_frame_masked) - wsi->lws_rx_parse_state = - LWS_RXPS_07_COLLECT_FRAME_KEY_1; - else { - if (wsi->u.ws.rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - } - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_1: - wsi->u.ws.mask[0] = c; - if (c) - wsi->u.ws.all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_2: - wsi->u.ws.mask[1] = c; - if (c) - wsi->u.ws.all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_3: - wsi->u.ws.mask[2] = c; - if (c) - wsi->u.ws.all_zero_nonce = 0; - wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4; - break; - - case LWS_RXPS_07_COLLECT_FRAME_KEY_4: - wsi->u.ws.mask[3] = c; - if (c) - wsi->u.ws.all_zero_nonce = 0; - - if (wsi->u.ws.rx_packet_length) - wsi->lws_rx_parse_state = - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; - else { - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - break; - - case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: - - assert(wsi->u.ws.rx_ubuf); - - if (wsi->u.ws.rx_draining_ext) - goto drain_extension; - - if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce) - c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3]; - - wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c; - - if (--wsi->u.ws.rx_packet_length == 0) { - /* spill because we have the whole frame */ - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - goto spill; - } - - /* - * if there's no protocol max frame size given, we are - * supposed to default to context->pt_serv_buf_size - */ - if (!wsi->protocol->rx_buffer_size && - wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size) - break; - - if (wsi->protocol->rx_buffer_size && - wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size) - break; - - /* spill because we filled our rx buffer */ -spill: - - handled = 0; - - /* - * is this frame a control packet we should take care of at this - * layer? If so service it and hide it from the user callback - */ - - switch (wsi->u.ws.opcode) { - case LWSWSOPC_CLOSE: - pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE]; - if (lws_check_opt(wsi->context->options, - LWS_SERVER_OPTION_VALIDATE_UTF8) && - wsi->u.ws.rx_ubuf_head > 2 && - lws_check_utf8(&wsi->u.ws.utf8, pp + 2, - wsi->u.ws.rx_ubuf_head - 2)) - goto utf8_fail; - - /* is this an acknowledgement of our close? */ - if (wsi->state == LWSS_AWAITING_CLOSE_ACK) { - /* - * fine he has told us he is closing too, let's - * finish our close - */ - lwsl_parser("seen server's close ack\n"); - return -1; - } - - lwsl_parser("client sees server close len = %d\n", - wsi->u.ws.rx_ubuf_head); - if (wsi->u.ws.rx_ubuf_head >= 2) { - close_code = (pp[0] << 8) | pp[1]; - if (close_code < 1000 || - close_code == 1004 || - close_code == 1005 || - close_code == 1006 || - close_code == 1012 || - close_code == 1013 || - close_code == 1014 || - close_code == 1015 || - (close_code >= 1016 && close_code < 3000) - ) { - pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff; - pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff; - } - } - if (user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, - wsi->user_space, pp, - wsi->u.ws.rx_ubuf_head)) - return -1; - - if (lws_partial_buffered(wsi)) - /* - * if we're in the middle of something, - * we can't do a normal close response and - * have to just close our end. - */ - wsi->socket_is_permanently_unusable = 1; - else - /* - * parrot the close packet payload back - * we do not care about how it went, we are closing - * immediately afterwards - */ - lws_write(wsi, (unsigned char *) - &wsi->u.ws.rx_ubuf[LWS_PRE], - wsi->u.ws.rx_ubuf_head, - LWS_WRITE_CLOSE); - wsi->state = LWSS_RETURNED_CLOSE_ALREADY; - /* close the connection */ - return -1; - - case LWSWSOPC_PING: - lwsl_info("received %d byte ping, sending pong\n", - wsi->u.ws.rx_ubuf_head); - - /* he set a close reason on this guy, ignore PING */ - if (wsi->u.ws.close_in_ping_buffer_len) - goto ping_drop; - - if (wsi->u.ws.ping_pending_flag) { - /* - * there is already a pending ping payload - * we should just log and drop - */ - lwsl_parser("DROP PING since one pending\n"); - goto ping_drop; - } - - /* control packets can only be < 128 bytes long */ - if (wsi->u.ws.rx_ubuf_head > 128 - 3) { - lwsl_parser("DROP PING payload too large\n"); - goto ping_drop; - } - - /* stash the pong payload */ - memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE, - &wsi->u.ws.rx_ubuf[LWS_PRE], - wsi->u.ws.rx_ubuf_head); - - wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head; - wsi->u.ws.ping_pending_flag = 1; - - /* get it sent as soon as possible */ - lws_callback_on_writable(wsi); -ping_drop: - wsi->u.ws.rx_ubuf_head = 0; - handled = 1; - break; - - case LWSWSOPC_PONG: - lwsl_info("client receied pong\n"); - lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE], - wsi->u.ws.rx_ubuf_head); - - if (wsi->pending_timeout == - PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) { - lwsl_info("%p: received expected PONG\n", wsi); - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - } - - /* issue it */ - callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG; - break; - - case LWSWSOPC_CONTINUATION: - case LWSWSOPC_TEXT_FRAME: - case LWSWSOPC_BINARY_FRAME: - break; - - default: - - lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); - - /* - * It's something special we can't understand here. - * Pass the payload up to the extension's parsing - * state machine. - */ - - eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; - eff_buf.token_len = wsi->u.ws.rx_ubuf_head; - - if (lws_ext_cb_active(wsi, - LWS_EXT_CB_EXTENDED_PAYLOAD_RX, - &eff_buf, 0) <= 0) { - /* not handled or failed */ - lwsl_ext("Unhandled ext opc 0x%x\n", - wsi->u.ws.opcode); - wsi->u.ws.rx_ubuf_head = 0; - - return 0; - } - handled = 1; - break; - } - - /* - * No it's real payload, pass it up to the user callback. - * It's nicely buffered with the pre-padding taken care of - * so it can be sent straight out again using lws_write - */ - if (handled) - goto already_done; - - eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE]; - eff_buf.token_len = wsi->u.ws.rx_ubuf_head; - - if (wsi->u.ws.opcode == LWSWSOPC_PONG && !eff_buf.token_len) - goto already_done; - -drain_extension: - lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len); - - n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0); - lwsl_ext("Ext RX returned %d\n", n); - if (n < 0) { - wsi->socket_is_permanently_unusable = 1; - return -1; - } - - lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len); - - if (rx_draining_ext && !eff_buf.token_len) { - lwsl_debug(" --- ending drain on 0 read result\n"); - goto already_done; - } - - if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) { - if (lws_check_utf8(&wsi->u.ws.utf8, - (unsigned char *)eff_buf.token, - eff_buf.token_len)) - goto utf8_fail; - - /* we are ending partway through utf-8 character? */ - if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && - wsi->u.ws.utf8 && !n) { - lwsl_info("FINAL utf8 error\n"); -utf8_fail: - lwsl_info("utf8 error\n"); - return -1; - } - } - - if (eff_buf.token_len < 0 && - callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) - goto already_done; - - if (!eff_buf.token) - goto already_done; - - eff_buf.token[eff_buf.token_len] = '\0'; - - if (!wsi->protocol->callback) - goto already_done; - - if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) - lwsl_info("Client doing pong callback\n"); - - if (n && eff_buf.token_len) - /* extension had more... main loop will come back - * we want callback to be done with this set, if so, - * because lws_is_final() hides it was final until the - * last chunk - */ - lws_add_wsi_to_draining_ext_list(wsi); - else - lws_remove_wsi_from_draining_ext_list(wsi); - - if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY || - wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION || - wsi->state == LWSS_AWAITING_CLOSE_ACK) - goto already_done; - - m = wsi->protocol->callback(wsi, - (enum lws_callback_reasons)callback_action, - wsi->user_space, eff_buf.token, eff_buf.token_len); - - /* if user code wants to close, let caller know */ - if (m) - return 1; - -already_done: - wsi->u.ws.rx_ubuf_head = 0; - break; - default: - lwsl_err("client rx illegal state\n"); - return 1; - } - - return 0; - -illegal_ctl_length: - lwsl_warn("Control frame asking for extended length is illegal\n"); - - /* kill the connection */ - return -1; -} - - diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c deleted file mode 100644 index ded4e4bf0b..0000000000 --- a/thirdparty/lws/client/client.c +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "private-libwebsockets.h" - -int -lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len) -{ - int m; - - switch (wsi->mode) { - case LWSCM_WSCL_WAITING_PROXY_REPLY: - case LWSCM_WSCL_ISSUE_HANDSHAKE: - case LWSCM_WSCL_WAITING_SERVER_REPLY: - case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: - case LWSCM_WS_CLIENT: - while (len) { - /* - * we were accepting input but now we stopped doing so - */ - if (lws_is_flowcontrolled(wsi)) { - lwsl_debug("%s: caching %ld\n", __func__, (long)len); - lws_rxflow_cache(wsi, *buf, 0, len); - return 0; - } - if (wsi->u.ws.rx_draining_ext) { -#if !defined(LWS_NO_CLIENT) - if (wsi->mode == LWSCM_WS_CLIENT) - m = lws_client_rx_sm(wsi, 0); - else -#endif - m = lws_rx_sm(wsi, 0); - if (m < 0) - return -1; - continue; - } - /* account for what we're using in rxflow buffer */ - if (wsi->rxflow_buffer) - wsi->rxflow_pos++; - - if (lws_client_rx_sm(wsi, *(*buf)++)) { - lwsl_debug("client_rx_sm exited\n"); - return -1; - } - len--; - } - lwsl_debug("%s: finished with %ld\n", __func__, (long)len); - return 0; - default: - break; - } - - return 0; -} - -LWS_VISIBLE LWS_EXTERN void -lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) -{ - wsi->client_http_body_pending = !!something_left_to_send; -} - -int -lws_client_socket_service(struct lws_context *context, struct lws *wsi, - struct lws_pollfd *pollfd) -{ - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *p = (char *)&pt->serv_buf[0]; - const char *cce = NULL; - unsigned char c; - char *sb = p; - int n = 0; - ssize_t len = 0; -#if defined(LWS_WITH_SOCKS5) - char conn_mode = 0, pending_timeout = 0; -#endif - - switch (wsi->mode) { - - case LWSCM_WSCL_WAITING_CONNECT: - - /* - * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE - * timeout protection set in client-handshake.c - */ - - if (!lws_client_connect_2(wsi)) { - /* closed */ - lwsl_client("closed\n"); - return -1; - } - - /* either still pending connection, or changed mode */ - return 0; - -#if defined(LWS_WITH_SOCKS5) - /* SOCKS Greeting Reply */ - case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: - case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: - case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: - - /* handle proxy hung up on us */ - - if (pollfd->revents & LWS_POLLHUP) { - lwsl_warn("SOCKS connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - goto bail3; - } - - n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); - if (n < 0) { - if (LWS_ERRNO == LWS_EAGAIN) { - lwsl_debug("SOCKS read EAGAIN, retrying\n"); - return 0; - } - lwsl_err("ERROR reading from SOCKS socket\n"); - goto bail3; - } - - switch (wsi->mode) { - - case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY: - if (pt->serv_buf[0] != SOCKS_VERSION_5) - goto socks_reply_fail; - - if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { - lwsl_client("SOCKS greeting reply: No Auth Method\n"); - socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); - conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; - pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; - goto socks_send; - } - - if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { - lwsl_client("SOCKS greeting reply: User/Pw Method\n"); - socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD, &len); - conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY; - pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; - goto socks_send; - } - goto socks_reply_fail; - - case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY: - if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || - pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) - goto socks_reply_fail; - - lwsl_client("SOCKS password OK, sending connect\n"); - socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len); - conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY; - pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; -socks_send: - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing to socks proxy\n"); - goto bail3; - } - - lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); - wsi->mode = conn_mode; - break; - -socks_reply_fail: - lwsl_notice("socks reply: v%d, err %d\n", - pt->serv_buf[0], pt->serv_buf[1]); - goto bail3; - - case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY: - if (pt->serv_buf[0] != SOCKS_VERSION_5 || - pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) - goto socks_reply_fail; - - lwsl_client("socks connect OK\n"); - - /* free stash since we are done with it */ - lws_free_set_NULL(wsi->u.hdr.stash); - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - wsi->vhost->socks_proxy_address)) - goto bail3; - - wsi->c_port = wsi->vhost->socks_proxy_port; - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - goto start_ws_handshake; - } - break; -#endif - - case LWSCM_WSCL_WAITING_PROXY_REPLY: - - /* handle proxy hung up on us */ - - if (pollfd->revents & LWS_POLLHUP) { - - lwsl_warn("Proxy connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - - goto bail3; - } - - n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); - if (n < 0) { - if (LWS_ERRNO == LWS_EAGAIN) { - lwsl_debug("Proxy read returned EAGAIN... retrying\n"); - return 0; - } - lwsl_err("ERROR reading from proxy socket\n"); - goto bail3; - } - - pt->serv_buf[13] = '\0'; - if (strcmp(sb, "HTTP/1.0 200 ") && - strcmp(sb, "HTTP/1.1 200 ")) { - lwsl_err("ERROR proxy: %s\n", sb); - goto bail3; - } - - /* clear his proxy connection timeout */ - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* fallthru */ - - case LWSCM_WSCL_ISSUE_HANDSHAKE: - - /* - * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE - * timeout protection set in client-handshake.c - * - * take care of our lws_callback_on_writable - * happening at a time when there's no real connection yet - */ -#if defined(LWS_WITH_SOCKS5) -start_ws_handshake: -#endif - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) - return -1; - -#ifdef LWS_OPENSSL_SUPPORT - /* we can retry this... just cook the SSL BIO the first time */ - - if (wsi->use_ssl && !wsi->ssl && - lws_ssl_client_bio_create(wsi) < 0) { - cce = "bio_create failed"; - goto bail3; - } - - if (wsi->use_ssl) { - n = lws_ssl_client_connect1(wsi); - if (!n) - return 0; - if (n < 0) { - cce = "lws_ssl_client_connect1 failed"; - goto bail3; - } - } else - wsi->ssl = NULL; - - /* fallthru */ - - case LWSCM_WSCL_WAITING_SSL: - - if (wsi->use_ssl) { - n = lws_ssl_client_connect2(wsi); - if (!n) - return 0; - if (n < 0) { - cce = "lws_ssl_client_connect2 failed"; - goto bail3; - } - } else - wsi->ssl = NULL; -#endif - - wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2; - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, - context->timeout_secs); - - /* fallthru */ - - case LWSCM_WSCL_ISSUE_HANDSHAKE2: - p = lws_generate_client_handshake(wsi, p); - if (p == NULL) { - if (wsi->mode == LWSCM_RAW) - return 0; - - lwsl_err("Failed to generate handshake for client\n"); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); - return 0; - } - - /* send our request to the server */ - lws_latency_pre(context, wsi); - - n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb); - lws_latency(context, wsi, "send lws_issue_raw", n, - n == p - sb); - switch (n) { - case LWS_SSL_CAPABLE_ERROR: - lwsl_debug("ERROR writing to client socket\n"); - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); - return 0; - case LWS_SSL_CAPABLE_MORE_SERVICE: - lws_callback_on_writable(wsi); - break; - } - - if (wsi->client_http_body_pending) { - wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY; - lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, - context->timeout_secs); - /* user code must ask for writable callback */ - break; - } - - goto client_http_body_sent; - - case LWSCM_WSCL_ISSUE_HTTP_BODY: - if (wsi->client_http_body_pending) { - lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, - context->timeout_secs); - /* user code must ask for writable callback */ - break; - } -client_http_body_sent: - wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; - wsi->u.hdr.lextable_pos = 0; - wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY; - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, - context->timeout_secs); - break; - - case LWSCM_WSCL_WAITING_SERVER_REPLY: - /* - * handle server hanging up on us... - * but if there is POLLIN waiting, handle that first - */ - if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == - LWS_POLLHUP) { - - lwsl_debug("Server connection %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - cce = "Peer hung up"; - goto bail3; - } - - if (!(pollfd->revents & LWS_POLLIN)) - break; - - /* interpret the server response - * - * HTTP/1.1 101 Switching Protocols - * Upgrade: websocket - * Connection: Upgrade - * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= - * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== - * Sec-WebSocket-Protocol: chat - * - * we have to take some care here to only take from the - * socket bytewise. The browser may (and has been seen to - * in the case that onopen() performs websocket traffic) - * coalesce both handshake response and websocket traffic - * in one packet, since at that point the connection is - * definitively ready from browser pov. - */ - len = 1; - while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && - len > 0) { - n = lws_ssl_capable_read(wsi, &c, 1); - lws_latency(context, wsi, "send lws_issue_raw", n, - n == 1); - switch (n) { - case 0: - case LWS_SSL_CAPABLE_ERROR: - cce = "read failed"; - goto bail3; - case LWS_SSL_CAPABLE_MORE_SERVICE: - return 0; - } - - if (lws_parse(wsi, c)) { - lwsl_warn("problems parsing header\n"); - goto bail3; - } - } - - /* - * hs may also be coming in multiple packets, there is a 5-sec - * libwebsocket timeout still active here too, so if parsing did - * not complete just wait for next packet coming in this state - */ - if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) - break; - - /* - * otherwise deal with the handshake. If there's any - * packet traffic already arrived we'll trigger poll() again - * right away and deal with it that way - */ - return lws_client_interpret_server_handshake(wsi); - -bail3: - lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); - if (cce) - lwsl_info("reason: %s\n", cce); - wsi->protocol->callback(wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, (void *)cce, cce ? strlen(cce) : 0); - wsi->already_did_cce = 1; - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); - return -1; - - case LWSCM_WSCL_WAITING_EXTENSION_CONNECT: - lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n"); - break; - - case LWSCM_WSCL_PENDING_CANDIDATE_CHILD: - lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n"); - break; - default: - break; - } - - return 0; -} - -/* - * In-place str to lower case - */ - -static void -strtolower(char *s) -{ - while (*s) { -#ifdef LWS_PLAT_OPTEE - int tolower_optee(int c); - *s = tolower_optee((int)*s); -#else - *s = tolower((int)*s); -#endif - s++; - } -} - -int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed_client(struct lws *wsi) -{ - lwsl_debug("%s: wsi %p\n", __func__, wsi); - /* if we can't go back to accept new headers, drop the connection */ - if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { - lwsl_info("%s: %p: close connection\n", __func__, wsi); - return 1; - } - - /* we don't support chained client connections yet */ - return 1; -#if 0 - /* otherwise set ourselves up ready to go again */ - wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; - wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED; - wsi->u.http.rx_content_length = 0; - wsi->hdr_parsing_completed = 0; - - /* He asked for it to stay alive indefinitely */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* - * As client, nothing new is going to come until we ask for it - * we can drop the ah, if any - */ - if (wsi->u.hdr.ah) { - lws_header_table_force_to_detachable_state(wsi); - lws_header_table_detach(wsi, 0); - } - - /* If we're (re)starting on headers, need other implied init */ - wsi->u.hdr.ues = URIES_IDLE; - - lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); - - return 0; -#endif -} - -LWS_VISIBLE LWS_EXTERN unsigned int -lws_http_client_http_response(struct lws *wsi) -{ - if (!wsi->u.http.ah) - return 0; - - return wsi->u.http.ah->http_response; -} - -int -lws_client_interpret_server_handshake(struct lws *wsi) -{ - int n, len, okay = 0, port = 0, ssl = 0; - int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; - struct lws_context *context = wsi->context; - const char *pc, *prot, *ads = NULL, *path, *cce = NULL; - struct allocated_headers *ah = NULL; - char *p, *q; - char new_path[300]; -#ifndef LWS_NO_EXTENSIONS - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *sb = (char *)&pt->serv_buf[0]; - const struct lws_ext_options *opts; - const struct lws_extension *ext; - char ext_name[128]; - const char *c, *a; - char ignore; - int more = 1; - void *v; -#endif - if (wsi->u.hdr.stash) - lws_free_set_NULL(wsi->u.hdr.stash); - - ah = wsi->u.hdr.ah; - if (!wsi->do_ws) { - /* we are being an http client... - */ - lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED); - wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; - wsi->u.http.ah = ah; - ah->http_response = 0; - } - - /* - * well, what the server sent looked reasonable for syntax. - * Now let's confirm it sent all the necessary headers - * - * http (non-ws) client will expect something like this - * - * HTTP/1.0.200 - * server:.libwebsockets - * content-type:.text/html - * content-length:.17703 - * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 - * - * - * - */ - - wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE; - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); - if (wsi->do_ws && !p) { - lwsl_info("no URI\n"); - cce = "HS: URI missing"; - goto bail3; - } - if (!p) { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); - wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; - } - if (!p) { - cce = "HS: URI missing"; - lwsl_info("no URI\n"); - goto bail3; - } - n = atoi(p); - if (ah) - ah->http_response = n; - - if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); - if (!p) { - cce = "HS: Redirect code but no Location"; - goto bail3; - } - - /* Relative reference absolute path */ - if (p[0] == '/') - { -#ifdef LWS_OPENSSL_SUPPORT - ssl = wsi->use_ssl; -#endif - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = wsi->c_port; - path = p + 1; /* +1 as lws_client_reset expects leading / to be omitted */ - } - /* Absolute (Full) URI */ - else if (strchr(p, ':')) - { - if (lws_parse_uri(p, &prot, &ads, &port, &path)) { - cce = "HS: URI did not parse"; - goto bail3; - } - - if (!strcmp(prot, "wss") || !strcmp(prot, "https")) - ssl = 1; - } - /* Relative reference relative path */ - else - { - /* This doesn't try to calculate an absolute path, that will be left to the server */ -#ifdef LWS_OPENSSL_SUPPORT - ssl = wsi->use_ssl; -#endif - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); - port = wsi->c_port; - path = new_path + 1; /* +1 as lws_client_reset expects leading / to be omitted */ - strncpy(new_path, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI), sizeof(new_path)); - new_path[sizeof(new_path) - 1] = '\0'; - q = strrchr(new_path, '/'); - if (q) - { - strncpy(q + 1, p, sizeof(new_path) - (q - new_path) - 1); - new_path[sizeof(new_path) - 1] = '\0'; - } - else - { - path = p; - } - } - -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->use_ssl && !ssl) { - cce = "HS: Redirect attempted SSL downgrade"; - goto bail3; - } -#endif - - if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) { - /* there are two ways to fail out with NULL return... - * simple, early problem where the wsi is intact, or - * we went through with the reconnect attempt and the - * wsi is already closed. In the latter case, the wsi - * has beet set to NULL additionally. - */ - lwsl_err("Redirect failed\n"); - cce = "HS: Redirect failed"; - if (wsi) - goto bail3; - - return 1; - } - return 0; - } - - if (!wsi->do_ws) { - -#ifdef LWS_WITH_HTTP_PROXY - wsi->perform_rewrite = 0; - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { - if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE), - "text/html", 9)) - wsi->perform_rewrite = 1; - } -#endif - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) { - lwsl_err("Problem allocating wsi user mem\n"); - cce = "HS: OOM"; - goto bail2; - } - - /* he may choose to send us stuff in chunked transfer-coding */ - wsi->chunked = 0; - wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { - wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING), - "chunked"); - /* first thing is hex, after payload there is crlf */ - wsi->chunk_parser = ELCP_HEX; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { - wsi->u.http.rx_content_length = - atoll(lws_hdr_simple_ptr(wsi, - WSI_TOKEN_HTTP_CONTENT_LENGTH)); - lwsl_notice("%s: incoming content length %llu\n", __func__, - (unsigned long long)wsi->u.http.rx_content_length); - wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length; - } else /* can't do 1.1 without a content length or chunked */ - if (!wsi->chunked) - wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE; - - /* - * we seem to be good to go, give client last chance to check - * headers and OK it - */ - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { - - cce = "HS: disallowed by client filter"; - goto bail2; - } - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* call him back to inform him he is up */ - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { - cce = "HS: disallowed at ESTABLISHED"; - goto bail3; - } - - /* free up his parsing allocations */ - lws_header_table_detach(wsi, 0); - - lwsl_notice("%s: client connection up\n", __func__); - - return 0; - } - - if (p && !strncmp(p, "401", 3)) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%s'\n", p); - cce = "HS: ws upgrade unauthorized"; - goto bail3; - } - - if (p && strncmp(p, "101", 3)) { - lwsl_warn( - "lws_client_handshake: got bad HTTP response '%s'\n", p); - cce = "HS: ws upgrade response not 101"; - goto bail3; - } - - if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) { - lwsl_info("no ACCEPT\n"); - cce = "HS: ACCEPT missing"; - goto bail3; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE); - if (!p) { - lwsl_info("no UPGRADE\n"); - cce = "HS: UPGRADE missing"; - goto bail3; - } - strtolower(p); - if (strcmp(p, "websocket")) { - lwsl_warn( - "lws_client_handshake: got bad Upgrade header '%s'\n", p); - cce = "HS: Upgrade to something other than websocket"; - goto bail3; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION); - if (!p) { - lwsl_info("no Connection hdr\n"); - cce = "HS: CONNECTION missing"; - goto bail3; - } - strtolower(p); - if (strcmp(p, "upgrade")) { - lwsl_warn("lws_client_int_s_hs: bad header %s\n", p); - cce = "HS: UPGRADE malformed"; - goto bail3; - } - - pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - if (!pc) { - lwsl_parser("lws_client_int_s_hs: no protocol list\n"); - } else - lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc); - - /* - * confirm the protocol the server wants to talk was in the list - * of protocols we offered - */ - - len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); - if (!len) { - lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n"); - /* - * no protocol name to work from, - * default to first protocol - */ - n = 0; - wsi->protocol = &wsi->vhost->protocols[0]; - goto check_extensions; - } - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL); - len = strlen(p); - - while (pc && *pc && !okay) { - if (!strncmp(pc, p, len) && - (pc[len] == ',' || pc[len] == '\0')) { - okay = 1; - continue; - } - while (*pc && *pc++ != ',') - ; - while (*pc && *pc == ' ') - pc++; - } - - if (!okay) { - lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p); - cce = "HS: PROTOCOL malformed"; - goto bail2; - } - - /* - * identify the selected protocol struct and set it - */ - n = 0; - wsi->protocol = NULL; - while (wsi->vhost->protocols[n].callback && !wsi->protocol) { - if (strcmp(p, wsi->vhost->protocols[n].name) == 0) { - wsi->protocol = &wsi->vhost->protocols[n]; - break; - } - n++; - } - - if (wsi->protocol == NULL) { - lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p); - cce = "HS: Cannot match protocol"; - goto bail2; - } - -check_extensions: - /* - * stitch protocol choice into the vh protocol linked list - * We always insert ourselves at the start of the list - * - * X <-> B - * X <-> pAn <-> pB - */ - //lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n", - // __func__, - // wsi->vhost->same_vh_protocol_list[n], - // wsi->same_vh_protocol_prev); - wsi->same_vh_protocol_prev = /* guy who points to us */ - &wsi->vhost->same_vh_protocol_list[n]; - wsi->same_vh_protocol_next = /* old first guy is our next */ - wsi->vhost->same_vh_protocol_list[n]; - /* we become the new first guy */ - wsi->vhost->same_vh_protocol_list[n] = wsi; - - if (wsi->same_vh_protocol_next) - /* old first guy points back to us now */ - wsi->same_vh_protocol_next->same_vh_protocol_prev = - &wsi->same_vh_protocol_next; - -#ifndef LWS_NO_EXTENSIONS - /* instantiate the accepted extensions */ - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { - lwsl_ext("no client extensions allowed by server\n"); - goto check_accept; - } - - /* - * break down the list of server accepted extensions - * and go through matching them or identifying bogons - */ - - if (lws_hdr_copy(wsi, sb, context->pt_serv_buf_size, WSI_TOKEN_EXTENSIONS) < 0) { - lwsl_warn("ext list from server failed to copy\n"); - cce = "HS: EXT: list too big"; - goto bail2; - } - - c = sb; - n = 0; - ignore = 0; - a = NULL; - while (more) { - - if (*c && (*c != ',' && *c != '\t')) { - if (*c == ';') { - ignore = 1; - if (!a) - a = c + 1; - } - if (ignore || *c == ' ') { - c++; - continue; - } - - ext_name[n] = *c++; - if (n < sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - ignore = 0; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - /* check we actually support it */ - - lwsl_notice("checking client ext %s\n", ext_name); - - n = 0; - ext = wsi->vhost->extensions; - while (ext && ext->callback) { - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - n = 1; - lwsl_notice("instantiating client ext %s\n", ext_name); - - /* instantiate the extension on this conn */ - - wsi->active_extensions[wsi->count_act_ext] = ext; - - /* allow him to construct his ext instance */ - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_CLIENT_CONSTRUCT, - (void *)&wsi->act_ext_user[wsi->count_act_ext], - (void *)&opts, 0)) { - lwsl_info(" ext %s failed construction\n", ext_name); - ext++; - continue; - } - - /* - * allow the user code to override ext defaults if it - * wants to - */ - ext_name[0] = '\0'; - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_WS_EXT_DEFAULTS, - (char *)ext->name, ext_name, - sizeof(ext_name))) { - cce = "HS: EXT: failed setting defaults"; - goto bail2; - } - - if (ext_name[0] && - lws_ext_parse_options(ext, wsi, wsi->act_ext_user[ - wsi->count_act_ext], opts, ext_name, - strlen(ext_name))) { - lwsl_err("%s: unable to parse user defaults '%s'", - __func__, ext_name); - cce = "HS: EXT: failed parsing defaults"; - goto bail2; - } - - /* - * give the extension the server options - */ - if (a && lws_ext_parse_options(ext, wsi, - wsi->act_ext_user[wsi->count_act_ext], - opts, a, c - a)) { - lwsl_err("%s: unable to parse remote def '%s'", - __func__, a); - cce = "HS: EXT: failed parsing options"; - goto bail2; - } - - if (ext->callback(lws_get_context(wsi), ext, wsi, - LWS_EXT_CB_OPTION_CONFIRM, - wsi->act_ext_user[wsi->count_act_ext], - NULL, 0)) { - lwsl_err("%s: ext %s rejects server options %s", - __func__, ext->name, a); - cce = "HS: EXT: Rejects server options"; - goto bail2; - } - - wsi->count_act_ext++; - - ext++; - } - - if (n == 0) { - lwsl_warn("Unknown ext '%s'!\n", ext_name); - cce = "HS: EXT: unknown ext"; - goto bail2; - } - - a = NULL; - n = 0; - } - -check_accept: -#endif - - /* - * Confirm his accept token is the one we precomputed - */ - - p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT); - if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) { - lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p, - wsi->u.hdr.ah->initial_handshake_hash_base64); - cce = "HS: Accept hash wrong"; - goto bail2; - } - - /* allocate the per-connection user memory (if any) */ - if (lws_ensure_user_space(wsi)) { - lwsl_err("Problem allocating wsi user mem\n"); - cce = "HS: OOM"; - goto bail2; - } - - /* - * we seem to be good to go, give client last chance to check - * headers and OK it - */ - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { - cce = "HS: Rejected by filter cb"; - goto bail2; - } - - /* clear his proxy connection timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - /* free up his parsing allocations */ - lws_header_table_detach(wsi, 0); - - lws_union_transition(wsi, LWSCM_WS_CLIENT); - wsi->state = LWSS_ESTABLISHED; - lws_restart_ws_ping_pong_timer(wsi); - - wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* - * create the frame buffer for this connection according to the - * size mentioned in the protocol definition. If 0 there, then - * use a big default for compatibility - */ - n = wsi->protocol->rx_buffer_size; - if (!n) - n = context->pt_serv_buf_size; - n += LWS_PRE; - wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "client frame buffer"); - if (!wsi->u.ws.rx_ubuf) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - cce = "HS: OOM"; - goto bail2; - } - wsi->u.ws.rx_ubuf_alloc = n; - lwsl_info("Allocating client RX buffer %d\n", n); - -#if !defined(LWS_WITH_ESP32) - if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n, - sizeof n)) { - lwsl_warn("Failed to set SNDBUF to %d", n); - cce = "HS: SO_SNDBUF failed"; - goto bail3; - } -#endif - - lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name); - - /* call him back to inform him he is up */ - - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED, - wsi->user_space, NULL, 0)) { - cce = "HS: Rejected at CLIENT_ESTABLISHED"; - goto bail3; - } -#ifndef LWS_NO_EXTENSIONS - /* - * inform all extensions, not just active ones since they - * already know - */ - ext = wsi->vhost->extensions; - - while (ext && ext->callback) { - v = NULL; - for (n = 0; n < wsi->count_act_ext; n++) - if (wsi->active_extensions[n] == ext) - v = wsi->act_ext_user[n]; - - ext->callback(context, ext, wsi, - LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0); - ext++; - } -#endif - - return 0; - -bail3: - close_reason = LWS_CLOSE_STATUS_NOSTATUS; - -bail2: - if (wsi->protocol) - wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, (void *)cce, - (unsigned int)strlen(cce)); - wsi->already_did_cce = 1; - - lwsl_info("closing connection due to bail2 connection error\n"); - - /* closing will free up his parsing allocations */ - lws_close_free_wsi(wsi, close_reason); - - return 1; -} - - -char * -lws_generate_client_handshake(struct lws *wsi, char *pkt) -{ - char buf[128], hash[20], key_b64[40], *p = pkt; - struct lws_context *context = wsi->context; - const char *meth; - int n; -#ifndef LWS_NO_EXTENSIONS - const struct lws_extension *ext; - int ext_count = 0; -#endif - const char *pp = lws_hdr_simple_ptr(wsi, - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); - - meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); - if (!meth) { - meth = "GET"; - wsi->do_ws = 1; - } else { - wsi->do_ws = 0; - } - - if (!strcmp(meth, "RAW")) { - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - lwsl_notice("client transition to raw\n"); - - if (pp) { - const struct lws_protocols *pr; - - pr = lws_vhost_name_to_protocol(wsi->vhost, pp); - - if (!pr) { - lwsl_err("protocol %s not enabled on vhost\n", - pp); - return NULL; - } - - lws_bind_protocol(wsi, pr); - } - - if ((wsi->protocol->callback)(wsi, - LWS_CALLBACK_RAW_ADOPT, - wsi->user_space, NULL, 0)) - return NULL; - - lws_header_table_force_to_detachable_state(wsi); - lws_union_transition(wsi, LWSCM_RAW); - lws_header_table_detach(wsi, 1); - - return NULL; - } - - if (wsi->do_ws) { - /* - * create the random key - */ - n = lws_get_random(context, hash, 16); - if (n != 16) { - lwsl_err("Unable to read from random dev %s\n", - SYSTEM_RANDOM_FILEPATH); - return NULL; - } - - lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64)); - } - - /* - * 04 example client handshake - * - * GET /chat HTTP/1.1 - * Host: server.example.com - * Upgrade: websocket - * Connection: Upgrade - * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - * Sec-WebSocket-Origin: http://example.com - * Sec-WebSocket-Protocol: chat, superchat - * Sec-WebSocket-Version: 4 - */ - - p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth, - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); - - p += sprintf(p, "Pragma: no-cache\x0d\x0a" - "Cache-Control: no-cache\x0d\x0a"); - - p += sprintf(p, "Host: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); - - if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { - if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) - p += sprintf(p, "Origin: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); - else - p += sprintf(p, "Origin: http://%s\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); - } - - if (wsi->do_ws) { - p += sprintf(p, "Upgrade: websocket\x0d\x0a" - "Connection: Upgrade\x0d\x0a" - "Sec-WebSocket-Key: "); - strcpy(p, key_b64); - p += strlen(key_b64); - p += sprintf(p, "\x0d\x0a"); - if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) - p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)); - - /* tell the server what extensions we could support */ - -#ifndef LWS_NO_EXTENSIONS - ext = wsi->vhost->extensions; - while (ext && ext->callback) { - n = lws_ext_cb_all_exts(context, wsi, - LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, - (char *)ext->name, 0); - if (n) { /* an extension vetos us */ - lwsl_ext("ext %s vetoed\n", (char *)ext->name); - ext++; - continue; - } - n = wsi->vhost->protocols[0].callback(wsi, - LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, - wsi->user_space, (char *)ext->name, 0); - - /* - * zero return from callback means - * go ahead and allow the extension, - * it's what we get if the callback is - * unhandled - */ - - if (n) { - ext++; - continue; - } - - /* apply it */ - - if (ext_count) - *p++ = ','; - else - p += sprintf(p, "Sec-WebSocket-Extensions: "); - p += sprintf(p, "%s", ext->client_offer); - ext_count++; - - ext++; - } - if (ext_count) - p += sprintf(p, "\x0d\x0a"); -#endif - - if (wsi->ietf_spec_revision) - p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a", - wsi->ietf_spec_revision); - - /* prepare the expected server accept response */ - - key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ - n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); - - lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); - - lws_b64_encode_string(hash, 20, - wsi->u.hdr.ah->initial_handshake_hash_base64, - sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64)); - } - - /* give userland a chance to append, eg, cookies */ - - if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, - wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12)) - return NULL; - - p += sprintf(p, "\x0d\x0a"); - - return p; -} - diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c deleted file mode 100644 index 962c6e3cb5..0000000000 --- a/thirdparty/lws/client/ssl-client.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "private-libwebsockets.h" - -extern int openssl_websocket_private_data_index, - openssl_SSL_CTX_private_data_index; - -extern void -lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info); - -extern int lws_ssl_get_error(struct lws *wsi, int n); - -#if defined(USE_WOLFSSL) -#else - -static int -OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ -#if defined(LWS_WITH_MBEDTLS) - lwsl_notice("%s\n", __func__); - - return 0; -#else - SSL *ssl; - int n; - struct lws *wsi; - - /* keep old behaviour accepting self-signed server certs */ - if (!preverify_ok) { - int err = X509_STORE_CTX_get_error(x509_ctx); - - if (err != X509_V_OK) { - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - - if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && - wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) { - lwsl_notice("accepting self-signed certificate (verify_callback)\n"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; // ok - } else if ((err == X509_V_ERR_CERT_NOT_YET_VALID || - err == X509_V_ERR_CERT_HAS_EXPIRED) && - wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) { - if (err == X509_V_ERR_CERT_NOT_YET_VALID) - lwsl_notice("accepting not yet valid certificate (verify_callback)\n"); - else if (err == X509_V_ERR_CERT_HAS_EXPIRED) - lwsl_notice("accepting expired certificate (verify_callback)\n"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; // ok - } - } - } - - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - - n = lws_get_context_protocol(wsi->context, 0).callback(wsi, - LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, - x509_ctx, ssl, preverify_ok); - - /* keep old behaviour if something wrong with server certs */ - /* if ssl error is overruled in callback and cert is ok, - * X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and - * return value is 0 from callback */ - if (!preverify_ok) { - int err = X509_STORE_CTX_get_error(x509_ctx); - - if (err != X509_V_OK) { /* cert validation error was not handled in callback */ - int depth = X509_STORE_CTX_get_error_depth(x509_ctx); - const char* msg = X509_verify_cert_error_string(err); - lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth); - return preverify_ok; // not ok - } - } - /* convert callback return code from 0 = OK to verify callback return value 1 = OK */ - return !n; -#endif -} -#endif - -int -lws_ssl_client_bio_create(struct lws *wsi) -{ - char hostname[128], *p; - - 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->ssl = SSL_new(wsi->vhost->ssl_client_ctx); - if (!wsi->ssl) { - lwsl_err("SSL_new failed: %s\n", - ERR_error_string(lws_ssl_get_error(wsi, 0), NULL)); - lws_ssl_elaborate_error(); - return -1; - } - -#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) - if (wsi->vhost->ssl_info_event_mask) - SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback); -#endif - -#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host - X509_VERIFY_PARAM *param; - (void)param; - - if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { - param = SSL_get0_param(wsi->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); - } - -#endif - -#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) -#ifndef USE_OLD_CYASSL - /* OpenSSL_client_verify_callback will be called @ SSL_connect() */ - SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); -#endif -#endif - -#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS) - SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); -#endif - /* - * use server name indication (SNI), if supported, - * when establishing connection - */ -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL -#ifdef CYASSL_SNI_HOST_NAME - CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); -#endif -#else -#ifdef WOLFSSL_SNI_HOST_NAME - wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname)); -#endif -#endif -#else -#if defined(LWS_WITH_MBEDTLS) - SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback); -#else -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - SSL_set_tlsext_host_name(wsi->ssl, hostname); -#endif -#endif -#endif - -#ifdef USE_WOLFSSL - /* - * wolfSSL/CyaSSL does certificate verification differently - * from OpenSSL. - * If we should ignore the certificate, we need to set - * this before SSL_new and SSL_connect is called. - * Otherwise the connect will simply fail with error code -155 - */ -#ifdef USE_OLD_CYASSL - if (wsi->use_ssl == 2) - CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); -#else - if (wsi->use_ssl == 2) - wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); -#endif -#endif /* USE_WOLFSSL */ - -#if !defined(LWS_WITH_MBEDTLS) - wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE); - SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); -#else - SSL_set_fd(wsi->ssl, wsi->desc.sockfd); -#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_WITH_MBEDTLS) - BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ -#endif -#endif - -#if !defined(LWS_WITH_MBEDTLS) - SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, - wsi); -#endif - - return 0; -} - -#if defined(LWS_WITH_MBEDTLS) -int ERR_get_error(void) -{ - return 0; -} -#endif - -int -lws_ssl_client_connect1(struct lws *wsi) -{ - struct lws_context *context = wsi->context; - int n = 0; - - lws_latency_pre(context, wsi); - - n = SSL_connect(wsi->ssl); - - lws_latency(context, wsi, - "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0); - - if (n < 0) { - n = lws_ssl_get_error(wsi, n); - - if (n == SSL_ERROR_WANT_READ) - goto some_wait; - - if (n == SSL_ERROR_WANT_WRITE) { - /* - * wants us to retry connect due to - * state of the underlying ssl layer... - * but since it may be stalled on - * blocked write, no incoming data may - * arrive to trigger the retry. - * Force (possibly many times if the SSL - * state persists in returning the - * condition code, but other sockets - * are getting serviced inbetweentimes) - * us to get called back when writable. - */ - lwsl_info("%s: WANT_WRITE... retrying\n", __func__); - lws_callback_on_writable(wsi); -some_wait: - wsi->mode = LWSCM_WSCL_WAITING_SSL; - - return 0; /* no error */ - } - - { - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *p = (char *)&pt->serv_buf[0]; - char *sb = p; - - lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n", - n, errno, ERR_error_string(n, sb)); - lws_ssl_elaborate_error(); -#if defined(LWS_WITH_MBEDTLS) - if (n == SSL_ERROR_SYSCALL) - return -1; -#endif - } - - n = -1; - } - - if (n <= 0) { - /* - * retry if new data comes until we - * run into the connection timeout or win - */ - - unsigned long error = ERR_get_error(); - - if (error != SSL_ERROR_NONE) { - struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - char *p = (char *)&pt->serv_buf[0]; - char *sb = p; - lwsl_err("SSL connect error %lu: %s\n", - error, ERR_error_string(error, sb)); - return -1; - } - - return 0; - } - - return 1; -} - -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 = 0; - - if (wsi->mode == LWSCM_WSCL_WAITING_SSL) { - lws_latency_pre(context, wsi); - n = SSL_connect(wsi->ssl); - lwsl_debug("%s: SSL_connect says %d\n", __func__, n); - - lws_latency(context, wsi, - "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0); - - if (n < 0) { - n = lws_ssl_get_error(wsi, n); - - if (n == SSL_ERROR_WANT_READ) { - lwsl_info("SSL_connect WANT_READ... retrying\n"); - - 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 - */ - unsigned long error = ERR_get_error(); - if (error != SSL_ERROR_NONE) { - lwsl_err("SSL connect error %lu: %s\n", - error, ERR_error_string(error, sb)); - return -1; - } - } - } - -#if defined(LWS_WITH_MBEDTLS) - { - X509 *peer = SSL_get_peer_certificate(wsi->ssl); - - if (!peer) { - lwsl_notice("peer did not provide cert\n"); - - return -1; - } - lwsl_notice("peer provided cert\n"); - } -#endif - -#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); - - lwsl_debug("get_verify says %d\n", n); - - if (n != X509_V_OK) { - if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && - (wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { - lwsl_notice("accepting self-signed certificate\n"); - } else if ((n == X509_V_ERR_CERT_NOT_YET_VALID || - n == X509_V_ERR_CERT_HAS_EXPIRED) && - (wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) { - lwsl_notice("accepting expired certificate\n"); - } else if (n == X509_V_ERR_CERT_NOT_YET_VALID) { - lwsl_notice("Cert is from the future... " - "probably our clock... accepting...\n"); - } else { - lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n", - n, ERR_error_string(n, sb)); - lws_ssl_elaborate_error(); - return -1; - } - } - -#endif /* USE_WOLFSSL */ - - return 1; -} - - -int lws_context_init_client_ssl(struct lws_context_creation_info *info, - struct lws_vhost *vhost) -{ - SSL_METHOD *method = NULL; - struct lws wsi; - unsigned long error; - const char *ca_filepath = info->ssl_ca_filepath; -#if !defined(LWS_WITH_MBEDTLS) - const char *cipher_list = info->ssl_cipher_list; - const char *private_key_filepath = info->ssl_private_key_filepath; - const char *cert_filepath = info->ssl_cert_filepath; - int n; - - if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW) - return 0; - - /* - * for backwards-compatibility default to using ssl_... members, but - * if the newer client-specific ones are given, use those - */ - if (info->client_ssl_cipher_list) - cipher_list = info->client_ssl_cipher_list; - if (info->client_ssl_cert_filepath) - cert_filepath = info->client_ssl_cert_filepath; - if (info->client_ssl_private_key_filepath) - private_key_filepath = info->client_ssl_private_key_filepath; -#endif - if (info->client_ssl_ca_filepath) - ca_filepath = info->client_ssl_ca_filepath; - - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) - return 0; - - if (vhost->ssl_client_ctx) - return 0; - - if (info->provided_client_ssl_ctx) { - /* use the provided OpenSSL context if given one */ - vhost->ssl_client_ctx = info->provided_client_ssl_ctx; - /* nothing for lib to delete */ - vhost->user_supplied_ssl_ctx = 1; - - return 0; - } - - /* basic openssl init already happened in context init */ - - /* choose the most recent spin of the api */ -#if defined(LWS_HAVE_TLS_CLIENT_METHOD) - method = (SSL_METHOD *)TLS_client_method(); -#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD) - method = (SSL_METHOD *)TLSv1_2_client_method(); -#else - method = (SSL_METHOD *)SSLv23_client_method(); -#endif - if (!method) { - error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - /* create context */ - vhost->ssl_client_ctx = SSL_CTX_new(method); - if (!vhost->ssl_client_ctx) { - error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, ERR_error_string(error, - (char *)vhost->context->pt[0].serv_buf)); - return 1; - } - - lwsl_notice("created client ssl context for %s\n", vhost->name); - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION); -#endif - -#if defined(LWS_WITH_MBEDTLS) - if (ca_filepath) { - lws_filepos_t len; - uint8_t *buf; - /* - * prototype this here, the shim does not export it in the - * header, and we need to use the shim unchanged for ESP32 case - */ - X509 *d2i_X509(X509 **cert, const unsigned char *buffer, long len); - - if (alloc_file(vhost->context, ca_filepath, &buf, &len)) { - lwsl_err("Load CA cert file %s failed\n", ca_filepath); - return 1; - } - - vhost->x509_client_CA = d2i_X509(NULL, buf, len); - free(buf); - if (!vhost->x509_client_CA) { - lwsl_err("client CA: x509 parse failed\n"); - return 1; - } - - SSL_CTX_add_client_CA(vhost->ssl_client_ctx, - vhost->x509_client_CA); - - lwsl_notice("client loaded CA for verification %s\n", ca_filepath); - } -#else - SSL_CTX_set_options(vhost->ssl_client_ctx, - SSL_OP_CIPHER_SERVER_PREFERENCE); - - if (cipher_list) - SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list); - -#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) - /* loads OS default CA certs */ - SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx); -#endif - - /* openssl init for cert verification (for client sockets) */ - if (!ca_filepath) { - if (!SSL_CTX_load_verify_locations( - vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) - lwsl_err("Unable to load SSL Client certs from %s " - "(set by LWS_OPENSSL_CLIENT_CERTS) -- " - "client ssl isn't going to work\n", - LWS_OPENSSL_CLIENT_CERTS); - } else - if (!SSL_CTX_load_verify_locations( - vhost->ssl_client_ctx, ca_filepath, NULL)) { - lwsl_err( - "Unable to load SSL Client certs " - "file from %s -- client ssl isn't " - "going to work\n", info->client_ssl_ca_filepath); - lws_ssl_elaborate_error(); - } - else - lwsl_info("loaded ssl_ca_filepath\n"); - - /* - * callback allowing user code to load extra verification certs - * helping the client to verify server identity - */ - - /* support for client-side certificate authentication */ - if (cert_filepath) { - lwsl_notice("%s: doing cert filepath\n", __func__); - n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx, - cert_filepath); - if (n < 1) { - lwsl_err("problem %d getting cert '%s'\n", n, - cert_filepath); - lws_ssl_elaborate_error(); - return 1; - } - lwsl_notice("Loaded client cert %s\n", cert_filepath); - } - if (private_key_filepath) { - lwsl_notice("%s: doing private key filepath\n", __func__); - lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info); - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx, - private_key_filepath, SSL_FILETYPE_PEM) != 1) { - lwsl_err("use_PrivateKey_file '%s'\n", - private_key_filepath); - lws_ssl_elaborate_error(); - return 1; - } - lwsl_notice("Loaded client cert private key %s\n", - private_key_filepath); - - /* verify private key */ - if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - return 1; - } - } -#endif - /* - * give him a fake wsi with context set, so he can use - * lws_get_context() in the callback - */ - memset(&wsi, 0, sizeof(wsi)); - wsi.vhost = vhost; - wsi.context = vhost->context; - - vhost->protocols[0].callback(&wsi, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, - vhost->ssl_client_ctx, NULL, 0); - - return 0; -} |