Coverage Report

Created: 2025-08-29 06:41

/src/rtpproxy/src/rtpp_command.c
Line
Count
Source (jump to first uncovered line)
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 <assert.h>
37
#include <errno.h>
38
#include <stddef.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <unistd.h>
43
44
#include "rtpp_debug.h"
45
#include "rtpp_log.h"
46
#include "rtpp_cfg.h"
47
#include "rtpp_defines.h"
48
#include "rtpp_types.h"
49
#include "rtpp_log_obj.h"
50
#include "rtpp_codeptr.h"
51
#include "rtpp_refcnt.h"
52
#include "rtpp_time.h"
53
#include "rtpp_command.h"
54
#include "rtpp_command_async.h"
55
#include "commands/rpcpv1_copy.h"
56
#include "commands/rpcpv1_delete.h"
57
#include "rtpp_command_parse.h"
58
#include "commands/rpcpv1_play.h"
59
#include "rtpp_command_ecodes.h"
60
#include "rtpp_command_args.h"
61
#include "rtpp_command_sub.h"
62
#include "rtpp_command_private.h"
63
#include "commands/rpcpv1_record.h"
64
#include "commands/rpcpv1_norecord.h"
65
#include "rtpp_command_rcache.h"
66
#include "commands/rpcpv1_query.h"
67
#include "commands/rpcpv1_stats.h"
68
#include "commands/rpcpv1_ul.h"
69
#include "commands/rpcpv1_ul_subc.h"
70
#include "commands/rpcpv1_ver.h"
71
#include "rtpp_command_reply.h"
72
#include "rtpp_controlfd.h"
73
#include "rtpp_hash_table.h"
74
#include "rtpp_mallocs.h"
75
#include "rtpp_netio_async.h"
76
#include "rtpp_network.h"
77
#include "rtpp_pipe.h"
78
#include "rtpp_port_table.h"
79
#include "rtpp_stream.h"
80
#include "rtpp_session.h"
81
#include "rtpp_socket.h"
82
#include "rtpp_util.h"
83
#include "rtpp_stats.h"
84
#include "rtpp_weakref.h"
85
#include "rtpp_proc_async.h"
86
#include "rtpp_command_ctx.h"
87
#include "rtpp_command_reply.h"
88
#include "rtpp_command_stats.h"
89
90
struct rtpp_command_priv {
91
    struct rtpp_command pub;
92
    struct rtpp_command_ctx ctx;
93
};
94
95
struct d_opts;
96
97
static void handle_info(const struct rtpp_cfg *, struct rtpp_command *);
98
99
struct create_listener_args {
100
    const struct rtpp_cfg *cfs;
101
    const struct sockaddr *ia;
102
    struct rtpp_socket **fds;
103
    int *port;
104
};
105
106
static enum rtpp_ptu_rval
107
create_listener(struct create_listener_args *ctap, unsigned int port, struct rtpp_socket **fdp)
108
0
{
109
0
    struct sockaddr_storage iac;
110
0
    struct rtpp_socket *fd;
111
0
    int so_rcvbuf;
112
0
    enum rtpp_ptu_rval rval = RTPP_PTU_BRKERR;
113
114
0
    fd = rtpp_socket_ctor(ctap->cfs->rtpp_proc_cf->netio,
115
0
        ctap->ia->sa_family, SOCK_DGRAM);
116
0
    if (fd == NULL) {
117
0
        RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "can't create %s socket",
118
0
            SA_AF2STR(ctap->ia));
119
0
        goto e0;
120
0
    }
121
0
    memcpy(&iac, ctap->ia, SA_LEN(ctap->ia));
122
0
    satosin(&iac)->sin_port = htons(port);
123
0
    if (CALL_SMETHOD(fd, bind2, sstosa(&iac), SA_LEN(ctap->ia)) != 0) {
124
0
        if (errno != EADDRINUSE && errno != EACCES) {
125
0
            RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "can't bind to the %s port %d",
126
0
                SA_AF2STR(ctap->ia), port);
127
0
        } else {
128
0
            rval = RTPP_PTU_ONEMORE;
129
0
        }
130
0
        goto e1;
131
0
    }
132
0
    if ((ctap->ia->sa_family == AF_INET) && (ctap->cfs->tos >= 0) &&
133
0
      (CALL_SMETHOD(fd, settos, ctap->cfs->tos) == -1))
134
0
        RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "unable to set TOS to %d", ctap->cfs->tos);
135
0
    so_rcvbuf = 256 * 1024;
136
0
    if (CALL_SMETHOD(fd, setrbuf, so_rcvbuf) == -1)
137
0
        RTPP_ELOG(ctap->cfs->glog, RTPP_LOG_ERR, "unable to set 256K receive buffer size");
138
0
    if (CALL_SMETHOD(fd, setnonblock) < 0)
139
0
        goto e1;
140
0
    CALL_SMETHOD(fd, settimestamp);
141
0
    *fdp = fd;
142
0
    return (RTPP_PTU_OK);
143
0
e1:
144
0
    RTPP_OBJ_DECREF(fd);
145
0
e0:
146
0
    return rval;
147
0
}
148
149
static int
150
create_twinlistener(unsigned int port, void *ap)
151
0
{
152
0
    int rval, i;
153
0
    struct create_listener_args *ctap;
154
155
0
    RTPP_DBG_ASSERT(port >= 1 && IS_VALID_PORT(port - 1));
156
0
    ctap = (struct create_listener_args *)ap;
157
0
    ctap->fds[0] = ctap->fds[1] = NULL;
158
159
0
    for (i = 0; i < 2; i++) {
160
0
        rval = create_listener(ctap, port, &(ctap->fds[i]));
161
0
        if (rval != RTPP_PTU_OK)
162
0
            goto failure;
163
0
        port++;
164
0
    }
165
0
    RTPP_DBG_ASSERT(port > 2 && IS_VALID_PORT(port - 2));
166
0
    *ctap->port = port - 2;
167
0
    return RTPP_PTU_OK;
168
169
0
failure:
170
0
    for (i = 0; i < 2; i++) {
171
0
        if (ctap->fds[i] == NULL)
172
0
            continue;
173
0
        RTPP_OBJ_DECREF(ctap->fds[i]);
174
0
        ctap->fds[i] = NULL;
175
0
    }
176
0
    return rval;
177
0
}
178
179
int
180
rtpp_create_listener(const struct rtpp_cfg *cfsp, const struct sockaddr *ia, int *port,
181
  struct rtpp_socket **fds)
182
0
{
183
0
    struct create_listener_args cta;
184
0
    int i;
185
0
    struct rtpp_port_table *rpp;
186
187
0
    memset(&cta, '\0', sizeof(cta));
188
0
    cta.cfs = cfsp;
189
0
    cta.fds = fds;
190
0
    cta.ia = ia;
191
0
    cta.port = port;
192
193
0
    for (i = 0; i < 2; i++)
194
0
        fds[i] = NULL;
195
196
0
    rpp = RTPP_PT_SELECT(cfsp, ia->sa_family);
197
0
    return (CALL_METHOD(rpp, get_port, create_twinlistener,
198
0
      &cta));
199
0
}
200
201
struct rtpp_command *
202
rtpp_command_ctor(const struct rtpp_cfg *cfsp, int controlfd,
203
  const struct rtpp_timestamp *dtime, struct rtpp_command_stats *csp, int umode)
204
0
{
205
0
    struct rtpp_command_priv *pvt;
206
0
    struct rtpp_command *cmd;
207
208
0
    pvt = rtpp_rzmalloc(sizeof(struct rtpp_command_priv), PVT_RCOFFS(pvt));
209
0
    if (pvt == NULL) {
210
0
        return (NULL);
211
0
    }
212
0
    cmd = &(pvt->pub);
213
0
    pvt->ctx.controlfd = controlfd;
214
0
    pvt->ctx.cfs = cfsp;
215
0
    pvt->ctx.dtime.wall = dtime->wall;
216
0
    pvt->ctx.dtime.mono = dtime->mono;
217
0
    cmd->dtime = &pvt->ctx.dtime;
218
0
    pvt->ctx.csp = csp;
219
0
    cmd->glog = cfsp->glog;
220
0
    pvt->ctx.umode = umode;
221
0
    cmd->reply = rtpc_reply_ctor(&pvt->ctx);
222
0
    if (cmd->reply == NULL) {
223
0
        RTPP_OBJ_DECREF(cmd);
224
0
        return (NULL);
225
0
    }
226
0
    RTPP_OBJ_DTOR_ATTACH_OBJ(cmd, cmd->reply);
227
0
    return (cmd);
228
0
}
229
230
void
231
rtpp_command_set_raddr(struct rtpp_command *cmd, const struct sockaddr *raddr, socklen_t rlen)
232
0
{
233
0
    struct rtpp_command_priv *pvt;
234
235
0
    PUB2PVT(cmd, pvt);
236
0
    memcpy(&pvt->ctx.raddr, raddr, rlen);
237
0
    pvt->ctx.rlen = rlen;
238
0
}
239
240
struct rtpp_sockaddr
241
rtpp_command_get_raddr(const struct rtpp_command *cmd)
242
0
{
243
0
    struct rtpp_sockaddr raddr;
244
0
    const struct rtpp_command_priv *pvt;
245
246
0
    PUB2PVT(cmd, pvt);
247
0
    raddr.a = &pvt->ctx.raddr;
248
0
    raddr.l = pvt->ctx.rlen;
249
0
    return (raddr);
250
0
}
251
252
struct rtpp_command_stats *
253
rtpp_command_get_stats(const struct rtpp_command *cmd)
254
0
{
255
0
    const struct rtpp_command_priv *pvt;
256
257
0
    PUB2PVT(cmd, pvt);
258
0
    return (pvt->ctx.csp);
259
0
}
260
261
struct rtpp_command *
262
get_command(const struct rtpp_cfg *cfsp, struct rtpp_ctrl_sock *rcsp, int controlfd, int *rval,
263
  const struct rtpp_timestamp *dtime, struct rtpp_command_stats *csp,
264
  struct rtpp_cmd_rcache *rcache_obj)
265
0
{
266
0
    char *bp;
267
0
    int len;
268
0
    struct rtpp_command *cmd;
269
0
    struct rtpp_command_priv *pvt;
270
0
    int umode = RTPP_CTRL_ISDG(rcsp);
271
0
    size_t bsize;
272
0
    socklen_t asize, *lp;
273
0
    struct sockaddr *raddr;
274
275
0
    cmd = rtpp_command_ctor(cfsp, controlfd, dtime, csp, umode);
276
0
    if (cmd == NULL) {
277
0
        bp = rcsp->emrg.buf;
278
0
        bsize = sizeof(rcsp->emrg.buf);
279
0
    } else {
280
0
        bp = cmd->buf;
281
0
        bsize = sizeof(cmd->buf);
282
0
    }
283
0
    if (umode == 0) {
284
0
        for (;;) {
285
0
            len = read(controlfd, bp, bsize - 1);
286
0
            if (len == 0) {
287
0
                RTPP_LOG(cfsp->glog, RTPP_LOG_DBUG,
288
0
                  "EOF before receiving any command data");
289
0
                if (cmd != NULL)
290
0
                    RTPP_OBJ_DECREF(cmd);
291
0
                *rval = GET_CMD_EOF;
292
0
                return (NULL);
293
0
            }
294
0
            if (len != -1 || (errno != EAGAIN && errno != EINTR))
295
0
                break;
296
0
        }
297
0
    } else {
298
0
        PUB2PVT(cmd, pvt);
299
0
        if (cmd == NULL) {
300
0
            asize = sizeof(rcsp->emrg.addr);
301
0
            lp = &asize;
302
0
            raddr = sstosa(&rcsp->emrg.addr);
303
0
        } else {
304
0
            pvt->ctx.rlen = sizeof(pvt->ctx.raddr);
305
0
            lp = &pvt->ctx.rlen;
306
0
            raddr = sstosa(&pvt->ctx.raddr);
307
0
        }
308
0
        len = recvfrom(controlfd, bp, bsize - 1, 0, raddr, lp);
309
0
    }
310
0
    if (len == -1) {
311
0
        if (errno != EAGAIN && errno != EINTR)
312
0
            RTPP_ELOG(cfsp->glog, RTPP_LOG_ERR, "can't read from control socket");
313
0
        if (cmd != NULL)
314
0
            RTPP_OBJ_DECREF(cmd);
315
0
        *rval = GET_CMD_IOERR;
316
0
        return (NULL);
317
0
    }
318
0
    if (cmd == NULL) {
319
0
        *rval = GET_CMD_ENOMEM;
320
0
        csp->ncmds_rcvd.cnt++;
321
0
        csp->ncmds_errs.cnt++;
322
0
        return (NULL);
323
0
    }
324
0
    cmd->buf[len] = '\0';
325
326
0
    if (rtpp_command_split(cmd, len, rval, rcache_obj) != 0) {
327
        /* Error reply is handled by the rtpp_command_split() */
328
0
        RTPP_OBJ_DECREF(cmd);
329
0
        return (NULL);
330
0
    }
331
0
    return (cmd);
332
0
}
333
334
0
#define ISAMPAMP(vp) ((vp)->len == 2 && (vp)->s[0] == '&' && (vp)->s[1] == '&')
335
336
static int
337
rtpp_command_guard_retrans(struct rtpp_command *cmd,
338
  struct rtpp_cmd_rcache *rcache_obj)
339
0
{
340
0
    size_t len;
341
0
    struct rtpp_command_priv *pvt;
342
0
    struct rtpp_cmd_rcache_entry *cres;
343
344
0
    PUB2PVT(cmd, pvt);
345
0
    cres = CALL_METHOD(rcache_obj, lookup, rtpp_str_fix(&pvt->ctx.cookie));
346
0
    if (cres == NULL) {
347
0
        RTPP_OBJ_BORROW(cmd, rcache_obj);
348
0
        pvt->ctx.rcache_obj = rcache_obj;
349
0
        return (0);
350
0
    }
351
0
    len = cres->reply->len;
352
0
    int r = rtpp_anetio_sendto_na(pvt->ctx.cfs->rtpp_proc_cf->netio, pvt->ctx.controlfd,
353
0
      cres->reply->s, len, 0, sstosa(&pvt->ctx.raddr), pvt->ctx.rlen, cres->rcnt);
354
0
    if (r != 0)
355
0
        RTPP_OBJ_DECREF(cres);
356
0
    pvt->ctx.csp->ncmds_rcvd.cnt--;
357
0
    pvt->ctx.csp->ncmds_rcvd_ndups.cnt++;
358
0
    return (1);
359
0
}
360
361
int
362
rtpp_command_split(struct rtpp_command *cmd, int len, int *rval,
363
  struct rtpp_cmd_rcache *rcache_obj)
364
0
{
365
0
    rtpp_str_const_t *ap;
366
0
    struct rtpp_command_priv *pvt;
367
0
    struct rtpp_command_args *cap;
368
0
    char mbuf[RTPP_CMD_BUFLEN];
369
370
0
    PUB2PVT(cmd, pvt);
371
0
    if (len > 0 && cmd->buf[len - 1] == '\n') {
372
0
        RTPP_LOG(pvt->ctx.cfs->glog, RTPP_LOG_DBUG, "received command \"%.*s\\n\"",
373
0
          len - 1, cmd->buf);
374
0
    } else {
375
0
        RTPP_LOG(pvt->ctx.cfs->glog, RTPP_LOG_DBUG, "received command \"%s\"",
376
0
          cmd->buf);
377
0
    }
378
0
    pvt->ctx.csp->ncmds_rcvd.cnt++;
379
380
0
    cap = &cmd->args;
381
0
    rtpp_strsplit(cmd->buf, mbuf, len, sizeof(mbuf));
382
0
    char *mp, *mp_next;
383
0
    ap = cap->v;
384
0
    for (mp = mbuf; mp != NULL && (mp - mbuf) < len; mp = mp_next) {
385
0
        rtpp_str_const_t tap;
386
0
        mp_next = memchr(mp, '\0', len - (mp - mbuf));
387
0
        if (mp_next == NULL) {
388
0
            tap.len = mbuf + len - mp;
389
0
        } else {
390
0
            tap.len = mp_next - mp;
391
0
            mp_next += 1;
392
0
        }
393
0
        if (tap.len == 0) {
394
0
            continue;
395
0
        }
396
0
        tap.s = cmd->buf + (mp - mbuf);
397
0
        size_t slen = strlen(tap.s);
398
0
        RTPP_DBG_ASSERT(slen <= tap.len);
399
0
        if (slen < tap.len) {
400
            /* \0 inside a parameter is not allowed */
401
0
            goto synerr;
402
0
        }
403
0
        *ap = tap;
404
405
0
        if (cap == &cmd->args) {
406
            /* Stream communication mode doesn't use cookie */
407
0
            if (pvt->ctx.umode != 0 && cap->c == 0 && pvt->ctx.cookie.s == NULL) {
408
0
                pvt->ctx.cookie = *ap;
409
0
                if (rtpp_command_guard_retrans(cmd, rcache_obj)) {
410
0
                    *rval = GET_CMD_OK;
411
0
                    return (1);
412
0
                }
413
0
                assert(CALL_SMETHOD(cmd->reply, appendf, "%.*s ", ap->len, ap->s) == 0);
414
0
                CALL_SMETHOD(cmd->reply, commit);
415
0
                continue;
416
0
            }
417
0
        }
418
419
0
        if (ISAMPAMP(ap)) {
420
0
            if (cmd->subc.n == (MAX_SUBC_NUM - 1))
421
0
                goto synerr;
422
0
            ap->s = NULL;
423
0
            ap->len = 0;
424
0
            cap = &cmd->subc.args[cmd->subc.n];
425
0
            cmd->subc.n += 1;
426
0
            ap = cap->v;
427
0
            continue;
428
0
        }
429
0
        cap->c++;
430
0
        if (++ap >= &cap->v[RTPC_MAX_ARGC])
431
0
            goto synerr;
432
0
    }
433
0
    if (cmd->args.c < 1 || (pvt->ctx.umode != 0 && pvt->ctx.cookie.s == NULL)) {
434
0
        goto synerr;
435
0
    }
436
0
    for (int i = 0; i < cmd->subc.n; i++) {
437
0
        cap = &cmd->subc.args[i];
438
0
        if (cap->c < 1) {
439
0
            goto synerr;
440
0
        }
441
0
    }
442
443
    /* Step I: parse parameters that are common to all ops */
444
0
    if (rtpp_command_pre_parse(pvt->ctx.cfs, cmd) != 0) {
445
        /* Error reply is handled by the rtpp_command_pre_parse() */
446
0
        *rval = GET_CMD_INVAL;
447
0
        return (1);
448
0
    }
449
450
0
    return (0);
451
0
synerr:
452
0
    RTPP_LOG(pvt->ctx.cfs->glog, RTPP_LOG_ERR, "command syntax error");
453
0
    CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_1);
454
0
    *rval = GET_CMD_INVAL;
455
0
    return (1);
456
457
0
}
458
459
int
460
handle_command(const struct rtpp_cfg *cfsp, struct rtpp_command *cmd)
461
0
{
462
0
    int i, verbose, rval;
463
0
    const char *cp;
464
0
    const char *recording_name;
465
0
    struct rtpp_session *spa;
466
0
    int norecord_all;
467
468
0
    spa = NULL;
469
0
    recording_name = NULL;
470
0
    norecord_all = 0;
471
472
    /* Step II: parse parameters that are specific to a particular op and run simple ops */
473
0
    switch (cmd->cca.op) {
474
0
    case VER_FEATURE:
475
0
        handle_ver_feature(cfsp, cmd);
476
0
        return 0;
477
478
0
    case GET_VER:
479
        /* This returns base version. */
480
0
        CALL_SMETHOD(cmd->reply, deliver_number, CPROTOVER);
481
0
        return 0;
482
483
0
    case DELETE_ALL:
484
        /* Delete all active sessions */
485
0
        RTPP_LOG(cfsp->glog, RTPP_LOG_INFO, "deleting all active sessions");
486
0
        CALL_SMETHOD(cfsp->sessions_wrt, purge);
487
0
        CALL_SMETHOD(cfsp->sessions_ht, purge);
488
0
        CALL_SMETHOD(cmd->reply, deliver_ok);
489
0
        return 0;
490
491
0
    case INFO:
492
0
        handle_info(cfsp, cmd);
493
0
        return 0;
494
495
0
    case PLAY:
496
        /*
497
         * P callid pname codecs from_tag to_tag
498
         *
499
         *   <codecs> could be either comma-separated list of supported
500
         *   payload types or word "session" (without quotes), in which
501
         *   case list saved on last session update will be used instead.
502
         */
503
0
        cmd->cca.opts.play = rtpp_command_play_opts_parse(cmd);
504
0
        if (cmd->cca.opts.play == NULL) {
505
0
            RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "can't parse options");
506
0
            return 0;
507
0
        }
508
0
        break;
509
510
0
    case COPY:
511
0
        recording_name = cmd->args.v[2].s;
512
        /* Fallthrough */
513
0
    case RECORD:
514
0
        cmd->cca.opts.record = rtpp_command_record_opts_parse(cfsp, cmd, &cmd->args);
515
0
        if (cmd->cca.opts.record == NULL) {
516
0
            RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "can't parse options");
517
0
            return 0;
518
0
        }
519
0
        RTPP_OBJ_DTOR_ATTACH_OBJ(cmd, cmd->cca.opts.record);
520
0
        break;
521
522
0
    case NORECORD:
523
0
        if (cmd->args.v[0].s[1] == 'A' || cmd->args.v[0].s[1] == 'a') {
524
0
            if (cmd->args.v[0].s[2] != '\0') {
525
0
                RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "command syntax error");
526
0
                CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_2);
527
0
                return 0;
528
0
            }
529
0
            norecord_all = 1;
530
0
        } else {
531
0
            if (cmd->args.v[0].s[1] != '\0') {
532
0
                RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "command syntax error");
533
0
                CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_3);
534
0
                return 0;
535
0
            }
536
0
            norecord_all = 0;
537
0
        }
538
0
        break;
539
540
0
    case DELETE:
541
        /* D[w] call_id from_tag [to_tag] */
542
0
        cmd->cca.opts.delete = rtpp_command_del_opts_parse(cmd, &cmd->args);
543
0
        if (cmd->cca.opts.delete == NULL) {
544
0
            RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "can't parse options");
545
0
            return 0;
546
0
        }
547
0
        RTPP_OBJ_DTOR_ATTACH_OBJ(cmd, cmd->cca.opts.delete);
548
0
        break;
549
550
0
    case UPDATE:
551
0
    case LOOKUP:
552
0
        cmd->cca.opts.ul = rtpp_command_ul_opts_parse(cfsp, cmd);
553
0
        if (cmd->cca.opts.ul == NULL) {
554
0
            RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "can't parse options");
555
0
            return 0;
556
0
        }
557
0
        break;
558
559
0
    case GET_STATS:
560
0
        verbose = 0;
561
0
        for (cp = cmd->args.v[0].s + 1; *cp != '\0'; cp++) {
562
0
            switch (*cp) {
563
0
            case 'v':
564
0
            case 'V':
565
0
                verbose = 1;
566
0
                break;
567
568
0
            default:
569
0
                RTPP_LOG(cfsp->glog, RTPP_LOG_ERR,
570
0
                  "STATS: unknown command modifier `%c'", *cp);
571
0
                CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_5);
572
0
                return 0;
573
0
            }
574
0
        }
575
0
        i = handle_get_stats(cfsp->rtpp_stats, cmd, verbose);
576
0
        if (i != 0) {
577
0
            CALL_SMETHOD(cmd->reply, deliver_error, i);
578
0
        }
579
0
        return 0;
580
581
0
    default:
582
0
        break;
583
0
    }
584
585
0
    for (int i = 0; i < cmd->subc.n; i++) {
586
0
        if (rtpp_subcommand_ul_opts_parse(cfsp, cmd, &cmd->subc.args[i],
587
0
          &cmd->after_success[i]) != 0) {
588
0
            if (cmd->cca.op == UPDATE || cmd->cca.op == LOOKUP)
589
0
                rtpp_command_ul_opts_free(cmd->cca.opts.ul);
590
0
            CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_SUBC);
591
0
            return 0;
592
0
        }
593
0
        RTPP_DBG_ASSERT(cmd->after_success[i].handler != NULL);
594
0
    }
595
596
    /*
597
     * Record and delete need special handling since they apply to all
598
     * streams in the session.
599
     */
600
0
    switch (cmd->cca.op) {
601
0
    case DELETE:
602
0
        i = handle_delete(cfsp, &cmd->cca);
603
0
        break;
604
605
0
    case RECORD:
606
0
        i = handle_record(cfsp, cmd);
607
0
        break;
608
609
0
    case NORECORD:
610
0
        i = handle_norecord(cfsp, &cmd->cca, norecord_all);
611
0
        break;
612
613
0
    default:
614
0
        i = find_stream(cfsp, cmd->cca.call_id, cmd->cca.from_tag,
615
0
        cmd->cca.to_tag, &spa);
616
0
        if (i != -1) {
617
0
            if (cmd->cca.op != UPDATE)
618
0
            i = NOT(i);
619
0
            RTPP_DBG_ASSERT(cmd->sp == NULL);
620
0
            RTPP_OBJ_DTOR_ATTACH_OBJ(cmd, spa);
621
0
            cmd->sp = spa;
622
0
        }
623
0
        break;
624
0
    }
625
626
0
    if (i == -1 && cmd->cca.op != UPDATE) {
627
0
        rtpp_str_t to_tag = cmd->cca.to_tag ? *cmd->cca.to_tag :
628
0
          (rtpp_str_t){.len = 4, .s = "NONE"};
629
0
        RTPP_LOG(cfsp->glog, RTPP_LOG_INFO,
630
0
          "%s request failed: session %.*s, tags %.*s/%.*s not found", cmd->cca.rname,
631
0
          (int)cmd->cca.call_id->len, cmd->cca.call_id->s, (int)cmd->cca.from_tag->len,
632
0
          cmd->cca.from_tag->s, (int)to_tag.len, to_tag.s);
633
0
        switch (cmd->cca.op) {
634
0
        case LOOKUP:
635
0
            rtpp_command_ul_opts_free(cmd->cca.opts.ul);
636
0
            ul_reply_port(cmd, NULL);
637
0
            return 0;
638
639
0
        case PLAY:
640
0
            rtpp_command_play_opts_free(cmd->cca.opts.play);
641
0
            break;
642
643
0
        case COPY:
644
0
        case RECORD:
645
0
            RTPP_DBG_ASSERT(CALL_SMETHOD(cmd->cca.opts.record->rcnt, peek) == 1);
646
0
            break;
647
648
0
        case DELETE:
649
0
            RTPP_DBG_ASSERT(CALL_SMETHOD(cmd->cca.opts.delete->rcnt, peek) == 1);
650
0
            break;
651
652
0
        default:
653
0
            RTPP_DBG_ASSERT(cmd->cca.opts.ptr == NULL);
654
0
            break;
655
0
        }
656
0
        CALL_SMETHOD(cmd->reply, deliver_error, ECODE_SESUNKN);
657
0
        return 0;
658
0
    }
659
660
0
    switch (cmd->cca.op) {
661
0
    case DELETE:
662
0
    case NORECORD:
663
0
        CALL_SMETHOD(cmd->reply, deliver_ok);
664
0
        break;
665
666
0
    case NOPLAY:
667
0
        CALL_SMETHOD(spa->rtp->stream[i], handle_noplay);
668
0
        CALL_SMETHOD(cmd->reply, deliver_ok);
669
0
        break;
670
671
0
    case PLAY:
672
0
        rtpp_command_play_handle(spa->rtp->stream[i], cmd, cfsp);
673
0
        break;
674
675
0
    case COPY:
676
0
        if (handle_copy(cfsp, cmd, spa, i, recording_name, cmd->cca.opts.record) != 0) {
677
0
            CALL_SMETHOD(cmd->reply, deliver_error, ECODE_CPYFAIL);
678
0
            return 0;
679
0
        }
680
0
    case RECORD:
681
0
        break;
682
683
0
    case QUERY:
684
0
        rval = handle_query(cfsp, cmd, spa->rtp, i);
685
0
        if (rval != 0) {
686
0
            CALL_SMETHOD(cmd->reply, deliver_error, rval);
687
0
        }
688
0
        break;
689
690
0
    case LOOKUP:
691
0
    case UPDATE:
692
0
        rtpp_command_ul_handle(cfsp, cmd, i);
693
0
        rtpp_command_ul_opts_free(cmd->cca.opts.ul);
694
0
        break;
695
696
0
    default:
697
        /* Programmatic error, should not happen */
698
0
        abort();
699
0
    }
700
701
0
    return 0;
702
0
}
703
704
static void
705
handle_info(const struct rtpp_cfg *cfsp, struct rtpp_command *cmd)
706
0
{
707
#if 0
708
    struct rtpp_session *spa, *spb;
709
    char addrs[4][256];
710
    int brief;
711
#endif
712
0
    int aerr, i, load;
713
0
    unsigned long long packets_in, packets_out;
714
0
    unsigned long long sessions_created;
715
0
    int sessions_active, rtp_streams_active;
716
0
    const char *opts;
717
718
0
    opts = &cmd->args.v[0].s[1];
719
#if 0
720
    brief = 0;
721
#endif
722
0
    load = 0;
723
0
    for (i = 0; opts[i] != '\0'; i++) {
724
0
        switch (opts[i]) {
725
0
        case 'b':
726
0
        case 'B':
727
#if 0
728
            brief = 1;
729
#endif
730
0
            break;
731
732
0
        case 'l':
733
0
        case 'L':
734
0
            load = 1;
735
0
            break;
736
737
0
        default:
738
0
            RTPP_LOG(cfsp->glog, RTPP_LOG_ERR, "command syntax error");
739
0
            CALL_SMETHOD(cmd->reply, deliver_error, ECODE_PARSE_7);
740
0
            return;
741
0
        }
742
0
    }
743
744
0
    packets_in = CALL_SMETHOD(cfsp->rtpp_stats, getlvalbyname, "npkts_rcvd");
745
0
    packets_out = CALL_SMETHOD(cfsp->rtpp_stats, getlvalbyname, "npkts_relayed") +
746
0
      CALL_SMETHOD(cfsp->rtpp_stats, getlvalbyname, "npkts_played");
747
0
    sessions_created = CALL_SMETHOD(cfsp->rtpp_stats, getlvalbyname,
748
0
      "nsess_created");
749
0
    sessions_active = sessions_created - CALL_SMETHOD(cfsp->rtpp_stats,
750
0
      getlvalbyname, "nsess_destroyed");
751
0
    rtp_streams_active = CALL_SMETHOD(cfsp->rtp_streams_wrt, get_length);
752
0
    aerr = CALL_SMETHOD(cmd->reply, appendf, "sessions created: %llu\nactive sessions: %d\n"
753
0
      "active streams: %d\npackets received: %llu\npackets transmitted: %llu\n",
754
0
      sessions_created, sessions_active, rtp_streams_active, packets_in, packets_out);
755
0
    if (load != 0 && aerr == 0) {
756
0
          aerr = CALL_SMETHOD(cmd->reply, appendf, "average load: %f\n",
757
0
            CALL_METHOD(cfsp->rtpp_cmd_cf, get_aload));
758
0
    }
759
#if 0
760
XXX this needs work to fix it after rtp/rtcp split 
761
    for (i = 0; i < cfsp->nsessions && brief == 0; i++) {
762
        spa = cfsp->sessions[i];
763
        if (spa == NULL || spa->stream[0]->sidx != i)
764
            continue;
765
        /* RTCP twin session */
766
        if (spa->rtcp == NULL) {
767
            spb = spa->rtp;
768
            buf[len++] = '\t';
769
        } else {
770
            spb = spa->rtcp;
771
            buf[len++] = '\t';
772
            buf[len++] = 'C';
773
            buf[len++] = ' ';
774
        }
775
776
        addr2char_r(spb->laddr[1], addrs[0], sizeof(addrs[0]));
777
        if (spb->addr[1] == NULL) {
778
            strcpy(addrs[1], "NONE");
779
        } else {
780
            sprintf(addrs[1], "%s:%d", addr2char(spb->addr[1]),
781
              addr2port(spb->addr[1]));
782
        }
783
        addr2char_r(spb->laddr[0], addrs[2], sizeof(addrs[2]));
784
        if (spb->addr[0] == NULL) {
785
            strcpy(addrs[3], "NONE");
786
        } else {
787
            sprintf(addrs[3], "%s:%d", addr2char(spb->addr[0]),
788
              addr2port(spb->addr[0]));
789
        }
790
791
        len += snprintf(buf + len, sizeof(buf) - len,
792
          "%s/%s: caller = %s:%d/%s, callee = %s:%d/%s, "
793
          "stats = %lu/%lu/%lu/%lu, ttl = %d/%d\n",
794
          spb->call_id, spb->tag, addrs[0], spb->stream[1]->port, addrs[1],
795
          addrs[2], spb->stream[0]->port, addrs[3], spa->pcount[0], spa->pcount[1],
796
          spa->pcount[2], spa->pcount[3], spb->ttl[0], spb->ttl[1]);
797
        if (len + 512 > sizeof(buf)) {
798
            rtpc_doreply(cmd, buf, len);
799
            len = 0;
800
        }
801
    }
802
#endif
803
0
    if (aerr == 0) {
804
0
        CALL_SMETHOD(cmd->reply, commit);
805
0
        CALL_SMETHOD(cmd->reply, deliver, 0);
806
0
    } else {
807
0
        CALL_SMETHOD(cmd->reply, deliver_error, ECODE_NOMEM_6);
808
0
    }
809
0
}