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 : }
|