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); }
static void rtpp_cmd_queue_run(void *arg) { struct rtpp_cmd_async_cf *cmd_cf; struct rtpp_cmd_pollset *psp; int i, nready, rval; double sptime; #if 0 double eptime, tused; #endif struct rtpp_command_stats *csp; struct rtpp_stats_obj *rtpp_stats_cf; cmd_cf = (struct rtpp_cmd_async_cf *)arg; rtpp_stats_cf = cmd_cf->cf_save->stable->rtpp_stats; csp = &cmd_cf->cstats; psp = &cmd_cf->pset; for (;;) { sptime = getdtime(); pthread_mutex_lock(&psp->pfds_mutex); if (psp->pfds_used == 0) { pthread_mutex_unlock(&psp->pfds_mutex); if (wait_next_clock(cmd_cf) == TSTATE_CEASE) { break; } continue; } nready = poll(psp->pfds, psp->pfds_used, 2); if (nready == 0) { pthread_mutex_unlock(&psp->pfds_mutex); if (wait_next_clock(cmd_cf) == TSTATE_CEASE) { break; } continue; } if (nready < 0 && errno == EINTR) { pthread_mutex_unlock(&psp->pfds_mutex); continue; } if (nready > 0) { for (i = 0; i < psp->pfds_used; i++) { if ((psp->pfds[i].revents & (POLLERR | POLLHUP)) != 0) { if (RTPP_CTRL_ACCEPTABLE(psp->rccs[i]->csock)) { goto closefd; } if (psp->rccs[i]->csock->type == RTPC_STDIO && (psp->pfds[i].revents & POLLIN) == 0) { goto closefd; } } if ((psp->pfds[i].revents & POLLIN) == 0) { continue; } if (RTPP_CTRL_ISSTREAM(psp->rccs[i]->csock)) { rval = process_commands_stream(cmd_cf->cf_save, psp->rccs[i], sptime, csp, rtpp_stats_cf); } else { rval = process_commands(psp->rccs[i]->csock, cmd_cf->cf_save, psp->pfds[i].fd, sptime, csp, rtpp_stats_cf, cmd_cf->rcache); } /* * Shut down non-datagram sockets that got I/O error * and also all non-continuous UNIX sockets are recycled * after each use. */ if (!RTPP_CTRL_ISDG(psp->rccs[i]->csock) && (rval == -1 || !RTPP_CTRL_ISSTREAM(psp->rccs[i]->csock))) { closefd: if (psp->rccs[i]->csock->type == RTPC_STDIO && psp->rccs[i]->csock->exit_on_close != 0) { cmd_cf->cf_save->stable->slowshutdown = 1; } rtpp_cmd_connection_dtor(psp->rccs[i]); psp->pfds_used--; if (psp->pfds_used > 0 && i < psp->pfds_used) { memcpy(&psp->pfds[i], &psp->pfds[i + 1], (psp->pfds_used - i) * sizeof(struct pollfd)); memcpy(&psp->rccs[i], &psp->rccs[i + 1], (psp->pfds_used - i) * sizeof(struct rtpp_ctrl_connection *)); } } } } pthread_mutex_unlock(&psp->pfds_mutex); if (nready > 0) { rtpp_anetio_pump(cmd_cf->cf_save->stable->rtpp_netio_cf); } #if 0 eptime = getdtime(); pthread_mutex_lock(&cmd_cf->cmd_mutex); recfilter_apply(&cmd_cf->average_load, (eptime - sptime + tused) * cmd_cf->cf_save->stable->target_pfreq); pthread_mutex_unlock(&cmd_cf->cmd_mutex); #endif flush_cstats(rtpp_stats_cf, csp); #if 0 #if RTPP_DEBUG if (last_ctick % (unsigned int)cmd_cf->cf_save->stable->target_pfreq == 0 || last_ctick < 1000) { rtpp_log_write(RTPP_LOG_DBUG, cmd_cf->cf_save->stable->glog, "rtpp_cmd_queue_run %lld sptime %f eptime %f, CSV: %f,%f,%f,%f,%f", \ last_ctick, sptime, eptime, (double)last_ctick / cmd_cf->cf_save->stable->target_pfreq, \ eptime - sptime + tused, eptime, sptime, tused); rtpp_log_write(RTPP_LOG_DBUG, cmd_cf->cf_save->stable->glog, "run %lld average load %f, CSV: %f,%f", last_ctick, \ cmd_cf->average_load.lastval * 100.0, (double)last_ctick / cmd_cf->cf_save->stable->target_pfreq, cmd_cf->average_load.lastval); } #endif #endif } }
void remove_session(struct cfg *cf, struct rtpp_session *sp) { int i; /* Make sure structure is properly locked */ assert(pthread_mutex_islocked(&cf->glock) == 1); assert(pthread_mutex_islocked(&cf->sessinfo.lock) == 1); rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->pcount[0], sp->pcount[1], sp->pcount[2], sp->pcount[3]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTCP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->rtcp->pcount[0], sp->rtcp->pcount[1], sp->rtcp->pcount[2], sp->rtcp->pcount[3]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "session on ports %d/%d is cleaned up", sp->ports[0], sp->ports[1]); for (i = 0; i < 2; i++) { if (sp->addr[i] != NULL) free(sp->addr[i]); if (sp->prev_addr[i] != NULL) free(sp->prev_addr[i]); if (sp->rtcp->addr[i] != NULL) free(sp->rtcp->addr[i]); if (sp->rtcp->prev_addr[i] != NULL) free(sp->rtcp->prev_addr[i]); if (sp->fds[i] != -1) { close(sp->fds[i]); assert(cf->sessinfo.sessions[sp->sidx[i]] == sp); cf->sessinfo.sessions[sp->sidx[i]] = NULL; assert(cf->sessinfo.pfds[sp->sidx[i]].fd == sp->fds[i]); cf->sessinfo.pfds[sp->sidx[i]].fd = -1; cf->sessinfo.pfds[sp->sidx[i]].events = 0; } if (sp->rtcp->fds[i] != -1) { close(sp->rtcp->fds[i]); assert(cf->sessinfo.sessions[sp->rtcp->sidx[i]] == sp->rtcp); cf->sessinfo.sessions[sp->rtcp->sidx[i]] = NULL; assert(cf->sessinfo.pfds[sp->rtcp->sidx[i]].fd == sp->rtcp->fds[i]); cf->sessinfo.pfds[sp->rtcp->sidx[i]].fd = -1; cf->sessinfo.pfds[sp->rtcp->sidx[i]].events = 0; } if (sp->rrcs[i] != NULL) rclose(sp, sp->rrcs[i], 1); if (sp->rtcp->rrcs[i] != NULL) rclose(sp, sp->rtcp->rrcs[i], 1); if (sp->rtps[i] != NULL) { cf->rtp_servers[sp->sridx] = NULL; rtp_server_free(sp->rtps[i]); } if (sp->codecs[i] != NULL) free(sp->codecs[i]); if (sp->rtcp->codecs[i] != NULL) free(sp->rtcp->codecs[i]); } if (sp->timeout_data.notify_tag != NULL) free(sp->timeout_data.notify_tag); hash_table_remove(cf, sp); if (sp->call_id != NULL) free(sp->call_id); if (sp->tag != NULL) free(sp->tag); rtpp_log_close(sp->log); free(sp->rtcp); rtp_resizer_free(&sp->resizers[0]); rtp_resizer_free(&sp->resizers[1]); free(sp); cf->sessions_active--; }
struct ul_opts * rtpp_command_ul_opts_parse(struct cfg *cf, struct rtpp_command *cmd, struct common_cmd_args *ccap) { int len, tpf, n, i; char c; char *cp, *t; const char *errmsg; struct sockaddr_storage tia; struct ul_opts *ulop; ulop = malloc(sizeof(struct ul_opts)); if (ulop == NULL) { reply_error(cf, cmd, ECODE_NOMEM_1); goto err_undo_0; } memset(ulop, '\0', sizeof(struct ul_opts)); ul_opts_init(cf, ulop); if (ccap->op == UPDATE && cmd->argc > 6) { if (cmd->argc == 8) { ulop->socket_name_u = cmd->argv[6]; ulop->notify_tag = cmd->argv[7]; } else { ulop->socket_name_u = cmd->argv[5]; ulop->notify_tag = cmd->argv[6]; ccap->to_tag = NULL; } if (strncmp("unix:", ulop->socket_name_u, 5) == 0) ulop->socket_name_u += 5; len = url_unquote((uint8_t *)ulop->notify_tag, strlen(ulop->notify_tag)); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error - invalid URL encoding"); reply_error(cf, cmd, ECODE_PARSE_10); goto err_undo_1; } ulop->notify_tag[len] = '\0'; } ulop->addr = cmd->argv[2]; ulop->port = cmd->argv[3]; /* Process additional command modifiers */ for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': ulop->asymmetric = 1; break; case 'e': case 'E': if (ulop->lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_11); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[1]; ulop->lidx--; break; case 'i': case 'I': if (ulop->lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_12); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[0]; ulop->lidx--; break; case '6': ulop->pf = AF_INET6; break; case 's': case 'S': ulop->asymmetric = 0; break; case 'w': case 'W': ulop->weak = 1; break; case 'z': case 'Z': ulop->requested_nsamples = (strtol(cp + 1, &cp, 10) / 10) * 80; if (ulop->requested_nsamples <= 0) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_13); goto err_undo_1; } 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->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_14); goto err_undo_1; } ulop->codecs = malloc(cp - t + 1); if (ulop->codecs == NULL) { reply_error(cf, cmd, ECODE_NOMEM_2); goto err_undo_1; } memcpy(ulop->codecs, t, cp - t); ulop->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->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_15); goto err_undo_1; } c = t[len]; t[len] = '\0'; pthread_mutex_unlock(&cf->glock); ulop->local_addr = host2bindaddr(cf, t, tpf, &errmsg); pthread_mutex_lock(&cf->glock); if (ulop->local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "invalid local address: %s: %s", t, errmsg); reply_error(cf, cmd, ECODE_INVLARG_1); goto err_undo_1; } 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->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_16); goto err_undo_1; } c = t[len]; t[len] = '\0'; ulop->local_addr = alloca(sizeof(struct sockaddr_storage)); pthread_mutex_unlock(&cf->glock); n = resolve(ulop->local_addr, tpf, t, SERVICE, AI_PASSIVE); pthread_mutex_lock(&cf->glock); if (n != 0) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "invalid remote address: %s: %s", t, gai_strerror(n)); reply_error(cf, cmd, ECODE_INVLARG_2); goto err_undo_1; } if (local4remote(ulop->local_addr, satoss(ulop->local_addr)) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "can't find local address for remote address: %s", t); reply_error(cf, cmd, ECODE_INVLARG_3); goto err_undo_1; } ulop->local_addr = addr2bindaddr(cf, ulop->local_addr, &errmsg); if (ulop->local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "invalid local address: %s", errmsg); reply_error(cf, cmd, ECODE_INVLARG_4); goto err_undo_1; } t[len] = c; cp--; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "unknown command modifier `%c'", *cp); break; } } if (ulop->addr != NULL && ulop->port != NULL && strlen(ulop->addr) >= 7) { pthread_mutex_unlock(&cf->glock); n = resolve(sstosa(&tia), ulop-> pf, ulop->addr, ulop->port, AI_NUMERICHOST); pthread_mutex_lock(&cf->glock); if (n == 0) { if (!ishostnull(sstosa(&tia))) { for (i = 0; i < 2; i++) { ulop->ia[i] = malloc(SS_LEN(&tia)); if (ulop->ia[i] == NULL) { reply_error(cf, cmd, ECODE_NOMEM_3); goto err_undo_1; } memcpy(ulop->ia[i], &tia, SS_LEN(&tia)); } /* Set port for RTCP, will work both for IPv4 and IPv6 */ n = ntohs(satosin(ulop->ia[1])->sin_port); satosin(ulop->ia[1])->sin_port = htons(n + 1); } } else { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "getaddrinfo: %s", gai_strerror(n)); } } return (ulop); err_undo_1: rtpp_command_ul_opts_free(ulop); err_undo_0: return (NULL); }
static void process_rtp(struct cfg *cf, double dtime, int alarm_tick) { int readyfd, skipfd, ridx; struct rtpp_session *sp; struct rtp_packet *packet; /* Relay RTP/RTCP */ skipfd = 0; pthread_mutex_lock(&cf->sessinfo.lock); for (readyfd = 0; readyfd < cf->sessinfo.nsessions; readyfd++) { sp = cf->sessinfo.sessions[readyfd]; if (alarm_tick != 0 && sp != NULL && sp->rtcp != NULL && sp->sidx[0] == readyfd) { if (get_ttl(sp) == 0) { rtpp_log_write(RTPP_LOG_INFO, sp->log, "session timeout"); rtpp_notify_schedule(cf, sp); remove_session(cf, sp); } else { if (sp->ttl[0] != 0) sp->ttl[0]--; if (sp->ttl[1] != 0) sp->ttl[1]--; } } if (cf->sessinfo.pfds[readyfd].fd == -1) { /* Deleted session, count and move one */ skipfd++; continue; } /* Find index of the call leg within a session */ for (ridx = 0; ridx < 2; ridx++) if (cf->sessinfo.pfds[readyfd].fd == sp->fds[ridx]) break; /* * Can't happen. */ assert(ridx != 2); /* Compact pfds[] and sessions[] by eliminating removed sessions */ if (skipfd > 0) { cf->sessinfo.pfds[readyfd - skipfd] = cf->sessinfo.pfds[readyfd]; cf->sessinfo.sessions[readyfd - skipfd] = cf->sessinfo.sessions[readyfd]; sp->sidx[ridx] = readyfd - skipfd; } if (sp->complete != 0) { if ((cf->sessinfo.pfds[readyfd].revents & POLLIN) != 0) rxmit_packets(cf, sp, ridx, dtime); if (sp->resizers[ridx].output_nsamples > 0) { while ((packet = rtp_resizer_get(&sp->resizers[ridx], dtime)) != NULL) { send_packet(cf, sp, ridx, packet); rtp_packet_free(packet); } } } } /* Trim any deleted sessions at the end */ cf->sessinfo.nsessions -= skipfd; pthread_mutex_unlock(&cf->sessinfo.lock); }
void remove_session(struct cfg *cf, struct rtpp_session *sp) { int i; double session_time; session_time = getdtime() - sp->init_ts; /* Make sure structure is properly locked */ assert(pthread_mutex_islocked(&cf->glock) == 1); assert(pthread_mutex_islocked(&cf->sessinfo.lock) == 1); rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->pcount[0], sp->pcount[1], sp->pcount[2], sp->pcount[3]); if (sp->pcount[0] == 0 && sp->pcount[1] == 0) { CALL_METHOD(cf->stable->rtpp_stats, updatebyname, "nsess_nortp", 1); } else if (sp->pcount[0] == 0 || sp->pcount[1] == 0) { CALL_METHOD(cf->stable->rtpp_stats, updatebyname, "nsess_owrtp", 1); } rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTCP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->rtcp->pcount[0], sp->rtcp->pcount[1], sp->rtcp->pcount[2], sp->rtcp->pcount[3]); if (sp->rtcp->pcount[0] == 0 && sp->rtcp->pcount[1] == 0) { CALL_METHOD(cf->stable->rtpp_stats, updatebyname, "nsess_nortcp", 1); } else if (sp->rtcp->pcount[0] == 0 || sp->rtcp->pcount[1] == 0) { CALL_METHOD(cf->stable->rtpp_stats, updatebyname, "nsess_owrtcp", 1); } rtpp_log_write(RTPP_LOG_INFO, sp->log, "session on ports %d/%d is cleaned up", sp->ports[0], sp->ports[1]); for (i = 0; i < 2; i++) { if (sp->addr[i] != NULL) free(sp->addr[i]); if (sp->prev_addr[i] != NULL) free(sp->prev_addr[i]); if (sp->rtcp->addr[i] != NULL) free(sp->rtcp->addr[i]); if (sp->rtcp->prev_addr[i] != NULL) free(sp->rtcp->prev_addr[i]); if (sp->fds[i] != -1) { shutdown(sp->fds[i], SHUT_RDWR); close(sp->fds[i]); assert(cf->sessinfo.sessions[sp->sidx[i]] == sp); cf->sessinfo.sessions[sp->sidx[i]] = NULL; assert(cf->sessinfo.pfds_rtp[sp->sidx[i]].fd == sp->fds[i]); cf->sessinfo.pfds_rtp[sp->sidx[i]].fd = -1; cf->sessinfo.pfds_rtp[sp->sidx[i]].events = 0; } if (sp->rtcp->fds[i] != -1) { shutdown(sp->rtcp->fds[i], SHUT_RDWR); close(sp->rtcp->fds[i]); assert(cf->sessinfo.pfds_rtcp[sp->rtcp->sidx[i]].fd == sp->rtcp->fds[i]); cf->sessinfo.pfds_rtcp[sp->rtcp->sidx[i]].fd = -1; cf->sessinfo.pfds_rtcp[sp->rtcp->sidx[i]].events = 0; } if (sp->rrcs[i] != NULL) { rclose(sp, sp->rrcs[i], 1); if (sp->record_single_file != 0) { sp->rtcp->rrcs[i] = NULL; sp->rrcs[NOT(i)] = NULL; sp->rtcp->rrcs[NOT(i)] = NULL; } } if (sp->rtcp->rrcs[i] != NULL) rclose(sp, sp->rtcp->rrcs[i], 1); if (sp->rtps[i] != NULL) { cf->rtp_servers[sp->sridx] = NULL; rtp_server_free(sp->rtps[i]); } if (sp->codecs[i] != NULL) free(sp->codecs[i]); if (sp->rtcp->codecs[i] != NULL) free(sp->rtcp->codecs[i]); if (sp->resizers[i] != NULL) rtp_resizer_free(sp->resizers[i]); } if (sp->timeout_data.notify_tag != NULL) free(sp->timeout_data.notify_tag); if (sp->hte != NULL) CALL_METHOD(cf->stable->sessions_ht, remove, sp->call_id, sp->hte); if (sp->call_id != NULL) free(sp->call_id); if (sp->tag != NULL) free(sp->tag); if (sp->tag_nomedianum != NULL) free(sp->tag_nomedianum); rtpp_log_close(sp->log); free(sp->rtcp); free(sp); cf->sessions_active--; CALL_METHOD(cf->stable->rtpp_stats, updatebyname, "nsess_destroyed", 1); CALL_METHOD(cf->stable->rtpp_stats, updatebyname_d, "total_duration", session_time); }
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; }
void * ropen(struct cfg *cf, struct rtpp_session *sp, char *rname, int orig) { struct rtpp_record_channel *rrc; const char *sdir, *suffix1, *suffix2; char *cp, *tmp; int n, port, rval, remote; struct sockaddr_storage raddr; pcap_hdr_t pcap_hdr; remote = (rname != NULL && strncmp("udp:", rname, 4) == 0) ? 1 : 0; rrc = malloc(sizeof(*rrc)); if (rrc == NULL) { rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "can't allocate memory"); return NULL; } memset(rrc, 0, sizeof(*rrc)); if (remote) { tmp = strdup(rname + 4); if (tmp == NULL) { rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "can't allocate memory"); return NULL; } rrc->mode = MODE_REMOTE_RTP; rrc->needspool = 0; cp = strrchr(tmp, ':'); if (cp == NULL) { rtpp_log_write(RTPP_LOG_ERR, sp->log, "remote recording target specification should include port number"); free(rrc); free(tmp); return NULL; } *cp = '\0'; cp++; if (sp->rtcp == NULL) { /* Handle RTCP (increase target port by 1) */ port = atoi(cp); if (port <= 0 || port > ((sp->rtcp != NULL) ? 65534 : 65535)) { rtpp_log_write(RTPP_LOG_ERR, sp->log, "invalid port in the remote recording target specification"); free(rrc); free(tmp); return NULL; } sprintf(cp, "%d", port + 1); } n = resolve(sstosa(&raddr), AF_INET, tmp, cp, AI_PASSIVE); if (n != 0) { rtpp_log_write(RTPP_LOG_ERR, sp->log, "ropen: getaddrinfo: %s", gai_strerror(n)); free(rrc); free(tmp); return NULL; } rrc->fd = socket(AF_INET, SOCK_DGRAM, 0); if (rrc->fd == -1) { rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "ropen: can't create socket"); free(rrc); free(tmp); return NULL; } if (connect(rrc->fd, sstosa(&raddr), SA_LEN(sstosa(&raddr))) == -1) { rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "ropen: can't connect socket"); close(rrc->fd); free(rrc); free(tmp); return NULL; } free(tmp); return (void *)(rrc); } if (cf->stable.rdir == NULL) { rtpp_log_write(RTPP_LOG_ERR, sp->log, "directory for saving local recordings is not configured"); free(rrc); return NULL; } if (cf->stable.record_pcap != 0) { rrc->mode = MODE_LOCAL_PCAP; } else { rrc->mode = MODE_LOCAL_PKT; } if (sp->record_single_file != 0) { suffix1 = suffix2 = ""; } else { suffix1 = (orig != 0) ? ".o" : ".a"; suffix2 = (sp->rtcp != NULL) ? ".rtp" : ".rtcp"; } if (cf->stable.sdir == NULL) { sdir = cf->stable.rdir; rrc->needspool = 0; } else { sdir = cf->stable.sdir; rrc->needspool = 1; if (rname == NULL) { sprintf(rrc->rpath, "%s/%s=%s%s%s", cf->stable.rdir, sp->call_id, sp->tag_nomedianum, suffix1, suffix2); } else { sprintf(rrc->rpath, "%s/%s%s", cf->stable.rdir, rname, suffix2); } } if (rname == NULL) { sprintf(rrc->spath, "%s/%s=%s%s%s", sdir, sp->call_id, sp->tag_nomedianum, suffix1, suffix2); } else { sprintf(rrc->spath, "%s/%s%s", sdir, rname, suffix2); } rrc->fd = open(rrc->spath, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); if (rrc->fd == -1) { rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "can't open file %s for writing", rrc->spath); free(rrc); return NULL; } if (rrc->mode == MODE_LOCAL_PCAP) { pcap_hdr.magic_number = PCAP_MAGIC; pcap_hdr.version_major = PCAP_VER_MAJR; pcap_hdr.version_minor = PCAP_VER_MINR; pcap_hdr.thiszone = 0; pcap_hdr.sigfigs = 0; pcap_hdr.snaplen = 65535; pcap_hdr.network = PCAP_FORMAT; rval = write(rrc->fd, &pcap_hdr, sizeof(pcap_hdr)); if (rval == -1) { close(rrc->fd); rtpp_log_ewrite(RTPP_LOG_ERR, sp->log, "%s: error writing header", rrc->spath); free(rrc); return NULL; } if (rval < sizeof(pcap_hdr)) { close(rrc->fd); rtpp_log_write(RTPP_LOG_ERR, sp->log, "%s: short write writing header", rrc->spath); free(rrc); return NULL; } } return (void *)(rrc); }
int get_command(struct cfg_stable *cfs, int controlfd, struct rtpp_command *cmd) { char **ap; char *cp; int len, i; if (cfs->umode == 0) { for (;;) { len = read(controlfd, cmd->buf, sizeof(cmd->buf) - 1); if (len != -1 || (errno != EAGAIN && errno != EINTR)) break; sched_yield(); } } else { cmd->rlen = sizeof(cmd->raddr); len = recvfrom(controlfd, cmd->buf, sizeof(cmd->buf) - 1, 0, sstosa(&cmd->raddr), &cmd->rlen); } if (len == -1) { if (errno != EAGAIN && errno != EINTR) rtpp_log_ewrite(RTPP_LOG_ERR, cfs->glog, "can't read from control socket"); return (-1); } cmd->buf[len] = '\0'; rtpp_log_write(RTPP_LOG_DBUG, cfs->glog, "received command \"%s\"", cmd->buf); cp = cmd->buf; cmd->argc = 0; memset(cmd->argv, 0, sizeof(cmd->argv)); for (ap = cmd->argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) if (**ap != '\0') { cmd->argc++; if (++ap >= &cmd->argv[10]) break; } cmd->cookie = NULL; if (cmd->argc < 1 || (cfs->umode != 0 && cmd->argc < 2)) { rtpp_log_write(RTPP_LOG_ERR, cfs->glog, "command syntax error"); reply_error(cfs, controlfd, cmd, 0); return (0); } /* Stream communication mode doesn't use cookie */ if (cfs->umode != 0) { cmd->cookie = cmd->argv[0]; for (i = 1; i < cmd->argc; i++) cmd->argv[i - 1] = cmd->argv[i]; cmd->argc--; cmd->argv[cmd->argc] = NULL; } else { cmd->cookie = NULL; } return (cmd->argc); }
struct rtpp_command * get_command(struct cfg *cf, int controlfd, int *rval, double dtime, struct rtpp_command_stats *csp, int umode, struct rtpp_cmd_rcache_obj *rcache_obj) { char **ap; char *cp; int len, i; struct rtpp_command *cmd; cmd = malloc(sizeof(struct rtpp_command)); if (cmd == NULL) { *rval = ENOMEM; return (NULL); } memset(cmd, 0, sizeof(struct rtpp_command)); cmd->controlfd = controlfd; cmd->dtime = dtime; cmd->csp = csp; cmd->umode = umode; if (umode == 0) { for (;;) { len = read(controlfd, cmd->buf, sizeof(cmd->buf) - 1); if (len != -1 || (errno != EAGAIN && errno != EINTR)) break; } } else { cmd->rlen = sizeof(cmd->raddr); len = recvfrom(controlfd, cmd->buf, sizeof(cmd->buf) - 1, 0, sstosa(&cmd->raddr), &cmd->rlen); } if (len == -1) { if (errno != EAGAIN && errno != EINTR) rtpp_log_ewrite(RTPP_LOG_ERR, cf->stable->glog, "can't read from control socket"); free(cmd); *rval = -1; return (NULL); } cmd->buf[len] = '\0'; rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "received command \"%s\"", cmd->buf); csp->ncmds_rcvd.cnt++; cp = cmd->buf; for (ap = cmd->argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) { if (**ap != '\0') { cmd->argc++; if (++ap >= &cmd->argv[RTPC_MAX_ARGC]) break; } } if (cmd->argc < 1 || (umode != 0 && cmd->argc < 2)) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_1); *rval = 0; free(cmd); return (NULL); } /* Stream communication mode doesn't use cookie */ if (umode != 0) { cmd->cookie = cmd->argv[0]; if (CALL_METHOD(rcache_obj, lookup, cmd->cookie, cmd->buf_r, sizeof(cmd->buf_r)) == 1) { len = strlen(cmd->buf_r); rtpp_anetio_sendto(cf->stable->rtpp_netio_cf, cmd->controlfd, cmd->buf_r, len, 0, sstosa(&cmd->raddr), cmd->rlen); csp->ncmds_rcvd.cnt--; csp->ncmds_rcvd_ndups.cnt++; *rval = 0; free(cmd); return (NULL); } cmd->rcache_obj = rcache_obj; for (i = 1; i < cmd->argc; i++) cmd->argv[i - 1] = cmd->argv[i]; cmd->argc--; cmd->argv[cmd->argc] = NULL; } /* Step I: parse parameters that are common to all ops */ if (rtpp_command_pre_parse(cf, cmd) != 0) { /* Error reply is handled by the rtpp_command_pre_parse() */ *rval = 0; free(cmd); return (NULL); } return (cmd); }
void remove_session(struct cfg *cf, struct rtpp_session *sp) { int i; rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->pcount[0], sp->pcount[1], sp->pcount[2], sp->pcount[3]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "RTCP stats: %lu in from callee, %lu " "in from caller, %lu relayed, %lu dropped", sp->rtcp->pcount[0], sp->rtcp->pcount[1], sp->rtcp->pcount[2], sp->rtcp->pcount[3]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "session on ports %d/%d is cleaned up", sp->ports[0], sp->ports[1]); for (i = 0; i < 2; i++) { if (sp->addr[i] != NULL) free(sp->addr[i]); if (sp->prev_addr[i] != NULL) free(sp->prev_addr[i]); if (sp->rtcp->addr[i] != NULL) free(sp->rtcp->addr[i]); if (sp->rtcp->prev_addr[i] != NULL) free(sp->rtcp->prev_addr[i]); //if(sp->stream[i]->bio != NULL) { // BIO_free(sp->stream[i]->bio); //} if (sp->stream[i] != NULL) { rtpp_dtls_free_stream(sp->stream[i]); } if (sp->rtcp->stream[i] != NULL) { rtpp_dtls_free_stream(sp->rtcp->stream[i]); } if (sp->fds[i] != -1) { close(sp->fds[i]); assert(cf->sessions[sp->sidx[i]] == sp); cf->sessions[sp->sidx[i]] = NULL; assert(cf->pfds[sp->sidx[i]].fd == sp->fds[i]); cf->pfds[sp->sidx[i]].fd = -1; cf->pfds[sp->sidx[i]].events = 0; } if (sp->rtcp->fds[i] != -1) { close(sp->rtcp->fds[i]); assert(cf->sessions[sp->rtcp->sidx[i]] == sp->rtcp); cf->sessions[sp->rtcp->sidx[i]] = NULL; assert(cf->pfds[sp->rtcp->sidx[i]].fd == sp->rtcp->fds[i]); cf->pfds[sp->rtcp->sidx[i]].fd = -1; cf->pfds[sp->rtcp->sidx[i]].events = 0; } if (sp->rrcs[i] != NULL) rclose(sp, sp->rrcs[i], 1); if (sp->rtcp->rrcs[i] != NULL) rclose(sp, sp->rtcp->rrcs[i], 1); if (sp->rtps[i] != NULL) { cf->rtp_servers[sp->sridx] = NULL; rtp_server_free(sp->rtps[i]); } if (sp->codecs[i] != NULL) free(sp->codecs[i]); if (sp->rtcp->codecs[i] != NULL) free(sp->rtcp->codecs[i]); if (sp->bridgeBindAddr[i] != NULL) // VLAN Support (FRN4811) free(sp->bridgeBindAddr[i]); if (sp->ice_u[i] != NULL) { if (sp->ice_u[i]->local_user_name != NULL) free(sp->ice_u[i]->local_user_name); if (sp->ice_u[i]->local_password != NULL) free(sp->ice_u[i]->local_password); if (sp->ice_u[i]->remote_user_name != NULL) free(sp->ice_u[i]->remote_user_name); if (sp->ice_u[i]->remote_password != NULL) free(sp->ice_u[i]->remote_password); free(sp->ice_u[i]); } if (sp->ice_candidate_list[i] != NULL) // ICE remote candidate support delete_ice_candidate(sp, i); if (sp->rtcp->ice_candidate_list[i] != NULL) delete_ice_candidate(sp->rtcp, i); if (sp->transcode) rtp_transcoder_free(&sp->trans[i],cf); if (sp->secure) { rtpp_srtp_free_context(&sp->srtp[i]); rtpp_srtp_free_context(&sp->rtcp->srtp[i]); } rtp_resizer_free(&sp->resizers[i]); }// for rtpp_stun_agent_remove(sp); //Remove stun agent Context. remove_session_frm_active_rsz_lst(sp); rtpp_log_write(RTPP_LOG_INFO, sp->log, "timed resizer lists cleaned up"); if (sp->timeout_data.notify_tag != NULL) free(sp->timeout_data.notify_tag); hash_table_remove(cf, sp); if (sp->call_id != NULL) free(sp->call_id); if (sp->tag != NULL) free(sp->tag); rtpp_log_close(sp->log); free(sp->rtcp); free(sp); cf->sessions_active--; }
/* Call Backs */ int dtls_verify_cb(int ok, X509_STORE_CTX* store) { SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); rtpp_stream *st = (rtpp_stream*)SSL_get_app_data(ssl); int depth = X509_STORE_CTX_get_error_depth(store); X509* cert = X509_STORE_CTX_get_current_cert(store); int override = 1; char buf[512]; int err = X509_STORE_CTX_get_error(store); bzero(buf, 512); X509_NAME_oneline(X509_get_subject_name(cert),buf,sizeof(buf)); rtpp_log_write(RTPP_LOG_INFO, glog, "depth[%d] subject = %s\n", depth, buf); bzero(buf, 512); X509_NAME_oneline(X509_get_issuer_name(cert),buf,sizeof(buf)); rtpp_log_write(RTPP_LOG_INFO, glog, "depth[%d] issuer = %s\n", depth, buf); rtpp_log_write(RTPP_LOG_INFO, glog, "verify error:ok=%d num=%d:%s\n", ok,err, X509_verify_cert_error_string(err)); rtpp_log_write(RTPP_LOG_INFO, glog, "depth:%d, error code is %d\n", depth,store->error); switch (err) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: rtpp_log_write(RTPP_LOG_INFO, glog, "issuer= %s\n",buf); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_CERT_NOT_YET_VALID: rtpp_log_write(RTPP_LOG_INFO, glog, "notBefore\n"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_CERT_HAS_EXPIRED: rtpp_log_write(RTPP_LOG_INFO, glog, "notAfter\n"); break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: rtpp_log_write(RTPP_LOG_INFO, glog, "unable to decrypt cert signature\n"); break; case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: rtpp_log_write(RTPP_LOG_INFO, glog, "Critical Extension\n"); break; case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: rtpp_log_write(RTPP_LOG_INFO, glog, "unable to decode issuer public key\n"); break; case X509_V_ERR_OUT_OF_MEM: rtpp_log_write(RTPP_LOG_ERR, glog, "Out of memory \n"); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: if (st->remote_fp_algorithm && st->remote_fp_value) { ok = 1; rtpp_log_write(RTPP_LOG_INFO, glog,"{%d}fingerprint verification st:%p fd:%d ssl:%p\n",depth, st, st->fd, ssl); if(!dtls_verify_digest(st,cert)) { //override = 0;/* should be Zero*/ ok = 0; } } rtpp_log_write(RTPP_LOG_INFO, glog, "Self signed certificate issue\n"); break; case X509_V_ERR_CERT_REVOKED: rtpp_log_write(RTPP_LOG_INFO, glog, "certitifcate revoked\n"); break; case X509_V_ERR_INVALID_CA: rtpp_log_write(RTPP_LOG_INFO, glog, "invalid CA\n"); break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: rtpp_log_write(RTPP_LOG_INFO, glog, "path length exceeded\n"); break; case X509_V_ERR_INVALID_PURPOSE: rtpp_log_write(RTPP_LOG_INFO, glog, "invalid purpose\n"); break; case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_CERT_REJECTED: rtpp_log_write(RTPP_LOG_INFO, glog, "certificate untrusted/rejected\n"); break; default: rtpp_log_write(RTPP_LOG_INFO, glog, "default: error code is %d (check x509_vfy.h)\n", store->error); break; } if (!ok) { if (override) { rtpp_log_write(RTPP_LOG_INFO, glog, "something wrong with the cert[%d]!!! error code:%d (x509_vfy.h) Ignoring for Now\n",depth, store->error); ok=1; } else rtpp_log_write(RTPP_LOG_INFO, glog, "verify error:num=%d:%s depth:%d\n", err, X509_verify_cert_error_string(err),depth); }
struct rtpp_command * rtpp_command_stream_get(struct cfg *cf, struct rtpp_cmd_connection *rcs, int *rval, double dtime, struct rtpp_command_stats *csp) { char **ap; char *cp, *cp1; int len; struct rtpp_command *cmd; if (rcs->inbuf_epos == rcs->inbuf_ppos) { *rval = EAGAIN; return (NULL); } cp = &(rcs->inbuf[rcs->inbuf_ppos]); len = rcs->inbuf_epos - rcs->inbuf_ppos; cp1 = memchr(cp, '\n', len); if (cp1 == NULL) { *rval = EAGAIN; return (NULL); } cmd = rtpp_zmalloc(sizeof(struct rtpp_command)); if (cmd == NULL) { *rval = ENOMEM; return (NULL); } cmd->controlfd = rcs->controlfd_out; cmd->dtime = dtime; cmd->csp = csp; cmd->umode = 0; if (rcs->rlen > 0) { cmd->rlen = rcs->rlen; memcpy(&cmd->raddr, &rcs->raddr, rcs->rlen); } len = cp1 - cp; memcpy(cmd->buf, cp, len); cmd->buf[len] = '\0'; rcs->inbuf_ppos += len + 1; rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "received command \"%s\"", cmd->buf); csp->ncmds_rcvd.cnt++; cp = cmd->buf; for (ap = cmd->argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) { if (**ap != '\0') { cmd->argc++; if (++ap >= &cmd->argv[RTPC_MAX_ARGC]) break; } } if (cmd->argc < 1) { rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_1); *rval = EINVAL; free(cmd); return (NULL); } /* Step I: parse parameters that are common to all ops */ if (rtpp_command_pre_parse(cf, cmd) != 0) { /* Error reply is handled by the rtpp_command_pre_parse() */ *rval = 0; free(cmd); return (NULL); } return (cmd); }
/** * rtpp_parse_bridge_modifier - Handles the bridge modifier "B/b" in UPDATE and LOOKUP commands * Argument format: ip4/6:netId-ipAddr * * @param[in] cf - Config * @param[in] arg - Bridge modifier argument (from the command) * * @param[out] isIpV6 - 1 if IPV6 * @param[out] netId - Network Id (if name) * @param[out] ipAddr - IP address * * @return 0 (success), -1 (failure) * * Notes: Added for FRN4811 */ int rtpp_parse_bridge_modifier(struct cfg *cf, char *arg, int *isIpV6, char *netId, char *ipAddr) { if (arg == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "Bridge modifier argument is NULL"); return -1; } rtpp_log_write(RTPP_LOG_DBUG, cf->glog, "Bridge modifier argument = %s", arg); // Get IP ver (ip4 or ip6) char *pColon = strchr(arg, ':'); if (pColon == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "IP addr version not present"); return -1; } char ipVer[4]; strncpy(ipVer, arg, 3); ipVer[3] = '\0'; if (strcasecmp(ipVer, IPV4_TYPE_IN_CMD) == 0) { *isIpV6 = 0; } else if (strcasecmp(ipVer, IPV6_TYPE_IN_CMD) == 0) { *isIpV6 = 1; } else { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "IP version (%s) invalid", ipVer); return -1; } // Get network id char *pHyphen = strchr(pColon, '-'); if (pHyphen == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "Network Id not present"); return -1; } // Copy network id char *netIdStart = pColon + 1; // Exclude : int len = pHyphen - netIdStart; if (len <= 0 || len > MAX_NETWORK_ID_LEN) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "Invalid network Id length (%d)", len); return -1; } // Copy network id strncpy(netId, netIdStart, len); *(netId+len) = '\0'; // Get IP address char *ipAddrStart = pHyphen + 1; // Exclude - len = strlen(ipAddrStart); if (len <= 0 || len > MAX_IP_ADDR_LEN) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "IP address not present"); return -1; } // Copy IP address strncpy(ipAddr, ipAddrStart, len); *(ipAddr+len) = '\0'; rtpp_log_write(RTPP_LOG_DBUG, cf->glog, "Ver: %s, NetworkId: %s, IP address: %s", ipVer, netId, ipAddr); return 0; }
static void handle_info(struct cfg *cf, struct rtpp_command *cmd, const char *opts) { #if 0 struct rtpp_session *spa, *spb; char addrs[4][256]; #endif int len, i, brief, load; char buf[1024 * 8]; unsigned long long packets_in, packets_out; brief = 0; load = 0; for (i = 0; opts[i] != '\0'; i++) { switch (opts[i]) { case 'b': case 'B': brief = 1; break; case 'l': case 'L': load = 1; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error"); reply_error(cf, cmd, ECODE_PARSE_7); return; } } packets_in = CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_rcvd"); packets_out = CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_relayed") + CALL_METHOD(cf->stable->rtpp_stats, getlvalbyname, "npkts_played"); pthread_mutex_lock(&cf->sessinfo.lock); len = snprintf(buf, sizeof(buf), "sessions created: %llu\nactive sessions: %d\n" "active streams: %d\npackets received: %llu\npackets transmitted: %llu\n", cf->sessions_created, cf->sessions_active, cf->sessinfo.nsessions, packets_in, packets_out); if (load != 0) { len += snprintf(buf + len, sizeof(buf) - len, "average load: %f\n", CALL_METHOD(cf->stable->rtpp_cmd_cf, get_aload)); } #if 0 XXX this needs work to fix it after rtp/rtcp split for (i = 0; i < cf->sessinfo.nsessions && brief == 0; i++) { spa = cf->sessinfo.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 += snprintf(buf + len, sizeof(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)) { rtpc_doreply(cf, buf, len, cmd); len = 0; } } #endif pthread_mutex_unlock(&cf->sessinfo.lock); if (len > 0) { rtpc_doreply(cf, buf, len, cmd, 0); } }
static void rtpp_proc_async_run(void *arg) { struct cfg *cf; double last_tick_time; int alarm_tick, i, ndrain, rtp_only; struct rtpp_proc_async_cf *proc_cf; long long ncycles_ref; #ifdef RTPP_DEBUG int ncycles_ref_pre, last_ctick; #endif struct sign_arg *s_a; struct rtpp_wi *wi, *wis[10]; struct sthread_args *sender; double tp[4]; struct rtpp_proc_rstats *rstats; struct rtpp_stats_obj *stats_cf; cf = (struct cfg *)arg; proc_cf = cf->stable->rtpp_proc_cf; stats_cf = cf->stable->rtpp_stats; rstats = &proc_cf->rstats; last_tick_time = 0; wi = rtpp_queue_get_item(proc_cf->time_q, 0); s_a = (struct sign_arg *)rtpp_wi_sgnl_get_data(wi, NULL); #ifdef RTPP_DEBUG last_ctick = s_a->clock_tick; ncycles_ref_pre = s_a->ncycles_ref; #endif rtpp_wi_free(wi); tp[0] = getdtime(); for (;;) { i = rtpp_queue_get_items(proc_cf->time_q, wis, 10, 0); if (i <= 0) { continue; } i -= 1; s_a = (struct sign_arg *)rtpp_wi_sgnl_get_data(wis[i], NULL); ndrain = (s_a->ncycles_ref - ncycles_ref) / (cf->stable->target_pfreq / MAX_RTP_RATE); #ifdef RTPP_DEBUG last_ctick = s_a->clock_tick; ncycles_ref_pre = ncycles_ref; #endif ncycles_ref = s_a->ncycles_ref; for(; i > -1; i--) { rtpp_wi_free(wis[i]); } tp[1] = getdtime(); #if RTPP_DEBUG if (last_ctick % (unsigned int)cf->stable->target_pfreq == 0 || last_ctick < 1000) { rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "run %lld sptime %f, CSV: %f,%f,%f", \ last_ctick, tp[1], (double)last_ctick / cf->stable->target_pfreq, \ ((double)ncycles_ref / cf->stable->target_pfreq) - tp[1], tp[1]); } #endif if (ndrain < 1) { ndrain = 1; } #if RTPP_DEBUG if (ndrain > 1) { rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "run %lld " \ "ncycles_ref %lld, ncycles_ref_pre %lld, ndrain %d CSV: %f,%f,%d", \ last_ctick, ncycles_ref, ncycles_ref_pre, ndrain, \ (double)last_ctick / cf->stable->target_pfreq, ndrain); } #endif alarm_tick = 0; if (last_tick_time == 0 || last_tick_time > tp[1]) { last_tick_time = tp[1]; } else if (last_tick_time + (double)TIMETICK < tp[1]) { alarm_tick = 1; last_tick_time = tp[1]; } if (alarm_tick || (ncycles_ref % 7) == 0) { rtp_only = 0; } else { rtp_only = 1; } pthread_mutex_lock(&cf->sessinfo.lock); if (cf->sessinfo.nsessions > 0) { if (rtp_only == 0) { i = poll(cf->sessinfo.pfds_rtcp, cf->sessinfo.nsessions, 0); } i = poll(cf->sessinfo.pfds_rtp, cf->sessinfo.nsessions, 0); pthread_mutex_unlock(&cf->sessinfo.lock); if (i < 0 && errno == EINTR) { rtpp_command_async_wakeup(cf->stable->rtpp_cmd_cf); tp[0] = getdtime(); continue; } } else { pthread_mutex_unlock(&cf->sessinfo.lock); } tp[2] = getdtime(); sender = rtpp_anetio_pick_sender(proc_cf->op); if (rtp_only == 0) { pthread_mutex_lock(&cf->glock); process_rtp(cf, tp[2], alarm_tick, ndrain, sender, rstats); } else { process_rtp_only(cf, tp[2], ndrain, sender, rstats); pthread_mutex_lock(&cf->glock); } if (cf->rtp_nsessions > 0) { process_rtp_servers(cf, tp[2], sender, rstats); } pthread_mutex_unlock(&cf->glock); rtpp_anetio_pump_q(sender); rtpp_command_async_wakeup(cf->stable->rtpp_cmd_cf); tp[3] = getdtime(); flush_rstats(stats_cf, rstats); #if RTPP_DEBUG recfilter_apply(&proc_cf->sleep_time, tp[1] - tp[0]); recfilter_apply(&proc_cf->poll_time, tp[2] - tp[1]); recfilter_apply(&proc_cf->proc_time, tp[3] - tp[2]); #endif tp[0] = tp[3]; #if RTPP_DEBUG if (last_ctick % (unsigned int)cf->stable->target_pfreq == 0 || last_ctick < 1000) { #if 0 rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "run %lld eptime %f, CSV: %f,%f,%f", \ last_ctick, tp[3], (double)last_ctick / cf->stable->target_pfreq, tp[3] - tp[1], tp[3]); #endif rtpp_log_write(RTPP_LOG_DBUG, cf->stable->glog, "run %lld eptime %f sleep_time %f poll_time %f proc_time %f CSV: %f,%f,%f,%f", \ last_ctick, tp[3], proc_cf->sleep_time.lastval, proc_cf->poll_time.lastval, proc_cf->proc_time.lastval, \ (double)last_ctick / cf->stable->target_pfreq, proc_cf->sleep_time.lastval, proc_cf->poll_time.lastval, proc_cf->proc_time.lastval); } #endif } }
int handle_command(struct cfg *cf, int controlfd, double dtime) { int len, argc, i, j, pidx, asymmetric; int external, pf, lidx, playcount, weak; 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; 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; 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; 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; } 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; 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) { j = ishostseq(cf->bindaddr[0], spa->laddr[i]) ? 0 : 1; 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"); j = ishostseq(cf->bindaddr[0], lia[0]) ? 0 : 1; if (create_listener(cf, cf->bindaddr[j], &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; }
static void rxmit_packets(struct cfg *cf, struct rtpp_session *sp, int ridx, double dtime) { int ndrain, i, port; struct rtp_packet *packet = NULL; /* Repeat since we may have several packets queued on the same socket */ for (ndrain = 0; ndrain < 5; ndrain++) { if (packet != NULL) rtp_packet_free(packet); packet = rtp_recv(sp->fds[ridx]); if (packet == NULL) break; packet->laddr = sp->laddr[ridx]; packet->rport = sp->ports[ridx]; packet->rtime = dtime; i = 0; if (sp->addr[ridx] != NULL) { /* Check that the packet is authentic, drop if it isn't */ if (sp->asymmetric[ridx] == 0) { if (memcmp(sp->addr[ridx], &packet->raddr, packet->rlen) != 0) { if (sp->canupdate[ridx] == 0) { /* * Continue, since there could be good packets in * queue. */ continue; } /* Signal that an address has to be updated */ i = 1; } else if (sp->canupdate[ridx] != 0 && sp->last_update[ridx] != 0 && dtime - sp->last_update[ridx] > UPDATE_WINDOW) { sp->canupdate[ridx] = 0; } } else { /* * For asymmetric clients don't check * source port since it may be different. */ if (!ishostseq(sp->addr[ridx], sstosa(&packet->raddr))) /* * Continue, since there could be good packets in * queue. */ continue; } sp->pcount[ridx]++; } else { sp->pcount[ridx]++; sp->addr[ridx] = malloc(packet->rlen); if (sp->addr[ridx] == NULL) { sp->pcount[3]++; rtpp_log_write(RTPP_LOG_ERR, sp->log, "can't allocate memory for remote address - " "removing session"); remove_session(cf, GET_RTP(sp)); /* Break, sp is invalid now */ break; } /* Signal that an address have to be updated. */ i = 1; } /* * Update recorded address if it's necessary. Set "untrusted address" * flag in the session state, so that possible future address updates * from that client won't get address changed immediately to some * bogus one. */ if (i != 0) { sp->untrusted_addr[ridx] = 1; memcpy(sp->addr[ridx], &packet->raddr, packet->rlen); if (sp->prev_addr[ridx] == NULL || memcmp(sp->prev_addr[ridx], &packet->raddr, packet->rlen) != 0) { sp->canupdate[ridx] = 0; } port = ntohs(satosin(&packet->raddr)->sin_port); rtpp_log_write(RTPP_LOG_INFO, sp->log, "%s's address filled in: %s:%d (%s)", (ridx == 0) ? "callee" : "caller", addr2char(sstosa(&packet->raddr)), port, (sp->rtp == NULL) ? "RTP" : "RTCP"); /* * Check if we have updated RTP while RTCP is still * empty or contains address that differs from one we * used when updating RTP. Try to guess RTCP if so, * should be handy for non-NAT'ed clients, and some * NATed as well. */ if (sp->rtcp != NULL && (sp->rtcp->addr[ridx] == NULL || !ishostseq(sp->rtcp->addr[ridx], sstosa(&packet->raddr)))) { if (sp->rtcp->addr[ridx] == NULL) { sp->rtcp->addr[ridx] = malloc(packet->rlen); if (sp->rtcp->addr[ridx] == NULL) { sp->pcount[3]++; rtpp_log_write(RTPP_LOG_ERR, sp->log, "can't allocate memory for remote address - " "removing session"); remove_session(cf, sp); /* Break, sp is invalid now */ break; } } memcpy(sp->rtcp->addr[ridx], &packet->raddr, packet->rlen); satosin(sp->rtcp->addr[ridx])->sin_port = htons(port + 1); /* Use guessed value as the only true one for asymmetric clients */ sp->rtcp->canupdate[ridx] = NOT(sp->rtcp->asymmetric[ridx]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "guessing RTCP port " "for %s to be %d", (ridx == 0) ? "callee" : "caller", port + 1); } } if (sp->resizers[ridx].output_nsamples > 0) rtp_resizer_enqueue(&sp->resizers[ridx], &packet); if (packet != NULL) send_packet(cf, sp, ridx, packet); } if (packet != NULL) rtp_packet_free(packet); }
static int handle_delete(struct cfg *cf, char *call_id, char *from_tag, char *to_tag, int weak) { int ndeleted; unsigned int medianum; struct rtpp_session *spa, *spb; int cmpr, cmpr1, idx; ndeleted = 0; for (spa = session_findfirst(cf, call_id); spa != NULL;) { medianum = 0; if ((cmpr1 = compare_session_tags(spa->tag, from_tag, &medianum)) != 0) { idx = 1; cmpr = cmpr1; } else if (to_tag != NULL && (cmpr1 = compare_session_tags(spa->tag, to_tag, &medianum)) != 0) { idx = 0; cmpr = cmpr1; } else { spa = session_findnext(spa); continue; } if (weak) spa->weak[idx] = 0; else spa->strong = 0; /* * This seems to be stable from reiterations, the only side * effect is less efficient work. */ if (spa->strong || spa->weak[0] || spa->weak[1]) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "delete: medianum=%u: removing %s flag, seeing flags to" " continue session (strong=%d, weak=%d/%d)", medianum, weak ? ( idx ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->weak[0], spa->weak[1]); /* Skipping to next possible stream for this call */ ++ndeleted; spa = session_findnext(spa); continue; } rtpp_log_write(RTPP_LOG_INFO, spa->log, "forcefully deleting session %u on ports %d/%d", medianum, spa->ports[0], spa->ports[1]); /* Search forward before we do removal */ spb = spa; spa = session_findnext(spa); remove_session(cf, spb); ++ndeleted; if (cmpr != 2) { break; } } if (ndeleted == 0) { return -1; } return 0; }
int main(int argc, char **argv) { int i, len, timeout, controlfd, alarm_tick; double sptime, eptime, last_tick_time; unsigned long delay; struct cfg cf; char buf[256]; memset(&cf, 0, sizeof(cf)); init_config(&cf, argc, argv); seedrandom(); init_hash_table(&cf.stable); init_port_table(&cf); controlfd = init_controlfd(&cf); if (cf.stable.nodaemon == 0) { if (rtpp_daemon(0, 0) == -1) err(1, "can't switch into daemon mode"); /* NOTREACHED */ } glog = cf.stable.glog = rtpp_log_open(&cf.stable, "rtpproxy", NULL, LF_REOPEN); atexit(ehandler); rtpp_log_write(RTPP_LOG_INFO, cf.stable.glog, "rtpproxy started, pid %d", getpid()); if (cf.timeout_socket != NULL) { cf.timeout_handler = rtpp_notify_init(glog, cf.timeout_socket); if (cf.timeout_handler == NULL) { rtpp_log_ewrite(RTPP_LOG_ERR, glog, "can't start notification thread"); exit(1); } } i = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); if (i >= 0) { len = sprintf(buf, "%u\n", (unsigned int)getpid()); write(i, buf, len); close(i); } else { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable.glog, "can't open pidfile for writing"); } signal(SIGHUP, fatsignal); signal(SIGINT, fatsignal); signal(SIGKILL, fatsignal); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, fatsignal); signal(SIGXCPU, fatsignal); signal(SIGXFSZ, fatsignal); signal(SIGVTALRM, fatsignal); signal(SIGPROF, fatsignal); signal(SIGUSR1, fatsignal); signal(SIGUSR2, fatsignal); if (cf.stable.run_uname != NULL || cf.stable.run_gname != NULL) { if (drop_privileges(&cf) != 0) { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable.glog, "can't switch to requested user/group"); exit(1); } } cf.stable.controlfd = controlfd; cf.sessinfo.sessions[0] = NULL; cf.sessinfo.nsessions = 0; cf.rtp_nsessions = 0; rtpp_command_async_init(&cf); sptime = 0; last_tick_time = 0; for (;;) { pthread_mutex_lock(&cf.glock); pthread_mutex_lock(&cf.sessinfo.lock); if (cf.rtp_nsessions > 0 || cf.sessinfo.nsessions > 0) { timeout = RTPS_TICKS_MIN; } else { timeout = TIMETICK * 1000; } pthread_mutex_unlock(&cf.sessinfo.lock); pthread_mutex_unlock(&cf.glock); eptime = getdtime(); delay = (eptime - sptime) * 1000000.0; if (delay < (1000000 / POLL_LIMIT)) { usleep((1000000 / POLL_LIMIT) - delay); sptime = getdtime(); } else { sptime = eptime; } pthread_mutex_lock(&cf.sessinfo.lock); if (cf.sessinfo.nsessions > 0) { i = poll(cf.sessinfo.pfds, cf.sessinfo.nsessions, timeout); pthread_mutex_unlock(&cf.sessinfo.lock); if (i < 0 && errno == EINTR) continue; } else { pthread_mutex_unlock(&cf.sessinfo.lock); usleep(timeout * 1000); } eptime = getdtime(); pthread_mutex_lock(&cf.glock); if (cf.rtp_nsessions > 0) { process_rtp_servers(&cf, eptime); } pthread_mutex_unlock(&cf.glock); if (eptime > last_tick_time + TIMETICK) { alarm_tick = 1; last_tick_time = eptime; } else { alarm_tick = 0; } pthread_mutex_lock(&cf.glock); process_rtp(&cf, eptime, alarm_tick); pthread_mutex_unlock(&cf.glock); } exit(0); }
int main(int argc, char **argv) { int i, len; double eval, clk; long long ncycles_ref, counter; double eptime; double add_delay; struct cfg cf; char buf[256]; struct recfilter loop_error; struct PFD phase_detector; useconds_t usleep_time; struct sched_param sparam; #if RTPP_DEBUG double sleep_time, filter_lastval; #endif memset(&cf, 0, sizeof(cf)); cf.stable = malloc(sizeof(struct rtpp_cfg_stable)); if (cf.stable == NULL) { err(1, "can't allocate memory for the struct rtpp_cfg_stable"); /* NOTREACHED */ } memset(cf.stable, '\0', sizeof(struct rtpp_cfg_stable)); cf.stable->ctrl_socks = malloc(sizeof(struct rtpp_list)); if (cf.stable->ctrl_socks == NULL) { err(1, "can't allocate memory for the struct rtpp_cfg_stable"); /* NOTREACHED */ } memset(cf.stable->ctrl_socks, '\0', sizeof(struct rtpp_list)); RTPP_LIST_RESET(cf.stable->ctrl_socks); init_config(&cf, argc, argv); seedrandom(); cf.stable->sessions_ht = rtpp_hash_table_ctor(); if (cf.stable->sessions_ht == NULL) { err(1, "can't allocate memory for the hash table"); /* NOTREACHED */ } cf.stable->rtpp_stats = rtpp_stats_ctor(); if (cf.stable->rtpp_stats == NULL) { err(1, "can't allocate memory for the stats data"); /* NOTREACHED */ } init_port_table(&cf); if (rtpp_controlfd_init(&cf) != 0) { err(1, "can't inilialize control socket%s", cf.stable->ctrl_socks->len > 1 ? "s" : ""); } if (cf.stable->nodaemon == 0) { if (rtpp_daemon(0, 0) == -1) err(1, "can't switch into daemon mode"); /* NOTREACHED */ } if (rtpp_notify_init() != 0) errx(1, "can't start notification thread"); cf.stable->glog = rtpp_log_open(cf.stable, "rtpproxy", NULL, LF_REOPEN); rtpp_log_setlevel(cf.stable->glog, cf.stable->log_level); _sig_cf = &cf; atexit(ehandler); rtpp_log_write(RTPP_LOG_INFO, cf.stable->glog, "rtpproxy started, pid %d", getpid()); i = open(cf.stable->pid_file, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); if (i >= 0) { len = sprintf(buf, "%u\n", (unsigned int)getpid()); write(i, buf, len); close(i); } else { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable->glog, "can't open pidfile for writing"); } signal(SIGHUP, sighup); signal(SIGINT, fatsignal); signal(SIGKILL, fatsignal); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, fatsignal); signal(SIGXCPU, fatsignal); signal(SIGXFSZ, fatsignal); signal(SIGVTALRM, fatsignal); signal(SIGPROF, fatsignal); signal(SIGUSR1, fatsignal); signal(SIGUSR2, fatsignal); if (cf.stable->sched_policy != SCHED_OTHER) { sparam.sched_priority = sched_get_priority_max(cf.stable->sched_policy); if (sched_setscheduler(0, cf.stable->sched_policy, &sparam) == -1) { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable->glog, "sched_setscheduler(SCHED_%s, %d)", (cf.stable->sched_policy == SCHED_FIFO) ? "FIFO" : "RR", sparam.sched_priority); } } if (cf.stable->run_uname != NULL || cf.stable->run_gname != NULL) { if (drop_privileges(&cf) != 0) { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable->glog, "can't switch to requested user/group"); exit(1); } } set_rlimits(&cf); cf.sessinfo.sessions[0] = NULL; cf.sessinfo.nsessions = 0; cf.rtp_nsessions = 0; rtpp_command_async_init(&cf); rtpp_proc_async_init(&cf); counter = 0; recfilter_init(&loop_error, 0.96, 0.0, 0); PFD_init(&phase_detector, 2.0); #ifdef HAVE_SYSTEMD_SD_DAEMON_H sd_notify(0, "READY=1"); #endif #ifdef RTPP_CHECK_LEAKS rtpp_memdeb_setbaseln(); #endif for (;;) { eptime = getdtime(); clk = (eptime + cf.stable->sched_offset) * cf.stable->target_pfreq; ncycles_ref = llrint(clk); eval = PFD_get_error(&phase_detector, clk); #if RTPP_DEBUG filter_lastval = loop_error.lastval; #endif if (eval != 0.0) { recfilter_apply(&loop_error, sigmoid(eval)); } #if RTPP_DEBUG if (counter % (unsigned int)cf.stable->target_pfreq == 0 || counter < 1000) { rtpp_log_write(RTPP_LOG_DBUG, cf.stable->glog, "run %lld ncycles %f raw error1 %f, filter lastval %f, filter nextval %f", counter, clk, eval, filter_lastval, loop_error.lastval); } #endif add_delay = freqoff_to_period(cf.stable->target_pfreq, 1.0, loop_error.lastval); usleep_time = add_delay * 1000000.0; #if RTPP_DEBUG if (counter % (unsigned int)cf.stable->target_pfreq == 0 || counter < 1000) { rtpp_log_write(RTPP_LOG_DBUG, cf.stable->glog, "run %lld filter lastval %f, filter nextval %f, error %f", counter, filter_lastval, loop_error.lastval, sigmoid(eval)); rtpp_log_write(RTPP_LOG_DBUG, cf.stable->glog, "run %lld extra sleeping time %llu", counter, usleep_time); } sleep_time = getdtime(); #endif rtpp_proc_async_wakeup(cf.stable->rtpp_proc_cf, counter, ncycles_ref); usleep(usleep_time); #if RTPP_DEBUG sleep_time = getdtime() - sleep_time; if (counter % (unsigned int)cf.stable->target_pfreq == 0 || counter < 1000 || sleep_time > add_delay * 2.0) { rtpp_log_write(RTPP_LOG_DBUG, cf.stable->glog, "run %lld sleeping time required %llu sleeping time actual %f, CSV: %f,%f,%f", \ counter, usleep_time, sleep_time, (double)counter / cf.stable->target_pfreq, ((double)usleep_time) / 1000.0, sleep_time * 1000.0); } #endif counter += 1; if (cf.stable->slowshutdown != 0) { pthread_mutex_lock(&cf.sessinfo.lock); if (cf.sessinfo.nsessions == 0) { /* The below unlock is not necessary, but does not hurt either */ pthread_mutex_unlock(&cf.sessinfo.lock); rtpp_log_write(RTPP_LOG_INFO, cf.stable->glog, "deorbiting-burn sequence completed, exiting"); break; } pthread_mutex_unlock(&cf.sessinfo.lock); } } #ifdef HAVE_SYSTEMD_SD_DAEMON_H sd_notify(0, "STATUS=Exited"); #endif #ifdef RTPP_CHECK_LEAKS exit(rtpp_memdeb_dumpstats(&cf) == 0 ? 0 : 1); #else exit(0); #endif }