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