diff --git a/src/modules/websocket/websocket.c b/src/modules/websocket/websocket.c index a1a23cf8463..abfd1276bef 100644 --- a/src/modules/websocket/websocket.c +++ b/src/modules/websocket/websocket.c @@ -362,6 +362,11 @@ static int pv_get_ws_conid_f(struct sip_msg *msg, pv_param_t *param, return pv_get_sintval(msg, param, res, msg->rcv.proto_reserved1); } +static const char* ws_rpc_dump_doc[2] = { + "List websocket connections", + 0 +}; + static const char* ws_rpc_close_doc[2] = { "Close a websocket connection by id", 0 @@ -378,6 +383,7 @@ static const char* ws_rpc_pong_doc[2] = { }; rpc_export_t ws_rpc_cmds[] = { + {"ws.dump", ws_rpc_dump, ws_rpc_dump_doc, 0}, {"ws.close", ws_rpc_close, ws_rpc_close_doc, 0}, {"ws.ping", ws_rpc_ping, ws_rpc_ping_doc, 0}, {"ws.pong", ws_rpc_pong, ws_rpc_pong_doc, 0}, diff --git a/src/modules/websocket/ws_conn.c b/src/modules/websocket/ws_conn.c index 1e06cef07c6..2ba205bad76 100644 --- a/src/modules/websocket/ws_conn.c +++ b/src/modules/websocket/ws_conn.c @@ -709,3 +709,186 @@ struct mi_root *ws_mi_dump(struct mi_root *cmd, void *param) return rpl_tree; } + + +static int ws_rpc_add_node(rpc_t* rpc, void* ctx, void* ih, ws_connection_t *wsc) +{ + int interval; + char *src_proto, *dst_proto, *pong, *sub_protocol; + char src_ip[IP6_MAX_STR_SIZE + 1], dst_ip[IP6_MAX_STR_SIZE + 1]; + struct tcp_connection *con = tcpconn_get(wsc->id, 0, 0, 0, 0); + char rplbuf[512]; + + if (con) + { + src_proto = (con->rcv.proto== PROTO_WS) ? "ws" : "wss"; + memset(src_ip, 0, IP6_MAX_STR_SIZE + 1); + ip_addr2sbuf(&con->rcv.src_ip, src_ip, IP6_MAX_STR_SIZE); + + dst_proto = (con->rcv.proto == PROTO_WS) ? "ws" : "wss"; + memset(dst_ip, 0, IP6_MAX_STR_SIZE + 1); + ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, IP6_MAX_STR_SIZE); + + pong = wsc->awaiting_pong ? "awaiting Pong, " : ""; + + interval = (int)time(NULL) - wsc->last_used; + if (wsc->sub_protocol == SUB_PROTOCOL_SIP) + sub_protocol = "sip"; + else if (wsc->sub_protocol == SUB_PROTOCOL_MSRP) + sub_protocol = "msrp"; + else + sub_protocol = "**UNKNOWN**"; + + if (snprintf(rplbuf, 512, + "%d: %s:%s:%hu -> %s:%s:%hu (state: %s" + ", %s last used %ds ago" + ", sub-protocol: %s)", + wsc->id, + src_proto, + strlen(src_ip) ? src_ip : "*", + con->rcv.src_port, + dst_proto, + strlen(dst_ip) ? dst_ip : "*", + con->rcv.dst_port, + wsconn_state_str[wsc->state], + pong, + interval, + sub_protocol) < 0) { + tcpconn_put(con); + rpc->fault(ctx, 500, "Failed to print connection details"); + return -1; + } + if (rpc->array_add(ih, "s", rplbuf)<0) + { + tcpconn_put(con); + rpc->fault(ctx, 500, "Failed to add to response"); + return -1; + } + + tcpconn_put(con); + return 1; + } + else + return 0; +} + +void ws_rpc_dump(rpc_t* rpc, void* ctx) +{ + int h, connections = 0, truncated = 0, order = 0, found = 0; + ws_connection_t *wsc; + str sorder = {0}; + void* th; + void* ih; + void* dh; + + if(rpc->scan(ctx, "*S", &sorder)==1) + { + if (sorder.len == 7 && strncasecmp(sorder.s, "id_hash", 7) == 0) { + order = 0; + } else if (sorder.len == 9 && strncasecmp(sorder.s, "used_desc", 9) == 0) { + order = 1; + } else if (sorder.len == 8 && strncasecmp(sorder.s, "used_asc", 8) == 0) { + order = 2; + } else { + LM_WARN("bad display order parameter\n"); + rpc->fault(ctx, 400, str_status_bad_param.s); + return; + } + } + + /* add root node */ + if (rpc->add(ctx, "{", &th) < 0) + { + rpc->fault(ctx, 500, "Internal error root reply"); + return; + } + if(rpc->struct_add(th, "[{", + "connections", "info", &ih, &dh)<0) + { + rpc->fault(ctx, 500, "Internal error connections structure"); + return; + } + + WSCONN_LOCK; + if (order == 0) + { + for (h = 0; h < TCP_ID_HASH_SIZE; h++) + { + wsc = wsconn_id_hash[h]; + while(wsc) + { + if ((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) + { + WSCONN_UNLOCK; + return; + } + + + connections += found; + if (connections >= MAX_WS_CONNS_DUMP) + { + truncated = 1; + break; + } + + wsc = wsc->id_next; + } + + if (truncated == 1) + break; + } + } + else if (order == 1) + { + wsc = wsconn_used_list->head; + while (wsc) + { + if ((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) + { + WSCONN_UNLOCK; + return; + } + + connections += found; + if (connections >= MAX_WS_CONNS_DUMP) + { + truncated = 1; + break; + } + + wsc = wsc->used_next; + } + } + else + { + wsc = wsconn_used_list->tail; + while (wsc) + { + if ((found = ws_rpc_add_node(rpc, ctx, ih, wsc)) < 0) + { + WSCONN_UNLOCK; + return; + } + + connections += found; + if (connections >= MAX_WS_CONNS_DUMP) + { + truncated = 1; + break; + } + + wsc = wsc->used_prev; + } + } + WSCONN_UNLOCK; + + if(rpc->struct_add(dh, "ds", + "wscounter", "truncated", + connections, (truncated==1)?"yes":"no")<0) + { + rpc->fault(ctx, 500, "Internal error adding info structure"); + return; + } + + return; +} diff --git a/src/modules/websocket/ws_conn.h b/src/modules/websocket/ws_conn.h index f66a97dad99..05f659acb0d 100644 --- a/src/modules/websocket/ws_conn.h +++ b/src/modules/websocket/ws_conn.h @@ -99,4 +99,5 @@ int wsconn_put(ws_connection_t *wsc); ws_connection_t **wsconn_get_list(void); int wsconn_put_list(ws_connection_t **list); struct mi_root *ws_mi_dump(struct mi_root *cmd, void *param); +void ws_rpc_dump(rpc_t* rpc, void* ctx); #endif /* _WS_CONN_H */