Coverage Report

Created: 2026-09-03 06:59

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