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