Coverage Report

Created: 2023-09-25 06:44

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