struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdata) { int hash; pj_str_t *remote_tag; struct ast_taskprocessor *serializer; if (!rdata->msg_info.msg) { return NULL; } if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { remote_tag = &rdata->msg_info.from->tag; } else { remote_tag = &rdata->msg_info.to->tag; } /* Compute the hash from the SIP message call-id and remote-tag */ hash = pjstr_hash(&rdata->msg_info.cid->id); hash = pjstr_hash_add(remote_tag, hash); hash = ast_str_hash_restrict(hash); serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]); if (serializer) { ast_debug(3, "Calculated serializer %s to use for %s\n", ast_taskprocessor_name(serializer), pjsip_rx_data_get_info(rdata)); } return serializer; }
/*! * \internal * \brief Find the request tdata to get the serializer it used. * \since 14.0.0 * * \param rdata The incoming message. * * \retval serializer on success. * \retval NULL on error or could not find the serializer. */ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata) { struct ast_taskprocessor *serializer = NULL; pj_str_t tsx_key; pjsip_transaction *tsx; pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, &rdata->msg_info.cseq->method, rdata); tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (!tsx) { ast_debug(1, "Could not find transaction for %s.\n", pjsip_rx_data_get_info(rdata)); return NULL; } ast_debug(3, "Found transaction %s for %s.\n", tsx->obj_name, pjsip_rx_data_get_info(rdata)); if (tsx->last_tx) { const char *serializer_name; serializer_name = tsx->last_tx->mod_data[distributor_mod.id]; if (!ast_strlen_zero(serializer_name)) { serializer = ast_taskprocessor_get(serializer_name, TPS_REF_IF_EXISTS); if (serializer) { ast_debug(3, "Found serializer %s on transaction %s\n", serializer_name, tsx->obj_name); } } } #ifdef HAVE_PJ_TRANSACTION_GRP_LOCK pj_grp_lock_release(tsx->grp_lock); #else pj_mutex_unlock(tsx->mutex); #endif return serializer; }
static void local_log_rx_msg(pjsip_rx_data* rdata) { LOG_VERBOSE("RX %d bytes %s from %s %s:%d:\n" "--start msg--\n\n" "%.*s\n" "--end msg--", rdata->msg_info.len, pjsip_rx_data_get_info(rdata), rdata->tp_info.transport->type_name, rdata->pkt_info.src_name, rdata->pkt_info.src_port, (int)rdata->msg_info.len, rdata->msg_info.msg_buf); }
/* Notification on incoming messages */ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) { PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n" "%.*s\n" "--end msg--", rdata->msg_info.len, pjsip_rx_data_get_info(rdata), rdata->tp_info.transport->type_name, rdata->pkt_info.src_name, rdata->pkt_info.src_port, (int)rdata->msg_info.len, rdata->msg_info.msg_buf)); /* Always return false, otherwise messages will not get processed! */ return PJ_FALSE; }
/* Notification on incoming messages */ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) { if (!app.enable_msg_logging) return PJ_FALSE; PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n" "%.*s\n" "--end msg--", rdata->msg_info.len, pjsip_rx_data_get_info(rdata), rdata->tp_info.transport->type_name, rdata->pkt_info.src_name, rdata->pkt_info.src_port, (int)rdata->msg_info.len, rdata->msg_info.msg_buf)); return PJ_FALSE; }
static pj_bool_t on_rx_msg(pjsip_rx_data *rdata) { if (msg_log_enabled) { PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%s:%d:\n" "%.*s\n" "--end msg--", rdata->msg_info.len, pjsip_rx_data_get_info(rdata), rdata->tp_info.transport->type_name, rdata->pkt_info.src_name, rdata->pkt_info.src_port, rdata->msg_info.len, rdata->msg_info.msg_buf)); } return PJ_FALSE; }
static void transfer_client_cb(pjsip_evsub *sub, pjsip_event *event) { auto link = getSIPVoIPLink(); if (not link) { RING_ERR("no more VoIP link"); return; } auto mod_ua_id = link->getModId(); switch (pjsip_evsub_get_state(sub)) { case PJSIP_EVSUB_STATE_ACCEPTED: if (!event) return; pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); break; case PJSIP_EVSUB_STATE_TERMINATED: pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL); break; case PJSIP_EVSUB_STATE_ACTIVE: { if (!event) return; pjsip_rx_data* r_data = event->body.rx_msg.rdata; if (!r_data) return; std::string request(pjsip_rx_data_get_info(r_data)); pjsip_status_line status_line = { 500, *pjsip_get_status_text(500) }; if (!r_data->msg_info.msg) return; if (r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD and request.find("NOTIFY") != std::string::npos) { pjsip_msg_body *body = r_data->msg_info.msg->body; if (!body) return; if (pj_stricmp2(&body->content_type.type, "message") or pj_stricmp2(&body->content_type.subtype, "sipfrag")) return; if (pjsip_parse_status_line((char*) body->data, body->len, &status_line) != PJ_SUCCESS) return; } if (!r_data->msg_info.cid) return; auto call = static_cast<SIPCall *>(pjsip_evsub_get_mod_data(sub, mod_ua_id)); if (!call) return; if (status_line.code / 100 == 2) { if (call->inv) call->terminateSipSession(PJSIP_SC_GONE); Manager::instance().hangupCall(call->getCallId()); pjsip_evsub_set_mod_data(sub, mod_ua_id, NULL); } break; } case PJSIP_EVSUB_STATE_NULL: case PJSIP_EVSUB_STATE_SENT: case PJSIP_EVSUB_STATE_PENDING: case PJSIP_EVSUB_STATE_UNKNOWN: default: break; } }
/* * This is the handler to receive message for this test. It is used to * control and verify the behavior of the message transmitted by the * transaction. */ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) { if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { /* * The TEST1_BRANCH_ID test performs the verifications for transaction * retransmission mechanism. It will not answer the incoming request * with any response. */ pjsip_msg *msg = rdata->msg_info.msg; PJ_LOG(4,(THIS_FILE, " received request")); /* Only wants to take INVITE or OPTIONS method. */ if (msg->line.req.method.id != PJSIP_INVITE_METHOD && msg->line.req.method.id != PJSIP_OPTIONS_METHOD) { PJ_LOG(3,(THIS_FILE, " error: received unexpected method %.*s", msg->line.req.method.name.slen, msg->line.req.method.name.ptr)); test_complete = -600; return PJ_TRUE; } if (recv_count == 0) { recv_count++; //pj_gettimeofday(&recv_last); recv_last = rdata->pkt_info.timestamp; } else { pj_time_val now; unsigned msec_expected, msec_elapsed; int max_received; //pj_gettimeofday(&now); now = rdata->pkt_info.timestamp; PJ_TIME_VAL_SUB(now, recv_last); msec_elapsed = now.sec*1000 + now.msec; ++recv_count; msec_expected = (1<<(recv_count-2))*PJSIP_T1_TIMEOUT; if (msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (msec_expected > PJSIP_T2_TIMEOUT) msec_expected = PJSIP_T2_TIMEOUT; max_received = 11; } else { max_received = 7; } if (DIFF(msec_expected, msec_elapsed) > TEST1_ALLOWED_DIFF) { PJ_LOG(3,(THIS_FILE, " error: expecting retransmission no. %d in %d " "ms, received in %d ms", recv_count-1, msec_expected, msec_elapsed)); test_complete = -610; } if (recv_count > max_received) { PJ_LOG(3,(THIS_FILE, " error: too many messages (%d) received", recv_count)); test_complete = -620; } //pj_gettimeofday(&recv_last); recv_last = rdata->pkt_info.timestamp; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { /* * The TEST4_BRANCH_ID test simulates transport failure after several * retransmissions. */ recv_count++; if (recv_count == TEST4_RETRANSMIT_CNT) { /* Simulate transport failure. */ pjsip_loop_set_failure(loop, 2, NULL); } else if (recv_count > TEST4_RETRANSMIT_CNT) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -631; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { /* * The TEST5_BRANCH_ID test simulates user terminating the transaction * after several retransmissions. */ recv_count++; if (recv_count == TEST5_RETRANSMIT_CNT+1) { pj_str_t key; pjsip_transaction *tsx; pjsip_tsx_create_key( rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &rdata->msg_info.msg->line.req.method, rdata); tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); } else { PJ_LOG(3,(THIS_FILE, " error: uac transaction not found!")); test_complete = -633; } } else if (recv_count > TEST5_RETRANSMIT_CNT+1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -634; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { /* * The TEST6_BRANCH_ID test successfull non-INVITE transaction. */ pj_status_t status; recv_count++; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 202, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); test_complete = -636; } return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { /* * The TEST7_BRANCH_ID test successfull non-INVITE transaction * with provisional response. */ pj_status_t status; pjsip_response_addr res_addr; struct response *r; pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; recv_count++; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -640; return PJ_TRUE; } /* Respond with provisional response */ status = pjsip_endpt_create_response(endpt, rdata, 100, NULL, &tdata); pj_assert(status == PJ_SUCCESS); status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); pj_assert(status == PJ_SUCCESS); status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); pj_assert(status == PJ_SUCCESS); /* Create the final response. */ status = pjsip_endpt_create_response(endpt, rdata, 202, NULL, &tdata); pj_assert(status == PJ_SUCCESS); /* Schedule sending final response in couple of of secs. */ r = pj_pool_alloc(tdata->pool, sizeof(*r)); r->res_addr = res_addr; r->tdata = tdata; if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); timer.entry.cb = &send_response_callback; timer.entry.user_data = r; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); return PJ_TRUE; } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { /* * The TEST8_BRANCH_ID test failed INVITE transaction. */ pjsip_method *method; pj_status_t status; method = &rdata->msg_info.msg->line.req.method; recv_count++; if (method->id == PJSIP_INVITE_METHOD) { if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 301, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); test_complete = -636; } } else if (method->id == PJSIP_ACK_METHOD) { if (recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; /* Schedule timer to destroy transaction after 5 seconds. * This is to make sure that transaction does not * retransmit ACK. */ pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); pj_strcpy(&timer.tsx_key, &key); timer.entry.id = 301; timer.entry.cb = &terminate_tsx_callback; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } if (recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); test_complete = -639; } } else if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { /* * The TEST9_BRANCH_ID test failed INVITE transaction with * provisional response. */ pjsip_method *method; pj_status_t status; method = &rdata->msg_info.msg->line.req.method; recv_count++; if (method->id == PJSIP_INVITE_METHOD) { pjsip_response_addr res_addr; struct response *r; pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; if (recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -650; return PJ_TRUE; } /* Respond with provisional response */ status = pjsip_endpt_create_response(endpt, rdata, 100, NULL, &tdata); pj_assert(status == PJ_SUCCESS); status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); pj_assert(status == PJ_SUCCESS); status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); pj_assert(status == PJ_SUCCESS); /* Create the final response. */ status = pjsip_endpt_create_response(endpt, rdata, 302, NULL, &tdata); pj_assert(status == PJ_SUCCESS); /* Schedule sending final response in couple of of secs. */ r = pj_pool_alloc(tdata->pool, sizeof(*r)); r->res_addr = res_addr; r->tdata = tdata; if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); timer.entry.cb = &send_response_callback; timer.entry.user_data = r; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } else if (method->id == PJSIP_ACK_METHOD) { if (recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; /* Schedule timer to destroy transaction after 5 seconds. * This is to make sure that transaction does not * retransmit ACK. */ pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); pj_strcpy(&timer.tsx_key, &key); timer.entry.id = 302; timer.entry.cb = &terminate_tsx_callback; pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); } if (recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", recv_count)); test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); test_complete = -639; } return PJ_TRUE; } return PJ_FALSE; }
static pj_bool_t distributor(pjsip_rx_data *rdata) { pjsip_dialog *dlg; struct distributor_dialog_data *dist = NULL; struct ast_taskprocessor *serializer = NULL; pjsip_rx_data *clone; if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) { /* * Ignore everything until we are fully booted. Let the * peer retransmit messages until we are ready. */ return PJ_TRUE; } dlg = find_dialog(rdata); if (dlg) { ast_debug(3, "Searching for serializer associated with dialog %s for %s\n", dlg->obj_name, pjsip_rx_data_get_info(rdata)); dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY); if (dist) { ao2_lock(dist); serializer = ao2_bump(dist->serializer); ao2_unlock(dist); if (serializer) { ast_debug(3, "Found serializer %s associated with dialog %s\n", ast_taskprocessor_name(serializer), dlg->obj_name); } } } if (serializer) { /* We have a serializer so we know where to send the message. */ } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) { ast_debug(3, "No dialog serializer for %s. Using request transaction as basis.\n", pjsip_rx_data_get_info(rdata)); serializer = find_request_serializer(rdata); if (!serializer) { /* * Pick a serializer for the unmatched response. * We couldn't determine what serializer originally * sent the request or the serializer is gone. */ serializer = ast_sip_get_distributor_serializer(rdata); } } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) { /* We have a BYE or CANCEL request without a serializer. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); ao2_cleanup(dist); return PJ_TRUE; } else { if (ast_taskprocessor_alert_get()) { /* * When taskprocessors get backed up, there is a good chance that * we are being overloaded and need to defer adding new work to * the system. To defer the work we will ignore the request and * rely on the peer's transport layer to retransmit the message. * We usually work off the overload within a few seconds. The * alternative is to send back a 503 response to these requests * and be done with it. */ ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n", pjsip_rx_data_get_info(rdata)); ao2_cleanup(dist); return PJ_TRUE; } /* Pick a serializer for the out-of-dialog request. */ serializer = ast_sip_get_distributor_serializer(rdata); } if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) { ast_taskprocessor_unreference(serializer); ao2_cleanup(dist); return PJ_TRUE; } if (dist) { ao2_lock(dist); clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint); ao2_unlock(dist); ao2_cleanup(dist); } if (ast_sip_push_task(serializer, distribute, clone)) { ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]); pjsip_rx_data_free_cloned(clone); } ast_taskprocessor_unreference(serializer); return PJ_TRUE; }
static pjsip_dialog *find_dialog(pjsip_rx_data *rdata) { pj_str_t tsx_key; pjsip_transaction *tsx; pjsip_dialog *dlg; pj_str_t *local_tag; pj_str_t *remote_tag; if (!rdata->msg_info.msg) { return NULL; } if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { local_tag = &rdata->msg_info.to->tag; remote_tag = &rdata->msg_info.from->tag; } else { local_tag = &rdata->msg_info.from->tag; remote_tag = &rdata->msg_info.to->tag; } /* We can only call the convenient method for * 1) responses * 2) non-CANCEL requests * 3) CANCEL requests with a to-tag */ if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG || pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || rdata->msg_info.to->tag.slen != 0) { dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag, remote_tag, PJ_FALSE); if (dlg) { return dlg; } } /* * There may still be a matching dialog if this is * 1) an incoming CANCEL request without a to-tag * 2) an incoming response to a dialog-creating request. */ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { /* CANCEL requests will need to match the INVITE we initially received. Any * other request type will either have been matched already or is not in * dialog */ pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS, pjsip_get_invite_method(), rdata); } else { pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, &rdata->msg_info.cseq->method, rdata); } tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (!tsx) { ast_debug(3, "Could not find matching transaction for %s\n", pjsip_rx_data_get_info(rdata)); return NULL; } dlg = pjsip_tsx_get_dlg(tsx); #ifdef HAVE_PJ_TRANSACTION_GRP_LOCK pj_grp_lock_release(tsx->grp_lock); #else pj_mutex_unlock(tsx->mutex); #endif return dlg; }