1 /*
2
3 Copyright (c) 2018 Martin Sustrik
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom
10 the Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 IN THE SOFTWARE.
22
23 */
24
25 #include <errno.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #define DILL_DISABLE_RAW_NAMES
31 #include "libdillimpl.h"
32 #include "iol.h"
33 #include "utils.h"
34
35 dill_unique_id(dill_ws_type);
36
37 struct dill_ws_sock {
38 struct dill_hvfs hvfs;
39 struct dill_msock_vfs mvfs;
40 int u;
41 int flags;
42 unsigned int indone : 1;
43 unsigned int outdone: 1;
44 unsigned int server : 1;
45 unsigned int mem : 1;
46 uint16_t status;
47 uint8_t msglen;
48 uint8_t msg[128];
49 };
50
51 DILL_CHECK_STORAGE(dill_ws_sock, dill_ws_storage)
52
53 static void *dill_ws_hquery(struct dill_hvfs *hvfs, const void *type);
54 static void dill_ws_hclose(struct dill_hvfs *hvfs);
55 static int dill_ws_msendl(struct dill_msock_vfs *mvfs,
56 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
57 static ssize_t dill_ws_mrecvl(struct dill_msock_vfs *mvfs,
58 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
59
dill_ws_hquery(struct dill_hvfs * hvfs,const void * type)60 static void *dill_ws_hquery(struct dill_hvfs *hvfs, const void *type) {
61 struct dill_ws_sock *self = (struct dill_ws_sock*)hvfs;
62 if(type == dill_msock_type) return &self->mvfs;
63 if(type == dill_ws_type) return self;
64 errno = ENOTSUP;
65 return NULL;
66 }
67
dill_ws_attach_client_mem(int s,int flags,const char * resource,const char * host,struct dill_ws_storage * mem,int64_t deadline)68 int dill_ws_attach_client_mem(int s, int flags, const char *resource,
69 const char *host, struct dill_ws_storage *mem, int64_t deadline) {
70 int err;
71 if(dill_slow(!mem)) {err = EINVAL; goto error;}
72 if(dill_slow(!dill_hquery(s, dill_bsock_type))) {err = errno; goto error;}
73 struct dill_ws_sock *self = (struct dill_ws_sock*)mem;
74 /* Take ownership of the underlying socket. */
75 s = dill_hown(s);
76 if(dill_slow(s < 0)) {err = errno; goto error;}
77 self->hvfs.query = dill_ws_hquery;
78 self->hvfs.close = dill_ws_hclose;
79 self->mvfs.msendl = dill_ws_msendl;
80 self->mvfs.mrecvl = dill_ws_mrecvl;
81 self->u = s;
82 self->flags = flags;
83 self->indone = 0;
84 self->outdone = 0;
85 self->server = 0;
86 self->mem = 1;
87 self->status = 0;
88 self->msglen = 0;
89 if(flags & DILL_WS_NOHTTP) {
90 int h = dill_hmake(&self->hvfs);
91 if(dill_slow(h < 0)) {err = errno; goto error;}
92 return h;
93 }
94 if(dill_slow(!resource || !host)) {err = EINVAL; goto error;}
95 struct dill_http_storage http_mem;
96 s = dill_http_attach_mem(s, &http_mem);
97 if(dill_slow(s < 0)) {err = errno; goto error;}
98 /* Send HTTP request. */
99 int rc = dill_http_sendrequest(s, "GET", resource, deadline);
100 if(dill_slow(rc < 0)) {err = errno; goto error;}
101 rc = dill_http_sendfield(s, "Host", host, deadline);
102 if(dill_slow(rc < 0)) {err = errno; goto error;}
103 rc = dill_http_sendfield(s, "Upgrade", "websocket", deadline);
104 if(dill_slow(rc < 0)) {err = errno; goto error;}
105 rc = dill_http_sendfield(s, "Connection", "Upgrade", deadline);
106 if(dill_slow(rc < 0)) {err = errno; goto error;}
107 char request_key[WS_KEY_SIZE];
108 rc = dill_ws_request_key(request_key);
109 if(dill_slow(rc < 0)) {err = errno; goto error;}
110 rc = dill_http_sendfield(s, "Sec-WebSocket-Key", request_key, deadline);
111 if(dill_slow(rc < 0)) {err = errno; goto error;}
112 rc = dill_http_sendfield(s, "Sec-WebSocket-Version", "13", deadline);
113 if(dill_slow(rc < 0)) {err = errno; goto error;}
114 /* TODO: Protocol, Extensions? */
115 rc = dill_http_done(s, deadline);
116 if(dill_slow(rc < 0)) {err = errno; goto error;}
117
118 /* Receive HTTP response from the server. */
119 char reason[256];
120 rc = dill_http_recvstatus(s, reason, sizeof(reason), deadline);
121 if(dill_slow(rc < 0)) {err = errno; goto error;}
122 if(dill_slow(rc != 101)) {errno = EPROTO; return -1;}
123 int has_upgrade = 0;
124 int has_connection = 0;
125 int has_key = 0;
126 while(1) {
127 char name[256];
128 char value[256];
129 rc = dill_http_recvfield(s, name, sizeof(name), value, sizeof(value),
130 deadline);
131 if(rc < 0 && errno == EPIPE) break;
132 if(dill_slow(rc < 0)) {err = errno; goto error;}
133 if(strcasecmp(name, "Upgrade") == 0) {
134 if(has_upgrade || strcasecmp(value, "websocket") != 0) {
135 err = EPROTO; goto error;}
136 has_upgrade = 1;
137 continue;
138 }
139 if(strcasecmp(name, "Connection") == 0) {
140 if(has_connection || strcasecmp(value, "Upgrade") != 0) {
141 err = EPROTO; goto error;}
142 has_connection = 1;
143 continue;
144 }
145 if(strcasecmp(name, "Sec-WebSocket-Accept") == 0) {
146 if(has_key) {err = EPROTO; goto error;}
147 char response_key[WS_KEY_SIZE];
148 rc = dill_ws_response_key(request_key, response_key);
149 if(dill_slow(rc < 0)) {err = errno; goto error;}
150 if(dill_slow(strcmp(value, response_key) != 0)) {
151 err = EPROTO; goto error;}
152 has_key = 1;
153 continue;
154 }
155 }
156 if(!has_upgrade || !has_connection || !has_key) {err = EPROTO; goto error;}
157
158 s = dill_http_detach(s, deadline);
159 if(dill_slow(s < 0)) {err = errno; goto error;}
160 self->u = s;
161 int h = dill_hmake(&self->hvfs);
162 if(dill_slow(h < 0)) {err = errno; goto error;}
163 return h;
164 error:
165 if(s >= 0) dill_hclose(s);
166 errno = err;
167 return -1;
168 }
169
dill_ws_attach_client(int s,int flags,const char * resource,const char * host,int64_t deadline)170 int dill_ws_attach_client(int s, int flags, const char *resource,
171 const char *host, int64_t deadline) {
172 int err;
173 struct dill_ws_sock *obj = malloc(sizeof(struct dill_ws_sock));
174 if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
175 s = dill_ws_attach_client_mem(s, flags, resource, host,
176 (struct dill_ws_storage*)obj, deadline);
177 if(dill_slow(s < 0)) {err = errno; goto error2;}
178 obj->mem = 0;
179 return s;
180 error2:
181 free(obj);
182 error1:
183 if(s >= 0) dill_hclose(s);
184 errno = err;
185 return -1;
186 }
187
dill_ws_attach_server_mem(int s,int flags,char * resource,size_t resourcelen,char * host,size_t hostlen,struct dill_ws_storage * mem,int64_t deadline)188 int dill_ws_attach_server_mem(int s, int flags,
189 char *resource, size_t resourcelen,
190 char *host, size_t hostlen,
191 struct dill_ws_storage *mem, int64_t deadline) {
192 int err;
193 if(dill_slow(!mem)) {err = EINVAL; goto error;}
194 if(dill_slow(!dill_hquery(s, dill_bsock_type))) {err = errno; goto error;}
195 struct dill_ws_sock *self = (struct dill_ws_sock*)mem;
196 /* Take ownership of the underlying socket. */
197 s = dill_hown(s);
198 if(dill_slow(s < 0)) {err = errno; goto error;}
199 self->hvfs.query = dill_ws_hquery;
200 self->hvfs.close = dill_ws_hclose;
201 self->mvfs.msendl = dill_ws_msendl;
202 self->mvfs.mrecvl = dill_ws_mrecvl;
203 self->u = s;
204 self->flags = flags;
205 self->indone = 0;
206 self->outdone = 0;
207 self->server = 1;
208 self->mem = 1;
209 self->status = 0;
210 self->msglen = 0;
211 if(flags & DILL_WS_NOHTTP) {
212 int h = dill_hmake(&self->hvfs);
213 if(dill_slow(h < 0)) {err = errno; goto error;}
214 return h;
215 }
216 struct dill_http_storage http_mem;
217 s = dill_http_attach_mem(s, &http_mem);
218 if(dill_slow(s < 0)) {err = errno; goto error;}
219 /* Receive the HTTP request from the client. */
220 char command[32];
221 int rc = dill_http_recvrequest(s, command, sizeof(command),
222 resource, resourcelen, deadline);
223 if(dill_slow(rc < 0)) {err = errno; goto error;}
224 if(dill_slow(strcmp(command, "GET") != 0)) {err = EPROTO; goto error;}
225 int has_host = 0;
226 int has_upgrade = 0;
227 int has_connection = 0;
228 int has_key = 0;
229 int has_version = 0;
230 char response_key[WS_KEY_SIZE];
231 while(1) {
232 char name[256];
233 char value[256];
234 rc = dill_http_recvfield(s, name, sizeof(name), value, sizeof(value),
235 deadline);
236 if(rc < 0 && errno == EPIPE) break;
237 if(dill_slow(rc < 0)) {err = errno; goto error;}
238 if(strcasecmp(name, "Host") == 0) {
239 if(has_host != 0) {err = EPROTO; goto error;}
240 /* TODO: Is this the correct error code? */
241 if(dill_slow(strlen(value) >= hostlen)) {
242 err = EMSGSIZE; goto error;}
243 strcpy(host, value);
244 has_host = 1;
245 continue;
246 }
247 if(strcasecmp(name, "Upgrade") == 0) {
248 if(has_upgrade || strcasecmp(value, "websocket") != 0) {
249 err = EPROTO; goto error;}
250 has_upgrade = 1;
251 continue;
252 }
253 if(strcasecmp(name, "Connection") == 0) {
254 if(has_connection || strcasecmp(value, "Upgrade") != 0) {
255 err = EPROTO; goto error;}
256 has_connection = 1;
257 continue;
258 }
259 if(strcasecmp(name, "Sec-WebSocket-Key") == 0) {
260 if(has_key) {err = EPROTO; goto error;}
261 /* Generate the key to be sent back to the client. */
262 rc = dill_ws_response_key(value, response_key);
263 if(dill_slow(rc < 0)) {err = errno; goto error;}
264 has_key = 1;
265 continue;
266 }
267 if(strcasecmp(name, "Sec-WebSocket-Version") == 0) {
268 if(has_version || strcasecmp(value, "13") != 0) {
269 err = EPROTO; goto error;}
270 has_version = 1;
271 continue;
272 }
273 }
274 if(dill_slow(!has_upgrade || !has_connection || !has_key || !has_version)) {
275 err = EPROTO; goto error;}
276
277 /* Send HTTP response back to the client. */
278 rc = dill_http_sendstatus(s, 101, "Switching Protocols", deadline);
279 if(dill_slow(rc < 0)) {err = errno; goto error;}
280 rc = dill_http_sendfield(s, "Upgrade", "websocket", deadline);
281 if(dill_slow(rc < 0)) {err = errno; goto error;}
282 rc = dill_http_sendfield(s, "Connection", "Upgrade", deadline);
283 if(dill_slow(rc < 0)) {err = errno; goto error;}
284 rc = dill_http_sendfield(s, "Sec-WebSocket-Accept", response_key, deadline);
285 if(dill_slow(rc < 0)) {err = errno; goto error;}
286
287 s = dill_http_detach(s, deadline);
288 if(dill_slow(s < 0)) {err = errno; goto error;}
289 self->u = s;
290 int h = dill_hmake(&self->hvfs);
291 if(dill_slow(h < 0)) {err = errno; goto error;}
292 return h;
293 error:
294 if(s >= 0) {
295 int rc = dill_hclose(s);
296 dill_assert(rc == 0);
297 }
298 errno = err;
299 return -1;
300 }
301
dill_ws_attach_server(int s,int flags,char * resource,size_t resourcelen,char * host,size_t hostlen,int64_t deadline)302 int dill_ws_attach_server(int s, int flags, char *resource, size_t resourcelen,
303 char *host, size_t hostlen, int64_t deadline) {
304 int err;
305 struct dill_ws_sock *obj = malloc(sizeof(struct dill_ws_sock));
306 if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
307 s = dill_ws_attach_server_mem(s, flags, resource, resourcelen,
308 host, hostlen, (struct dill_ws_storage*)obj, deadline);
309 if(dill_slow(s < 0)) {err = errno; goto error2;}
310 obj->mem = 0;
311 return s;
312 error2:
313 free(obj);
314 error1:
315 if(s >= 0) dill_hclose(s);
316 errno = err;
317 return -1;
318 }
319
dill_ws_sendl_base(struct dill_msock_vfs * mvfs,uint8_t type,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)320 static int dill_ws_sendl_base(struct dill_msock_vfs *mvfs, uint8_t type,
321 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
322 struct dill_ws_sock *self = dill_cont(mvfs, struct dill_ws_sock, mvfs);
323 if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
324 size_t len;
325 int rc = dill_iolcheck(first, last, NULL, &len);
326 if(dill_slow(rc < 0)) return -1;
327 uint8_t buf[12];
328 size_t sz;
329 buf[0] = 0x80 | type;
330 if(len > 0xffff) {
331 buf[1] = 127;
332 dill_putll(buf + 2, len);
333 sz = 10;
334 }
335 else if(len > 125) {
336 buf[1] = 126;
337 dill_puts(buf + 2, len);
338 sz = 4;
339 }
340 else {
341 buf[1] = (uint8_t)len;
342 sz = 2;
343 }
344 uint8_t mask[4];
345 if(!self->server) {
346 rc = dill_random(mask, sizeof(mask));
347 if(dill_slow(rc < 0)) return -1;
348 buf[1] |= 0x80;
349 memcpy(buf + sz, mask, 4);
350 sz += 4;
351 }
352 rc = dill_bsend(self->u, buf, sz, deadline);
353 if(dill_slow(rc < 0)) return -1;
354 /* On the server side we can send the payload as is. */
355 if(self->server) {
356 rc = dill_bsendl(self->u, first, last, deadline);
357 if(dill_slow(rc < 0)) return -1;
358 return 0;
359 }
360 /* On the client side, the payload has to be masked. */
361 size_t pos = 0;
362 while(first) {
363 size_t i;
364 for(i = 0; i != first->iol_len; ++i) {
365 uint8_t b = ((uint8_t*)first->iol_base)[i] ^
366 mask[pos++ % sizeof(mask)];
367 rc = dill_bsend(self->u, &b, 1, deadline);
368 if(dill_slow(rc < 0)) return -1;
369 }
370 first = first->iol_next;
371 }
372 return 0;
373 }
374
dill_ws_recvl_base(struct dill_msock_vfs * mvfs,int * flags,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)375 static ssize_t dill_ws_recvl_base(struct dill_msock_vfs *mvfs, int *flags,
376 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
377 struct dill_ws_sock *self = dill_cont(mvfs, struct dill_ws_sock, mvfs);
378 if(dill_slow(self->indone)) {errno = EPIPE; return -1;}
379 int rc = dill_iolcheck(first, last, NULL, NULL);
380 if(dill_slow(rc < 0)) return -1;
381 size_t res = 0;
382 /* Message may consist of multiple frames. Read them one by one. */
383 struct dill_iolist it;
384 if(first) it = *first;
385 while(1) {
386 struct dill_iolist iol_msg;
387 uint8_t hdr1[2];
388 rc = dill_brecv(self->u, hdr1, sizeof(hdr1), deadline);
389 if(dill_slow(rc < 0)) return -1;
390 if(hdr1[0] & 0x70) {errno = EPROTO; return -1;}
391 int opcode = hdr1[0] & 0x0f;
392 /* TODO: Other opcodes. */
393 switch(opcode) {
394 case 0:
395 break;
396 case 1:
397 if(flags) *flags = DILL_WS_TEXT;
398 break;
399 case 2:
400 if(flags) *flags = DILL_WS_BINARY;
401 break;
402 case 8:
403 it.iol_base = &self->status;
404 it.iol_len = 2;
405 it.iol_next = &iol_msg;
406 it.iol_rsvd = 0;
407 iol_msg.iol_base = self->msg;
408 iol_msg.iol_len = sizeof(self->msg);
409 iol_msg.iol_next = NULL;
410 iol_msg.iol_rsvd = 0;
411 self->indone = 1;
412 break;
413 default:
414 errno = EPROTO;
415 return -1;
416 }
417 if(!self->server ^ !(hdr1[1] & 0x80)) {errno = EPROTO; return -1;}
418 size_t sz = hdr1[1] & 0x7f;
419 if(sz == 126) {
420 uint8_t hdr2[2];
421 rc = dill_brecv(self->u, hdr2, sizeof(hdr2), deadline);
422 if(dill_slow(rc < 0)) return -1;
423 sz = dill_gets(hdr2);
424 }
425 else if(sz == 127) {
426 uint8_t hdr2[8];
427 rc = dill_brecv(self->u, hdr2, sizeof(hdr2), deadline);
428 if(dill_slow(rc < 0)) return -1;
429 sz = dill_getll(hdr2);
430 }
431 res += sz;
432 /* Control frames cannot be fragmented or longer than 125 bytes. */
433 if(dill_slow(opcode >= 8 && (sz > 125 || !(hdr1[0] & 0x80)))) {
434 errno = EPROTO; return -1;}
435 uint8_t mask[4];
436 if(self->server) {
437 rc = dill_brecv(self->u, mask, sizeof(mask), deadline);
438 if(dill_slow(rc < 0)) return -1;
439 }
440 /* Frame may be read into multiple iolist elements. */
441 while(1) {
442 size_t toread = sz < it.iol_len ? sz : it.iol_len;
443 if(toread > 0) {
444 rc = dill_brecv(self->u, it.iol_base, toread, deadline);
445 if(dill_slow(rc < 0)) return -1;
446 if(self->server) {
447 size_t i;
448 for(i = 0; i != toread; ++i)
449 ((uint8_t*)it.iol_base)[i] ^= mask[i % 4];
450 }
451 }
452 sz -= toread;
453 if(sz == 0) break;
454 if(dill_slow(!it.iol_next)) {errno = EMSGSIZE; return -1;}
455 it = *it.iol_next;
456 }
457 if(hdr1[0] & 0x80) break;
458 }
459 if(dill_slow(self->indone)) {
460 if(dill_slow(res == 1)) {errno = EPROTO; return -1;}
461 if(res >= 2) {
462 self->status = dill_gets((uint8_t*)&self->status);
463 if(dill_slow(self->status < 1000 || self->status > 4999)) {
464 errno = EPROTO; return -1;}
465 self->msglen = (uint8_t)res - 2;
466 }
467 else self->msglen = 0;
468 errno = EPIPE;
469 return -1;
470 }
471 return res;
472 }
473
dill_ws_send(int s,int flags,const void * buf,size_t len,int64_t deadline)474 int dill_ws_send(int s, int flags, const void *buf, size_t len,
475 int64_t deadline) {
476 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
477 if(dill_slow(!self)) return -1;
478 struct dill_iolist iol = {(void*)buf, len, NULL, 0};
479 return dill_ws_sendl_base(&self->mvfs, (flags & DILL_WS_TEXT) ? 0x1 : 0x2,
480 &iol, &iol, deadline);
481 }
482
dill_ws_recv(int s,int * flags,void * buf,size_t len,int64_t deadline)483 ssize_t dill_ws_recv(int s, int *flags, void *buf, size_t len,
484 int64_t deadline) {
485 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
486 if(dill_slow(!self)) return -1;
487 struct dill_iolist iol = {(void*)buf, len, NULL, 0};
488 return dill_ws_recvl_base(&self->mvfs, flags, &iol, &iol, deadline);
489 }
490
dill_ws_sendl(int s,int flags,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)491 int dill_ws_sendl(int s, int flags, struct dill_iolist *first,
492 struct dill_iolist *last, int64_t deadline) {
493 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
494 if(dill_slow(!self)) return -1;
495 return dill_ws_sendl_base(&self->mvfs, (flags & DILL_WS_TEXT) ? 0x1 : 0x2,
496 first, last, deadline);
497 }
498
dill_ws_recvl(int s,int * flags,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)499 ssize_t dill_ws_recvl(int s, int *flags, struct dill_iolist *first,
500 struct dill_iolist *last, int64_t deadline) {
501 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
502 if(dill_slow(!self)) return -1;
503 return dill_ws_recvl_base(&self->mvfs, flags, first, last, deadline);
504 }
505
dill_ws_msendl(struct dill_msock_vfs * mvfs,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)506 static int dill_ws_msendl(struct dill_msock_vfs *mvfs,
507 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
508 struct dill_ws_sock *self = dill_cont(mvfs, struct dill_ws_sock, mvfs);
509 return dill_ws_sendl_base(mvfs, (self->flags & DILL_WS_TEXT) ? 0x1 : 0x2,
510 first, last, deadline);
511 }
512
dill_ws_mrecvl(struct dill_msock_vfs * mvfs,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)513 static ssize_t dill_ws_mrecvl(struct dill_msock_vfs *mvfs,
514 struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
515 struct dill_ws_sock *self = dill_cont(mvfs, struct dill_ws_sock, mvfs);
516 int flags;
517 ssize_t sz = dill_ws_recvl_base(mvfs, &flags, first, last, deadline);
518 if(dill_slow(sz < 0)) return -1;
519 if(dill_slow((flags & DILL_WS_TEXT) != (self->flags & DILL_WS_TEXT))) {
520 errno = EPROTO; return -1;}
521 return sz;
522 }
523
dill_ws_done(int s,int status,const void * buf,size_t len,int64_t deadline)524 int dill_ws_done(int s, int status, const void *buf, size_t len,
525 int64_t deadline) {
526 if(dill_slow((status != 0 && (status < 1000 || status > 4999)) ||
527 (!buf && len > 0))) {errno = EINVAL; return -1;}
528 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
529 if(dill_slow(!self)) return -1;
530 if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
531 struct dill_iolist iol2 = {(void*)buf, len, NULL, 0};
532 uint8_t sbuf[2];
533 dill_puts(sbuf, status);
534 struct dill_iolist iol1 = {status ? sbuf : NULL, status ? sizeof(sbuf) : 0,
535 &iol2, 0};
536 int rc = dill_ws_sendl_base(&self->mvfs, 0x8, &iol1, &iol2, deadline);
537 if(dill_slow(rc < 0)) return -1;
538 self->outdone = 1;
539 return 0;
540 }
541
dill_ws_detach(int s,int status,const void * buf,size_t len,int64_t deadline)542 int dill_ws_detach(int s, int status, const void *buf, size_t len,
543 int64_t deadline) {
544 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
545 if(dill_slow(!self)) return -1;
546 if(!self->outdone) {
547 int rc = dill_ws_done(s, status, buf, len, deadline);
548 if(dill_slow(rc < 0)) return -1;
549 }
550 while(1) {
551 struct dill_iolist iol = {NULL, SIZE_MAX, NULL, 0};
552 ssize_t sz = dill_ws_recvl_base(&self->mvfs, NULL, &iol, &iol,
553 deadline);
554 if(sz < 0) {
555 if(errno == EPIPE) break;
556 return -1;
557 }
558 }
559 int u = self->u;
560 if(!self->mem) free(self);
561 return u;
562 }
563
dill_ws_hclose(struct dill_hvfs * hvfs)564 static void dill_ws_hclose(struct dill_hvfs *hvfs) {
565 struct dill_ws_sock *self = (struct dill_ws_sock*)hvfs;
566 int rc = dill_hclose(self->u);
567 dill_assert(rc == 0);
568 if(!self->mem) free(self);
569 }
570
dill_ws_status(int s,int * status,void * buf,size_t len)571 ssize_t dill_ws_status(int s, int *status, void *buf, size_t len) {
572 if(dill_slow(!buf && len)) {errno = EINVAL; return -1;}
573 struct dill_ws_sock *self = dill_hquery(s, dill_ws_type);
574 if(dill_slow(!self)) return -1;
575 if(dill_slow(!self->indone)) {errno = EAGAIN; return -1;}
576 if(status) *status = self->status;
577 if(buf) {
578 if(dill_slow(len < self->msglen)) {errno = EMSGSIZE; return -1;}
579 memcpy(buf, self->msg, self->msglen);
580 }
581 return self->msglen;
582 }
583
584 /******************************************************************************/
585 /* Helper functions. */
586 /******************************************************************************/
587
dill_ws_request_key(char * request_key)588 int dill_ws_request_key(char *request_key) {
589 if(dill_slow(!request_key)) {errno = EINVAL; return -1;}
590 uint8_t nonce[16];
591 int rc = dill_random(nonce, sizeof(nonce));
592 if(dill_slow(rc < 0)) return -1;
593 rc = dill_base64_encode(nonce, sizeof(nonce), request_key, WS_KEY_SIZE);
594 if(dill_slow(rc < 0)) {errno = EFAULT; return -1;}
595 return 0;
596 }
597
dill_ws_response_key(const char * request_key,char * response_key)598 int dill_ws_response_key(const char *request_key, char *response_key) {
599 if(dill_slow(!request_key)) {errno = EINVAL; return -1;}
600 if(dill_slow(!response_key)) {errno = EINVAL; return -1;}
601 /* Decode the request key and check whether it's a 16-byte nonce. */
602 uint8_t nonce[16];
603 int rc = dill_base64_decode(request_key, strlen(request_key),
604 nonce, sizeof(nonce));
605 if(dill_slow(rc != sizeof(nonce))) {errno = EPROTO; return -1;}
606 /* Create the response key. */
607 struct dill_sha1 sha1;
608 dill_sha1_init(&sha1);
609 int i;
610 /* The key sent in the original request. */
611 for(i = 0; request_key[i] != 0; ++i)
612 dill_sha1_hashbyte(&sha1, request_key[i]);
613 /* RFC 6455-specified UUID. */
614 const char *uuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
615 for(i = 0; uuid[i] != 0; ++i) dill_sha1_hashbyte(&sha1, uuid[i]);
616 /* Convert the SHA1 to Base64. */
617 rc = dill_base64_encode(dill_sha1_result(&sha1), DILL_SHA1_HASH_LEN,
618 response_key, WS_KEY_SIZE);
619 if(dill_slow(rc < 0)) {errno = EFAULT; return -1;}
620 return 0;
621 }
622
623