/* Timer callback to send response. */ static void send_response_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_transaction *tsx; struct response *r = (struct response*) entry->user_data; pj_status_t status; PJ_UNUSED_ARG(timer_heap); tsx = pjsip_tsx_layer_find_tsx(&r->tsx_key, PJ_TRUE); if (!tsx) { PJ_LOG(3,(THIS_FILE," error: timer unable to find transaction")); pjsip_tx_data_dec_ref(r->tdata); return; } status = pjsip_tsx_send_msg(tsx, r->tdata); if (status != PJ_SUCCESS) { // Some tests do expect failure! //PJ_LOG(3,(THIS_FILE," error: timer unable to send response")); pj_mutex_unlock(tsx->mutex); pjsip_tx_data_dec_ref(r->tdata); return; } pj_mutex_unlock(tsx->mutex); }
/// This provides function similar to the pjsip_endpt_send_request method /// but includes setting the SAS trail. It does not support the timeout, token /// or callback options. pj_status_t PJUtils::send_request(pjsip_endpoint* endpt, pjsip_tx_data* tdata) { pjsip_transaction* tsx; pj_status_t status; status = pjsip_tsx_create_uac(&mod_sprout_util, tdata, &tsx); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } pjsip_tsx_set_transport(tsx, &tdata->tp_sel); // Set the trail ID in the transaction from the message. set_trail(tsx, get_trail(tdata)); status = pjsip_tsx_send_msg(tsx, NULL); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return status; }
static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) { /* Check that this is our request. */ if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) { /* It is! */ /* Send response. */ pjsip_tx_data *tdata; pjsip_response_addr res_addr; pj_status_t status; status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) { recv_status = status; return PJ_TRUE; } status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } return PJ_TRUE; } /* Not ours. */ return PJ_FALSE; }
static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) { if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { pjsip_tx_data *tdata; pjsip_response_addr res_addr; pj_status_t status; status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error creating response", status); return PJ_TRUE; } status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { app_perror(" error in get response address", status); pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error sending response", status); pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } return PJ_TRUE; } return PJ_FALSE; }
/// This is a clone of the PJSIP pjsip_endpt_respond_stateless function, /// with the addition of code to reflect the trail on the request on to the /// response. All sprout application code should use this method instead. pj_status_t PJUtils::respond_stateless(pjsip_endpoint* endpt, pjsip_rx_data* rdata, int st_code, const pj_str_t* st_text, const pjsip_hdr* hdr_list, const pjsip_msg_body* body) { pj_status_t status; pjsip_response_addr res_addr; pjsip_tx_data* tdata; // Create response message status = create_response(endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) { return status; } // Add the message headers, if any if (hdr_list) { const pjsip_hdr* hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } // Add the message body, if any. if (body) { tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } // Get where to send request. status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } // Send! status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } return PJ_SUCCESS; }
static pj_bool_t authenticate(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; ast_assert(endpoint != NULL); if (is_ack) { return PJ_FALSE; } if (ast_sip_requires_authentication(endpoint, rdata)) { pjsip_tx_data *tdata; struct unidentified_request *unid; pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata); switch (ast_sip_check_authentication(endpoint, rdata, tdata)) { case AST_SIP_AUTHENTICATION_CHALLENGE: /* Send the 401 we created for them */ ast_sip_report_auth_challenge_sent(endpoint, rdata, tdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_SUCCESS: /* See note in endpoint_lookup about not holding an unnecessary write lock */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } ast_sip_report_auth_success(endpoint, rdata); break; case AST_SIP_AUTHENTICATION_FAILED: log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_ERROR: log_failed_request(rdata, "Error to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); pjsip_tx_data_dec_ref(tdata); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } pjsip_tx_data_dec_ref(tdata); } else if (endpoint == artificial_endpoint) { /* Uh. Oh. The artificial endpoint couldn't challenge so block the request. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } return PJ_FALSE; }
/* * create request benchmark */ static int create_request_bench(pj_timestamp *p_elapsed) { enum { COUNT = 100 }; unsigned i, j; pjsip_tx_data *tdata[COUNT]; pj_timestamp t1, t2, elapsed; pj_status_t status; pj_str_t str_target = pj_str("sip:[email protected]"); pj_str_t str_from = pj_str("\"Local User\" <sip:[email protected]>"); pj_str_t str_to = pj_str("\"Remote User\" <sip:[email protected]>"); pj_str_t str_contact = str_from; elapsed.u64 = 0; for (i=0; i<LOOP; i+=COUNT) { pj_bzero(tdata, sizeof(tdata)); pj_get_timestamp(&t1); for (j=0; j<COUNT; ++j) { status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &tdata[j]); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); goto on_error; } } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); for (j=0; j<COUNT; ++j) pjsip_tx_data_dec_ref(tdata[j]); } p_elapsed->u64 = elapsed.u64; return PJ_SUCCESS; on_error: for (i=0; i<COUNT; ++i) { if (tdata[i]) pjsip_tx_data_dec_ref(tdata[i]); } return -400; }
void SIPCall::sendSIPInfo(const char *const body, const char *const subtype) { if (not inv or not inv->dlg) throw VoipLinkException("Couldn't get invite dialog"); pj_str_t methodName = CONST_PJ_STR("INFO"); pjsip_method method; pjsip_method_init_np(&method, &methodName); /* Create request message. */ pjsip_tx_data *tdata; if (pjsip_dlg_create_request(inv->dlg, &method, -1, &tdata) != PJ_SUCCESS) { RING_ERR("[call:%s] Could not create dialog", getCallId().c_str()); return; } /* Create "application/<subtype>" message body. */ pj_str_t content; pj_cstr(&content, body); const pj_str_t type = CONST_PJ_STR("application"); pj_str_t pj_subtype; pj_cstr(&pj_subtype, subtype); tdata->msg->body = pjsip_msg_body_create(tdata->pool, &type, &pj_subtype, &content); if (tdata->msg->body == NULL) pjsip_tx_data_dec_ref(tdata); else pjsip_dlg_send_request(inv->dlg, tdata, getSIPVoIPLink()->getModId(), NULL); }
/// Rejects a request statelessly. void ICSCFProxy::reject_request(pjsip_rx_data* rdata, int status_code) { pj_status_t status; ACR* acr = _acr_factory->get_acr(get_trail(rdata), CALLING_PARTY); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { LOG_ERROR("Rejecting %.*s request with %d status code", rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, status_code); pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, status_code, NULL, &tdata); if (status == PJ_SUCCESS) { // Pass the response to the ACR. acr->tx_response(tdata->msg); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); if (status != PJ_SUCCESS) { // LCOV_EXCL_START pjsip_tx_data_dec_ref(tdata); // LCOV_EXCL_STOP } } } // Send the ACR and delete it. acr->send_message(); delete acr; }
/* Send one stateless request */ static pj_status_t submit_stateless_job(void) { pjsip_tx_data *tdata; pj_status_t status; status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, &app.client.dst_uri, &app.local_uri, &app.client.dst_uri, &app.local_contact, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating request", status); report_completion(701); return status; } status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL, NULL); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); app_perror(THIS_FILE, "Error sending stateless request", status); report_completion(701); return status; } return PJ_SUCCESS; }
/* * Call this function to create request to initiate REFER subscription. * */ PJ_DEF(pj_status_t) pjsip_xfer_initiate( pjsip_evsub *sub, const pj_str_t *refer_to_uri, pjsip_tx_data **p_tdata) { pjsip_xfer *xfer; const pj_str_t refer_to = { "Refer-To", 8}; pjsip_tx_data *tdata; pjsip_generic_string_hdr *hdr; pj_status_t status; /* sub and p_tdata argument must be valid. */ PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL); /* Get the xfer object. */ xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION); /* refer_to_uri argument MAY be NULL for subsequent REFER requests, * but it MUST be specified in the first REFER. */ PJ_ASSERT_RETURN((refer_to_uri || xfer->refer_to_uri.slen), PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(xfer->dlg); /* Create basic REFER request */ status = pjsip_evsub_initiate(sub, pjsip_get_refer_method(), -1, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Save Refer-To URI. */ if (refer_to_uri == NULL) { refer_to_uri = &xfer->refer_to_uri; } else { pj_strdup(xfer->dlg->pool, &xfer->refer_to_uri, refer_to_uri); } /* Create and add Refer-To header. */ hdr = pjsip_generic_string_hdr_create(tdata->pool, &refer_to, refer_to_uri); if (!hdr) { pjsip_tx_data_dec_ref(tdata); status = PJ_ENOMEM; goto on_return; } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); /* Done. */ *p_tdata = tdata; status = PJ_SUCCESS; on_return: pjsip_dlg_dec_lock(xfer->dlg); return status; }
/// Callback used for PJUtils::send_request_stateless static void stateless_send_cb(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont) { *cont = PJ_FALSE; bool retrying = false; StatelessSendState* sss = (StatelessSendState*)st->token; if ((sent <= 0) && (!sss->servers.empty())) { // Request to a resolved server failed. When sending statelessly // this means we couldn't get a transport, so couldn't connect to the // selected target, so we always blacklist. PJUtils::blacklist_server(sss->servers[sss->current_server]); // Can we do a retry? pj_status_t status = PJ_ENOTFOUND; ++sss->current_server; if (sss->current_server < (int)sss->servers.size()) { pjsip_tx_data* tdata = st->tdata; // According to RFC3263 we should generate a new branch identifier for // the message so there is no possibility of it being confused with // previous attempts. Not clear this is really necessary in this case, // but just in case ... PJUtils::generate_new_branch_id(tdata); // Add a reference to the tdata to stop PJSIP releasing it when we // return the callback. pjsip_tx_data_add_ref(tdata); // Set up destination info for the new server and resend the request. PJUtils::set_dest_info(tdata, sss->servers[sss->current_server]); status = pjsip_endpt_send_request_stateless(stack_data.endpt, tdata, (void*)sss, &stateless_send_cb); if (status == PJ_SUCCESS) { retrying = true; } else { pjsip_tx_data_dec_ref(tdata); } } } if ((sent > 0) || (!retrying)) { // Either the request was sent successfully, or we couldn't retry. delete sss; } }
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pj_uint32_t cseq; PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL); /* Make sure we don't have pending transaction. */ pj_mutex_lock(pubc->mutex); if (pubc->pending_tsx) { if (pubc->opt.queue_request) { pj_list_push_back(&pubc->pending_reqs, tdata); pj_mutex_unlock(pubc->mutex); PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another " "transaction pending")); return PJ_EPENDING; } else { pjsip_tx_data_dec_ref(tdata); pj_mutex_unlock(pubc->mutex); PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another " "transaction pending")); return PJ_EBUSY; } } pj_mutex_unlock(pubc->mutex); /* If via_addr is set, use this address for the Via header. */ if (pubc->via_addr.host.slen > 0) { tdata->via_addr = pubc->via_addr; tdata->via_tp = pubc->via_tp; } /* Invalidate message buffer. */ pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ cseq = ++pubc->cseq_hdr->cseq; cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Increment pending transaction first, since transaction callback * may be called even before send_request() returns! */ ++pubc->pending_tsx; status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, &tsx_callback); if (status!=PJ_SUCCESS) { // no need to decrement, callback has been called and it should // already decremented pending_tsx. Decrementing this here may // cause accessing freed memory location. //--pubc->pending_tsx; PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } return status; }
/* * Send PRACK request. */ PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv, pjsip_tx_data *tdata) { dlg_data *dd; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; PJ_ASSERT_ON_FAIL(dd != NULL, {pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; });
/* Callback to be called to handle incoming response outside * any transactions. This happens for example when 2xx/OK * for INVITE is received and transaction will be destroyed * immediately, so we need to forward the subsequent 2xx/OK * retransmission statelessly. */ static pj_bool_t proxy_on_rx_response( pjsip_rx_data *rdata ) { pjsip_tx_data *tdata; pjsip_response_addr res_addr; pjsip_via_hdr *hvia; pj_status_t status; /* Create response to be forwarded upstream (Via will be stripped here) */ status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, &tdata); if (status != PJ_SUCCESS) { app_perror("Error creating response", status); return PJ_TRUE; } /* Get topmost Via header */ hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (hvia == NULL) { /* Invalid response! Just drop it */ pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } /* Calculate the address to forward the response */ pj_bzero(&res_addr, sizeof(res_addr)); res_addr.dst_host.type = PJSIP_TRANSPORT_UDP; res_addr.dst_host.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); /* Destination address is Via's received param */ res_addr.dst_host.addr.host = hvia->recvd_param; if (res_addr.dst_host.addr.host.slen == 0) { /* Someone has messed up our Via header! */ res_addr.dst_host.addr.host = hvia->sent_by.host; } /* Destination port is the rport */ if (hvia->rport_param != 0 && hvia->rport_param != -1) res_addr.dst_host.addr.port = hvia->rport_param; if (res_addr.dst_host.addr.port == 0) { /* Ugh, original sender didn't put rport! * At best, can only send the response to the port in Via. */ res_addr.dst_host.addr.port = hvia->sent_by.port; } /* Forward response */ status = pjsip_endpt_send_response(global.endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { app_perror("Error forwarding response", status); return PJ_TRUE; } return PJ_TRUE; }
static pj_status_t rt_send_request(int thread_id) { pj_status_t status; pj_str_t target, from, to, contact, call_id; pjsip_tx_data *tdata; pj_time_val timeout_delay; pj_mutex_lock(rt_test_data[thread_id].mutex); /* Create a request message. */ target = pj_str(rt_target_uri); from = pj_str(FROM_HDR); to = pj_str(rt_target_uri); contact = pj_str(CONTACT_HDR); call_id = rt_test_data[thread_id].call_id; status = pjsip_endpt_create_request( endpt, &pjsip_options_method, &target, &from, &to, &contact, &call_id, -1, NULL, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); pj_mutex_unlock(rt_test_data[thread_id].mutex); return -610; } /* Start time. */ pj_get_timestamp(&rt_test_data[thread_id].send_time); /* Send the message (statelessly). */ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL); if (status != PJ_SUCCESS) { /* Immediate error! */ app_perror(" error: send request", status); pjsip_tx_data_dec_ref(tdata); pj_mutex_unlock(rt_test_data[thread_id].mutex); return -620; } /* Update counter. */ rt_test_data[thread_id].sent_request_count++; /* Set timeout timer. */ if (rt_test_data[thread_id].timeout_timer.user_data != NULL) { pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer); } timeout_delay.sec = 100; timeout_delay.msec = 0; rt_test_data[thread_id].timeout_timer.user_data = (void*)1; pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer, &timeout_delay); pj_mutex_unlock(rt_test_data[thread_id].mutex); return PJ_SUCCESS; }
/* * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. */ PJ_DEF(pj_status_t) pjsip_xfer_notify( pjsip_evsub *sub, pjsip_evsub_state state, int xfer_st_code, const pj_str_t *xfer_st_text, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_xfer *xfer; pjsip_param *param; const pj_str_t reason = { "noresource", 10 }; char *body; int bodylen; pjsip_msg_body *msg_body; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub, PJ_EINVAL); /* Get the xfer object. */ xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION); /* Lock object. */ pjsip_dlg_inc_lock(xfer->dlg); /* Create the NOTIFY request. * Note that reason is only used when state is TERMINATED, and * the defined termination reason for REFER is "noresource". */ status = pjsip_evsub_notify( sub, state, NULL, &reason, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Check status text */ if (xfer_st_text==NULL || xfer_st_text->slen==0) xfer_st_text = pjsip_get_status_text(xfer_st_code); /* Save st_code and st_text, for current_notify() */ xfer->last_st_code = xfer_st_code; pj_strdup(xfer->dlg->pool, &xfer->last_st_text, xfer_st_text); /* Create sipfrag content. */ body = (char*) pj_pool_alloc(tdata->pool, 128); bodylen = pj_ansi_snprintf(body, 128, "SIP/2.0 %u %.*s\r\n", xfer_st_code, (int)xfer_st_text->slen, xfer_st_text->ptr); PJ_ASSERT_ON_FAIL(bodylen > 0 && bodylen < 128, {status=PJ_EBUG; pjsip_tx_data_dec_ref(tdata); goto on_return; });
/*! * \internal * \brief Attempt to qualify the contact * * \details Sends a SIP OPTIONS request to the given contact in order to make * sure that contact is available. */ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact) { pjsip_tx_data *tdata; RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup); if (endpoint) { endpoint_local = ao2_bump(endpoint); } else { if (!ast_strlen_zero(contact->endpoint_name)) { endpoint_local = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name); } if (!endpoint_local) { endpoint_local = find_an_endpoint(contact); } if (!endpoint_local) { ast_log(LOG_WARNING, "Unable to find an endpoint to qualify contact %s. Deleting this contact\n", contact->uri); contact_deleted(contact); return -1; } } if (ast_sip_create_request("OPTIONS", NULL, endpoint_local, NULL, contact, &tdata)) { ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n", contact->uri); return -1; } /* If an outbound proxy is specified set it on this request */ if (!ast_strlen_zero(contact->outbound_proxy) && ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n", contact->uri); return -1; } init_start_time(contact); ao2_ref(contact, +1); if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb) != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n", contact->uri); update_contact_status(contact, UNAVAILABLE, 0); ao2_ref(contact, -1); return -1; } return 0; }
pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, void *token, pjsip_messaging_cb cb ) { pjsip_transaction *tsx; struct messaging_data *msg_data; /* Create transaction. */ tsx = pjsip_endpt_create_tsx(endpt); if (!tsx) { pjsip_tx_data_dec_ref(tdata); return -1; } /* Save parameters to messaging data and attach to tsx. */ msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); msg_data->cb = cb; msg_data->token = token; /* Init transaction. */ tsx->module_data[module_id] = msg_data; if (pjsip_tsx_init_uac(tsx, tdata) != 0) { pjsip_tx_data_dec_ref(tdata); pjsip_endpt_destroy_tsx(endpt, tsx); return -1; } pjsip_endpt_register_tsx(endpt, tsx); /* * Instruct transaction to send message. * Further events will be received via transaction's event. */ pjsip_tsx_on_tx_msg(tsx, tdata); /* Decrement reference counter. */ pjsip_tx_data_dec_ref(tdata); return 0; }
/*! * \internal * \brief Attempt to qualify the contact * * \details Sends a SIP OPTIONS request to the given contact in order to make * sure that contact is available. */ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact) { pjsip_tx_data *tdata; RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup); if (contact->authenticate_qualify) { endpoint_local = ao2_bump(endpoint); if (!endpoint_local) { /* * Find the "first" endpoint to completely qualify the contact - any * endpoint that is associated with the contact should do. */ endpoint_local = find_an_endpoint(contact); if (!endpoint_local) { ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n", contact->uri); return -1; } } } if (ast_sip_create_request("OPTIONS", NULL, NULL, NULL, contact, &tdata)) { ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n", contact->uri); return -1; } /* If an outbound proxy is specified set it on this request */ if (!ast_strlen_zero(contact->outbound_proxy) && ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n", contact->uri); return -1; } init_start_time(contact); ao2_ref(contact, +1); if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb) != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n", contact->uri); update_contact_status(contact, UNAVAILABLE); ao2_ref(contact, -1); return -1; } return 0; }
PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb) { pjsip_transaction *tsx; struct tsx_data *tsx_data; pj_status_t status; PJ_ASSERT_RETURN(endpt && tdata && (timeout==-1 || timeout>0), PJ_EINVAL); /* Check that transaction layer module is registered to endpoint */ PJ_ASSERT_RETURN(mod_stateful_util.id != -1, PJ_EINVALIDOP); PJ_UNUSED_ARG(timeout); status = pjsip_tsx_create_uac(&mod_stateful_util, tdata, &tsx); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } pjsip_tsx_set_transport(tsx, &tdata->tp_sel); tsx_data = PJ_POOL_ALLOC_T(tsx->pool, struct tsx_data); tsx_data->token = token; tsx_data->cb = cb; tsx->mod_data[mod_stateful_util.id] = tsx_data; status = pjsip_tsx_send_msg(tsx, NULL); if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata); return status; }
/// Sends a request statelessly, possibly retrying the specified number of /// times if the pj_status_t PJUtils::send_request_stateless(pjsip_tx_data* tdata, int retries) { pj_status_t status = PJ_SUCCESS; StatelessSendState* sss = new StatelessSendState; sss->current_server = 0; if (tdata->tp_sel.type != PJSIP_TPSELECTOR_TRANSPORT) { // No transport pre-selected so resolve the next hop to a set of servers. resolve_next_hop(tdata, retries, sss->servers, get_trail(tdata)); if (!sss->servers.empty()) { // Select the next target set up the destination info in the tdata and // send the request. sss->current_server = 0; set_dest_info(tdata, sss->servers[sss->current_server]); } else { // No servers found. status = PJ_ENOTFOUND; } } if (status == PJ_SUCCESS) { status = pjsip_endpt_send_request_stateless(stack_data.endpt, tdata, (void*)sss, stateless_send_cb); } if (status != PJ_SUCCESS) { // The assumption is that if pjsip_endpt_send_request_stateless fails // the callback is not called, so it is safe to free off the state data // and the request here. Also, this would be an unexpected error rather // than an indication that the selected destination server is down, so we // don't blacklist. LOG_ERROR("Failed to send request to %s", PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, PJUtils::next_hop(tdata->msg)).c_str()); pjsip_tx_data_dec_ref(tdata); delete sss; } return status; }
pjsip_tx_data* PJUtils::clone_tdata(pjsip_tx_data* tdata) { pjsip_tx_data* cloned_tdata; pj_status_t status; status = pjsip_endpt_create_tdata(stack_data.endpt, &cloned_tdata); if (status != PJ_SUCCESS) { return NULL; } // Always increment ref counter to 1. pjsip_tx_data_add_ref(cloned_tdata); // Clone the message from the supplied tdata. cloned_tdata->msg = pjsip_msg_clone(cloned_tdata->pool, tdata->msg); if (cloned_tdata->msg == NULL) { pjsip_tx_data_dec_ref(cloned_tdata); cloned_tdata = NULL; } // Copy the trail identifier to the cloned message. set_trail(cloned_tdata, get_trail(tdata)); if (tdata->msg->type == PJSIP_REQUEST_MSG) { // Substitute the branch value in the top Via header with a unique // branch identifier. generate_new_branch_id(cloned_tdata); } // If the original message already had a specified transport set this // on the clone. (Must use pjsip_tx_data_set_transport to ensure // reference counts get updated.) if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { pjsip_tx_data_set_transport(cloned_tdata, &tdata->tp_sel); } // If the message has any addr in dest_info, copy that if (tdata->dest_info.addr.count != 0) { pj_memcpy(&cloned_tdata->dest_info, &tdata->dest_info, sizeof(cloned_tdata->dest_info)); } return cloned_tdata; }
/* Calculate new target for the request */ static pj_status_t proxy_calculate_target(pjsip_rx_data *rdata, pjsip_tx_data *tdata) { pjsip_sip_uri *target; /* RFC 3261 Section 16.5 Determining Request Targets */ target = (pjsip_sip_uri*) tdata->msg->line.req.uri; /* If the Request-URI of the request contains an maddr parameter, the * Request-URI MUST be placed into the target set as the only target * URI, and the proxy MUST proceed to Section 16.6. */ if (target->maddr_param.slen) { proxy_postprocess(tdata); return PJ_SUCCESS; } /* If the domain of the Request-URI indicates a domain this element is * not responsible for, the Request-URI MUST be placed into the target * set as the only target, and the element MUST proceed to the task of * Request Forwarding (Section 16.6). */ if (!is_uri_local(target)) { proxy_postprocess(tdata); return PJ_SUCCESS; } /* If the target set for the request has not been predetermined as * described above, this implies that the element is responsible for the * domain in the Request-URI, and the element MAY use whatever mechanism * it desires to determine where to send the request. */ /* We're not interested to receive request destined to us, so * respond with 404/Not Found (only if request is not ACK!). */ if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pjsip_endpt_respond_stateless(global.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); } /* Delete the request since we're not forwarding it */ pjsip_tx_data_dec_ref(tdata); return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_FOUND); }
/// This is analogous to respond_stateless, although in this case to /// respond statefully on an existing transaction. Strangely there is /// no equivalent PJSIP API. pj_status_t PJUtils::respond_stateful(pjsip_endpoint* endpt, pjsip_transaction* uas_tsx, pjsip_rx_data* rdata, int st_code, const pj_str_t* st_text, const pjsip_hdr* hdr_list, const pjsip_msg_body* body) { pj_status_t status; pjsip_tx_data* tdata; status = create_response(stack_data.endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) { return status; } // Add the message headers, if any if (hdr_list) { const pjsip_hdr* hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } // Add the message body, if any. if (body) { tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } status = pjsip_tsx_send_msg(uas_tsx, tdata); return status; }
static pj_status_t send_response(pjsip_inv_session *inv, pjsip_rx_data *rdata, int code, pj_bool_t *has_initial) { pjsip_tx_data *tdata; pj_status_t status; if (*has_initial) { status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata); } else { status = pjsip_inv_initial_answer(inv, rdata, code, NULL, NULL, &tdata); } if (status != PJ_SUCCESS) { if (*has_initial) { status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE, NULL, NULL, &tdata); } else { status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_NOT_ACCEPTABLE, NULL, NULL, &tdata); } if (status == PJ_SUCCESS) { *has_initial = PJ_TRUE; pjsip_inv_send_msg(inv, tdata); } else { pjsip_inv_terminate(inv, 500, PJ_FALSE); return -1; } } else { *has_initial = PJ_TRUE; status = pjsip_inv_send_msg(inv, tdata); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } } return status; }
/* Test transaction layer. */ static int tsx_layer_test(void) { pj_str_t target, from, tsx_key; pjsip_tx_data *tdata; pjsip_transaction *tsx, *found; pj_status_t status; PJ_LOG(3,(THIS_FILE, " transaction layer test")); target = pj_str(TARGET_URI); from = pj_str(FROM_URI); status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return -110; } status = pjsip_tsx_create_uac(NULL, tdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); return -120; } pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); found = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE); if (found != tsx) { return -130; } pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); flush_events(500); if (pjsip_tx_data_dec_ref(tdata) != PJSIP_EBUFDESTROYED) { return -140; } return 0; }
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pj_uint32_t cseq; /* Make sure we don't have pending transaction. */ if (pubc->pending_tsx) { PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another " "transaction pending")); pjsip_tx_data_dec_ref( tdata ); return PJSIP_EBUSY; } /* Invalidate message buffer. */ pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ cseq = ++pubc->cseq_hdr->cseq; cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Increment pending transaction first, since transaction callback * may be called even before send_request() returns! */ ++pubc->pending_tsx; status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, &tsx_callback); if (status!=PJ_SUCCESS) { // no need to decrement, callback has been called and it should // already decremented pending_tsx. Decrementing this here may // cause accessing freed memory location. //--pubc->pending_tsx; PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } return status; }
/* Schedule timer to send response for the specified UAS transaction */ static void schedule_send_response( pjsip_rx_data *rdata, const pj_str_t *tsx_key, int status_code, int msec_delay ) { pj_status_t status; pjsip_tx_data *tdata; pj_timer_entry *t; struct response *r; pj_time_val delay; status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create response", status); test_complete = -198; return; } r = PJ_POOL_ALLOC_T(tdata->pool, struct response); pj_strdup(tdata->pool, &r->tsx_key, tsx_key); r->tdata = tdata; delay.sec = 0; delay.msec = msec_delay; pj_time_val_normalize(&delay); t = PJ_POOL_ZALLOC_T(tdata->pool, pj_timer_entry); t->user_data = r; t->cb = &send_response_timer; status = pjsip_endpt_schedule_timer(endpt, t, &delay); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); app_perror(" error: unable to schedule timer", status); test_complete = -199; return; } }
/* Handler to destroy the transport; called by transport manager */ static pj_status_t loop_destroy(pjsip_transport *tp) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); loop->thread_quit_flag = 1; /* Unlock transport mutex before joining thread. */ pj_lock_release(tp->lock); pj_thread_join(loop->thread); pj_thread_destroy(loop->thread); /* Clear pending send notifications. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, -PJSIP_ESHUTDOWN); } pj_list_erase(node); pjsip_tx_data_dec_ref(node->tdata); } /* Clear "incoming" packets in the queue. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; pj_list_erase(node); pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } /* Self destruct.. heheh.. */ pj_lock_destroy(loop->base.lock); pj_atomic_destroy(loop->base.ref_cnt); pjsip_endpt_release_pool(loop->base.endpt, loop->base.pool); return PJ_SUCCESS; }