1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2015 Tomasz Sterna
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
17  */
18 
19 /**
20  * this plugin implements WebSocket C2S access
21  * RFC 7395 : An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket
22  * http://tools.ietf.org/html/rfc7395
23  */
24 
25 #include "sx.h"
26 #include <stdarg.h>
27 #include <string.h>
28 
29 static const char websocket_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
30 
31 static http_parser_settings settings;
32 
33 /* parts of github.com/payden src/websock.c by Payden Sutherland follow */
34 #define MASK_LENGTH 4
35 #define FRAME_CHUNK_LENGTH 1024
36 
37 #define WS_OPCODE_CONTINUE 0x0
38 #define WS_OPCODE_TEXT 0x1
39 #define WS_OPCODE_BINARY 0x2
40 #define WS_OPCODE_CLOSE 0x8
41 #define WS_OPCODE_PING 0x9
42 #define WS_OPCODE_PONG 0xa
43 
44 #define WS_FRAGMENT_FIN (1 << 7)
45 
46 #define WS_CLOSE_NORMAL 1000
47 #define WS_CLOSE_GOING_AWAY 1001
48 #define WS_CLOSE_PROTOCOL_ERROR 1002
49 #define WS_CLOSE_NOT_ALLOWED 1003
50 #define WS_CLOSE_RESERVED 1004
51 #define WS_CLOSE_NO_CODE 1005
52 #define WS_CLOSE_DIRTY 1006
53 #define WS_CLOSE_WRONG_TYPE 1007
54 #define WS_CLOSE_POLICY_VIOLATION 1008
55 #define WS_CLOSE_MESSAGE_TOO_BIG 1009
56 #define WS_CLOSE_UNEXPECTED_ERROR 1011
57 
58 enum WS_FRAME_STATE {
59         sw_start = 0,
60         sw_got_two,
61         sw_got_short_len,
62         sw_got_full_len,
63         sw_loaded_mask
64 };
65 
66 typedef struct _libwebsock_frame {
67         unsigned int fin;
68         unsigned int opcode;
69         unsigned int mask_offset;
70         unsigned int payload_offset;
71         unsigned int rawdata_idx;
72         unsigned int rawdata_sz;
73         unsigned int size;
74         unsigned int payload_len_short;
75         unsigned int payload_len;
76         char *rawdata;
77         unsigned char mask[4];
78         enum WS_FRAME_STATE state;
79 } libwebsock_frame;
80 
libwebsock_read_header(libwebsock_frame * frame)81 static inline int libwebsock_read_header(libwebsock_frame *frame) {
82     int i, new_size;
83     enum WS_FRAME_STATE state;
84 
85     state = frame->state;
86     switch (state) {
87     case sw_start:
88         if (frame->rawdata_idx < 2) {
89             return 0;
90         }
91         frame->state = sw_got_two;
92     case sw_got_two:
93         frame->mask_offset = 2;
94         frame->fin = (*(frame->rawdata) & 0x80) == 0x80 ? 1 : 0;
95         frame->opcode = *(frame->rawdata) & 0xf;
96         frame->payload_len_short = *(frame->rawdata + 1) & 0x7f;
97         frame->state = sw_got_short_len;
98     case sw_got_short_len:
99         switch (frame->payload_len_short) {
100         case 126:
101             if (frame->rawdata_idx < 4) {
102                 return 0;
103             }
104             frame->mask_offset += 2;
105             frame->payload_offset = frame->mask_offset + MASK_LENGTH;
106             frame->payload_len = ntohs(
107                     *((unsigned short int *) (frame->rawdata + 2)));
108             frame->state = sw_got_full_len;
109             break;
110         case 127:
111             if (frame->rawdata_idx < 10) {
112                 return 0;
113             }
114             frame->mask_offset += 8;
115             frame->payload_offset = frame->mask_offset + MASK_LENGTH;
116             frame->payload_len = ntohl(*((unsigned int *) (frame->rawdata + 6)));
117             frame->state = sw_got_full_len;
118             break;
119         default:
120             frame->payload_len = frame->payload_len_short;
121             frame->payload_offset = frame->mask_offset + MASK_LENGTH;
122             frame->state = sw_got_full_len;
123             break;
124         }
125     case sw_got_full_len:
126         if (frame->rawdata_idx < frame->payload_offset) {
127             return 0;
128         }
129         for (i = 0; i < MASK_LENGTH; i++) {
130             frame->mask[i] = *(frame->rawdata + frame->mask_offset + i) & 0xff;
131         }
132         frame->state = sw_loaded_mask;
133         frame->size = frame->payload_offset + frame->payload_len;
134         if (frame->size > frame->rawdata_sz) {
135             new_size = frame->size;
136             new_size--;
137             new_size |= new_size >> 1;
138             new_size |= new_size >> 2;
139             new_size |= new_size >> 4;
140             new_size |= new_size >> 8;
141             new_size |= new_size >> 16;
142             new_size++;
143             frame->rawdata_sz = new_size;
144             frame->rawdata = (char *) realloc(frame->rawdata, new_size);
145         }
146         return 1;
147     case sw_loaded_mask:
148         return 1;
149     }
150     return 0;
151 }
152 
libwebsock_fragment_buffer(const char * data,unsigned int len,int flags)153 sx_buf_t libwebsock_fragment_buffer(const char *data, unsigned int len, int flags) {
154     unsigned int *payload_len_32_be;
155     unsigned short int *payload_len_short_be;
156     unsigned char finNopcode, payload_len_small;
157     unsigned int payload_offset = 2;
158     unsigned int frame_size;
159     char *frame;
160 
161     finNopcode = flags & 0xff;
162     if (len <= 125) {
163         frame_size = 2 + len;
164         payload_len_small = len & 0xff;
165     } else if (len > 125 && len <= 0xffff) {
166         frame_size = 4 + len;
167         payload_len_small = 126;
168         payload_offset += 2;
169     } else if (len > 0xffff && len <= 0xfffffff0) {
170         frame_size = 10 + len;
171         payload_len_small = 127;
172         payload_offset += 8;
173     } else {
174         _sx_debug(ZONE,
175                 "libwebsock does not support frame payload sizes over %u bytes long\n",
176                 0xfffffff0);
177         return NULL;
178     }
179     sx_buf_t buf = _sx_buffer_new(NULL, frame_size, NULL, NULL);
180     frame = buf->data;
181     payload_len_small &= 0x7f;
182     *frame = finNopcode;
183     *(frame + 1) = payload_len_small;
184     if (payload_len_small == 126) {
185         len &= 0xffff;
186         payload_len_short_be = (unsigned short *) ((char *) frame + 2);
187         *payload_len_short_be = htons(len);
188     }
189     if (payload_len_small == 127) {
190         payload_len_32_be = (unsigned int *) ((char *) frame + 2);
191         *payload_len_32_be++ = 0;
192         *payload_len_32_be = htonl(len);
193     }
194     memcpy(frame + payload_offset, data, len);
195 
196     return buf;
197 }
198 
199 int libwebsock_close_with_reason(sx_t s, _sx_websocket_conn_t sc, unsigned short code, const char *reason);
200 
libwebsock_send_fragment(sx_t s,_sx_websocket_conn_t sc,const char * data,unsigned int len,int flags)201 int libwebsock_send_fragment(sx_t s, _sx_websocket_conn_t sc, const char *data, unsigned int len, int flags) {
202     sx_buf_t buf = libwebsock_fragment_buffer(data, len, flags);
203     if (buf == NULL) {
204         return libwebsock_close_with_reason(s, sc, WS_CLOSE_UNEXPECTED_ERROR, "Internal server error");
205     }
206     jqueue_push(s->wbufq, buf, 0);
207     s->want_write = 1;
208     return _sx_event(s, event_WANT_WRITE, NULL);
209 }
210 
libwebsock_close_with_reason(sx_t s,_sx_websocket_conn_t sc,unsigned short code,const char * reason)211 int libwebsock_close_with_reason(sx_t s, _sx_websocket_conn_t sc, unsigned short code, const char *reason)
212 {
213     unsigned int len;
214     unsigned short code_be;
215     char buf[128]; //w3 spec on WebSockets API (http://dev.w3.org/html5/websockets/) says reason shouldn't be over 123 bytes.  I concur.
216     len = 2;
217     code_be = htobe16(code);
218     memcpy(buf, &code_be, 2);
219     if (reason) {
220         len += snprintf(buf + 2, 124, "%s", reason);
221     }
222 
223     sc->state = websocket_CLOSING;
224     int ret = libwebsock_send_fragment(s, sc, buf, len, WS_FRAGMENT_FIN | WS_OPCODE_CLOSE);
225 
226     sx_close(s);
227     return ret;
228 }
229 
libwebsock_close(sx_t s,_sx_websocket_conn_t sc)230 int libwebsock_close(sx_t s, _sx_websocket_conn_t sc)
231 {
232     return libwebsock_close_with_reason(s, sc, WS_CLOSE_NORMAL, NULL);
233 }
234 
libwebsock_fail_connection(sx_t s,_sx_websocket_conn_t sc,unsigned short close_code)235 void libwebsock_fail_connection(sx_t s, _sx_websocket_conn_t sc, unsigned short close_code) {
236     char close_frame[4] = { 0x88, 0x02, 0x00, 0x00 };
237     unsigned short *code_be = (unsigned short *) &close_frame[2];
238     *code_be = htobe16(WS_CLOSE_PROTOCOL_ERROR);
239 
240     sx_buf_t buf = _sx_buffer_new(NULL, sizeof(close_frame), NULL, NULL);
241     memcpy(buf->data, close_frame, buf->len);
242 
243     sc->state = websocket_CLOSING;
244     s->want_write = 1;
245     _sx_event(s, event_WANT_WRITE, NULL);
246 
247     sx_close(s);
248 }
249 
_sx_websocket_http_header_field(http_parser * parser,const char * chars,size_t length)250 static int _sx_websocket_http_header_field(http_parser *parser, const char *chars, size_t length) {
251     _sx_debug(ZONE, "HTTP header field '%.*s'", length, chars);
252     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
253     if(sc->header_value) {
254         // new field incoming
255         xhash_put(sc->headers,
256                   strunescape(sc->p, spool_print(sc->field)),
257                   strunescape(sc->p, spool_print(sc->value)));
258         sc->header_value = 0;
259         sc->field = spool_new(sc->p);
260     }
261     spool_escape(sc->field, chars, length);
262     return 0;
263 }
264 
_sx_websocket_http_header_value(http_parser * parser,const char * chars,size_t length)265 static int _sx_websocket_http_header_value(http_parser *parser, const char *chars, size_t length) {
266     _sx_debug(ZONE, "HTTP header value '%.*s'", length, chars);
267     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
268     if(!sc->header_value) {
269         // field name complete
270         sc->header_value = 1;
271         sc->value = spool_new(sc->p);
272     }
273     spool_escape(sc->value, chars, length);
274     return 0;
275 }
276 
_sx_websocket_http_headers_complete(http_parser * parser)277 static int _sx_websocket_http_headers_complete(http_parser *parser) {
278     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
279     _sx_debug(ZONE, "HTTP headers complete: %d %s HTTP/%d.%d", parser->status_code, http_method_str(parser->method), parser->http_major, parser->http_minor);
280     if (sc->header_value) {
281         /* pull last value by switching to field parser */
282         _sx_websocket_http_header_field(parser, "", 0);
283     }
284     return 1;
285 }
286 
_sx_websocket_http_return(sx_t s,char * status,char * headers_format,...)287 static void _sx_websocket_http_return(sx_t s, char *status, char *headers_format, ...) {
288     char* http =
289         "HTTP/1.1 %s\r\n"
290         "%s"
291         "Server: " PACKAGE_STRING "\r\n"
292         "Expires: Fri, 10 Oct 1997 10:10:10 GMT\r\n"
293         "Pragma: no-cache\r\n"
294         "Cache-control: private\r\n"
295         "\r\n";
296 
297     /* build additional headers */
298     char headers[1024];
299     va_list args;
300     va_start(args, headers_format);
301     vsnprintf(headers, sizeof(headers), headers_format, args);
302     va_end(args);
303 
304     /* build HTTP answer */
305     sx_buf_t buf = _sx_buffer_new(NULL, j_strlen(http) + j_strlen(status) + j_strlen(headers), NULL, NULL);
306     buf->len = sprintf(buf->data, http, status, headers);
307     jqueue_push(s->wbufq, buf, 0);
308 
309     /* stuff to write */
310     s->want_write = 1;
311     _sx_event(s, event_WANT_WRITE, NULL);
312 }
313 
_sx_websocket_rio(sx_t s,sx_plugin_t p,sx_buf_t buf)314 static int _sx_websocket_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
315     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
316     int i, j, ret, err;
317     char *newbuf;
318     sha1_state_t sha1;
319     unsigned char hash[20];
320 
321     /* if not wrapped yet */
322     if(!(s->flags & SX_WEBSOCKET_WRAPPER)) {
323         /* look for HTTP handshake */
324         if(s->state == state_NONE && sc->state == websocket_PRE && buf->len >= 5 && strncmp("GET /", buf->data, 5) == 0) {
325             _sx_debug(ZONE, "got HTTP handshake");
326             sc->state = websocket_HEADERS;
327         }
328 
329         /* pass buffers through http_parser */
330         if(s->state == state_NONE && sc->state == websocket_HEADERS) {
331             _sx_debug(ZONE, "parsing HTTP headers");
332             if(buf->len > 0) {
333                 _sx_debug(ZONE, "loading %d bytes into http_parser %.*s", buf->len, buf->len, buf->data);
334 
335                 ret = http_parser_execute(&sc->parser, &settings, buf->data, buf->len);
336 
337                 if (sc->parser.upgrade) {
338                     /* check for required websocket upgrade headers */
339                     char *upgrade = xhash_get(sc->headers, "Upgrade");
340                     char *connection = xhash_get(sc->headers, "Connection");
341                     char *key = xhash_get(sc->headers, "Sec-WebSocket-Key");
342                     char *proto = xhash_get(sc->headers, "Sec-WebSocket-Protocol");
343                     int version = j_atoi(xhash_get(sc->headers, "Sec-WebSocket-Version"), -1);
344                     if(j_strcmp(upgrade, "websocket") || connection == NULL || strcasestr(connection, "Upgrade") == NULL || j_strcmp(proto, "xmpp") || version != 13) {
345                         _sx_debug(ZONE, "Upgrade: %s", upgrade);
346                         _sx_debug(ZONE, "Connection: %s", connection);
347                         _sx_debug(ZONE, "Sec-WebSocket-Key: %s", key);
348                         _sx_debug(ZONE, "Sec-WebSocket-Protocol: %s", proto);
349                         _sx_debug(ZONE, "Sec-WebSocket-Version: %d", version);
350                         _sx_websocket_http_return(s, "400 Bad Request", "");
351                         sx_close(s);
352                         return -2;
353                     }
354 
355                     /* we're good to go */
356 
357                     sha1_init(&sha1);
358                     sha1_append(&sha1, key, j_strlen(key));
359                     sha1_append(&sha1, websocket_guid, sizeof(websocket_guid) -1);
360                     sha1_finish(&sha1, hash);
361                     char * accept = b64_encode(hash, sizeof(hash));
362 
363                     /* switch protocols */
364                     _sx_websocket_http_return(s, "101 Switching Protocols",
365                                               "Upgrade: websocket\r\n"
366                                               "Connection: Upgrade\r\n"
367                                               "Sec-WebSocket-Accept: %s\r\n"
368                                               "Sec-WebSocket-Protocol: xmpp\r\n",
369                                               accept);
370                     free(accept);
371 
372                     /* and move past headers */
373                     sc->state = websocket_ACTIVE;
374                     s->flags |= SX_WEBSOCKET_WRAPPER;
375 
376                     return 0;
377                 } else if (ret != buf->len) {
378                     /* throw an error */
379                     sx_error(s, stream_err_BAD_FORMAT, http_errno_description(sc->parser.http_errno));
380                     sx_close(s);
381                     return -2;
382                 } else if (p->private) {
383                     char *http_forward = p->private;
384                     _sx_debug(ZONE, "bouncing HTTP request to %s", http_forward);
385                     _sx_websocket_http_return(s, "301 Found", "Location: %s\r\nConnection: close\r\n", http_forward);
386                     sx_close(s);
387                     return -1;
388                 }
389 
390                 _sx_debug(ZONE, "unhandling HTTP request");
391                 _sx_websocket_http_return(s, "403 Forbidden", "Connection: close\r\n");
392                 sx_close(s);
393                 return -1;
394             }
395 
396             _sx_buffer_clear(buf);
397             /* flag we want to read */
398             s->want_read = 1;
399 
400             return 0;
401         }
402     }
403 
404     /* only bothering if it is active websocket */
405     if(!(s->flags & SX_WEBSOCKET_WRAPPER) || sc->state != websocket_ACTIVE)
406         return 1;
407 
408     _sx_debug(ZONE, "Unwraping WebSocket frame: %d bytes", buf->len);
409 
410     char *data = buf->data;
411     for (i = 0; i < buf->len;) {
412         libwebsock_frame *frame;
413         if (sc->frame == NULL) {
414             frame = (libwebsock_frame *) calloc(1, sizeof(libwebsock_frame));
415             frame->payload_len = -1;
416             frame->rawdata_sz = FRAME_CHUNK_LENGTH;
417             frame->rawdata = (char *) malloc(FRAME_CHUNK_LENGTH);
418             sc->frame = frame;
419         } else {
420             frame = sc->frame;
421         }
422 
423         *(frame->rawdata + frame->rawdata_idx++) = *data++;
424         i++;
425 
426         if (frame->state != sw_loaded_mask) {
427             err = libwebsock_read_header(frame);
428             if (err == -1) {
429                 if (sc->state != websocket_CLOSING) {
430                     libwebsock_fail_connection(s, sc, WS_CLOSE_PROTOCOL_ERROR);
431                 }
432                 return -2;
433             }
434             if (err == 0) {
435                 continue;
436             }
437         }
438 
439         if (frame->rawdata_idx < frame->size) {
440             if (buf->len - i >= frame->size - frame->rawdata_idx) {
441                 //remaining in current vector completes frame.  Copy remaining frame size
442                 memcpy(frame->rawdata + frame->rawdata_idx, data,
443                        frame->size - frame->rawdata_idx);
444                 data += frame->size - frame->rawdata_idx;
445                 i += frame->size - frame->rawdata_idx;
446                 frame->rawdata_idx = frame->size;
447             } else {
448                 //not complete frame, copy the rest of this vector into frame.
449                 memcpy(frame->rawdata + frame->rawdata_idx, data, buf->len - i);
450                 frame->rawdata_idx += buf->len - i;
451                 i = buf->len;
452                 _sx_debug(ZONE, "more frame data to come");
453                 continue;
454             }
455         }
456 
457         //have full frame at this point
458         _sx_debug(ZONE, "FIN: %d", frame->fin);
459         _sx_debug(ZONE, "Opcode: %x", frame->opcode);
460         _sx_debug(ZONE, "mask_offset: %d", frame->mask_offset);
461         _sx_debug(ZONE, "payload_offset: %d", frame->payload_offset);
462         _sx_debug(ZONE, "rawdata_idx: %d", frame->rawdata_idx);
463         _sx_debug(ZONE, "rawdata_sz: %d", frame->rawdata_sz);
464         _sx_debug(ZONE, "payload_len: %u", frame->payload_len);
465 
466         if (frame->opcode != WS_OPCODE_CONTINUE) {
467             sc->opcode = frame->opcode;
468         }
469 
470         switch (sc->opcode) {
471         case WS_OPCODE_TEXT:
472             /* unmask content */
473             for (j = 0; j < frame->payload_len; j++)
474                 frame->rawdata[frame->payload_offset + j] ^= frame->mask[j % 4];
475             _sx_debug(ZONE, "payload: %.*s", frame->payload_len, frame->rawdata + frame->payload_offset);
476             sc->buf = realloc(sc->buf, sc->buf_len + frame->payload_len);
477             newbuf = sc->buf + sc->buf_len;
478             strncpy(newbuf, frame->rawdata + frame->payload_offset, frame->payload_len);
479             sc->buf_len += frame->payload_len;
480             /* hack unclose <open ... /> */
481             if (frame->payload_len >= 7 && strncmp(newbuf, "<open", 5) == 0 && strncmp(newbuf + frame->payload_len - 2, "/>", 2) == 0) {
482                 sc->buf_len--;
483                 sc->buf[sc->buf_len - 1] = '>';
484             }
485             break;
486         case WS_OPCODE_CLOSE:
487             libwebsock_close(s, sc);
488             break;
489         case WS_OPCODE_PING:
490             libwebsock_send_fragment(s, sc, frame->rawdata + frame->payload_offset, frame->payload_len, WS_FRAGMENT_FIN | WS_OPCODE_PONG);
491             break;
492         case WS_OPCODE_PONG:
493             s->want_read = 1;
494         default:
495             _sx_debug(ZONE, "unhandled opcode: %x", frame->opcode);
496             break;
497         }
498 
499         free(frame->rawdata);
500         free(frame);
501         sc->frame = NULL;
502 
503         if (sc->state == websocket_CLOSING) {
504             _sx_buffer_clear(buf);
505             return 0;
506         }
507     }
508 
509     _sx_debug(ZONE, "passing buffer: %.*s", sc->buf_len, sc->buf);
510     _sx_buffer_set(buf, sc->buf, sc->buf_len, NULL);
511     sc->buf_len = 0;
512 
513     return 1;
514 }
515 
_sx_websocket_wio(sx_t s,sx_plugin_t p,sx_buf_t buf)516 static int _sx_websocket_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
517     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
518 
519     /* only bothering if it is active websocket */
520     if(!(s->flags & SX_WEBSOCKET_WRAPPER))
521         return 1;
522 
523     _sx_debug(ZONE, "in _sx_websocket_wio");
524 
525     if(buf->len > 0) {
526         _sx_debug(ZONE, "wrapping %d bytes in WebSocket frame", buf->len);
527         sx_buf_t frame = libwebsock_fragment_buffer(buf->data, buf->len, WS_FRAGMENT_FIN | WS_OPCODE_TEXT);
528         if (frame == NULL) {
529             return libwebsock_close_with_reason(s, sc, WS_CLOSE_UNEXPECTED_ERROR, "Internal server error");
530         }
531         _sx_buffer_set(buf, frame->data, frame->len, frame->data);
532         free(frame);
533     }
534     _sx_debug(ZONE, "passing %d bytes frame", buf->len);
535 
536     return 1;
537 }
538 
_sx_websocket_new(sx_t s,sx_plugin_t p)539 static void _sx_websocket_new(sx_t s, sx_plugin_t p) {
540     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
541 
542     if(sc != NULL)
543         return;
544 
545     _sx_debug(ZONE, "preparing for HTTP websocket connect for %d", s->tag);
546 
547     sc = (_sx_websocket_conn_t) calloc(1, sizeof(struct _sx_websocket_conn_st));
548 
549     sc->state   = websocket_PRE;
550     sc->p       = pool_new();
551     sc->field   = spool_new(sc->p);
552     sc->value   = spool_new(sc->p);
553     sc->headers = xhash_new(11);
554     sc->buf     = malloc(1024);
555     sc->parser.data = sc;
556 
557     /* initialize parser */
558     http_parser_init(&sc->parser, HTTP_REQUEST);
559 
560     s->plugin_data[p->index] = (void *) sc;
561 
562     /* bring the plugin online */
563     _sx_chain_io_plugin(s, p);
564 }
565 
566 /** cleanup */
_sx_websocket_free(sx_t s,sx_plugin_t p)567 static void _sx_websocket_free(sx_t s, sx_plugin_t p) {
568     _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
569 
570     if(sc == NULL)
571         return;
572 
573     log_debug(ZONE, "cleaning up websocket state");
574 
575     pool_free(sc->p);
576 
577     if (sc->frame) free(((libwebsock_frame *)sc->frame)->rawdata);
578     free(sc->frame);
579     free(sc);
580 
581     s->plugin_data[p->index] = NULL;
582 }
583 
584 /** args: none */
sx_websocket_init(sx_env_t env,sx_plugin_t p,va_list args)585 int sx_websocket_init(sx_env_t env, sx_plugin_t p, va_list args) {
586 
587     _sx_debug(ZONE, "initialising websocket plugin");
588 
589     p->server = _sx_websocket_new;
590     p->rio = _sx_websocket_rio;
591     p->wio = _sx_websocket_wio;
592     p->free = _sx_websocket_free;
593 
594     char *http_forward = va_arg(args, char*);
595     p->private = http_forward;
596 
597     settings.on_headers_complete = _sx_websocket_http_headers_complete;
598     settings.on_header_field = _sx_websocket_http_header_field;
599     settings.on_header_value = _sx_websocket_http_header_value;
600 
601     return 0;
602 }
603