/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 | } |