modules/websocket: Make sure that all WebSocket messages are sent on connections...
[sip-router] / modules / websocket / ws_frame.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2012 Crocodile RCS Ltd
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License 
19  * along with this program; if not, write to the Free Software 
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <limits.h>
25 #include <unistr.h>
26 #include "../../receive.h"
27 #include "../../stats.h"
28 #include "../../str.h"
29 #include "../../tcp_conn.h"
30 #include "../../tcp_server.h"
31 #include "../../lib/kcore/kstats_wrapper.h"
32 #include "../../lib/kmi/tree.h"
33 #include "../../mem/mem.h"
34 #include "ws_conn.h"
35 #include "ws_frame.h"
36 #include "ws_mod.h"
37
38 /*    0                   1                   2                   3
39       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
40      +-+-+-+-+-------+-+-------------+-------------------------------+
41      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
42      |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
43      |N|V|V|V|       |S|             |   (if payload len==126/127)   |
44      | |1|2|3|       |K|             |                               |
45      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
46      |     Extended payload length continued, if payload len == 127  |
47      + - - - - - - - - - - - - - - - +-------------------------------+
48      |                               |Masking-key, if MASK set to 1  |
49      +-------------------------------+-------------------------------+
50      | Masking-key (continued)       |          Payload Data         |
51      +-------------------------------- - - - - - - - - - - - - - - - +
52      :                     Payload Data continued ...                :
53      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
54      |                     Payload Data continued ...                |
55      +---------------------------------------------------------------+ */
56
57 typedef struct {
58         unsigned int fin;
59         unsigned int rsv1;
60         unsigned int rsv2;
61         unsigned int rsv3;
62         unsigned int opcode;
63         unsigned int mask;
64         unsigned int payload_len;
65         unsigned char masking_key[4];
66         char *payload_data;
67         ws_connection_t *wsc;
68 } ws_frame_t;
69
70 typedef enum
71 {
72         CONN_CLOSE_DO = 0,
73         CONN_CLOSE_DONT
74 } conn_close_t;
75
76 #define BYTE0_MASK_FIN          (0x80)
77 #define BYTE0_MASK_RSV1         (0x40)
78 #define BYTE0_MASK_RSV2         (0x20)
79 #define BYTE0_MASK_RSV3         (0x10)
80 #define BYTE0_MASK_OPCODE       (0x0F)
81 #define BYTE1_MASK_MASK         (0x80)
82 #define BYTE1_MASK_PAYLOAD_LEN  (0x7F)
83
84 #define OPCODE_CONTINUATION     (0x0)
85 #define OPCODE_TEXT_FRAME       (0x1)
86 #define OPCODE_BINARY_FRAME     (0x2)
87 /* 0x3 - 0x7 are reserved for further non-control frames */
88 #define OPCODE_CLOSE            (0x8)
89 #define OPCODE_PING             (0x9)
90 #define OPCODE_PONG             (0xa)
91 /* 0xb - 0xf are reserved for further control frames */
92
93 /* Time (in seconds) after which to send a keepalive on an idle connection */
94 int ws_keepalive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
95 int ws_keepalive_mechanism = DEFAULT_KEEPALIVE_MECHANISM;
96 str ws_ping_application_data = {0, 0};
97
98 stat_var *ws_failed_connections;
99 stat_var *ws_local_closed_connections;
100 stat_var *ws_received_frames;
101 stat_var *ws_remote_closed_connections;
102 stat_var *ws_transmitted_frames;
103
104 /* WebSocket status text */
105 static str str_status_normal_closure = str_init("Normal closure");
106 static str str_status_protocol_error = str_init("Protocol error");
107 static str str_status_unsupported_opcode = str_init("Unsupported opcode");
108 static str str_status_message_too_big = str_init("Message too big");
109
110 /* MI command status text */
111 static str str_status_empty_param = str_init("Empty connection ID parameter");
112 static str str_status_too_many_params = str_init("Too many parameters");
113 static str str_status_bad_param = str_init("Bad connection ID parameter");
114 static str str_status_error_closing = str_init("Error closing connection");
115 static str str_status_error_sending = str_init("Error sending frame");
116
117 static int encode_and_send_ws_frame(ws_frame_t *frame, conn_close_t conn_close)
118 {
119         int pos = 0, extended_length;
120         unsigned int frame_length;
121         char *send_buf;
122         struct tcp_connection *con;
123         struct dest_info dst;
124         union sockaddr_union *from = NULL;
125         union sockaddr_union local_addr;
126
127         LM_DBG("encoding WebSocket frame\n");
128
129         if (frame->wsc->state != WS_S_OPEN)
130         {
131                 LM_WARN("sending on closing connection\n");
132                 return -1;
133         }
134
135         wsconn_update(frame->wsc);
136
137         /* Validate the first byte */
138         if (!frame->fin)
139         {
140                 LM_ERR("WebSocket fragmentation not supported in the sip "
141                         "sub-protocol\n");
142                 return -1;
143         }
144
145         if (frame->rsv1 || frame->rsv2 || frame->rsv3)
146         {
147                 LM_ERR("WebSocket reserved fields with non-zero values\n");
148                 return -1;
149         }
150
151         switch(frame->opcode)
152         {
153         case OPCODE_TEXT_FRAME:
154         case OPCODE_BINARY_FRAME:
155                 LM_DBG("supported non-control frame: 0x%x\n",
156                         (unsigned char) frame->opcode);
157                 break;
158         case OPCODE_CLOSE:
159         case OPCODE_PING:
160         case OPCODE_PONG:
161                 LM_DBG("supported control frame: 0x%x\n",
162                         (unsigned char) frame->opcode);
163                 break;
164         default:
165                 LM_ERR("unsupported opcode: 0x%x\n",
166                         (unsigned char) frame->opcode);
167                 return -1;
168         }
169
170         /* validate the second byte */
171         if (frame->mask)
172         {
173                 LM_ERR("this is a server - all messages sent will be "
174                         "unmasked\n");
175                 return -1;
176         }
177
178         if (frame->payload_len < 126) extended_length = 0;
179         else if (frame->payload_len <= USHRT_MAX ) extended_length = 2;
180         else if (frame->payload_len <= UINT_MAX) extended_length = 4;
181         else
182         {
183                 LM_ERR("Kamailio only supports WebSocket frames with payload "
184                         "<= %u\n", UINT_MAX);
185                 return -1;
186         }
187
188         /* Allocate send buffer and build frame */
189         frame_length = frame->payload_len + extended_length + 2;
190         if ((send_buf = pkg_malloc(sizeof(unsigned char) * frame_length))
191                         == NULL)
192         {
193                 LM_ERR("allocating send buffer from pkg memory\n");
194                 return -1;
195         }
196         memset(send_buf, 0, sizeof(unsigned char) * frame_length);
197         send_buf[pos++] = 0x80 | (frame->opcode & 0xff);
198         if (extended_length == 0)
199                 send_buf[pos++] = (frame->payload_len & 0xff);
200         else if (extended_length == 2)
201         {
202                 send_buf[pos++] = 126;
203                 send_buf[pos++] = (frame->payload_len & 0xff00) >> 8;
204                 send_buf[pos++] = (frame->payload_len & 0x00ff) >> 0;
205         }
206         else
207         {
208                 send_buf[pos++] = 127;
209                 send_buf[pos++] = (frame->payload_len & 0xff000000) >> 24;
210                 send_buf[pos++] = (frame->payload_len & 0x00ff0000) >> 16;
211                 send_buf[pos++] = (frame->payload_len & 0x0000ff00) >> 8;
212                 send_buf[pos++] = (frame->payload_len & 0x000000ff) >> 0;
213         }
214         memcpy(&send_buf[pos], frame->payload_data, frame->payload_len);
215
216         if ((con = tcpconn_get(frame->wsc->id, 0, 0, 0, 0)) == NULL)
217         {
218                 LM_WARN("TCP/TLS connection get failed\n");
219                 if (wsconn_rm(frame->wsc) < 0)
220                         LM_ERR("removing WebSocket connection\n");
221                 return -1;
222         }
223         init_dst_from_rcv(&dst, &con->rcv);
224         if (conn_close == CONN_CLOSE_DO)
225         {
226                 dst.send_flags.f |= SND_F_CON_CLOSE;
227                 if (wsconn_rm(frame->wsc) < 0)
228                 {
229                         LM_ERR("removing WebSocket connection\n");
230                         return -1;
231                 }
232         }
233
234         if (dst.proto == PROTO_TCP)
235         {
236                 if (unlikely(tcp_disable))
237                 {
238                         STATS_TX_DROPS;
239                         LM_WARN("TCP disabled\n");
240                         return -1;
241                 }               
242         }
243 #ifdef USE_TLS
244         else if (dst.proto == PROTO_TLS)
245         {
246                 if (unlikely(tls_disable))
247                 {
248                         STATS_TX_DROPS;
249                         LM_WARN("TLS disabled\n");
250                         return -1;
251                 }               
252         }
253 #endif /* USE_TLS */
254
255         if (unlikely((dst.send_flags.f & SND_F_FORCE_SOCKET)
256                 && dst.send_sock))
257         {
258                 local_addr = dst.send_sock->su;
259                 su_setport(&local_addr, 0);
260                 from = &local_addr;
261         }
262
263         /* Regardless of what has been set before _always_ use existing
264            connections for WebSockets.  This is required because a WebSocket
265            server (which Kamailio is) CANNOT create connections. */
266         dst.send_flags.f |= SND_F_FORCE_CON_REUSE;
267
268         if (tcp_send(&dst, from, send_buf, frame_length) < 0)
269         {
270                 STATS_TX_DROPS;
271                 LM_ERR("sending WebSocket frame\n");
272                 pkg_free(send_buf);
273                 update_stat(ws_failed_connections, 1);
274                 if (wsconn_rm(frame->wsc) < 0)
275                         LM_ERR("removing WebSocket connection\n");
276                 return -1;
277         }
278
279         update_stat(ws_transmitted_frames, 1);
280
281         pkg_free(send_buf);
282         return 0;
283 }
284
285 static int close_connection(ws_connection_t *wsc, ws_close_type_t type,
286                                 short int status, str reason)
287 {
288         char *data;
289         ws_frame_t frame;
290
291         data = pkg_malloc(sizeof(char) * (reason.len + 2));
292         if (data == NULL)
293         {
294                 LM_ERR("allocating pkg memory\n");
295                 return -1;
296         }
297
298         if (wsc->state == WS_S_OPEN)
299         {
300                 data[0] = (status & 0xff00) >> 8;
301                 data[1] = (status & 0x00ff) >> 0;
302                 memcpy(&data[2], reason.s, reason.len);
303
304                 memset(&frame, 0, sizeof(frame));
305                 frame.fin = 1;
306                 frame.opcode = OPCODE_CLOSE;
307                 frame.payload_len = reason.len + 2;
308                 frame.payload_data = data;
309                 frame.wsc = wsc;
310
311                 if (encode_and_send_ws_frame(&frame,
312                         type ==
313                         REMOTE_CLOSE ? CONN_CLOSE_DO : CONN_CLOSE_DONT) < 0)
314                 {       
315                         LM_ERR("sending WebSocket close\n");
316                         pkg_free(data);
317                         return -1;
318                 }
319
320                 pkg_free(data);
321
322                 if (type == LOCAL_CLOSE)
323                 {
324                         frame.wsc->state = WS_S_CLOSING;
325                         update_stat(ws_local_closed_connections, 1);
326                 }
327                 else
328                         update_stat(ws_remote_closed_connections, 1);
329         }
330         else /* if (frame->wsc->state == WS_S_CLOSING) */
331                 wsconn_close_now(wsc);
332
333         return 0;
334 }
335
336 static int decode_and_validate_ws_frame(ws_frame_t *frame,
337                                         tcp_event_info_t *tcpinfo)
338 {
339         unsigned int i, len = tcpinfo->len;
340         int mask_start, j;
341         char *buf = tcpinfo->buf;
342
343         LM_DBG("decoding WebSocket frame\n");
344
345         if ((frame->wsc = wsconn_get(tcpinfo->con->id)) == NULL)
346         {
347                 LM_ERR("WebSocket connection not found\n");
348                 return -1;
349         }
350
351         wsconn_update(frame->wsc);
352
353         /* Decode and validate first 9 bits */
354         if (len < 2)
355         {
356                 LM_WARN("message is too short\n");
357                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
358                                         str_status_protocol_error) < 0)
359                         LM_ERR("closing connection\n");
360                 return -1;
361         }
362         frame->fin = (buf[0] & 0xff) & BYTE0_MASK_FIN;
363         frame->rsv1 = (buf[0] & 0xff) & BYTE0_MASK_RSV1;
364         frame->rsv2 = (buf[0] & 0xff) & BYTE0_MASK_RSV2;
365         frame->rsv3 = (buf[0] & 0xff) & BYTE0_MASK_RSV3;
366         frame->opcode = (buf[0] & 0xff) & BYTE0_MASK_OPCODE;
367         frame->mask = (buf[1] & 0xff) & BYTE1_MASK_MASK;
368         
369         if (!frame->fin)
370         {
371                 LM_WARN("WebSocket fragmentation not supported in the sip "
372                         "sub-protocol\n");
373                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
374                                         str_status_protocol_error) < 0)
375                         LM_ERR("closing connection\n");
376                 return -1;
377         }
378
379         if (frame->rsv1 || frame->rsv2 || frame->rsv3)
380         {
381                 LM_WARN("WebSocket reserved fields with non-zero values\n");
382                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
383                                         str_status_protocol_error) < 0)
384                         LM_ERR("closing connection\n");
385                 return -1;
386         }
387
388         switch(frame->opcode)
389         {
390         case OPCODE_TEXT_FRAME:
391         case OPCODE_BINARY_FRAME:
392                 LM_DBG("supported non-control frame: 0x%x\n",
393                         (unsigned char) frame->opcode);
394                 break;
395
396         case OPCODE_CLOSE:
397         case OPCODE_PING:
398         case OPCODE_PONG:
399                 LM_DBG("supported control frame: 0x%x\n",
400                         (unsigned char) frame->opcode);
401                 break;
402
403         default:
404                 LM_WARN("unsupported opcode: 0x%x\n",
405                         (unsigned char) frame->opcode);
406                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1008,
407                                         str_status_unsupported_opcode) < 0)
408                         LM_ERR("closing connection\n");
409                 return -1;
410         }
411
412         if (!frame->mask)
413         {
414                 LM_WARN("this is a server - all received messages must be "
415                         "masked\n");
416                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
417                                         str_status_protocol_error) < 0)
418                         LM_ERR("closing connection\n");
419                 return -1;
420         }
421
422         /* Decode and validate length */
423         frame->payload_len = (buf[1] & 0xff) & BYTE1_MASK_PAYLOAD_LEN;
424         if (frame->payload_len == 126)
425         {
426                 if (len < 4)
427                 {
428                         LM_WARN("message is too short\n");
429                         if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
430                                                 str_status_protocol_error) < 0)
431                                 LM_ERR("closing connection\n");
432                         return -1;
433                 }
434                 mask_start = 4;
435
436                 frame->payload_len =      ((buf[2] & 0xff) <<  8)
437                                         | ((buf[3] & 0xff) <<  0);
438         }
439         else if (frame->payload_len == 127)
440         {
441                 if (len < 10)
442                 {
443                         LM_WARN("message is too short\n");
444                         if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
445                                                 str_status_protocol_error) < 0)
446                                 LM_ERR("closing connection\n");
447                         return -1;
448                 }
449                 mask_start = 10;
450
451                 if ((buf[2] & 0xff) != 0 || (buf[3] & 0xff) != 0
452                         || (buf[4] & 0xff) != 0 || (buf[5] & 0xff) != 0)
453                 {
454                         LM_WARN("message is too long\n");
455                         if (close_connection(frame->wsc, LOCAL_CLOSE, 1009,
456                                                 str_status_message_too_big) < 0)
457                                 LM_ERR("closing connection\n");
458                         return -1;
459                 }
460
461                 /* Only decoding the last four bytes of the length...
462                    This limits the size of WebSocket messages that can be
463                    handled to 2^32 = which should be plenty for SIP! */
464                 frame->payload_len =      ((buf[6] & 0xff) << 24)
465                                         | ((buf[7] & 0xff) << 16)
466                                         | ((buf[8] & 0xff) <<  8)
467                                         | ((buf[9] & 0xff) <<  0);
468         }
469         else
470                 mask_start = 2;
471
472         /* Decode mask */
473         frame->masking_key[0] = (buf[mask_start + 0] & 0xff);
474         frame->masking_key[1] = (buf[mask_start + 1] & 0xff);
475         frame->masking_key[2] = (buf[mask_start + 2] & 0xff);
476         frame->masking_key[3] = (buf[mask_start + 3] & 0xff);
477
478         /* Decode and unmask payload */
479         if (len != frame->payload_len + mask_start + 4)
480         {
481                 LM_WARN("message not complete frame size %u but received %u\n",
482                         frame->payload_len + mask_start + 4, len);
483                 if (close_connection(frame->wsc, LOCAL_CLOSE, 1002,
484                                         str_status_protocol_error) < 0)
485                         LM_ERR("closing connection\n");
486                 return -1;
487         }
488         frame->payload_data = &buf[mask_start + 4];
489         for (i = 0; i < frame->payload_len; i++)
490         {
491                 j = i % 4;
492                 frame->payload_data[i]
493                         = frame->payload_data[i] ^ frame->masking_key[j];
494         }
495
496         LM_DBG("Rx (decoded): %.*s\n",
497                 (int) frame->payload_len, frame->payload_data);
498
499         return frame->opcode;
500 }
501
502 static int handle_close(ws_frame_t *frame)
503 {
504         unsigned short code = 0;
505         str reason = {0, 0};
506
507         if (frame->payload_len >= 2)
508                 code =    ((frame->payload_data[0] & 0xff) << 8)
509                         | ((frame->payload_data[1] & 0xff) << 0);
510
511         if (frame->payload_len > 2)
512         {
513                 reason.s = &frame->payload_data[2];
514                 reason.len = frame->payload_len - 2;
515         }
516
517         LM_DBG("Rx Close: %hu %.*s\n", code, reason.len, reason.s);
518
519         if (close_connection(frame->wsc,
520                 frame->wsc->state == WS_S_OPEN ? REMOTE_CLOSE : LOCAL_CLOSE,
521                 1000, str_status_normal_closure) < 0)
522         {
523                 LM_ERR("closing connection\n");
524                 return -1;
525         }
526         
527         return 0;
528 }
529
530 static int handle_ping(ws_frame_t *frame)
531 {
532         LM_DBG("Rx Ping: %.*s\n", frame->payload_len, frame->payload_data);
533
534         frame->opcode = OPCODE_PONG;
535         frame->mask = 0;
536
537         if (encode_and_send_ws_frame(frame, CONN_CLOSE_DONT) < 0)
538         {
539                 LM_ERR("sending Pong\n");
540                 return -1;
541         }
542
543         return 0;
544 }
545
546 static int handle_pong(ws_frame_t *frame)
547 {
548         LM_DBG("Rx Pong: %.*s\n", frame->payload_len, frame->payload_data);
549
550         if (strncmp(frame->payload_data, ws_ping_application_data.s,
551                         ws_ping_application_data.len) == 0)
552                 frame->wsc->awaiting_pong = 0;
553
554         return 0;
555 }
556
557 int ws_frame_receive(void *data)
558 {
559         ws_frame_t ws_frame;
560         tcp_event_info_t *tcpinfo = (tcp_event_info_t *) data;
561
562         update_stat(ws_received_frames, 1);
563
564         if (tcpinfo == NULL || tcpinfo->buf == NULL || tcpinfo->len <= 0)
565         {
566                 LM_WARN("received bad frame\n");
567                 return -1;
568         }
569
570         switch(decode_and_validate_ws_frame(&ws_frame, tcpinfo))
571         {
572         case OPCODE_TEXT_FRAME:
573         case OPCODE_BINARY_FRAME:
574                 return receive_msg(ws_frame.payload_data, ws_frame.payload_len,
575                                 tcpinfo->rcv);
576
577         case OPCODE_CLOSE:
578                 return handle_close(&ws_frame);
579
580         case OPCODE_PING:
581                 return handle_ping(&ws_frame);
582
583         case OPCODE_PONG:
584                 return handle_pong(&ws_frame);
585
586         default:
587                 LM_WARN("received bad frame\n");
588                 return -1;
589         }
590
591         return 0;
592 }
593
594 int ws_frame_transmit(void *data)
595 {
596         ws_event_info_t *wsev = (ws_event_info_t *) data;
597         ws_frame_t frame;
598
599         memset(&frame, 0, sizeof(frame));
600         frame.fin = 1;
601         /* Can't be sure whether this message is UTF-8 or not so check to see
602            if it "might" be UTF-8 and send as binary if it definitely isn't */
603         frame.opcode = (u8_check((uint8_t *) wsev->buf, wsev->len) == NULL) ?
604                                 OPCODE_TEXT_FRAME : OPCODE_BINARY_FRAME;
605         frame.payload_len = wsev->len;
606         frame.payload_data = wsev->buf;
607         frame.wsc = wsconn_get(wsev->id);
608
609         if (encode_and_send_ws_frame(&frame, CONN_CLOSE_DONT) < 0)
610         {       
611                 LM_ERR("sending SIP message\n");
612                 return -1;
613         }
614
615         return 0;
616 }
617
618 static int ping_pong(ws_connection_t *wsc, int opcode)
619 {
620         ws_frame_t frame;
621
622         memset(&frame, 0, sizeof(frame));
623         frame.fin = 1;
624         frame.opcode = opcode;
625         frame.payload_len = ws_ping_application_data.len;
626         frame.payload_data = ws_ping_application_data.s;
627         frame.wsc = wsc;
628
629         if (encode_and_send_ws_frame(&frame, CONN_CLOSE_DONT) < 0)
630         {       
631                 LM_ERR("sending keepalive\n");
632                 return -1;
633         }
634
635         if (opcode == OPCODE_PING)
636                 wsc->awaiting_pong = 1;
637
638         return 0;
639 }
640
641 struct mi_root *ws_mi_close(struct mi_root *cmd, void *param)
642 {
643         unsigned int id;
644         struct mi_node *node = NULL;
645         ws_connection_t *wsc;
646
647         node = cmd->node.kids;
648         if (node == NULL)
649                 return 0;
650         if (node->value.s == NULL || node->value.len == 0)
651         {
652                 LM_WARN("empty connection ID parameter\n");
653                 return init_mi_tree(400, str_status_empty_param.s,
654                                         str_status_empty_param.len);
655         }
656         if (str2int(&node->value, &id) < 0)
657         {
658                 LM_ERR("converting string to int\n");
659                 return 0;
660         }
661         if (node->next != NULL)
662         {
663                 LM_WARN("too many parameters\n");
664                 return init_mi_tree(400, str_status_too_many_params.s,
665                                         str_status_too_many_params.len);
666         }
667
668         if ((wsc = wsconn_get(id)) == NULL)
669         {
670                 LM_WARN("bad connection ID parameter\n");
671                 return init_mi_tree(400, str_status_bad_param.s,
672                                         str_status_bad_param.len);
673         }
674
675         if (close_connection(wsc, LOCAL_CLOSE, 1000,
676                                 str_status_normal_closure) < 0)
677         {
678                 LM_WARN("closing connection\n");
679                 return init_mi_tree(500, str_status_error_closing.s,
680                                         str_status_error_closing.len);
681         }
682
683         return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
684 }
685
686 static struct mi_root *mi_ping_pong(struct mi_root *cmd, void *param,
687                                         int opcode)
688 {
689         unsigned int id;
690         struct mi_node *node = NULL;
691         ws_connection_t *wsc;
692
693         node = cmd->node.kids;
694         if (node == NULL)
695                 return 0;
696         if (node->value.s == NULL || node->value.len == 0)
697         {
698                 LM_WARN("empty connection ID parameter\n");
699                 return init_mi_tree(400, str_status_empty_param.s,
700                                         str_status_empty_param.len);
701         }
702         if (str2int(&node->value, &id) < 0)
703         {
704                 LM_ERR("converting string to int\n");
705                 return 0;
706         }
707         if (node->next != NULL)
708         {
709                 LM_WARN("too many parameters\n");
710                 return init_mi_tree(400, str_status_too_many_params.s,
711                                         str_status_too_many_params.len);
712         }
713
714         if ((wsc = wsconn_get(id)) == NULL)
715         {
716                 LM_WARN("bad connection ID parameter\n");
717                 return init_mi_tree(400, str_status_bad_param.s,
718                                         str_status_bad_param.len);
719         }
720
721         if (ping_pong(wsc, opcode) < 0)
722         {
723                 LM_WARN("sending %s\n", OPCODE_PING ? "Ping" : "Pong");
724                 return init_mi_tree(500, str_status_error_sending.s,
725                                         str_status_error_sending.len);
726         }
727
728         return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
729 }
730
731 struct mi_root *ws_mi_ping(struct mi_root *cmd, void *param)
732 {
733         return mi_ping_pong(cmd, param, OPCODE_PING);
734 }
735
736 struct mi_root *ws_mi_pong(struct mi_root *cmd, void *param)
737 {
738         return mi_ping_pong(cmd, param, OPCODE_PONG);
739 }
740
741 void ws_keepalive(unsigned int ticks, void *param)
742 {
743         int check_time = (int) time(NULL) - ws_keepalive_timeout;
744         ws_connection_t *wsc = wsconn_used_list->head;
745
746         while (wsc && wsc->last_used < check_time)
747         {
748                 if (wsc->state == WS_S_CLOSING
749                         || wsc->awaiting_pong)
750                 {
751                         LM_WARN("forcibly closing connection\n");
752                         wsconn_close_now(wsc);
753                 }
754                 else
755                         ping_pong(wsconn_used_list->head,
756                           ws_keepalive_mechanism == KEEPALIVE_MECHANISM_PING
757                                         ? OPCODE_PING : OPCODE_PONG);
758                 wsc = wsconn_used_list->head;
759         }
760         
761 }