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