static int handle_record(struct cfg *cf, char *call_id, char *from_tag, char *to_tag) { int nrecorded, idx; struct rtpp_session *spa; nrecorded = 0; for (spa = session_findfirst(cf, call_id); spa != NULL; spa = session_findnext(cf, spa)) { if (compare_session_tags(spa->tag, from_tag, NULL) != 0) { idx = 1; } else if (to_tag != NULL && (compare_session_tags(spa->tag, to_tag, NULL)) != 0) { idx = 0; } else { continue; } nrecorded++; handle_copy(cf, spa, idx, NULL); handle_copy(cf, spa, NOT(idx), NULL); } return (nrecorded == 0 ? -1 : 0); }
static void dispatch(struct pfiled *pfiled, struct io *io) { if (cmdline_verbose) { fprintf(stderr, "io: 0x%p, req: 0x%p, op %u\n", (void *)io, (void *)io->req, io->req->op); } switch (io->req->op) { case X_READ: case X_WRITE: handle_read_write(pfiled, io); break; case X_INFO: handle_info(pfiled, io); break; case X_COPY: handle_copy(pfiled, io); break; case X_DELETE: handle_delete(pfiled, io); break; case X_OPEN: handle_open(pfiled, io); break; case X_CLOSE: handle_close(pfiled, io); break; // case X_SNAPSHOT: case X_SYNC: default: handle_unknown(pfiled, io); } }
static int handle_record(struct cfg *cf, struct common_cmd_args *ccap, int record_single_file) { int nrecorded, idx; struct rtpp_session *spa; nrecorded = 0; for (spa = session_findfirst(cf, ccap->call_id); spa != NULL; spa = session_findnext(cf, spa)) { if (compare_session_tags(spa->tag, ccap->from_tag, NULL) != 0) { idx = 1; } else if (ccap->to_tag != NULL && (compare_session_tags(spa->tag, ccap->to_tag, NULL)) != 0) { idx = 0; } else { continue; } if (handle_copy(cf, spa, idx, NULL, record_single_file) == 0) { nrecorded++; } } return (nrecorded == 0 ? -1 : 0); }
int rtpp_command_ul_handle(struct cfg *cf, struct rtpp_command *cmd, struct ul_opts *ulop, int sidx) { int pidx, lport, sessions_active; struct rtpp_socket *fds[2]; const char *actor; struct rtpp_session *spa, *spb; pidx = 1; lport = 0; spa = spb = NULL; fds[0] = fds[1] = NULL; if (sidx != -1) { RTPP_DBG_ASSERT(cmd->cca.op == UPDATE || cmd->cca.op == LOOKUP); spa = cmd->sp; if (spa->rtp->stream[sidx]->fd == NULL || ulop->new_port != 0) { if (ulop->local_addr != NULL) { spa->rtp->stream[sidx]->laddr = ulop->local_addr; } if (rtpp_create_listener(cf, spa->rtp->stream[sidx]->laddr, &lport, fds) == -1) { RTPP_LOG(spa->log, RTPP_LOG_ERR, "can't create listener"); reply_error(cmd, ECODE_LSTFAIL_1); goto err_undo_0; } if (spa->rtp->stream[sidx]->fd != NULL && ulop->new_port != 0) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "new port requested, releasing %d/%d, replacing with %d/%d", spa->rtp->stream[sidx]->port, spa->rtcp->stream[sidx]->port, lport, lport + 1); CALL_METHOD(cf->stable->sessinfo, update, spa, sidx, fds); } else { RTPP_DBG_ASSERT(spa->rtp->stream[sidx]->fd == NULL); spa->rtp->stream[sidx]->fd = fds[0]; RTPP_DBG_ASSERT(spa->rtcp->stream[sidx]->fd == NULL); spa->rtcp->stream[sidx]->fd = fds[1]; CALL_METHOD(cf->stable->sessinfo, append, spa, sidx); } spa->rtp->stream[sidx]->port = lport; spa->rtcp->stream[sidx]->port = lport + 1; if (spa->complete == 0) { cmd->csp->nsess_complete.cnt++; } spa->complete = 1; } if (ulop->weak) spa->rtp->stream[sidx]->weak = 1; else if (cmd->cca.op == UPDATE) spa->strong = 1; lport = spa->rtp->stream[sidx]->port; ulop->lia[0] = spa->rtp->stream[sidx]->laddr; pidx = (sidx == 0) ? 1 : 0; if (cmd->cca.op == UPDATE) { CALL_METHOD(spa->rtp->stream[0]->ttl, reset_with, cf->stable->max_setup_ttl); CALL_METHOD(spa->rtp->stream[1]->ttl, reset_with, cf->stable->max_setup_ttl); RTPP_LOG(spa->log, RTPP_LOG_INFO, "adding %s flag to existing session, new=%d/%d/%d", ulop->weak ? ( sidx ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->rtp->stream[0]->weak, spa->rtp->stream[1]->weak); } else { CALL_METHOD(spa->rtp->stream[0]->ttl, reset_with, cf->stable->max_ttl); CALL_METHOD(spa->rtp->stream[1]->ttl, reset_with, cf->stable->max_ttl); } RTPP_LOG(spa->log, RTPP_LOG_INFO, "lookup on ports %d/%d, session timer restarted", spa->rtp->stream[0]->port, spa->rtp->stream[1]->port); } else { struct rtpp_hash_table_entry *hte; RTPP_DBG_ASSERT(cmd->cca.op == UPDATE); RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO, "new session %s, tag %s requested, type %s", cmd->cca.call_id, cmd->cca.from_tag, ulop->weak ? "weak" : "strong"); if (cf->stable->slowshutdown != 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_INFO, "proxy is in the deorbiting-burn mode, new session rejected"); reply_error(cmd, ECODE_SLOWSHTDN); goto err_undo_0; } if (ulop->local_addr != NULL) { ulop->lia[0] = ulop->lia[1] = ulop->local_addr; } if (rtpp_create_listener(cf, ulop->lia[0], &lport, fds) == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "can't create listener"); reply_error(cmd, ECODE_LSTFAIL_2); goto err_undo_0; } /* * Session creation. If creation is requested with weak flag, * set weak[0]. */ spa = rtpp_session_ctor(cf->stable, &cmd->cca, cmd->dtime, ulop->lia, ulop->weak, lport, fds); if (spa == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_4, ulop, fds, NULL); return (-1); } hte = CALL_METHOD(cf->stable->sessions_ht, append_refcnt, spa->call_id, spa->rcnt); if (hte == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_5, ulop, NULL, spa); return (-1); } if (CALL_METHOD(cf->stable->sessions_wrt, reg, spa->rcnt, spa->seuid) != 0) { CALL_METHOD(cf->stable->sessions_ht, remove, spa->call_id, hte); handle_nomem(cf, cmd, ECODE_NOMEM_8, ulop, NULL, spa); return (-1); } cmd->csp->nsess_created.cnt++; /* * Each session can consume up to 5 open file descriptors (2 RTP, * 2 RTCP and 1 logging) so that warn user when he is likely to * exceed 80% mark on hard limit. */ sessions_active = CALL_METHOD(cf->stable->sessions_wrt, get_length); if (sessions_active > (rtpp_rlim_max(cf) * 80 / (100 * 5)) && cf->nofile_limit_warned == 0) { cf->nofile_limit_warned = 1; RTPP_LOG(cf->stable->glog, RTPP_LOG_WARN, "passed 80%% " "threshold on the open file descriptors limit (%d), " "consider increasing the limit using -L command line " "option", (int)rtpp_rlim_max(cf)); } RTPP_LOG(spa->log, RTPP_LOG_INFO, "new session on a port %d created, " "tag %s", lport, cmd->cca.from_tag); if (cf->stable->record_all != 0) { handle_copy(cf, spa, 0, NULL, 0); handle_copy(cf, spa, 1, NULL, 0); } /* Save ref, it will be decref'd by the command disposal code */ RTPP_DBG_ASSERT(cmd->sp == NULL); cmd->sp = spa; } if (cmd->cca.op == UPDATE) { if (!CALL_METHOD(cf->stable->rtpp_tnset_cf, isenabled) && ulop->notify_socket != NULL) RTPP_LOG(spa->log, RTPP_LOG_ERR, "must permit notification socket with -n"); if (spa->timeout_data.notify_tag != NULL) { free(spa->timeout_data.notify_tag); spa->timeout_data.notify_tag = NULL; } if (ulop->notify_socket != NULL) { struct rtpp_tnotify_target *rttp; rttp = CALL_METHOD(cf->stable->rtpp_tnset_cf, lookup, ulop->notify_socket, (cmd->rlen > 0) ? sstosa(&cmd->raddr) : NULL, (cmd->rlen > 0) ? cmd->laddr : NULL); if (rttp == NULL) { RTPP_LOG(spa->log, RTPP_LOG_ERR, "invalid socket name %s", ulop->notify_socket); ulop->notify_socket = NULL; } else { RTPP_LOG(spa->log, RTPP_LOG_INFO, "setting timeout handler"); spa->timeout_data.notify_target = rttp; spa->timeout_data.notify_tag = strdup(ulop->notify_tag); } } else if (spa->timeout_data.notify_target != NULL) { spa->timeout_data.notify_target = NULL; RTPP_LOG(spa->log, RTPP_LOG_INFO, "disabling timeout handler"); } } if (ulop->ia[0] != NULL && ulop->ia[1] != NULL) { CALL_METHOD(spa->rtp->stream[pidx], prefill_addr, &(ulop->ia[0]), cmd->dtime); CALL_METHOD(spa->rtcp->stream[pidx], prefill_addr, &(ulop->ia[1]), cmd->dtime); } spa->rtp->stream[pidx]->asymmetric = spa->rtcp->stream[pidx]->asymmetric = ulop->asymmetric; spa->rtp->stream[pidx]->latch_info.latched = spa->rtcp->stream[pidx]->latch_info.latched = ulop->asymmetric; if (spa->rtp->stream[pidx]->codecs != NULL) { free(spa->rtp->stream[pidx]->codecs); spa->rtp->stream[pidx]->codecs = NULL; } if (ulop->codecs != NULL) { spa->rtp->stream[pidx]->codecs = ulop->codecs; ulop->codecs = NULL; } spa->rtp->stream[NOT(pidx)]->ptime = ulop->requested_ptime; actor = CALL_METHOD(spa->rtp->stream[pidx], get_actor); if (ulop->requested_ptime > 0) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "RTP packets from %s " "will be resized to %d milliseconds", actor, ulop->requested_ptime); } else if (spa->rtp->stream[pidx]->resizer != NULL) { RTPP_LOG(spa->log, RTPP_LOG_INFO, "Resizing of RTP " "packets from %s has been disabled", actor); } if (ulop->requested_ptime > 0) { if (spa->rtp->stream[pidx]->resizer != NULL) { rtp_resizer_set_ptime(spa->rtp->stream[pidx]->resizer, ulop->requested_ptime); } else { spa->rtp->stream[pidx]->resizer = rtp_resizer_new(ulop->requested_ptime); } } else if (spa->rtp->stream[pidx]->resizer != NULL) { rtp_resizer_free(cf->stable->rtpp_stats, spa->rtp->stream[pidx]->resizer); spa->rtp->stream[pidx]->resizer = NULL; } RTPP_DBG_ASSERT(lport != 0); ulop->reply.port = lport; ulop->reply.ia = ulop->lia[0]; if (cf->stable->advaddr[0] != NULL) { if (cf->stable->bmode != 0 && cf->stable->advaddr[1] != NULL && ulop->lia[0] == cf->stable->bindaddr[1]) { ulop->reply.ia_ov = cf->stable->advaddr[1]; } else { ulop->reply.ia_ov = cf->stable->advaddr[0]; } } ul_reply_port(cmd, &ulop->reply); rtpp_command_ul_opts_free(ulop); return (0); err_undo_0: rtpp_command_ul_opts_free(ulop); return (-1); }
int handle_command(struct cfg *cf, int controlfd, double dtime) { int len, argc, i, pidx, asymmetric; int external, pf, lidx, playcount, weak, tpf; int fds[2], lport, n; socklen_t rlen; char buf[1024 * 8]; char *cp, *call_id, *from_tag, *to_tag, *addr, *port, *cookie; char *pname, *codecs, *recording_name, *t; struct rtpp_session *spa, *spb; char **ap, *argv[10]; const char *rname, *errmsg; struct sockaddr *ia[2], *lia[2]; struct sockaddr_storage raddr; int requested_nsamples; enum {DELETE, RECORD, PLAY, NOPLAY, COPY, UPDATE, LOOKUP, QUERY} op; int max_argc; char *socket_name_u, *notify_tag; struct sockaddr *local_addr; char c; requested_nsamples = -1; ia[0] = ia[1] = NULL; spa = spb = NULL; lia[0] = lia[1] = cf->bindaddr[0]; lidx = 1; fds[0] = fds[1] = -1; recording_name = NULL; socket_name_u = notify_tag = NULL; local_addr = NULL; codecs = NULL; if (cf->umode == 0) { for (;;) { len = read(controlfd, buf, sizeof(buf) - 1); if (len != -1 || (errno != EAGAIN && errno != EINTR)) break; sched_yield(); } } else { rlen = sizeof(raddr); len = recvfrom(controlfd, buf, sizeof(buf) - 1, 0, sstosa(&raddr), &rlen); } if (len == -1) { if (errno != EAGAIN && errno != EINTR) rtpp_log_ewrite(RTPP_LOG_ERR, cf->glog, "can't read from control socket"); return -1; } buf[len] = '\0'; rtpp_log_write(RTPP_LOG_DBUG, cf->glog, "received command \"%s\"", buf); cp = buf; argc = 0; memset(argv, 0, sizeof(argv)); for (ap = argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) if (**ap != '\0') { argc++; if (++ap >= &argv[10]) break; } cookie = NULL; if (argc < 1 || (cf->umode != 0 && argc < 2)) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 0); return 0; } /* Stream communication mode doesn't use cookie */ if (cf->umode != 0) { cookie = argv[0]; for (i = 1; i < argc; i++) argv[i - 1] = argv[i]; argc--; argv[argc] = NULL; } else { cookie = NULL; } addr = port = NULL; switch (argv[0][0]) { case 'u': case 'U': /* U[opts] callid remote_ip remote_port from_tag [to_tag] */ op = UPDATE; rname = "update/create"; break; case 'l': case 'L': op = LOOKUP; rname = "lookup"; break; case 'd': case 'D': op = DELETE; rname = "delete"; break; case 'p': case 'P': /* * P callid pname codecs from_tag to_tag * * <codecs> could be either comma-separated list of supported * payload types or word "session" (without quotes), in which * case list saved on last session update will be used instead. */ op = PLAY; rname = "play"; playcount = 1; pname = argv[2]; codecs = argv[3]; break; case 'r': case 'R': op = RECORD; rname = "record"; break; case 'c': case 'C': op = COPY; rname = "copy"; break; case 's': case 'S': op = NOPLAY; rname = "noplay"; break; case 'v': case 'V': if (argv[0][1] == 'F' || argv[0][1] == 'f') { int i, known; /* * Wait for protocol version datestamp and check whether we * know it. */ if (argc != 2 && argc != 3) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 2); return 0; } /* * Only list 20081224 protocol mod as supported if * user actually enabled notification with -n */ if (strcmp(argv[1], "20081224") == 0 && cf->timeout_handler.socket_name == NULL) { reply_number(cf, controlfd, &raddr, rlen, cookie, 0); return 0; } for (known = i = 0; proto_caps[i].pc_id != NULL; ++i) { if (!strcmp(argv[1], proto_caps[i].pc_id)) { known = 1; break; } } reply_number(cf, controlfd, &raddr, rlen, cookie, known); return 0; } if (argc != 1 && argc != 2) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 2); return 0; } /* This returns base version. */ reply_number(cf, controlfd, &raddr, rlen, cookie, CPROTOVER); return 0; case 'i': case 'I': if (cookie == NULL) len = sprintf(buf, "sessions created: %llu\nactive sessions: %d\n" "active streams: %d\n", cf->sessions_created, cf->sessions_active, cf->nsessions / 2); else len = sprintf(buf, "%s sessions created: %llu\nactive sessions: %d\n" "active streams: %d\n", cookie, cf->sessions_created, cf->sessions_active, cf->nsessions / 2); for (i = 1; i < cf->nsessions; i++) { char addrs[4][256]; spa = cf->sessions[i]; if (spa == NULL || spa->sidx[0] != i) continue; /* RTCP twin session */ if (spa->rtcp == NULL) { spb = spa->rtp; buf[len++] = '\t'; } else { spb = spa->rtcp; buf[len++] = '\t'; buf[len++] = 'C'; buf[len++] = ' '; } addr2char_r(spb->laddr[1], addrs[0], sizeof(addrs[0])); if (spb->addr[1] == NULL) { strcpy(addrs[1], "NONE"); } else { sprintf(addrs[1], "%s:%d", addr2char(spb->addr[1]), addr2port(spb->addr[1])); } addr2char_r(spb->laddr[0], addrs[2], sizeof(addrs[2])); if (spb->addr[0] == NULL) { strcpy(addrs[3], "NONE"); } else { sprintf(addrs[3], "%s:%d", addr2char(spb->addr[0]), addr2port(spb->addr[0])); } len += sprintf(buf + len, "%s/%s: caller = %s:%d/%s, callee = %s:%d/%s, " "stats = %lu/%lu/%lu/%lu, ttl = %d/%d\n", spb->call_id, spb->tag, addrs[0], spb->ports[1], addrs[1], addrs[2], spb->ports[0], addrs[3], spa->pcount[0], spa->pcount[1], spa->pcount[2], spa->pcount[3], spb->ttl[0], spb->ttl[1]); if (len + 512 > sizeof(buf)) { doreply(cf, controlfd, buf, len, &raddr, rlen); len = 0; } } if (len > 0) doreply(cf, controlfd, buf, len, &raddr, rlen);; return 0; break; case 'q': case 'Q': op = QUERY; rname = "query"; break; case 'x': case 'X': /* Delete all active sessions */ rtpp_log_write(RTPP_LOG_INFO, cf->glog, "deleting all active sessions"); for (i = 1; i < cf->nsessions; i++) { spa = cf->sessions[i]; if (spa == NULL || spa->sidx[0] != i) continue; /* Skip RTCP twin session */ if (spa->rtcp != NULL) { remove_session(cf, spa); } } reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command"); reply_error(cf, controlfd, &raddr, rlen, cookie, 3); return 0; } call_id = argv[1]; if (op == UPDATE || op == LOOKUP || op == PLAY) { max_argc = (op == UPDATE ? 8 : 6); if (argc < 5 || argc > max_argc) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 4); return 0; } from_tag = argv[4]; to_tag = argv[5]; if (op == PLAY && argv[0][1] != '\0') playcount = atoi(argv[0] + 1); if (op == UPDATE && argc > 6) { socket_name_u = argv[6]; if (strncmp("unix:", socket_name_u, 5) == 0) socket_name_u += 5; if (argc == 8) { notify_tag = argv[7]; len = url_unquote((uint8_t *)notify_tag, strlen(notify_tag)); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error - invalid URL encoding"); reply_error(cf, controlfd, &raddr, rlen, cookie, 4); return 0; } notify_tag[len] = '\0'; } } } if (op == COPY) { if (argc < 4 || argc > 5) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } recording_name = argv[2]; from_tag = argv[3]; to_tag = argv[4]; } if (op == DELETE || op == RECORD || op == NOPLAY || op == QUERY) { if (argc < 3 || argc > 4) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } from_tag = argv[2]; to_tag = argv[3]; } if (op == DELETE || op == RECORD || op == COPY || op == NOPLAY) { /* D, R and S commands don't take any modifiers */ if (argv[0][1] != '\0') { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } } if (op == UPDATE || op == LOOKUP || op == DELETE) { addr = argv[2]; port = argv[3]; /* Process additional command modifiers */ external = 1; /* In bridge mode all clients are assumed to be asymmetric */ asymmetric = (cf->bmode != 0) ? 1 : 0; pf = AF_INET; weak = 0; for (cp = argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': asymmetric = 1; break; case 'e': case 'E': if (lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } lia[lidx] = cf->bindaddr[1]; lidx--; break; case 'i': case 'I': if (lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } lia[lidx] = cf->bindaddr[0]; lidx--; break; case '6': pf = AF_INET6; break; case 's': case 'S': asymmetric = 0; break; case 'w': case 'W': weak = 1; break; case 'z': case 'Z': requested_nsamples = (strtol(cp + 1, &cp, 10) / 10) * 80; if (requested_nsamples <= 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } cp--; break; case 'c': case 'C': cp += 1; for (t = cp; *cp != '\0'; cp++) { if (!isdigit(*cp) && *cp != ',') break; } if (t == cp) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } codecs = alloca(cp - t + 1); memcpy(codecs, t, cp - t); codecs[cp - t] = '\0'; cp--; break; case 'l': case 'L': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } c = t[len]; t[len] = '\0'; local_addr = host2bindaddr(cf, t, tpf, &errmsg); if (local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid local address: %s: %s", t, errmsg); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } t[len] = c; cp--; break; case 'r': case 'R': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } c = t[len]; t[len] = '\0'; local_addr = alloca(sizeof(struct sockaddr_storage)); n = resolve(local_addr, tpf, t, SERVICE, AI_PASSIVE); if (n != 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid remote address: %s: %s", t, gai_strerror(n)); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } if (local4remote(cf, local_addr, satoss(local_addr)) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "can't find local address for remote address: %s", t); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } local_addr = addr2bindaddr(cf, local_addr, &errmsg); if (local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid local address: %s", errmsg); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } t[len] = c; cp--; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command modifier `%c'", *cp); break; } } if (op != DELETE && addr != NULL && port != NULL && strlen(addr) >= 7) { struct sockaddr_storage tia; if ((n = resolve(sstosa(&tia), pf, addr, port, AI_NUMERICHOST)) == 0) { if (!ishostnull(sstosa(&tia))) { for (i = 0; i < 2; i++) { ia[i] = malloc(SS_LEN(&tia)); if (ia[i] == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 5, ia, fds, spa, spb); return 0; } memcpy(ia[i], &tia, SS_LEN(&tia)); } /* Set port for RTCP, will work both for IPv4 and IPv6 */ n = ntohs(satosin(ia[1])->sin_port); satosin(ia[1])->sin_port = htons(n + 1); } } else { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "getaddrinfo: %s", gai_strerror(n)); } } } /* * Record and delete need special handling since they apply to all * streams in the session. */ switch (op) { case DELETE: i = handle_delete(cf, call_id, from_tag, to_tag, weak); break; case RECORD: i = handle_record(cf, call_id, from_tag, to_tag); break; default: i = find_stream(cf, call_id, from_tag, to_tag, &spa); if (i != -1 && op != UPDATE) i = NOT(i); break; } if (i == -1 && op != UPDATE) { rtpp_log_write(RTPP_LOG_INFO, cf->glog, "%s request failed: session %s, tags %s/%s not found", rname, call_id, from_tag, to_tag != NULL ? to_tag : "NONE"); if (op == LOOKUP) { for (i = 0; i < 2; i++) if (ia[i] != NULL) free(ia[i]); reply_port(cf, controlfd, &raddr, rlen, cookie, 0, lia); return 0; } reply_error(cf, controlfd, &raddr, rlen, cookie, 8); return 0; } switch (op) { case DELETE: case RECORD: reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case NOPLAY: handle_noplay(cf, spa, i); reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case PLAY: handle_noplay(cf, spa, i); if (strcmp(codecs, "session") == 0) { if (spa->codecs[i] == NULL) { reply_error(cf, controlfd, &raddr, rlen, cookie, 6); return 0; } codecs = spa->codecs[i]; } if (playcount != 0 && handle_play(cf, spa, i, codecs, pname, playcount) != 0) { reply_error(cf, controlfd, &raddr, rlen, cookie, 6); return 0; } reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case COPY: handle_copy(cf, spa, i, recording_name); reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case QUERY: handle_query(cf, controlfd, &raddr, rlen, cookie, spa, i); return 0; case LOOKUP: case UPDATE: /* those are handled below */ break; default: /* Programmatic error, should not happen */ abort(); } pidx = 1; lport = 0; if (i != -1) { assert(op == UPDATE || op == LOOKUP); if (spa->fds[i] == -1) { if (local_addr != NULL) { spa->laddr[i] = local_addr; } if (create_listener(cf, spa->laddr[i], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener"); reply_error(cf, controlfd, &raddr, rlen, cookie, 7); return 0; } assert(spa->fds[i] == -1); spa->fds[i] = fds[0]; assert(spa->rtcp->fds[i] == -1); spa->rtcp->fds[i] = fds[1]; spa->ports[i] = lport; spa->rtcp->ports[i] = lport + 1; spa->complete = spa->rtcp->complete = 1; append_session(cf, spa, i); append_session(cf, spa->rtcp, i); } if (weak) spa->weak[i] = 1; else if (op == UPDATE) spa->strong = 1; lport = spa->ports[i]; lia[0] = spa->laddr[i]; pidx = (i == 0) ? 1 : 0; spa->ttl_mode = cf->ttl_mode; spa->ttl[0] = cf->max_ttl; spa->ttl[1] = cf->max_ttl; if (op == UPDATE) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "adding %s flag to existing session, new=%d/%d/%d", weak ? ( i ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->weak[0], spa->weak[1]); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "lookup on ports %d/%d, session timer restarted", spa->ports[0], spa->ports[1]); } else { assert(op == UPDATE); rtpp_log_write(RTPP_LOG_INFO, cf->glog, "new session %s, tag %s requested, type %s", call_id, from_tag, weak ? "weak" : "strong"); if (local_addr != NULL) { lia[0] = lia[1] = local_addr; if (lia[0] == NULL) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener: %s", t); reply_error(cf, controlfd, &raddr, rlen, cookie, 10); return 0; } } if (create_listener(cf, lia[0], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "can't create listener"); reply_error(cf, controlfd, &raddr, rlen, cookie, 10); return 0; } /* * Session creation. If creation is requested with weak flag, * set weak[0]. */ spa = malloc(sizeof(*spa)); if (spa == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 11, ia, fds, spa, spb); return 0; } /* spb is RTCP twin session for this one. */ spb = malloc(sizeof(*spb)); if (spb == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 12, ia, fds, spa, spb); return 0; } memset(spa, 0, sizeof(*spa)); memset(spb, 0, sizeof(*spb)); for (i = 0; i < 2; i++) { spa->fds[i] = spb->fds[i] = -1; spa->last_update[i] = 0; spb->last_update[i] = 0; } spa->call_id = strdup(call_id); if (spa->call_id == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 13, ia, fds, spa, spb); return 0; } spb->call_id = spa->call_id; spa->tag = strdup(from_tag); if (spa->tag == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 14, ia, fds, spa, spb); return 0; } spb->tag = spa->tag; for (i = 0; i < 2; i++) { spa->rrcs[i] = NULL; spb->rrcs[i] = NULL; spa->laddr[i] = lia[i]; spb->laddr[i] = lia[i]; } spa->strong = spa->weak[0] = spa->weak[1] = 0; if (weak) spa->weak[0] = 1; else spa->strong = 1; assert(spa->fds[0] == -1); spa->fds[0] = fds[0]; assert(spb->fds[0] == -1); spb->fds[0] = fds[1]; spa->ports[0] = lport; spb->ports[0] = lport + 1; spa->ttl[0] = cf->max_ttl; spa->ttl[1] = cf->max_ttl; spb->ttl[0] = -1; spb->ttl[1] = -1; spa->log = rtpp_log_open(cf, "rtpproxy", spa->call_id, 0); spb->log = spa->log; spa->rtcp = spb; spb->rtcp = NULL; spa->rtp = NULL; spb->rtp = spa; spa->sridx = spb->sridx = -1; append_session(cf, spa, 0); append_session(cf, spa, 1); append_session(cf, spb, 0); append_session(cf, spb, 1); hash_table_append(cf, spa); cf->sessions_created++; cf->sessions_active++; /* * Each session can consume up to 5 open file descriptors (2 RTP, * 2 RTCP and 1 logging) so that warn user when he is likely to * exceed 80% mark on hard limit. */ if (cf->sessions_active > (cf->nofile_limit.rlim_max * 80 / (100 * 5)) && cf->nofile_limit_warned == 0) { cf->nofile_limit_warned = 1; rtpp_log_write(RTPP_LOG_WARN, cf->glog, "passed 80%% " "threshold on the open file descriptors limit (%d), " "consider increasing the limit using -L command line " "option", (int)cf->nofile_limit.rlim_max); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "new session on a port %d created, " "tag %s", lport, from_tag); if (cf->record_all != 0) { handle_copy(cf, spa, 0, NULL); handle_copy(cf, spa, 1, NULL); } } if (op == UPDATE) { if (cf->timeout_handler.socket_name == NULL && socket_name_u != NULL) rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n"); if (spa->timeout_data.notify_tag != NULL) { free(spa->timeout_data.notify_tag); spa->timeout_data.notify_tag = NULL; } if (cf->timeout_handler.socket_name != NULL && socket_name_u != NULL) { if (strcmp(cf->timeout_handler.socket_name, socket_name_u) != 0) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "invalid socket name %s", socket_name_u); socket_name_u = NULL; } else { rtpp_log_write(RTPP_LOG_INFO, spa->log, "setting timeout handler"); spa->timeout_data.handler = &cf->timeout_handler; spa->timeout_data.notify_tag = strdup(notify_tag); } } else if (socket_name_u == NULL && spa->timeout_data.handler != NULL) { spa->timeout_data.handler = NULL; rtpp_log_write(RTPP_LOG_INFO, spa->log, "disabling timeout handler"); } } if (ia[0] != NULL && ia[1] != NULL) { if (spa->addr[pidx] != NULL) spa->last_update[pidx] = dtime; if (spa->rtcp->addr[pidx] != NULL) spa->rtcp->last_update[pidx] = dtime; /* * Unless the address provided by client historically * cannot be trusted and address is different from one * that we recorded update it. */ if (spa->untrusted_addr[pidx] == 0 && !(spa->addr[pidx] != NULL && SA_LEN(ia[0]) == SA_LEN(spa->addr[pidx]) && memcmp(ia[0], spa->addr[pidx], SA_LEN(ia[0])) == 0)) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "pre-filling %s's address " "with %s:%s", (pidx == 0) ? "callee" : "caller", addr, port); if (spa->addr[pidx] != NULL) { if (spa->canupdate[pidx] == 0) { if (spa->prev_addr[pidx] != NULL) free(spa->prev_addr[pidx]); spa->prev_addr[pidx] = spa->addr[pidx]; } else { free(spa->addr[pidx]); } } spa->addr[pidx] = ia[0]; ia[0] = NULL; } if (spa->rtcp->untrusted_addr[pidx] == 0 && !(spa->rtcp->addr[pidx] != NULL && SA_LEN(ia[1]) == SA_LEN(spa->rtcp->addr[pidx]) && memcmp(ia[1], spa->rtcp->addr[pidx], SA_LEN(ia[1])) == 0)) { if (spa->rtcp->addr[pidx] != NULL) { if (spa->rtcp->canupdate[pidx] == 0) { if (spa->rtcp->prev_addr[pidx] != NULL) free(spa->rtcp->prev_addr[pidx]); spa->rtcp->prev_addr[pidx] = spa->rtcp->addr[pidx]; } else { free(spa->rtcp->addr[pidx]); } } spa->rtcp->addr[pidx] = ia[1]; ia[1] = NULL; } } spa->asymmetric[pidx] = spa->rtcp->asymmetric[pidx] = asymmetric; spa->canupdate[pidx] = spa->rtcp->canupdate[pidx] = NOT(asymmetric); if (spa->codecs[pidx] != NULL) { free(spa->codecs[pidx]); spa->codecs[pidx] = NULL; } if (codecs != NULL) spa->codecs[pidx] = strdup(codecs); if (requested_nsamples > 0) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "RTP packets from %s " "will be resized to %d milliseconds", (pidx == 0) ? "callee" : "caller", requested_nsamples / 8); } else if (spa->resizers[pidx].output_nsamples > 0) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "Resizing of RTP " "packets from %s has been disabled", (pidx == 0) ? "callee" : "caller"); } spa->resizers[pidx].output_nsamples = requested_nsamples; for (i = 0; i < 2; i++) if (ia[i] != NULL) free(ia[i]); assert(lport != 0); reply_port(cf, controlfd, &raddr, rlen, cookie, lport, lia); return 0; }
int rtpp_command_ul_handle(struct cfg *cf, struct rtpp_command *cmd, struct common_cmd_args *ccap, struct ul_opts *ulop, struct rtpp_session *sp, int sidx) { int pidx, lport, i; int fds[2]; char *cp; struct rtpp_session *spa, *spb; pidx = 1; lport = 0; spa = spb = NULL; fds[0] = fds[1] = -1; if (sidx != -1) { assert(ccap->op == UPDATE || ccap->op == LOOKUP); spa = sp; if (spa->fds[sidx] == -1) { if (ulop->local_addr != NULL) { spa->laddr[sidx] = ulop->local_addr; } if (rtpp_create_listener(cf, spa->laddr[sidx], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener"); reply_error(cf, cmd, ECODE_LSTFAIL_1); goto err_undo_0; } assert(spa->fds[sidx] == -1); spa->fds[sidx] = fds[0]; assert(spa->rtcp->fds[sidx] == -1); spa->rtcp->fds[sidx] = fds[1]; spa->ports[sidx] = lport; spa->rtcp->ports[sidx] = lport + 1; if (spa->complete == 0) { cmd->csp->nsess_complete.cnt++; } spa->complete = spa->rtcp->complete = 1; append_session(cf, spa, sidx); } if (ulop->weak) spa->weak[sidx] = 1; else if (ccap->op == UPDATE) spa->strong = 1; lport = spa->ports[sidx]; ulop->lia[0] = spa->laddr[sidx]; pidx = (sidx == 0) ? 1 : 0; spa->ttl_mode = cf->stable->ttl_mode; spa->ttl[0] = cf->stable->max_ttl; spa->ttl[1] = cf->stable->max_ttl; if (ccap->op == UPDATE) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "adding %s flag to existing session, new=%d/%d/%d", ulop->weak ? ( sidx ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->weak[0], spa->weak[1]); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "lookup on ports %d/%d, session timer restarted", spa->ports[0], spa->ports[1]); } else { assert(ccap->op == UPDATE); rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog, "new session %s, tag %s requested, type %s", ccap->call_id, ccap->from_tag, ulop->weak ? "weak" : "strong"); if (cf->stable->slowshutdown != 0) { rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog, "proxy is in the deorbiting-burn mode, new session rejected"); reply_error(cf, cmd, ECODE_SLOWSHTDN); goto err_undo_0; } if (ulop->local_addr != NULL) { ulop->lia[0] = ulop->lia[1] = ulop->local_addr; } if (rtpp_create_listener(cf, ulop->lia[0], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "can't create listener"); reply_error(cf, cmd, ECODE_LSTFAIL_2); goto err_undo_0; } /* * Session creation. If creation is requested with weak flag, * set weak[0]. */ spa = malloc(sizeof(*spa)); if (spa == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_4, ulop, fds, spa, spb); return (-1); } /* spb is RTCP twin session for this one. */ spb = malloc(sizeof(*spb)); if (spb == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_5, ulop, fds, spa, spb); return (-1); } memset(spa, 0, sizeof(*spa)); memset(spb, 0, sizeof(*spb)); spa->init_ts = cmd->dtime; spb->init_ts = cmd->dtime; for (i = 0; i < 2; i++) { spa->fds[i] = spb->fds[i] = -1; spa->last_update[i] = 0; spb->last_update[i] = 0; } spa->call_id = strdup(ccap->call_id); if (spa->call_id == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_6, ulop, fds, spa, spb); return (-1); } spb->call_id = spa->call_id; spa->tag = strdup(ccap->from_tag); spa->tag_nomedianum = strdup(ccap->from_tag); if (spa->tag == NULL) { handle_nomem(cf, cmd, ECODE_NOMEM_7, ulop, fds, spa, spb); return (-1); } cp = strrchr(spa->tag_nomedianum, ';'); if (cp != NULL) *cp = '\0'; spb->tag = spa->tag; spb->tag_nomedianum = spa->tag_nomedianum; for (i = 0; i < 2; i++) { spa->rrcs[i] = NULL; spb->rrcs[i] = NULL; spa->laddr[i] = ulop->lia[i]; spb->laddr[i] = ulop->lia[i]; } spa->strong = spa->weak[0] = spa->weak[1] = 0; if (ulop->weak) spa->weak[0] = 1; else spa->strong = 1; assert(spa->fds[0] == -1); spa->fds[0] = fds[0]; assert(spb->fds[0] == -1); spb->fds[0] = fds[1]; spa->ports[0] = lport; spb->ports[0] = lport + 1; spa->ttl[0] = cf->stable->max_ttl; spa->ttl[1] = cf->stable->max_ttl; spb->ttl[0] = -1; spb->ttl[1] = -1; spa->log = rtpp_log_open(cf->stable, "rtpproxy", spa->call_id, 0); rtpp_log_setlevel(spa->log, cf->stable->log_level); spb->log = spa->log; spa->rtcp = spb; spb->rtcp = NULL; spa->rtp = NULL; spb->rtp = spa; spa->sridx = spb->sridx = -1; append_session(cf, spa, 0); append_session(cf, spa, 1); spa->hte = CALL_METHOD(cf->stable->sessions_ht, append, spa->call_id, spa); if (spa->hte == NULL) { remove_session(cf, spa); remove_session(cf, spb); handle_nomem(cf, cmd, ECODE_NOMEM_8, ulop, fds, spa, spb); return (-1); } cf->sessions_created++; cf->sessions_active++; cmd->csp->nsess_created.cnt++; /* * Each session can consume up to 5 open file descriptors (2 RTP, * 2 RTCP and 1 logging) so that warn user when he is likely to * exceed 80% mark on hard limit. */ if (cf->sessions_active > (rtpp_rlim_max(cf) * 80 / (100 * 5)) && cf->nofile_limit_warned == 0) { cf->nofile_limit_warned = 1; rtpp_log_write(RTPP_LOG_WARN, cf->stable->glog, "passed 80%% " "threshold on the open file descriptors limit (%d), " "consider increasing the limit using -L command line " "option", (int)rtpp_rlim_max(cf)); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "new session on a port %d created, " "tag %s", lport, ccap->from_tag); if (cf->stable->record_all != 0) { handle_copy(cf, spa, 0, NULL, 0); handle_copy(cf, spa, 1, NULL, 0); } } if (ccap->op == UPDATE) { if (rtpp_th_get_sn(cf->timeout_handler) == NULL && ulop->socket_name_u != NULL) rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n"); if (spa->timeout_data.notify_tag != NULL) { free(spa->timeout_data.notify_tag); spa->timeout_data.notify_tag = NULL; } if (rtpp_th_get_sn(cf->timeout_handler) != NULL && ulop->socket_name_u != NULL) { if (strcmp(rtpp_th_get_sn(cf->timeout_handler), ulop->socket_name_u) != 0) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "invalid socket name %s", ulop->socket_name_u); ulop->socket_name_u = NULL; } else { rtpp_log_write(RTPP_LOG_INFO, spa->log, "setting timeout handler"); spa->timeout_data.handler = cf->timeout_handler; spa->timeout_data.notify_tag = strdup(ulop->notify_tag); } } else if (ulop->socket_name_u == NULL && spa->timeout_data.handler != NULL) { spa->timeout_data.handler = NULL; rtpp_log_write(RTPP_LOG_INFO, spa->log, "disabling timeout handler"); } } if (ulop->ia[0] != NULL && ulop->ia[1] != NULL) { if (spa->addr[pidx] != NULL) spa->last_update[pidx] = cmd->dtime; if (spa->rtcp->addr[pidx] != NULL) spa->rtcp->last_update[pidx] = cmd->dtime; /* * Unless the address provided by client historically * cannot be trusted and address is different from one * that we recorded update it. */ if (spa->untrusted_addr[pidx] == 0 && !(spa->addr[pidx] != NULL && SA_LEN(ulop->ia[0]) == SA_LEN(spa->addr[pidx]) && memcmp(ulop->ia[0], spa->addr[pidx], SA_LEN(ulop->ia[0])) == 0)) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "pre-filling %s's address " "with %s:%s", (pidx == 0) ? "callee" : "caller", ulop->addr, ulop->port); if (spa->addr[pidx] != NULL) { if (spa->canupdate[pidx] == 0) { if (spa->prev_addr[pidx] != NULL) free(spa->prev_addr[pidx]); spa->prev_addr[pidx] = spa->addr[pidx]; } else { free(spa->addr[pidx]); } } spa->addr[pidx] = ulop->ia[0]; ulop->ia[0] = NULL; } if (spa->rtcp->untrusted_addr[pidx] == 0 && !(spa->rtcp->addr[pidx] != NULL && SA_LEN(ulop->ia[1]) == SA_LEN(spa->rtcp->addr[pidx]) && memcmp(ulop->ia[1], spa->rtcp->addr[pidx], SA_LEN(ulop->ia[1])) == 0)) { if (spa->rtcp->addr[pidx] != NULL) { if (spa->rtcp->canupdate[pidx] == 0) { if (spa->rtcp->prev_addr[pidx] != NULL) free(spa->rtcp->prev_addr[pidx]); spa->rtcp->prev_addr[pidx] = spa->rtcp->addr[pidx]; } else { free(spa->rtcp->addr[pidx]); } } spa->rtcp->addr[pidx] = ulop->ia[1]; ulop->ia[1] = NULL; } } spa->asymmetric[pidx] = spa->rtcp->asymmetric[pidx] = ulop->asymmetric; spa->canupdate[pidx] = spa->rtcp->canupdate[pidx] = NOT(ulop->asymmetric); if (spa->codecs[pidx] != NULL) { free(spa->codecs[pidx]); spa->codecs[pidx] = NULL; } if (ulop->codecs != NULL) { spa->codecs[pidx] = ulop->codecs; ulop->codecs = NULL; } if (ulop->requested_nsamples > 0) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "RTP packets from %s " "will be resized to %d milliseconds", (pidx == 0) ? "callee" : "caller", ulop->requested_nsamples / 8); } else if (spa->resizers[pidx] != NULL) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "Resizing of RTP " "packets from %s has been disabled", (pidx == 0) ? "callee" : "caller"); } if (ulop->requested_nsamples > 0) { if (spa->resizers[pidx] != NULL) { rtp_resizer_set_onsamples(spa->resizers[pidx], ulop->requested_nsamples); } else { spa->resizers[pidx] = rtp_resizer_new(ulop->requested_nsamples); } } else if (spa->resizers[pidx] != NULL) { rtp_resizer_free(spa->resizers[pidx]); spa->resizers[pidx] = NULL; } assert(lport != 0); ulop->reply.port = lport; ulop->reply.ia = ulop->lia[0]; if (cf->stable->advaddr[0] != NULL) { if (cf->stable->bmode != 0 && cf->stable->advaddr[1] != NULL && ulop->lia[0] == cf->stable->bindaddr[1]) { ulop->reply.ia_ov = cf->stable->advaddr[1]; } else { ulop->reply.ia_ov = cf->stable->advaddr[0]; } } ul_reply_port(cf, cmd, &ulop->reply); rtpp_command_ul_opts_free(ulop); return (0); err_undo_0: rtpp_command_ul_opts_free(ulop); return (-1); }
int handle_command(struct cfg *cf, struct rtpp_command *cmd) { int i, verbose, rval; int playcount; char *cp, *tcp; char *pname, *codecs, *recording_name; struct rtpp_session *spa; int record_single_file; struct ul_opts *ulop; struct d_opts dopt; spa = NULL; recording_name = NULL; codecs = NULL; /* Step II: parse parameters that are specific to a particular op and run simple ops */ switch (cmd->cca.op) { case VER_FEATURE: handle_ver_feature(cf, cmd); return 0; case GET_VER: /* This returns base version. */ reply_number(cf, cmd, CPROTOVER); return 0; case DELETE_ALL: /* Delete all active sessions */ rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog, "deleting all active sessions"); pthread_mutex_lock(&cf->sessinfo.lock); for (i = 0; i < cf->sessinfo.nsessions; i++) { spa = cf->sessinfo.sessions[i]; if (spa == NULL || spa->sidx[0] != i) continue; remove_session(cf, spa); } pthread_mutex_unlock(&cf->sessinfo.lock); reply_ok(cf, cmd); return 0; case INFO: handle_info(cf, cmd, &cmd->argv[0][1]); return 0; case PLAY: /* * P callid pname codecs from_tag to_tag * * <codecs> could be either comma-separated list of supported * payload types or word "session" (without quotes), in which * case list saved on last session update will be used instead. */ playcount = 1; pname = cmd->argv[2]; codecs = cmd->argv[3]; tcp = &(cmd->argv[0][1]); if (*tcp != '\0') { playcount = strtol(tcp, &cp, 10); if (cp == tcp || *cp != '\0') { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_6); return 0; } } break; case COPY: recording_name = cmd->argv[2]; /* Fallthrough */ case RECORD: if (cmd->argv[0][1] == 'S' || cmd->argv[0][1] == 's') { if (cmd->argv[0][2] != '\0') { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_2); return 0; } record_single_file = (cf->stable->record_pcap == 0) ? 0 : 1; } else { if (cmd->argv[0][1] != '\0') { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_3); return 0; } record_single_file = 0; } break; case DELETE: /* D[w] call_id from_tag [to_tag] */ dopt.weak = 0; for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'w': case 'W': dopt.weak = 1; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "DELETE: unknown command modifier `%c'", *cp); reply_error(cf, cmd, ECODE_PARSE_4); return 0; } } break; case UPDATE: case LOOKUP: ulop = rtpp_command_ul_opts_parse(cf, cmd); if (ulop == NULL) { return 0; } break; case GET_STATS: verbose = 0; for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'v': case 'V': verbose = 1; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "STATS: unknown command modifier `%c'", *cp); reply_error(cf, cmd, ECODE_PARSE_5); return 0; } } i = handle_get_stats(cf, cmd, verbose); if (i != 0) { reply_error(cf, cmd, i); } return 0; default: break; } /* * Record and delete need special handling since they apply to all * streams in the session. */ switch (cmd->cca.op) { case DELETE: i = handle_delete(cf, &cmd->cca, dopt.weak); break; case RECORD: i = handle_record(cf, &cmd->cca, record_single_file); break; default: i = find_stream(cf, cmd->cca.call_id, cmd->cca.from_tag, cmd->cca.to_tag, &spa); if (i != -1 && cmd->cca.op != UPDATE) i = NOT(i); break; } if (i == -1 && cmd->cca.op != UPDATE) { rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog, "%s request failed: session %s, tags %s/%s not found", cmd->cca.rname, cmd->cca.call_id, cmd->cca.from_tag, cmd->cca.to_tag != NULL ? cmd->cca.to_tag : "NONE"); if (cmd->cca.op == LOOKUP) { rtpp_command_ul_opts_free(ulop); ul_reply_port(cf, cmd, NULL); return 0; } reply_error(cf, cmd, ECODE_SESUNKN); return 0; } switch (cmd->cca.op) { case DELETE: case RECORD: reply_ok(cf, cmd); break; case NOPLAY: handle_noplay(cf, spa, i, cmd); reply_ok(cf, cmd); break; case PLAY: handle_noplay(cf, spa, i, cmd); if (strcmp(codecs, "session") == 0) { if (spa->codecs[i] == NULL) { reply_error(cf, cmd, ECODE_INVLARG_5); return 0; } codecs = spa->codecs[i]; } if (playcount != 0 && handle_play(cf, spa, i, codecs, pname, playcount, cmd) != 0) { reply_error(cf, cmd, ECODE_PLRFAIL); return 0; } reply_ok(cf, cmd); break; case COPY: if (handle_copy(cf, spa, i, recording_name, record_single_file) != 0) { reply_error(cf, cmd, ECODE_CPYFAIL); return 0; } reply_ok(cf, cmd); break; case QUERY: rval = handle_query(cf, cmd, spa, i); if (rval != 0) { reply_error(cf, cmd, rval); } break; case LOOKUP: case UPDATE: rtpp_command_ul_handle(cf, cmd, ulop, spa, i); break; default: /* Programmatic error, should not happen */ abort(); } return 0; }