1 /*
2 * Copyright (c) 2004-2006 Maxim Sobolev <sobomax@FreeBSD.org>
3 * Copyright (c) 2006-2014 Sippy Software, Inc., http://www.sippysoft.com
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #if defined(HAVE_CONFIG_H)
30 #include "config.h"
31 #endif
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <errno.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "rtpp_debug.h"
44 #include "rtpp_log.h"
45 #include "rtpp_cfg_stable.h"
46 #include "rtpp_defines.h"
47 #include "rtpp_types.h"
48 #include "rtpp_log_obj.h"
49 #include "rtpp_refcnt.h"
50 #include "rtpp_command.h"
51 #include "rtpp_command_async.h"
52 #include "rtpp_command_copy.h"
53 #include "rtpp_command_delete.h"
54 #include "rtpp_command_parse.h"
55 #include "rtpp_command_private.h"
56 #include "rtpp_command_record.h"
57 #include "rtpp_command_rcache.h"
58 #include "rtpp_command_query.h"
59 #include "rtpp_command_stats.h"
60 #include "rtpp_command_ul.h"
61 #include "rtpp_hash_table.h"
62 #include "rtpp_mallocs.h"
63 #include "rtpp_netio_async.h"
64 #include "rtpp_network.h"
65 #include "rtpp_tnotify_set.h"
66 #include "rtpp_pipe.h"
67 #include "rtpp_port_table.h"
68 #include "rtpp_stream.h"
69 #include "rtpp_session.h"
70 #include "rtpp_socket.h"
71 #include "rtpp_util.h"
72 #include "rtpp_stats.h"
73 #include "rtpp_weakref.h"
74
75 struct proto_cap proto_caps[] = {
76 /*
77 * The first entry must be basic protocol version and isn't shown
78 * as extension on -v.
79 */
80 { "20040107", "Basic RTP proxy functionality" },
81 { "20050322", "Support for multiple RTP streams and MOH" },
82 { "20060704", "Support for extra parameter in the V command" },
83 { "20071116", "Support for RTP re-packetization" },
84 { "20071218", "Support for forking (copying) RTP stream" },
85 { "20080403", "Support for RTP statistics querying" },
86 { "20081102", "Support for setting codecs in the update/lookup command" },
87 { "20081224", "Support for session timeout notifications" },
88 { "20090810", "Support for automatic bridging" },
89 { "20140323", "Support for tracking/reporting load" },
90 { "20140617", "Support for anchoring session connect time" },
91 { "20141004", "Support for extendable performance counters" },
92 { "20150330", "Support for allocating a new port (\"Un\"/\"Ln\" commands)" },
93 { "20150420", "Support for SEQ tracking and new rtpa_ counters; Q command extended" },
94 { "20150617", "Support for the wildcard %%CC_SELF%% as a disconnect notify target" },
95 { NULL, NULL }
96 };
97
98 struct rtpp_command_priv {
99 struct rtpp_command pub;
100 struct rtpp_cfg_stable *cfs;
101 int controlfd;
102 char *cookie;
103 int umode;
104 char buf_r[256];
105 struct rtpp_cmd_rcache *rcache_obj;
106 };
107
108 #define PUB2PVT(pubp) \
109 ((struct rtpp_command_priv *)((char *)(pubp) - offsetof(struct rtpp_command_priv, pub)))
110
111 struct d_opts;
112
113 static int create_twinlistener(unsigned int, void *);
114 static void handle_info(struct cfg *, struct rtpp_command *,
115 const char *);
116 static void handle_ver_feature(struct cfg *cf, struct rtpp_command *cmd);
117
118 struct create_twinlistener_args {
119 struct rtpp_cfg_stable *cfs;
120 struct sockaddr *ia;
121 struct rtpp_socket **fds;
122 int *port;
123 };
124
125 static int
create_twinlistener(unsigned int port,void * ap)126 create_twinlistener(unsigned int port, void *ap)
127 {
128 struct sockaddr_storage iac;
129 int rval, i, so_rcvbuf;
130 struct create_twinlistener_args *ctap;
131
132 RTPP_DBG_ASSERT(port >= 1 && IS_VALID_PORT(port - 1));
133
134 ctap = (struct create_twinlistener_args *)ap;
135
136 ctap->fds[0] = ctap->fds[1] = NULL;
137
138 rval = RTPP_PTU_BRKERR;
139 for (i = 0; i < 2; i++) {
140 ctap->fds[i] = rtpp_socket_ctor(ctap->ia->sa_family, SOCK_DGRAM);
141 if (ctap->fds[i] == NULL) {
142 RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "can't create %s socket",
143 SA_AF2STR(ctap->ia));
144 goto failure;
145 }
146 memcpy(&iac, ctap->ia, SA_LEN(ctap->ia));
147 satosin(&iac)->sin_port = htons(port);
148 if (CALL_METHOD(ctap->fds[i], bind, sstosa(&iac), SA_LEN(ctap->ia)) != 0) {
149 if (errno != EADDRINUSE && errno != EACCES) {
150 RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "can't bind to the %s port %d",
151 SA_AF2STR(ctap->ia), port);
152 } else {
153 rval = RTPP_PTU_ONEMORE;
154 }
155 goto failure;
156 }
157 port++;
158 if ((ctap->ia->sa_family == AF_INET) && (ctap->cfs->tos >= 0) &&
159 (CALL_METHOD(ctap->fds[i], settos, ctap->cfs->tos) == -1))
160 RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "unable to set TOS to %d", ctap->cfs->tos);
161 so_rcvbuf = 256 * 1024;
162 if (CALL_METHOD(ctap->fds[i], setrbuf, so_rcvbuf) == -1)
163 RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "unable to set 256K receive buffer size");
164 CALL_METHOD(ctap->fds[i], setnonblock);
165 CALL_METHOD(ctap->fds[i], settimestamp);
166 }
167 RTPP_DBG_ASSERT(port > 2 && IS_VALID_PORT(port - 2));
168 *ctap->port = port - 2;
169 return RTPP_PTU_OK;
170
171 failure:
172 for (i = 0; i < 2; i++)
173 if (ctap->fds[i] != NULL) {
174 CALL_SMETHOD(ctap->fds[i]->rcnt, decref);
175 ctap->fds[i] = NULL;
176 }
177 return rval;
178 }
179
180 int
rtpp_create_listener(struct cfg * cf,struct sockaddr * ia,int * port,struct rtpp_socket ** fds)181 rtpp_create_listener(struct cfg *cf, struct sockaddr *ia, int *port,
182 struct rtpp_socket **fds)
183 {
184 struct create_twinlistener_args cta;
185 int i;
186 struct rtpp_port_table *rpp;
187
188 memset(&cta, '\0', sizeof(cta));
189 cta.cfs = cf->stable;
190 cta.fds = fds;
191 cta.ia = ia;
192 cta.port = port;
193
194 for (i = 0; i < 2; i++)
195 fds[i] = NULL;
196
197 rpp = RTPP_PT_SELECT(cf->stable, ia->sa_family);
198 return (CALL_METHOD(rpp, get_port, create_twinlistener,
199 &cta));
200 }
201
202 void
rtpc_doreply(struct rtpp_command * cmd,char * buf,int len,int errd)203 rtpc_doreply(struct rtpp_command *cmd, char *buf, int len, int errd)
204 {
205 struct rtpp_command_priv *pvt;
206
207 pvt = PUB2PVT(cmd);
208
209 buf[len] = '\0';
210 if (len > 0 && buf[len - 1] == '\n') {
211 RTPP_LOG(pvt->cfs->glog, RTPP_LOG_DBUG, "sending reply \"%.*s\\n\"",
212 len - 1, buf);
213 } else {
214 RTPP_LOG(pvt->cfs->glog, RTPP_LOG_DBUG, "sending reply \"%s\"", buf);
215 }
216 if (pvt->umode == 0) {
217 write(pvt->controlfd, buf, len);
218 } else {
219 if (pvt->cookie != NULL) {
220 len = snprintf(pvt->buf_r, sizeof(pvt->buf_r), "%s %s", pvt->cookie,
221 buf);
222 buf = pvt->buf_r;
223 CALL_METHOD(pvt->rcache_obj, insert, pvt->cookie, pvt->buf_r, cmd->dtime);
224 }
225 rtpp_anetio_sendto(pvt->cfs->rtpp_netio_cf, pvt->controlfd, buf, len, 0,
226 sstosa(&cmd->raddr), cmd->rlen);
227 }
228 cmd->csp->ncmds_repld.cnt++;
229 if (errd == 0) {
230 cmd->csp->ncmds_succd.cnt++;
231 } else {
232 cmd->csp->ncmds_errs.cnt++;
233 }
234 }
235
236 static void
reply_number(struct rtpp_command * cmd,int number)237 reply_number(struct rtpp_command *cmd, int number)
238 {
239 int len;
240
241 len = snprintf(cmd->buf_t, sizeof(cmd->buf_t), "%d\n", number);
242 rtpc_doreply(cmd, cmd->buf_t, len, 0);
243 }
244
245 static void
reply_ok(struct rtpp_command * cmd)246 reply_ok(struct rtpp_command *cmd)
247 {
248
249 reply_number(cmd, 0);
250 }
251
252 void
reply_error(struct rtpp_command * cmd,int ecode)253 reply_error(struct rtpp_command *cmd,
254 int ecode)
255 {
256 int len;
257
258 len = snprintf(cmd->buf_t, sizeof(cmd->buf_t), "E%d\n", ecode);
259 rtpc_doreply(cmd, cmd->buf_t, len, 1);
260 }
261
262 void
free_command(struct rtpp_command * cmd)263 free_command(struct rtpp_command *cmd)
264 {
265 struct rtpp_command_priv *pvt;
266
267 pvt = PUB2PVT(cmd);
268 if (pvt->rcache_obj != NULL) {
269 CALL_SMETHOD(pvt->rcache_obj->rcnt, decref);
270 }
271 if (cmd->sp != NULL) {
272 CALL_SMETHOD(cmd->sp->rcnt, decref);
273 }
274 free(pvt);
275 }
276
277 struct rtpp_command *
rtpp_command_ctor(struct cfg * cf,int controlfd,double dtime,int * rval,struct rtpp_command_stats * csp,int umode)278 rtpp_command_ctor(struct cfg *cf, int controlfd, double dtime, int *rval,
279 struct rtpp_command_stats *csp, int umode)
280 {
281 struct rtpp_command_priv *pvt;
282 struct rtpp_command *cmd;
283
284 pvt = rtpp_zmalloc(sizeof(struct rtpp_command_priv));
285 if (pvt == NULL) {
286 *rval = ENOMEM;
287 return (NULL);
288 }
289 cmd = &(pvt->pub);
290 pvt->controlfd = controlfd;
291 pvt->cfs = cf->stable;
292 cmd->dtime = dtime;
293 cmd->csp = csp;
294 pvt->umode = umode;
295 return (cmd);
296 }
297
298 struct rtpp_command *
get_command(struct cfg * cf,int controlfd,int * rval,double dtime,struct rtpp_command_stats * csp,int umode,struct rtpp_cmd_rcache * rcache_obj)299 get_command(struct cfg *cf, int controlfd, int *rval, double dtime,
300 struct rtpp_command_stats *csp, int umode,
301 struct rtpp_cmd_rcache *rcache_obj)
302 {
303 char **ap;
304 char *cp;
305 int len, i;
306 struct rtpp_command *cmd;
307 struct rtpp_command_priv *pvt;
308
309 cmd = rtpp_command_ctor(cf, controlfd, dtime, rval, csp, umode);
310 if (cmd == NULL) {
311 return (NULL);
312 }
313 pvt = PUB2PVT(cmd);
314 if (umode == 0) {
315 for (;;) {
316 len = read(controlfd, cmd->buf, sizeof(cmd->buf) - 1);
317 if (len != -1 || (errno != EAGAIN && errno != EINTR))
318 break;
319 }
320 } else {
321 cmd->rlen = sizeof(cmd->raddr);
322 len = recvfrom(controlfd, cmd->buf, sizeof(cmd->buf) - 1, 0,
323 sstosa(&cmd->raddr), &cmd->rlen);
324 }
325 if (len == -1) {
326 if (errno != EAGAIN && errno != EINTR)
327 RTPP_ELOG(cf->stable->glog, RTPP_LOG_ERR, "can't read from control socket");
328 free_command(cmd);
329 *rval = -1;
330 return (NULL);
331 }
332 cmd->buf[len] = '\0';
333
334 if (len > 0 && cmd->buf[len - 1] == '\n') {
335 RTPP_LOG(cf->stable->glog, RTPP_LOG_DBUG, "received command \"%.*s\\n\"",
336 len - 1, cmd->buf);
337 } else {
338 RTPP_LOG(cf->stable->glog, RTPP_LOG_DBUG, "received command \"%s\"",
339 cmd->buf);
340 }
341 csp->ncmds_rcvd.cnt++;
342
343 cp = cmd->buf;
344 for (ap = cmd->argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) {
345 if (**ap != '\0') {
346 cmd->argc++;
347 if (++ap >= &cmd->argv[RTPC_MAX_ARGC])
348 break;
349 }
350 }
351 if (cmd->argc < 1 || (umode != 0 && cmd->argc < 2)) {
352 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error");
353 reply_error(cmd, ECODE_PARSE_1);
354 *rval = 0;
355 free_command(cmd);
356 return (NULL);
357 }
358
359 /* Stream communication mode doesn't use cookie */
360 if (umode != 0) {
361 pvt->cookie = cmd->argv[0];
362 if (CALL_METHOD(rcache_obj, lookup, pvt->cookie, pvt->buf_r, sizeof(pvt->buf_r)) == 1) {
363 len = strlen(pvt->buf_r);
364 rtpp_anetio_sendto(cf->stable->rtpp_netio_cf, controlfd, pvt->buf_r, len, 0,
365 sstosa(&cmd->raddr), cmd->rlen);
366 csp->ncmds_rcvd.cnt--;
367 csp->ncmds_rcvd_ndups.cnt++;
368 *rval = 0;
369 free_command(cmd);
370 return (NULL);
371 }
372 CALL_SMETHOD(rcache_obj->rcnt, incref);
373 pvt->rcache_obj = rcache_obj;
374 for (i = 1; i < cmd->argc; i++)
375 cmd->argv[i - 1] = cmd->argv[i];
376 cmd->argc--;
377 cmd->argv[cmd->argc] = NULL;
378 }
379
380 /* Step I: parse parameters that are common to all ops */
381 if (rtpp_command_pre_parse(cf, cmd) != 0) {
382 /* Error reply is handled by the rtpp_command_pre_parse() */
383 *rval = 0;
384 free_command(cmd);
385 return (NULL);
386 }
387
388 return (cmd);
389 }
390
391 struct d_opts {
392 int weak;
393 };
394
395 int
handle_command(struct cfg * cf,struct rtpp_command * cmd)396 handle_command(struct cfg *cf, struct rtpp_command *cmd)
397 {
398 int i, verbose, rval;
399 int playcount, ptime;
400 char *cp, *tcp;
401 char *pname, *codecs, *recording_name;
402 struct rtpp_session *spa;
403 int record_single_file;
404 struct ul_opts *ulop;
405 struct d_opts dopt;
406
407 spa = NULL;
408 recording_name = NULL;
409 codecs = NULL;
410
411 /* Step II: parse parameters that are specific to a particular op and run simple ops */
412 switch (cmd->cca.op) {
413 case VER_FEATURE:
414 handle_ver_feature(cf, cmd);
415 return 0;
416
417 case GET_VER:
418 /* This returns base version. */
419 reply_number(cmd, CPROTOVER);
420 return 0;
421
422 case DELETE_ALL:
423 /* Delete all active sessions */
424 RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO, "deleting all active sessions");
425 CALL_METHOD(cf->stable->sessions_wrt, purge);
426 CALL_METHOD(cf->stable->sessions_ht, purge);
427 reply_ok(cmd);
428 return 0;
429
430 case INFO:
431 handle_info(cf, cmd, &cmd->argv[0][1]);
432 return 0;
433
434 case PLAY:
435 /*
436 * P callid pname codecs from_tag to_tag
437 *
438 * <codecs> could be either comma-separated list of supported
439 * payload types or word "session" (without quotes), in which
440 * case list saved on last session update will be used instead.
441 */
442 playcount = 1;
443 pname = cmd->argv[2];
444 codecs = cmd->argv[3];
445 tcp = &(cmd->argv[0][1]);
446 if (*tcp != '\0') {
447 playcount = strtol(tcp, &cp, 10);
448 if (cp == tcp || *cp != '\0') {
449 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error");
450 reply_error(cmd, ECODE_PARSE_6);
451 return 0;
452 }
453 }
454 break;
455
456 case COPY:
457 recording_name = cmd->argv[2];
458 /* Fallthrough */
459 case RECORD:
460 if (cmd->argv[0][1] == 'S' || cmd->argv[0][1] == 's') {
461 if (cmd->argv[0][2] != '\0') {
462 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error");
463 reply_error(cmd, ECODE_PARSE_2);
464 return 0;
465 }
466 record_single_file = (cf->stable->record_pcap == 0) ? 0 : 1;
467 } else {
468 if (cmd->argv[0][1] != '\0') {
469 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error");
470 reply_error(cmd, ECODE_PARSE_3);
471 return 0;
472 }
473 record_single_file = 0;
474 }
475 break;
476
477 case DELETE:
478 /* D[w] call_id from_tag [to_tag] */
479 dopt.weak = 0;
480 for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) {
481 switch (*cp) {
482 case 'w':
483 case 'W':
484 dopt.weak = 1;
485 break;
486
487 default:
488 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR,
489 "DELETE: unknown command modifier `%c'", *cp);
490 reply_error(cmd, ECODE_PARSE_4);
491 return 0;
492 }
493 }
494 break;
495
496 case UPDATE:
497 case LOOKUP:
498 ulop = rtpp_command_ul_opts_parse(cf, cmd);
499 if (ulop == NULL) {
500 return 0;
501 }
502 break;
503
504 case GET_STATS:
505 verbose = 0;
506 for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) {
507 switch (*cp) {
508 case 'v':
509 case 'V':
510 verbose = 1;
511 break;
512
513 default:
514 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR,
515 "STATS: unknown command modifier `%c'", *cp);
516 reply_error(cmd, ECODE_PARSE_5);
517 return 0;
518 }
519 }
520 i = handle_get_stats(cf, cmd, verbose);
521 if (i != 0) {
522 reply_error(cmd, i);
523 }
524 return 0;
525
526 default:
527 break;
528 }
529
530 /*
531 * Record and delete need special handling since they apply to all
532 * streams in the session.
533 */
534 switch (cmd->cca.op) {
535 case DELETE:
536 i = handle_delete(cf, &cmd->cca, dopt.weak);
537 break;
538
539 case RECORD:
540 i = handle_record(cf, &cmd->cca, record_single_file);
541 break;
542
543 default:
544 i = find_stream(cf, cmd->cca.call_id, cmd->cca.from_tag,
545 cmd->cca.to_tag, &spa);
546 if (i != -1) {
547 if (cmd->cca.op != UPDATE)
548 i = NOT(i);
549 RTPP_DBG_ASSERT(cmd->sp == NULL);
550 cmd->sp = spa;
551 }
552 break;
553 }
554
555 if (i == -1 && cmd->cca.op != UPDATE) {
556 RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO,
557 "%s request failed: session %s, tags %s/%s not found", cmd->cca.rname,
558 cmd->cca.call_id, cmd->cca.from_tag, cmd->cca.to_tag != NULL ? cmd->cca.to_tag : "NONE");
559 if (cmd->cca.op == LOOKUP) {
560 rtpp_command_ul_opts_free(ulop);
561 ul_reply_port(cmd, NULL);
562 return 0;
563 }
564 reply_error(cmd, ECODE_SESUNKN);
565 return 0;
566 }
567
568 switch (cmd->cca.op) {
569 case DELETE:
570 case RECORD:
571 reply_ok(cmd);
572 break;
573
574 case NOPLAY:
575 CALL_SMETHOD(spa->rtp->stream[i], handle_noplay);
576 reply_ok(cmd);
577 break;
578
579 case PLAY:
580 CALL_SMETHOD(spa->rtp->stream[i], handle_noplay);
581 ptime = -1;
582 if (strcmp(codecs, "session") == 0) {
583 if (spa->rtp->stream[i]->codecs == NULL) {
584 reply_error(cmd, ECODE_INVLARG_5);
585 return 0;
586 }
587 codecs = spa->rtp->stream[i]->codecs;
588 ptime = spa->rtp->stream[i]->ptime;
589 }
590 if (playcount != 0 && CALL_SMETHOD(spa->rtp->stream[i], handle_play, codecs,
591 pname, playcount, cmd, ptime) != 0) {
592 reply_error(cmd, ECODE_PLRFAIL);
593 return 0;
594 }
595 reply_ok(cmd);
596 break;
597
598 case COPY:
599 if (handle_copy(cf, spa, i, recording_name, record_single_file) != 0) {
600 reply_error(cmd, ECODE_CPYFAIL);
601 return 0;
602 }
603 reply_ok(cmd);
604 break;
605
606 case QUERY:
607 rval = handle_query(cf, cmd, spa->rtp, i);
608 if (rval != 0) {
609 reply_error(cmd, rval);
610 }
611 break;
612
613 case LOOKUP:
614 case UPDATE:
615 rtpp_command_ul_handle(cf, cmd, ulop, i);
616 break;
617
618 default:
619 /* Programmatic error, should not happen */
620 abort();
621 }
622
623 return 0;
624 }
625
626 static void
handle_info(struct cfg * cf,struct rtpp_command * cmd,const char * opts)627 handle_info(struct cfg *cf, struct rtpp_command *cmd,
628 const char *opts)
629 {
630 #if 0
631 struct rtpp_session *spa, *spb;
632 char addrs[4][256];
633 int brief;
634 #endif
635 int len, i, load;
636 char buf[1024 * 8];
637 unsigned long long packets_in, packets_out;
638 unsigned long long sessions_created;
639 int sessions_active, rtp_streams_active;
640
641 #if 0
642 brief = 0;
643 #endif
644 load = 0;
645 for (i = 0; opts[i] != '\0'; i++) {
646 switch (opts[i]) {
647 case 'b':
648 case 'B':
649 #if 0
650 brief = 1;
651 #endif
652 break;
653
654 case 'l':
655 case 'L':
656 load = 1;
657 break;
658
659 default:
660 RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error");
661 reply_error(cmd, ECODE_PARSE_7);
662 return;
663 }
664 }
665
666 packets_in = CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_rcvd");
667 packets_out = CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_relayed") +
668 CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_played");
669 sessions_created = CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname,
670 "nsess_created");
671 sessions_active = sessions_created - CALL_METHOD(cf->stable->rtpp_stats,
672 getlvalbyname, "nsess_destroyed");
673 rtp_streams_active = CALL_METHOD(cf->stable->rtp_streams_wrt, get_length);
674 len = snprintf(buf, sizeof(buf), "sessions created: %llu\nactive sessions: %d\n"
675 "active streams: %d\npackets received: %llu\npackets transmitted: %llu\n",
676 sessions_created, sessions_active, rtp_streams_active, packets_in, packets_out);
677 if (load != 0) {
678 len += snprintf(buf + len, sizeof(buf) - len, "average load: %f\n",
679 CALL_METHOD(cf->stable->rtpp_cmd_cf, get_aload));
680 }
681 #if 0
682 XXX this needs work to fix it after rtp/rtcp split
683 for (i = 0; i < cf->sessinfo->nsessions && brief == 0; i++) {
684 spa = cf->sessinfo->sessions[i];
685 if (spa == NULL || spa->stream[0]->sidx != i)
686 continue;
687 /* RTCP twin session */
688 if (spa->rtcp == NULL) {
689 spb = spa->rtp;
690 buf[len++] = '\t';
691 } else {
692 spb = spa->rtcp;
693 buf[len++] = '\t';
694 buf[len++] = 'C';
695 buf[len++] = ' ';
696 }
697
698 addr2char_r(spb->laddr[1], addrs[0], sizeof(addrs[0]));
699 if (spb->addr[1] == NULL) {
700 strcpy(addrs[1], "NONE");
701 } else {
702 sprintf(addrs[1], "%s:%d", addr2char(spb->addr[1]),
703 addr2port(spb->addr[1]));
704 }
705 addr2char_r(spb->laddr[0], addrs[2], sizeof(addrs[2]));
706 if (spb->addr[0] == NULL) {
707 strcpy(addrs[3], "NONE");
708 } else {
709 sprintf(addrs[3], "%s:%d", addr2char(spb->addr[0]),
710 addr2port(spb->addr[0]));
711 }
712
713 len += snprintf(buf + len, sizeof(buf) - len,
714 "%s/%s: caller = %s:%d/%s, callee = %s:%d/%s, "
715 "stats = %lu/%lu/%lu/%lu, ttl = %d/%d\n",
716 spb->call_id, spb->tag, addrs[0], spb->stream[1]->port, addrs[1],
717 addrs[2], spb->stream[0]->port, addrs[3], spa->pcount[0], spa->pcount[1],
718 spa->pcount[2], spa->pcount[3], spb->ttl[0], spb->ttl[1]);
719 if (len + 512 > sizeof(buf)) {
720 rtpc_doreply(cmd, buf, len);
721 len = 0;
722 }
723 }
724 #endif
725 if (len > 0) {
726 rtpc_doreply(cmd, buf, len, 0);
727 }
728 }
729
730 static void
handle_ver_feature(struct cfg * cf,struct rtpp_command * cmd)731 handle_ver_feature(struct cfg *cf, struct rtpp_command *cmd)
732 {
733 int i, known;
734
735 /*
736 * Wait for protocol version datestamp and check whether we
737 * know it.
738 */
739 /*
740 * Only list 20081224 protocol mod as supported if
741 * user actually enabled notification with -n
742 */
743 if (strcmp(cmd->argv[1], "20081224") == 0 &&
744 !CALL_METHOD(cf->stable->rtpp_tnset_cf, isenabled)) {
745 reply_number(cmd, 0);
746 return;
747 }
748 for (known = i = 0; proto_caps[i].pc_id != NULL; ++i) {
749 if (!strcmp(cmd->argv[1], proto_caps[i].pc_id)) {
750 known = 1;
751 break;
752 }
753 }
754 reply_number(cmd, known);
755 }
756