1 /*
2 * Copyright (C) 2012-2013 Crocodile RCS Ltd
3 *
4 * This file is part of Kamailio, a free SIP server.
5 *
6 * Kamailio is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version
10 *
11 * Kamailio is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * Exception: permission to copy, modify, propagate, and distribute a work
21 * formed by combining OpenSSL toolkit software and the code in this file,
22 * such as linking with software components and libraries released under
23 * OpenSSL project license.
24 *
25 */
26
27 #include "../../core/locking.h"
28 #include "../../core/str.h"
29 #include "../../core/tcp_conn.h"
30 #include "../../core/fmsg.h"
31 #include "../../core/counters.h"
32 #include "../../core/kemi.h"
33 #include "../../core/mem/mem.h"
34 #include "ws_conn.h"
35 #include "websocket.h"
36
37 /* Maximum number of connections to display when using the ws.dump command */
38 #define MAX_WS_CONNS_DUMP 50
39
40 extern int ws_verbose_list;
41 extern str ws_event_callback;
42 extern int ws_keepalive_processes;
43 extern int ws_rm_delay_interval;
44
45 ws_connection_t **wsconn_id_hash = NULL;
46 #define wsconn_listadd tcpconn_listadd
47 #define wsconn_listrm tcpconn_listrm
48
49 gen_lock_t *wsconn_lock = NULL;
50 #define WSCONN_LOCK lock_get(wsconn_lock)
51 #define WSCONN_UNLOCK lock_release(wsconn_lock)
52
53 #define wsconn_ref(c) atomic_inc(&((c)->refcnt))
54 #define wsconn_unref(c) atomic_dec_and_test(&((c)->refcnt))
55
56 gen_lock_t *wsstat_lock = NULL;
57
58 ws_connection_list_t *wsconn_used_list = NULL;
59
60 stat_var *ws_current_connections;
61 stat_var *ws_max_concurrent_connections;
62 stat_var *ws_sip_current_connections;
63 stat_var *ws_sip_max_concurrent_connections;
64 stat_var *ws_msrp_current_connections;
65 stat_var *ws_msrp_max_concurrent_connections;
66
67 char *wsconn_state_str[] = {
68 "CONNECTING", /* WS_S_CONNECTING */
69 "OPEN", /* WS_S_OPEN */
70 "CLOSING", /* WS_S_CLOSING */
71 "CLOSED" /* WS_S_CLOSED */
72 };
73
74 /* RPC command status text */
75 static str str_status_bad_param = str_init("Bad display order parameter");
76
wsconn_init(void)77 int wsconn_init(void)
78 {
79 wsconn_lock = lock_alloc();
80 if(wsconn_lock == NULL) {
81 LM_ERR("allocating lock\n");
82 goto error;
83 }
84 if(lock_init(wsconn_lock) == 0) {
85 LM_ERR("initialising lock\n");
86 goto error;
87 }
88
89 wsstat_lock = lock_alloc();
90 if(wsstat_lock == NULL) {
91 LM_ERR("allocating lock\n");
92 goto error;
93 }
94 if(lock_init(wsstat_lock) == NULL) {
95 LM_ERR("initialising lock\n");
96 goto error;
97 }
98
99 wsconn_id_hash = (ws_connection_t **)shm_malloc(
100 TCP_ID_HASH_SIZE * sizeof(ws_connection_t *));
101 if(wsconn_id_hash == NULL) {
102 LM_ERR("allocating WebSocket hash-table\n");
103 goto error;
104 }
105 memset((void *)wsconn_id_hash, 0,
106 TCP_ID_HASH_SIZE * sizeof(ws_connection_t *));
107
108 wsconn_used_list = (ws_connection_list_t *)shm_malloc(
109 sizeof(ws_connection_list_t));
110 if(wsconn_used_list == NULL) {
111 LM_ERR("allocating WebSocket used list\n");
112 goto error;
113 }
114 memset((void *)wsconn_used_list, 0, sizeof(ws_connection_list_t));
115
116 return 0;
117
118 error:
119 if(wsconn_lock)
120 lock_dealloc((void *)wsconn_lock);
121 if(wsstat_lock)
122 lock_dealloc((void *)wsstat_lock);
123 wsconn_lock = wsstat_lock = NULL;
124
125 if(wsconn_id_hash)
126 shm_free(wsconn_id_hash);
127 if(wsconn_used_list)
128 shm_free(wsconn_used_list);
129 wsconn_id_hash = NULL;
130 wsconn_used_list = NULL;
131
132 return -1;
133 }
134
_wsconn_rm(ws_connection_t * wsc)135 static inline void _wsconn_rm(ws_connection_t *wsc)
136 {
137 wsconn_listrm(wsconn_id_hash[wsc->id_hash], wsc, id_next, id_prev);
138
139 update_stat(ws_current_connections, -1);
140 if(wsc->sub_protocol == SUB_PROTOCOL_SIP)
141 update_stat(ws_sip_current_connections, -1);
142 else if(wsc->sub_protocol == SUB_PROTOCOL_MSRP)
143 update_stat(ws_msrp_current_connections, -1);
144
145 shm_free(wsc);
146 }
147
wsconn_destroy(void)148 void wsconn_destroy(void)
149 {
150 int h;
151
152 if(wsconn_used_list) {
153 shm_free(wsconn_used_list);
154 wsconn_used_list = NULL;
155 }
156
157 if(wsconn_id_hash) {
158 WSCONN_UNLOCK;
159 WSCONN_LOCK;
160 for(h = 0; h < TCP_ID_HASH_SIZE; h++) {
161 ws_connection_t *wsc = wsconn_id_hash[h];
162 while(wsc) {
163 ws_connection_t *next = wsc->id_next;
164 _wsconn_rm(wsc);
165 wsc = next;
166 }
167 }
168 WSCONN_UNLOCK;
169
170 shm_free(wsconn_id_hash);
171 wsconn_id_hash = NULL;
172 }
173
174 if(wsconn_lock) {
175 lock_destroy(wsconn_lock);
176 lock_dealloc((void *)wsconn_lock);
177 wsconn_lock = NULL;
178 }
179
180 if(wsstat_lock) {
181 lock_destroy(wsstat_lock);
182 lock_dealloc((void *)wsstat_lock);
183 wsstat_lock = NULL;
184 }
185 }
186
wsconn_add(struct receive_info * rcv,unsigned int sub_protocol)187 int wsconn_add(struct receive_info *rcv, unsigned int sub_protocol)
188 {
189 int cur_cons, max_cons;
190 int id = rcv->proto_reserved1;
191 int id_hash = tcp_id_hash(id);
192 ws_connection_t *wsc;
193
194 LM_DBG("connection id [%d]\n", id);
195
196 /* Allocate and fill in new WebSocket connection */
197 wsc = shm_malloc(sizeof(ws_connection_t) + BUF_SIZE + 1);
198 if(wsc == NULL) {
199 LM_ERR("allocating shared memory\n");
200 return -1;
201 }
202 memset(wsc, 0, sizeof(ws_connection_t) + BUF_SIZE + 1);
203 wsc->id = id;
204 wsc->id_hash = id_hash;
205 wsc->state = WS_S_OPEN;
206 wsc->rcv = *rcv;
207 wsc->sub_protocol = sub_protocol;
208 wsc->run_event = 0;
209 wsc->frag_buf.s = ((char *)wsc) + sizeof(ws_connection_t);
210 atomic_set(&wsc->refcnt, 0);
211
212 LM_DBG("new wsc => [%p], ref => [%d]\n", wsc,
213 atomic_get(&wsc->refcnt));
214
215 WSCONN_LOCK;
216 /* Add to WebSocket connection table */
217 wsconn_listadd(wsconn_id_hash[wsc->id_hash], wsc, id_next, id_prev);
218
219 /* Add to the end of the WebSocket used list */
220 wsc->last_used = (int)time(NULL);
221 if(wsconn_used_list->head == NULL)
222 wsconn_used_list->head = wsconn_used_list->tail = wsc;
223 else {
224 wsc->used_prev = wsconn_used_list->tail;
225 wsconn_used_list->tail->used_next = wsc;
226 wsconn_used_list->tail = wsc;
227 }
228 wsconn_ref(wsc);
229
230 WSCONN_UNLOCK;
231
232 LM_DBG("added to conn_table wsc => [%p], ref => [%d]\n", wsc,
233 atomic_get(&wsc->refcnt));
234
235 /* Update connection statistics */
236 lock_get(wsstat_lock);
237
238 update_stat(ws_current_connections, 1);
239 cur_cons = get_stat_val(ws_current_connections);
240 max_cons = get_stat_val(ws_max_concurrent_connections);
241 if(max_cons < cur_cons)
242 update_stat(ws_max_concurrent_connections, cur_cons - max_cons);
243
244 if(wsc->sub_protocol == SUB_PROTOCOL_SIP) {
245 update_stat(ws_sip_current_connections, 1);
246 cur_cons = get_stat_val(ws_sip_current_connections);
247 max_cons = get_stat_val(ws_sip_max_concurrent_connections);
248 if(max_cons < cur_cons)
249 update_stat(ws_sip_max_concurrent_connections, cur_cons - max_cons);
250 } else if(wsc->sub_protocol == SUB_PROTOCOL_MSRP) {
251 update_stat(ws_msrp_current_connections, 1);
252 cur_cons = get_stat_val(ws_msrp_current_connections);
253 max_cons = get_stat_val(ws_msrp_max_concurrent_connections);
254 if(max_cons < cur_cons)
255 update_stat(
256 ws_msrp_max_concurrent_connections, cur_cons - max_cons);
257 }
258
259 lock_release(wsstat_lock);
260
261 return 0;
262 }
263
wsconn_run_route(ws_connection_t * wsc)264 static void wsconn_run_route(ws_connection_t *wsc)
265 {
266 int rt, backup_rt;
267 struct run_act_ctx ctx;
268 sip_msg_t *fmsg;
269 sr_kemi_eng_t *keng = NULL;
270 str evrtname = str_init("websocket:closed");
271
272 LM_DBG("wsconn_run_route event_route[websocket:closed]\n");
273
274 rt = route_lookup(&event_rt, evrtname.s);
275 if(rt < 0 || event_rt.rlist[rt] == NULL) {
276 if(ws_event_callback.len <= 0 || ws_event_callback.s == NULL) {
277 LM_DBG("event route does not exist");
278 return;
279 }
280 keng = sr_kemi_eng_get();
281 if(keng == NULL) {
282 LM_DBG("event route callback engine does not exist");
283 return;
284 } else {
285 rt = -1;
286 }
287 }
288
289 if(faked_msg_init() < 0) {
290 LM_ERR("faked_msg_init() failed\n");
291 return;
292 }
293
294 fmsg = faked_msg_next();
295 wsc->rcv.proto_reserved1 = wsc->id;
296 fmsg->rcv = wsc->rcv;
297
298 backup_rt = get_route_type();
299 set_route_type(EVENT_ROUTE);
300 init_run_actions_ctx(&ctx);
301 if(rt < 0) {
302 /* kemi script event route callback */
303 if(keng && sr_kemi_route(keng,fmsg, EVENT_ROUTE, &ws_event_callback,
304 &evrtname) < 0) {
305 LM_ERR("error running event route kemi callback\n");
306 }
307 } else {
308 /* native cfg event route */
309 run_top_route(event_rt.rlist[rt], fmsg, 0);
310 }
311 set_route_type(backup_rt);
312 }
313
wsconn_dtor(ws_connection_t * wsc)314 static void wsconn_dtor(ws_connection_t *wsc)
315 {
316 if(!wsc)
317 return;
318
319 LM_DBG("wsconn_dtor for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
320
321 if(wsc->run_event)
322 wsconn_run_route(wsc);
323
324 shm_free(wsc);
325
326 LM_DBG("wsconn_dtor for [%p] destroyed\n", wsc);
327 }
328
wsconn_rm(ws_connection_t * wsc,ws_conn_eventroute_t run_event_route)329 int wsconn_rm(ws_connection_t *wsc, ws_conn_eventroute_t run_event_route)
330 {
331 LM_DBG("wsconn_rm for [%p] refcnt [%d]\n", wsc, atomic_get(&wsc->refcnt));
332
333 if(run_event_route == WSCONN_EVENTROUTE_YES)
334 wsc->run_event = 1;
335
336 return wsconn_put(wsc);
337 }
338
wsconn_update(ws_connection_t * wsc)339 int wsconn_update(ws_connection_t *wsc)
340 {
341 if(!wsc) {
342 LM_ERR("wsconn_update: null pointer\n");
343 return -1;
344 }
345
346 WSCONN_LOCK;
347 wsc->last_used = (int)time(NULL);
348 if(wsconn_used_list->tail == wsc)
349 /* Already at the end of the list */
350 goto end;
351 if(wsconn_used_list->head == wsc)
352 wsconn_used_list->head = wsc->used_next;
353 if(wsc->used_prev)
354 wsc->used_prev->used_next = wsc->used_next;
355 if(wsc->used_next)
356 wsc->used_next->used_prev = wsc->used_prev;
357 wsc->used_prev = wsconn_used_list->tail;
358 wsc->used_next = NULL;
359 wsconn_used_list->tail->used_next = wsc;
360 wsconn_used_list->tail = wsc;
361
362 end:
363 WSCONN_UNLOCK;
364 return 0;
365 }
366
wsconn_close_now(ws_connection_t * wsc)367 void wsconn_close_now(ws_connection_t *wsc)
368 {
369 struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0);
370
371 if(wsconn_rm(wsc, WSCONN_EVENTROUTE_YES) < 0)
372 LM_ERR("removing WebSocket connection\n");
373
374 if(con == NULL) {
375 LM_ERR("getting TCP/TLS connection\n");
376 return;
377 }
378
379 tcpconn_put(con);
380 con->send_flags.f |= SND_F_CON_CLOSE;
381 con->state = S_CONN_BAD;
382 con->timeout = get_ticks_raw();
383 }
384
wsconn_detach_connection(ws_connection_t * wsc)385 void wsconn_detach_connection(ws_connection_t *wsc)
386 {
387 /* Remove from the WebSocket used list */
388 if(wsconn_used_list->head == wsc)
389 wsconn_used_list->head = wsc->used_next;
390 if(wsconn_used_list->tail == wsc)
391 wsconn_used_list->tail = wsc->used_prev;
392 if(wsc->used_prev)
393 wsc->used_prev->used_next = wsc->used_next;
394 if(wsc->used_next)
395 wsc->used_next->used_prev = wsc->used_prev;
396
397 /* remove from wsconn_id_hash */
398 wsconn_listrm(wsconn_id_hash[wsc->id_hash], wsc, id_next, id_prev);
399
400 /* stat */
401 update_stat(ws_current_connections, -1);
402 if(wsc->sub_protocol == SUB_PROTOCOL_SIP)
403 update_stat(ws_sip_current_connections, -1);
404 else if(wsc->sub_protocol == SUB_PROTOCOL_MSRP)
405 update_stat(ws_msrp_current_connections, -1);
406 }
407
408 /* mode controls if lock needs to be aquired */
wsconn_put_mode(ws_connection_t * wsc,int mode)409 int wsconn_put_mode(ws_connection_t *wsc, int mode)
410 {
411 if(!wsc)
412 return -1;
413
414 LM_DBG("wsconn_put start for [%p] refcnt [%d]\n", wsc,
415 atomic_get(&wsc->refcnt));
416
417 if(mode) {
418 WSCONN_LOCK;
419 }
420 if(wsc->state == WS_S_REMOVING) {
421 goto done;
422 }
423 /* refcnt == 0*/
424 if(wsconn_unref(wsc)) {
425 wsc->state = WS_S_REMOVING;
426 wsc->rmticks = get_ticks();
427 }
428 LM_DBG("wsconn_put end for [%p] refcnt [%d]\n", wsc,
429 atomic_get(&wsc->refcnt));
430
431 done:
432 if(mode) {
433 WSCONN_UNLOCK;
434 }
435
436 return 0;
437 }
438
439 /* must be called with unlocked WSCONN_LOCK */
wsconn_put(ws_connection_t * wsc)440 int wsconn_put(ws_connection_t *wsc)
441 {
442 return wsconn_put_mode(wsc, 1);
443 }
444
wsconn_get(int id)445 ws_connection_t *wsconn_get(int id)
446 {
447 int id_hash = tcp_id_hash(id);
448 ws_connection_t *wsc;
449
450 LM_DBG("wsconn_get for id [%d]\n", id);
451
452 WSCONN_LOCK;
453 for(wsc = wsconn_id_hash[id_hash]; wsc; wsc = wsc->id_next) {
454 if(wsc->id == id) {
455 wsconn_ref(wsc);
456 LM_DBG("wsconn_get returns wsc [%p] refcnt [%d]\n", wsc,
457 atomic_get(&wsc->refcnt));
458
459 WSCONN_UNLOCK;
460
461 return wsc;
462 }
463 }
464 WSCONN_UNLOCK;
465
466 return NULL;
467 }
468
wsconn_put_id(int id)469 int wsconn_put_id(int id)
470 {
471 int id_hash = tcp_id_hash(id);
472 ws_connection_t *wsc;
473
474 LM_DBG("wsconn put id [%d]\n", id);
475
476 WSCONN_LOCK;
477 for(wsc = wsconn_id_hash[id_hash]; wsc; wsc = wsc->id_next) {
478 if(wsc->id == id) {
479 LM_DBG("wsc [%p] refcnt [%d]\n", wsc,
480 atomic_get(&wsc->refcnt));
481 wsconn_put_mode(wsc, 0);
482
483 WSCONN_UNLOCK;
484
485 return 1;
486 }
487 }
488 WSCONN_UNLOCK;
489
490 return 0;
491 }
492
wsconn_get_list(void)493 ws_connection_t **wsconn_get_list(void)
494 {
495 ws_connection_t **list = NULL;
496 ws_connection_t *wsc = NULL;
497 size_t list_size = 0;
498 size_t list_len = 0;
499 size_t i = 0;
500
501 if(ws_verbose_list)
502 LM_DBG("wsconn get list - starting\n");
503
504 WSCONN_LOCK;
505
506 /* get the number of used connections */
507 wsc = wsconn_used_list->head;
508 while(wsc) {
509 if(ws_verbose_list)
510 LM_DBG("counter wsc [%p] prev => [%p] next => [%p]\n", wsc,
511 wsc->used_prev, wsc->used_next);
512 list_len++;
513 wsc = wsc->used_next;
514 }
515
516 if(!list_len)
517 goto end;
518
519 /* allocate a NULL terminated list of wsconn pointers */
520 list_size = (list_len + 1) * sizeof(ws_connection_t *);
521 list = pkg_malloc(list_size);
522 if(!list)
523 goto end;
524
525 memset(list, 0, list_size);
526
527 /* copy */
528 wsc = wsconn_used_list->head;
529 for(i = 0; i < list_len; i++) {
530 if(!wsc) {
531 LM_ERR("Wrong list length\n");
532 break;
533 }
534
535 list[i] = wsc;
536 wsconn_ref(wsc);
537 if(ws_verbose_list)
538 LM_DBG("wsc [%p] id [%d] ref++\n", wsc, wsc->id);
539
540 wsc = wsc->used_next;
541 }
542 list[i] = NULL; /* explicit NULL termination */
543
544 end:
545 WSCONN_UNLOCK;
546
547 if(ws_verbose_list)
548 LM_DBG("wsconn_get_list returns list [%p]"
549 " with [%d] members\n",
550 list, (int)list_len);
551
552 return list;
553 }
554
wsconn_put_list(ws_connection_t ** list_head)555 int wsconn_put_list(ws_connection_t **list_head)
556 {
557 ws_connection_t **list = NULL;
558 ws_connection_t *wsc = NULL;
559
560 LM_DBG("wsconn_put_list [%p]\n", list_head);
561
562 if(!list_head)
563 return -1;
564
565 list = list_head;
566 wsc = *list_head;
567 while(wsc) {
568 wsconn_put(wsc);
569 wsc = *(++list);
570 }
571
572 pkg_free(list_head);
573
574 return 0;
575 }
576
577
wsconn_get_list_ids(int idx)578 ws_connection_id_t *wsconn_get_list_ids(int idx)
579 {
580 ws_connection_id_t *list = NULL;
581 ws_connection_t *wsc = NULL;
582 size_t list_size = 0;
583 size_t list_len = 0;
584 size_t i = 0;
585
586 if(ws_verbose_list)
587 LM_DBG("wsconn get list ids - starting\n");
588
589 WSCONN_LOCK;
590
591 /* get the number of used connections */
592 wsc = wsconn_used_list->head;
593 while(wsc) {
594 if(wsc->id % ws_keepalive_processes == idx) {
595 if(ws_verbose_list) {
596 LM_DBG("counter wsc [%p] prev => [%p] next => [%p] (%d/%d)\n",
597 wsc, wsc->used_prev, wsc->used_next, wsc->id, idx);
598 }
599 list_len++;
600 }
601 wsc = wsc->used_next;
602 }
603
604 if(!list_len)
605 goto end;
606
607 /* allocate a NULL terminated list of wsconn pointers */
608 list_size = (list_len + 1) * sizeof(ws_connection_id_t);
609 list = pkg_malloc(list_size);
610 if(!list)
611 goto end;
612
613 memset(list, 0, list_size);
614
615 /* copy */
616 wsc = wsconn_used_list->head;
617 for(i = 0; i < list_len; i++) {
618 if(!wsc) {
619 LM_ERR("Wrong list length\n");
620 break;
621 }
622
623 if(wsc->id % ws_keepalive_processes == idx) {
624 list[i].id = wsc->id;
625 wsconn_ref(wsc);
626 if(ws_verbose_list) {
627 LM_DBG("wsc [%p] id [%d] (%d) - ref++\n", wsc, wsc->id, idx);
628 }
629 }
630 wsc = wsc->used_next;
631 }
632 list[i].id = -1; /* explicit -1 termination */
633
634 end:
635 WSCONN_UNLOCK;
636
637 if(ws_verbose_list) {
638 LM_DBG("wsconn get list id returns list [%p]"
639 " with [%d] members (%d)\n",
640 list, (int)list_len, idx);
641 }
642
643 return list;
644 }
645
wsconn_put_list_ids(ws_connection_id_t * list_head)646 int wsconn_put_list_ids(ws_connection_id_t *list_head)
647 {
648 ws_connection_id_t *list = NULL;
649 int i;
650
651 LM_DBG("wsconn put list id [%p]\n", list_head);
652
653 if(!list_head)
654 return -1;
655
656 list = list_head;
657 for(i=0; list[i].id!=-1; i++) {
658 wsconn_put_id(list[i].id);
659 }
660
661 pkg_free(list_head);
662
663 return 0;
664 }
665
ws_timer(unsigned int ticks,void * param)666 void ws_timer(unsigned int ticks, void *param)
667 {
668 ws_connection_list_t rmlist;
669 ws_connection_t *wsc;
670 ws_connection_t *next;
671 ticks_t nticks;
672 int h;
673
674 rmlist.head = NULL;
675 nticks = get_ticks();
676
677 WSCONN_LOCK;
678 for(h = 0; h < TCP_ID_HASH_SIZE; h++) {
679 wsc = wsconn_id_hash[h];
680 while(wsc) {
681 next = wsc->id_next;
682 if(wsc->state == WS_S_REMOVING
683 && wsc->rmticks <= nticks - ws_rm_delay_interval) {
684 wsconn_detach_connection(wsc);
685 wsc->id_next = rmlist.head;
686 rmlist.head = wsc;
687 }
688 wsc = next;
689 }
690 }
691 WSCONN_UNLOCK;
692
693 for(wsc = rmlist.head; wsc; ) {
694 next = wsc->id_next;
695 wsconn_dtor(wsc);
696 wsc = next;
697 }
698 }
699
ws_rpc_add_node(rpc_t * rpc,void * ctx,void * ih,ws_connection_t * wsc)700 static int ws_rpc_add_node(
701 rpc_t *rpc, void *ctx, void *ih, ws_connection_t *wsc)
702 {
703 int interval;
704 char *src_proto, *dst_proto, *pong, *sub_protocol;
705 char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1];
706 struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0);
707 char rplbuf[512];
708
709 if(con) {
710 src_proto = (con->rcv.proto == PROTO_WS) ? "ws" : "wss";
711 memset(src_ip, 0, IP6_MAX_STR_SIZE + 1);
712 ip_addr2sbuf(&con->rcv.src_ip, src_ip, IP6_MAX_STR_SIZE);
713
714 dst_proto = (con->rcv.proto == PROTO_WS) ? "ws" : "wss";
715 memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1);
716 ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, IP6_MAX_STR_SIZE);
717
718 pong = wsc->awaiting_pong ? "awaiting Pong, " : "";
719
720 interval = (int)time(NULL) - wsc->last_used;
721 if(wsc->sub_protocol == SUB_PROTOCOL_SIP)
722 sub_protocol = "sip";
723 else if(wsc->sub_protocol == SUB_PROTOCOL_MSRP)
724 sub_protocol = "msrp";
725 else
726 sub_protocol = "**UNKNOWN**";
727
728 if(snprintf(rplbuf, 512, "%d: %s:%s:%hu -> %s:%s:%hu (state: %s"
729 ", %s last used %ds ago"
730 ", sub-protocol: %s)",
731 wsc->id, src_proto, strlen(src_ip) ? src_ip : "*",
732 con->rcv.src_port, dst_proto, strlen(dst_ip) ? dst_ip : "*",
733 con->rcv.dst_port, wsconn_state_str[wsc->state], pong,
734 interval, sub_protocol)
735 < 0) {
736 tcpconn_put(con);
737 rpc->fault(ctx, 500, "Failed to print connection details");
738 return -1;
739 }
740 if(rpc->array_add(ih, "s", rplbuf) < 0) {
741 tcpconn_put(con);
742 rpc->fault(ctx, 500, "Failed to add to response");
743 return -1;
744 }
745
746 tcpconn_put(con);
747 return 1;
748 } else
749 return 0;
750 }
751
ws_rpc_dump(rpc_t * rpc,void * ctx)752 void ws_rpc_dump(rpc_t *rpc, void *ctx)
753 {
754 int h, connections = 0, truncated = 0, order = 0, found = 0;
755 ws_connection_t *wsc;
756 str sorder = {0};
757 void *th;
758 void *ih;
759 void *dh;
760
761 if(rpc->scan(ctx, "*S", &sorder) == 1) {
762 if(sorder.len == 7 && strncasecmp(sorder.s, "id_hash", 7) == 0) {
763 order = 0;
764 } else if(sorder.len == 9
765 && strncasecmp(sorder.s, "used_desc", 9) == 0) {
766 order = 1;
767 } else if(sorder.len == 8
768 && strncasecmp(sorder.s, "used_asc", 8) == 0) {
769 order = 2;
770 } else {
771 LM_WARN("bad display order parameter\n");
772 rpc->fault(ctx, 400, str_status_bad_param.s);
773 return;
774 }
775 }
776
777 /* add root node */
778 if(rpc->add(ctx, "{", &th) < 0) {
779 rpc->fault(ctx, 500, "Internal error root reply");
780 return;
781 }
782 if(rpc->struct_add(th, "[", "connections", &ih) < 0) {
783 rpc->fault(ctx, 500, "Internal error connections structure");
784 return;
785 }
786
787 WSCONN_LOCK;
788 if(order == 0) {
789 for(h = 0; h < TCP_ID_HASH_SIZE; h++) {
790 wsc = wsconn_id_hash[h];
791 while(wsc) {
792 if((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) {
793 WSCONN_UNLOCK;
794 return;
795 }
796
797
798 connections += found;
799 if(connections >= MAX_WS_CONNS_DUMP) {
800 truncated = 1;
801 break;
802 }
803
804 wsc = wsc->id_next;
805 }
806
807 if(truncated == 1)
808 break;
809 }
810 } else if(order == 1) {
811 wsc = wsconn_used_list->head;
812 while(wsc) {
813 if((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) {
814 WSCONN_UNLOCK;
815 return;
816 }
817
818 connections += found;
819 if(connections >= MAX_WS_CONNS_DUMP) {
820 truncated = 1;
821 break;
822 }
823
824 wsc = wsc->used_next;
825 }
826 } else {
827 wsc = wsconn_used_list->tail;
828 while(wsc) {
829 if((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) {
830 WSCONN_UNLOCK;
831 return;
832 }
833
834 connections += found;
835 if(connections >= MAX_WS_CONNS_DUMP) {
836 truncated = 1;
837 break;
838 }
839
840 wsc = wsc->used_prev;
841 }
842 }
843 WSCONN_UNLOCK;
844
845 if(rpc->struct_add(th, "{", "info", &dh) < 0) {
846 rpc->fault(ctx, 500, "Internal error info structure");
847 return;
848 }
849 if(rpc->struct_add(dh, "ds", "wscounter", connections, "truncated",
850 (truncated == 1) ? "yes" : "no")
851 < 0) {
852 rpc->fault(ctx, 500, "Internal error adding info structure");
853 return;
854 }
855
856 return;
857 }
858