LCOV - code coverage report
Current view: top level - lib - lwan-websocket.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 146 0.0 %
Date: 2023-04-18 16:19:03 Functions: 0 9 0.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2019 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
      18             :  * USA.
      19             :  */
      20             : 
      21             : #define _GNU_SOURCE
      22             : #include <assert.h>
      23             : #include <endian.h>
      24             : #include <errno.h>
      25             : #include <string.h>
      26             : #include <sys/socket.h>
      27             : 
      28             : #if defined(__x86_64__)
      29             : #include <emmintrin.h>
      30             : #endif
      31             : 
      32             : #include "lwan-io-wrappers.h"
      33             : #include "lwan-private.h"
      34             : 
      35             : enum ws_opcode {
      36             :     WS_OPCODE_CONTINUATION = 0,
      37             :     WS_OPCODE_TEXT = 1,
      38             :     WS_OPCODE_BINARY = 2,
      39             :     WS_OPCODE_CLOSE = 8,
      40             :     WS_OPCODE_PING = 9,
      41             :     WS_OPCODE_PONG = 10,
      42             : 
      43             :     WS_OPCODE_RSVD_1 = 3,
      44             :     WS_OPCODE_RSVD_2 = 4,
      45             :     WS_OPCODE_RSVD_3 = 5,
      46             :     WS_OPCODE_RSVD_4 = 6,
      47             :     WS_OPCODE_RSVD_5 = 7,
      48             : 
      49             :     WS_OPCODE_RSVD_CONTROL_1 = 11,
      50             :     WS_OPCODE_RSVD_CONTROL_2 = 12,
      51             :     WS_OPCODE_RSVD_CONTROL_3 = 13,
      52             :     WS_OPCODE_RSVD_CONTROL_4 = 14,
      53             :     WS_OPCODE_RSVD_CONTROL_5 = 15,
      54             : 
      55             :     WS_OPCODE_INVALID = 16,
      56             : };
      57             : 
      58           0 : static void write_websocket_frame(struct lwan_request *request,
      59             :                                   unsigned char header_byte,
      60             :                                   char *msg,
      61             :                                   size_t len)
      62             : {
      63           0 :     uint8_t frame[10] = { header_byte };
      64             :     size_t frame_len;
      65             : 
      66           0 :     if (len <= 125) {
      67           0 :         frame[1] = (uint8_t)len;
      68           0 :         frame_len = 2;
      69           0 :     } else if (len <= 65535) {
      70           0 :         frame[1] = 0x7e;
      71           0 :         memcpy(frame + 2, &(uint16_t){htons((uint16_t)len)}, sizeof(uint16_t));
      72           0 :         frame_len = 4;
      73             :     } else {
      74           0 :         frame[1] = 0x7f;
      75           0 :         memcpy(frame + 2, &(uint64_t){htobe64((uint64_t)len)},
      76             :                sizeof(uint64_t));
      77           0 :         frame_len = 10;
      78             :     }
      79             : 
      80           0 :     struct iovec vec[] = {
      81             :         {.iov_base = frame, .iov_len = frame_len},
      82             :         {.iov_base = msg, .iov_len = len},
      83             :     };
      84             : 
      85           0 :     lwan_writev(request, vec, N_ELEMENTS(vec));
      86           0 : }
      87             : 
      88           0 : static inline void lwan_response_websocket_write(struct lwan_request *request, unsigned char op)
      89             : {
      90           0 :     size_t len = lwan_strbuf_get_length(request->response.buffer);
      91           0 :     char *msg = lwan_strbuf_get_buffer(request->response.buffer);
      92           0 :     unsigned char header = 0x80 | op;
      93             : 
      94           0 :     if (!(request->conn->flags & CONN_IS_WEBSOCKET))
      95           0 :         return;
      96             : 
      97           0 :     write_websocket_frame(request, header, msg, len);
      98           0 :     lwan_strbuf_reset(request->response.buffer);
      99             : }
     100             : 
     101           0 : void lwan_response_websocket_write_text(struct lwan_request *request)
     102             : {
     103           0 :     lwan_response_websocket_write(request, WS_OPCODE_TEXT);
     104           0 : }
     105             : 
     106           0 : void lwan_response_websocket_write_binary(struct lwan_request *request)
     107             : {
     108           0 :     lwan_response_websocket_write(request, WS_OPCODE_BINARY);
     109           0 : }
     110             : 
     111           0 : static size_t get_frame_length(struct lwan_request *request, uint16_t header)
     112             : {
     113             :     uint64_t len;
     114             : 
     115           0 :     switch (header & 0x7f) {
     116           0 :     case 0x7e:
     117           0 :         lwan_recv(request, &len, 2, 0);
     118           0 :         len = (uint64_t)ntohs((uint16_t)len);
     119             : 
     120           0 :         if (len < 0x7e) {
     121           0 :             lwan_status_warning("Can't use 16-bit encoding for frame length of %zu",
     122             :                                 len);
     123           0 :             coro_yield(request->conn->coro, CONN_CORO_ABORT);
     124           0 :             __builtin_unreachable();
     125             :         }
     126             : 
     127           0 :         return (size_t)len;
     128           0 :     case 0x7f:
     129           0 :         lwan_recv(request, &len, 8, 0);
     130           0 :         len = be64toh(len);
     131             : 
     132           0 :         if (UNLIKELY(len > SSIZE_MAX)) {
     133           0 :             lwan_status_warning("Frame length of %zu won't fit a ssize_t",
     134             :                                 len);
     135           0 :             coro_yield(request->conn->coro, CONN_CORO_ABORT);
     136           0 :             __builtin_unreachable();
     137             :         }
     138           0 :         if (UNLIKELY(len <= 0xffff)) {
     139           0 :             lwan_status_warning("Can't use 64-bit encoding for frame length of %zu",
     140             :                                 len);
     141           0 :             coro_yield(request->conn->coro, CONN_CORO_ABORT);
     142           0 :             __builtin_unreachable();
     143             :         }
     144             : 
     145           0 :         return (size_t)len;
     146           0 :     default:
     147           0 :         return (size_t)(header & 0x7f);
     148             :     }
     149             : }
     150             : 
     151           0 : static void unmask(char *msg, size_t msg_len, char mask[static 4])
     152             : {
     153           0 :     const uint32_t mask32 = string_as_uint32(mask);
     154           0 :     char *msg_end = msg + msg_len;
     155             : 
     156             :     if (sizeof(void *) == 8) {
     157           0 :         const uint64_t mask64 = (uint64_t)mask32 << 32 | mask32;
     158             : 
     159             : #if defined(__x86_64__)
     160           0 :         const size_t len128 = msg_len / 16;
     161           0 :         if (len128) {
     162           0 :             const __m128i mask128 = _mm_setr_epi64((__m64)mask64, (__m64)mask64);
     163           0 :             for (size_t i = 0; i < len128; i++) {
     164           0 :                 __m128i v = _mm_loadu_si128((__m128i *)msg);
     165           0 :                 _mm_storeu_si128((__m128i *)msg, _mm_xor_si128(v, mask128));
     166           0 :                 msg += 16;
     167             :             }
     168             :         }
     169             : #endif
     170             : 
     171           0 :         const size_t len64 = (size_t)((msg_end - msg) / 8);
     172           0 :         for (size_t i = 0; i < len64; i++) {
     173           0 :             uint64_t v = string_as_uint64(msg);
     174           0 :             v ^= mask64;
     175           0 :             msg = mempcpy(msg, &v, sizeof(v));
     176             :         }
     177             :     }
     178             : 
     179           0 :     const size_t len32 = (size_t)((msg_end - msg) / 4);
     180           0 :     for (size_t i = 0; i < len32; i++) {
     181           0 :         uint32_t v = string_as_uint32(msg);
     182           0 :         v ^= mask32;
     183           0 :         msg = mempcpy(msg, &v, sizeof(v));
     184             :     }
     185             : 
     186           0 :     switch (msg_end - msg) {
     187           0 :     case 3:
     188           0 :         msg[2] ^= mask[2]; /* fallthrough */
     189           0 :     case 2:
     190           0 :         msg[1] ^= mask[1]; /* fallthrough */
     191           0 :     case 1:
     192           0 :         msg[0] ^= mask[0];
     193             :     }
     194           0 : }
     195             : 
     196           0 : static void send_websocket_pong(struct lwan_request *request, uint16_t header)
     197             : {
     198           0 :     const size_t len = header & 0x7f;
     199             :     char msg[128];
     200             :     char mask[4];
     201             : 
     202           0 :     assert(header & 0x80);
     203             : 
     204           0 :     if (UNLIKELY(len > 125)) {
     205           0 :         lwan_status_debug("Received PING opcode with length %zu."
     206             :                           "Max is 125. Aborting connection.",
     207             :                           len);
     208           0 :         coro_yield(request->conn->coro, CONN_CORO_ABORT);
     209           0 :         __builtin_unreachable();
     210             :     }
     211             : 
     212           0 :     struct iovec vec[] = {
     213             :         {.iov_base = mask, .iov_len = sizeof(mask)},
     214             :         {.iov_base = msg, .iov_len = len},
     215             :     };
     216             : 
     217           0 :     lwan_readv(request, vec, N_ELEMENTS(vec));
     218           0 :     unmask(msg, len, mask);
     219             : 
     220           0 :     write_websocket_frame(request, 0x80 | WS_OPCODE_PONG, msg, len);
     221           0 : }
     222             : 
     223           0 : int lwan_response_websocket_read_hint(struct lwan_request *request, size_t size_hint)
     224             : {
     225           0 :     enum ws_opcode opcode = WS_OPCODE_INVALID;
     226             :     enum ws_opcode last_opcode;
     227             :     uint16_t header;
     228           0 :     bool continuation = false;
     229             : 
     230           0 :     if (!(request->conn->flags & CONN_IS_WEBSOCKET))
     231           0 :         return ENOTCONN;
     232             : 
     233           0 :     lwan_strbuf_reset_trim(request->response.buffer, size_hint);
     234             : 
     235           0 : next_frame:
     236           0 :     last_opcode = opcode;
     237             : 
     238           0 :     if (!lwan_recv(request, &header, sizeof(header), continuation ? 0 : MSG_DONTWAIT))
     239           0 :         return EAGAIN;
     240           0 :     header = htons(header);
     241           0 :     continuation = false;
     242             : 
     243           0 :     if (UNLIKELY(header & 0x7000)) {
     244           0 :         lwan_status_debug("RSV1...RSV3 has non-zero value %d, aborting", header & 0x7000);
     245             :         /* No extensions are supported yet, so fail connection per RFC6455. */
     246           0 :         coro_yield(request->conn->coro, CONN_CORO_ABORT);
     247           0 :         __builtin_unreachable();
     248             :     }
     249           0 :     if (UNLIKELY(!(header & 0x80))) {
     250           0 :         lwan_status_debug("Client sent an unmasked WebSockets frame, aborting");
     251           0 :         coro_yield(request->conn->coro, CONN_CORO_ABORT);
     252           0 :         __builtin_unreachable();
     253             :     }
     254             : 
     255           0 :     opcode = (header & 0x0f00) >> 8;
     256           0 :     switch (opcode) {
     257           0 :     case WS_OPCODE_CONTINUATION:
     258           0 :         if (UNLIKELY(last_opcode > WS_OPCODE_BINARY)) {
     259             :             /* Continuation frames are only available for opcodes [0..2] */
     260           0 :             coro_yield(request->conn->coro, CONN_CORO_ABORT);
     261           0 :             __builtin_unreachable();
     262             :         }
     263             : 
     264           0 :         continuation = true;
     265           0 :         break;
     266             : 
     267           0 :     case WS_OPCODE_TEXT:
     268             :     case WS_OPCODE_BINARY:
     269           0 :         break;
     270             : 
     271           0 :     case WS_OPCODE_CLOSE:
     272           0 :         request->conn->flags &= ~CONN_IS_WEBSOCKET;
     273           0 :         break;
     274             : 
     275           0 :     case WS_OPCODE_PING:
     276           0 :         send_websocket_pong(request, header);
     277           0 :         goto next_frame;
     278             : 
     279           0 :     case WS_OPCODE_PONG:
     280             :     case WS_OPCODE_RSVD_1 ... WS_OPCODE_RSVD_5:
     281             :     case WS_OPCODE_RSVD_CONTROL_1 ... WS_OPCODE_RSVD_CONTROL_5:
     282             :     case WS_OPCODE_INVALID:
     283             :         /* RFC6455: ...the receiving endpoint MUST _Fail the WebSocket Connection_ */
     284           0 :         coro_yield(request->conn->coro, CONN_CORO_ABORT);
     285           0 :         __builtin_unreachable();
     286             :     }
     287             : 
     288           0 :     size_t frame_len = get_frame_length(request, header);
     289           0 :     char *msg = lwan_strbuf_extend_unsafe(request->response.buffer, frame_len);
     290           0 :     if (UNLIKELY(!msg)) {
     291           0 :         coro_yield(request->conn->coro, CONN_CORO_ABORT);
     292           0 :         __builtin_unreachable();
     293             :     }
     294             : 
     295             :     char mask[4];
     296           0 :     struct iovec vec[] = {
     297             :         {.iov_base = mask, .iov_len = sizeof(mask)},
     298             :         {.iov_base = msg, .iov_len = frame_len},
     299             :     };
     300           0 :     lwan_readv(request, vec, N_ELEMENTS(vec));
     301           0 :     unmask(msg, frame_len, mask);
     302             : 
     303           0 :     if (continuation && !(header & 0x8000))
     304           0 :         goto next_frame;
     305             : 
     306           0 :     return (request->conn->flags & CONN_IS_WEBSOCKET) ? 0 : ECONNRESET;
     307             : }
     308             : 
     309           0 : inline int lwan_response_websocket_read(struct lwan_request *request)
     310             : {
     311             :     /* Ensure that a rogue client won't keep increasing the memory usage in an
     312             :      * uncontrolled manner by curbing the backing store to 1KB at most by default.
     313             :      * If an application expects messages to be larger than 1024 bytes on average,
     314             :      * they can call lwan_response_websocket_read_hint() directly with a larger
     315             :      * value to avoid malloc chatter (things should still work, but will be
     316             :      * slightly more inefficient). */
     317           0 :     return lwan_response_websocket_read_hint(request, 1024);
     318             : }

Generated by: LCOV version 1.15-2-gb9d6727