large files: convert content-length to use lws_filepos_t
[platform/upstream/libwebsockets.git] / lib / client.c
index 4ffb1fa..5a57d18 100755 (executable)
@@ -81,7 +81,10 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
        const char *cce = NULL;
        unsigned char c;
        char *sb = p;
-       int n, len;
+       int n = 0, len = 0;
+#if defined(LWS_WITH_SOCKS5)
+       char conn_mode = 0, pending_timeout = 0;
+#endif
 
        switch (wsi->mode) {
 
@@ -101,6 +104,195 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
                /* either still pending connection, or changed mode */
                return 0;
 
+#if defined(LWS_WITH_SOCKS5)
+       /* SOCKS Greeting Reply */
+       case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
+
+               /* handle proxy hung up on us */
+
+               if (pollfd->revents & LWS_POLLHUP) {
+
+                       lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
+                                 (void *)wsi, pollfd->fd);
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       return 0;
+               }
+
+               n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+               if (n < 0) {
+                       if (LWS_ERRNO == LWS_EAGAIN) {
+                               lwsl_debug("SOCKS read returned EAGAIN..."
+                                       "retrying\n");
+                               return 0;
+                       }
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR reading from SOCKS socket\n");
+                       return 0;
+               }
+
+               /* processing greeting reply */
+               if (pt->serv_buf[0] == SOCKS_VERSION_5
+                       && pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH)
+               {
+                       lwsl_client("%s\n", "SOCKS greeting reply received "
+                               "- No Authentication Method");
+                       socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len);
+
+                       conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
+                       pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+                       lwsl_client("%s\n", "Sending SOCKS connect command");
+               }
+               else if (pt->serv_buf[0] == SOCKS_VERSION_5
+                               && pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD)
+               {
+                       lwsl_client("%s\n", "SOCKS greeting reply received "
+                               "- User Name Password Method");
+                       socks_generate_msg(wsi, SOCKS_MSG_USERNAME_PASSWORD,
+                               (size_t *)&len);
+
+                       conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY;
+                       pending_timeout = PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
+                       lwsl_client("%s\n", "Sending SOCKS user/password");
+               }
+               else
+               {
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR SOCKS greeting reply failed, method "
+                               "code: %d\n", pt->serv_buf[1]);
+                       return 0;
+               }
+
+               n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
+                        MSG_NOSIGNAL);
+               if (n < 0) {
+                       lwsl_debug("ERROR writing socks command to socks proxy "
+                               "socket\n");
+                       return 0;
+               }
+
+               lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
+               wsi->mode = conn_mode;
+
+               break;
+       /* SOCKS auth Reply */
+       case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
+
+               /* handle proxy hung up on us */
+
+               if (pollfd->revents & LWS_POLLHUP) {
+
+                       lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
+                                 (void *)wsi, pollfd->fd);
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       return 0;
+               }
+
+               n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+               if (n < 0) {
+                       if (LWS_ERRNO == LWS_EAGAIN) {
+                               lwsl_debug("SOCKS read returned EAGAIN... "
+                                       "retrying\n");
+                               return 0;
+                       }
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR reading from socks socket\n");
+                       return 0;
+               }
+
+               /* processing auth reply */
+               if (pt->serv_buf[0] == SOCKS_SUBNEGOTIATION_VERSION_1
+                       && pt->serv_buf[1] == SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
+               {
+                       lwsl_client("%s\n", "SOCKS password reply recieved - "
+                               "successful");
+                       socks_generate_msg(wsi, SOCKS_MSG_CONNECT, (size_t *)&len);
+
+                       conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
+                       pending_timeout =
+                               PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
+                       lwsl_client("%s\n", "Sending SOCKS connect command");
+               }
+               else
+               {
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR : SOCKS user/password reply failed, "
+                               "error code: %d\n", pt->serv_buf[1]);
+                       return 0;
+               }
+
+               n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len,
+                        MSG_NOSIGNAL);
+               if (n < 0) {
+                       lwsl_debug("ERROR writing connect command to SOCKS "
+                               "socket\n");
+                       return 0;
+               }
+
+               lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
+               wsi->mode = conn_mode;
+
+               break;
+
+       /* SOCKS connect command Reply */
+       case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
+
+               /* handle proxy hung up on us */
+
+               if (pollfd->revents & LWS_POLLHUP) {
+
+                       lwsl_warn("SOCKS connection %p (fd=%d) dead\n",
+                                 (void *)wsi, pollfd->fd);
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       return 0;
+               }
+
+               n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
+               if (n < 0) {
+                       if (LWS_ERRNO == LWS_EAGAIN) {
+                               lwsl_debug("SOCKS read returned EAGAIN... "
+                                       "retrying\n");
+                               return 0;
+                       }
+
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR reading from socks socket\n");
+                       return 0;
+               }
+
+               /* processing connect reply */
+               if (pt->serv_buf[0] == SOCKS_VERSION_5
+                       && pt->serv_buf[1] == SOCKS_REQUEST_REPLY_SUCCESS)
+               {
+                       lwsl_client("%s\n", "SOCKS connect reply recieved - "
+                               "successful");
+               }
+               else
+               {
+                       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
+                       lwsl_err("ERROR SOCKS connect reply failed, error "
+                               "code: %d\n", pt->serv_buf[1]);
+                       return 0;
+               }
+
+               /* free stash since we are done with it */
+               lws_free_set_NULL(wsi->u.hdr.stash);
+
+               if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+                       wsi->vhost->socks_proxy_address))
+                       goto bail3;
+               wsi->c_port = wsi->vhost->socks_proxy_port;
+
+               /* clear his proxy connection timeout */
+
+               lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+               goto start_ws_hanshake;
+#endif
        case LWSCM_WSCL_WAITING_PROXY_REPLY:
 
                /* handle proxy hung up on us */
@@ -114,7 +306,7 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
                        return 0;
                }
 
-               n = recv(wsi->sock, sb, context->pt_serv_buf_size, 0);
+               n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
                if (n < 0) {
                        if (LWS_ERRNO == LWS_EAGAIN) {
                                lwsl_debug("Proxy read returned EAGAIN... retrying\n");
@@ -149,6 +341,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
                 * take care of our lws_callback_on_writable
                 * happening at a time when there's no real connection yet
                 */
+#if defined(LWS_WITH_SOCKS5)
+start_ws_hanshake:
+#endif
                if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
                        return -1;
 
@@ -196,6 +391,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi,
        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;
@@ -317,7 +515,9 @@ client_http_body_sent:
 
 bail3:
                lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
-               wsi->vhost->protocols[0].callback(wsi,
+               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;
@@ -366,6 +566,9 @@ lws_http_transaction_completed_client(struct lws *wsi)
                return 1;
        }
 
+       /* we don't support chained client connections yet */
+       return 1;
+
        /* otherwise set ourselves up ready to go again */
        wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
        wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
@@ -409,7 +612,8 @@ lws_client_interpret_server_handshake(struct lws *wsi)
        struct lws_context *context = wsi->context;
        const char *pc, *prot, *ads = NULL, *path, *cce = NULL;
        struct allocated_headers *ah = NULL;
-       char *p;
+       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];
@@ -422,10 +626,10 @@ lws_client_interpret_server_handshake(struct lws *wsi)
        void *v;
 #endif
 
+       ah = wsi->u.hdr.ah;
        if (!wsi->do_ws) {
                /* we are being an http client...
                 */
-               ah = wsi->u.hdr.ah;
                lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
                wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
                wsi->u.http.ah = ah;
@@ -475,28 +679,79 @@ lws_client_interpret_server_handshake(struct lws *wsi)
                        goto bail3;
                }
 
-               if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
-                       cce = "HS: URI did not parse";
-                       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;
+                       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;
+                       }
+               }
 
-               lwsl_notice("ssl %d %s\n", ssl, prot);
+#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)) {
+               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";
-                       goto bail3;
+                       if (wsi)
+                               goto bail3;
+
+                       return 1;
                }
                return 0;
        }
 
        if (!wsi->do_ws) {
-               if (n != 200 && n != 304) {
+               if (n != 200 && n != 201 && n != 304 && n != 401) {
                        lwsl_notice("Connection failed with code %d\n", n);
-                       cce = "HS: Server did not return 200 or 304";
+                       cce = "HS: Server unrecognized response code";
                        goto bail2;
                }
 
@@ -531,8 +786,8 @@ lws_client_interpret_server_handshake(struct lws *wsi)
                        wsi->u.http.content_length =
                                        atoi(lws_hdr_simple_ptr(wsi,
                                                WSI_TOKEN_HTTP_CONTENT_LENGTH));
-                       lwsl_notice("%s: incoming content length %d\n", __func__,
-                                       wsi->u.http.content_length);
+                       lwsl_notice("%s: incoming content length %llu\n", __func__,
+                                       (unsigned long long)wsi->u.http.content_length);
                        wsi->u.http.content_remain = wsi->u.http.content_length;
                } else /* can't do 1.1 without a content length or chunked */
                        if (!wsi->chunked)
@@ -900,12 +1155,14 @@ check_accept:
        wsi->u.ws.rx_ubuf_alloc = n;
        lwsl_info("Allocating client RX buffer %d\n", n);
 
-       if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&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);
 
@@ -967,13 +1224,46 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
        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
+       } 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;
+
+               wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+               lws_union_transition(wsi, LWSCM_RAW);
+               lws_header_table_detach(wsi, 1);
+
+               return NULL;
+       }
 
        if (wsi->do_ws) {
                /*
@@ -1094,7 +1384,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 
        /* give userland a chance to append, eg, cookies */
 
-       wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
+       wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
                                wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12);
 
        p += sprintf(p, "\x0d\x0a");