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