/// Record-route ourselves, preserving the parameters on our original Route /// header. void MangelwurzelTsx::record_route(pjsip_msg* req, pj_pool_t* pool, pjsip_uri* uri) { pjsip_rr_hdr* rr_hdr = pjsip_rr_hdr_create(pool); rr_hdr->name_addr.uri = uri; pjsip_msg_insert_first_hdr(req, (pjsip_hdr*)rr_hdr); }
/* Postprocess the request before forwarding it */ static void proxy_postprocess(pjsip_tx_data *tdata) { /* Optionally record-route */ if (global.record_route) { char uribuf[128]; pj_str_t uri; const pj_str_t H_RR = { "Record-Route", 12 }; pjsip_generic_string_hdr *rr; pj_ansi_snprintf(uribuf, sizeof(uribuf), "<sip:%.*s:%d;lr>", (int)global.name[0].host.slen, global.name[0].host.ptr, global.name[0].port); uri = pj_str(uribuf); rr = pjsip_generic_string_hdr_create(tdata->pool, &H_RR, &uri); pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr); } }
/// Adds a Record-Route header to the message with the specified user name, /// port and transport. If the user parameter is NULL the user field is left /// blank. void PJUtils::add_record_route(pjsip_tx_data* tdata, const char* transport, int port, const char* user) { pjsip_rr_hdr* rr = pjsip_rr_hdr_create(tdata->pool); pjsip_sip_uri* uri = pjsip_sip_uri_create(tdata->pool, PJ_FALSE); uri->host = stack_data.name[0]; uri->port = port; pj_strdup2(tdata->pool, &uri->transport_param, transport); uri->lr_param = PJ_TRUE; if (user != NULL) { pj_strdup2(tdata->pool, &uri->user, user); } rr->name_addr.uri = (pjsip_uri*)uri; pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rr); LOG_DEBUG("Added Record-Route header, URI = %s", uri_to_string(PJSIP_URI_IN_ROUTING_HDR, rr->name_addr.uri).c_str()); }
void process_register_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); if (!PJSIP_URI_SCHEME_IS_SIP(uri)) { // Reject a non-SIP/SIPS URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). // LCOV_EXCL_START LOG_ERROR("Rejecting register request using non SIP URI"); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; // LCOV_EXCL_STOP } // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::aor_from_uri((pjsip_sip_uri*)uri); LOG_DEBUG("Process REGISTER for public ID %s", public_id.c_str()); // Get the call identifier and the cseq number from the respective headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; int cseq = rdata->msg_info.cseq->cseq; pjsip_msg *msg = rdata->msg_info.msg; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::TrailId trail = get_trail(rdata); LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, SASMarker::INIT_TIME, 1u); SAS::report_marker(start_marker); SAS::Marker calling_dn(trail, SASMarker::CALLING_DN, 1u); pjsip_sip_uri* calling_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); calling_dn.add_var_param(calling_uri->user.slen, calling_uri->user.ptr); SAS::report_marker(calling_dn); SAS::Marker cid_marker(trail, SASMarker::SIP_CALL_ID, 1u); cid_marker.add_var_param(rdata->msg_info.cid->id.slen, rdata->msg_info.cid->id.ptr); SAS::report_marker(cid_marker, SAS::Marker::Scope::TrailGroup); // Query the HSS for the associated URIs. // This should really include the private ID, but we don't yet have a // homestead API for it. Homestead won't be able to query a third-party HSS // without the private ID. Json::Value* uris = hss->get_associated_uris(public_id, trail); if ((uris == NULL) || (uris->size() == 0)) { // We failed to get the list of associated URIs. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. Reject with 403. LOG_ERROR("Rejecting register request with invalid public/private identity"); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_FORBIDDEN, NULL, NULL, NULL); return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris->get((Json::ArrayIndex)0, Json::Value::null).asString(); LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Find the expire headers in the message. pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); int expiry = 0; // The registration service uses optimistic locking to avoid concurrent // updates to the same AoR conflicting. This means we have to loop // reading, updating and writing the AoR until the write is successful. RegData::AoR* aor_data = NULL; do { if (aor_data != NULL) { delete aor_data; // LCOV_EXCL_LINE - Single-threaded tests mean we'll // always pass CAS. } // Find the current bindings for the AoR. aor_data = store->get_aor_data(aor); LOG_DEBUG("Retrieved AoR data %p", aor_data); if (aor_data == NULL) { // Failed to get data for the AoR because there is no connection // to the store. Reject the register with a 500 response. // LCOV_EXCL_START - local store (used in testing) never fails LOG_ERROR("Failed to get AoR binding for %s from store", aor.c_str()); st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; break; // LCOV_EXCL_STOP } // Now loop through all the contacts. If there are multiple contacts in // the contact header in the SIP message, pjsip parses them to separate // contact header structures. pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); while (contact != NULL) { if (contact->star) { // Wildcard contact, which can only be used to clear all bindings for // the AoR. aor_data->clear(); break; } pjsip_uri* uri = (contact->uri != NULL) ? (pjsip_uri*)pjsip_uri_get_uri(contact->uri) : NULL; if ((uri != NULL) && (PJSIP_URI_SCHEME_IS_SIP(uri))) { // The binding identifier is based on the +sip.instance parameter if // it is present. If not the contact URI is used instead. std::string contact_uri = PJUtils::uri_to_string(PJSIP_URI_IN_CONTACT_HDR, uri); std::string binding_id = get_binding_id(contact); if (binding_id == "") { binding_id = contact_uri; } LOG_DEBUG(". Binding identifier for contact = %s", binding_id.c_str()); // Find the appropriate binding in the bindings list for this AoR. RegData::AoR::Binding* binding = aor_data->get_binding(binding_id); if ((cid != binding->_cid) || (cseq > binding->_cseq)) { // Either this is a new binding, has come from a restarted device, or // is an update to an existing binding. binding->_uri = contact_uri; // TODO Examine Via header to see if we're the first hop // TODO Only if we're not the first hop, check that the top path header has "ob" parameter // Get the Path headers, if present. RFC 3327 allows us the option of // rejecting a request with a Path header if there is no corresponding // "path" entry in the Supported header but we don't do so on the assumption // that the edge proxy knows what it's doing. binding->_path_headers.clear(); pjsip_generic_string_hdr* path_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL); while (path_hdr) { std::string path = PJUtils::pj_str_to_string(&path_hdr->hvalue); LOG_DEBUG("Path header %s", path.c_str()); // Extract all the paths from this header. Utils::split_string(path, ',', binding->_path_headers, 0, true); // Look for the next header. path_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } binding->_cid = cid; binding->_cseq = cseq; binding->_priority = contact->q1000; binding->_params.clear(); pjsip_param* p = contact->other_param.next; while ((p != NULL) && (p != &contact->other_param)) { std::string pname = PJUtils::pj_str_to_string(&p->name); std::string pvalue = PJUtils::pj_str_to_string(&p->value); binding->_params.push_back(std::make_pair(pname, pvalue)); p = p->next; } // Calculate the expiry period for the updated binding. expiry = (contact->expires != -1) ? contact->expires : (expires != NULL) ? expires->ivalue : 300; if (expiry > 300) { // Expiry is too long, set it to the maximum of 300 seconds (5 minutes). expiry = 300; } binding->_expires = now + expiry; if (analytics != NULL) { // Generate an analytics log for this binding update. analytics->registration(aor, binding_id, contact_uri, expiry); } } } contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, contact->next); } } while (!store->set_aor_data(aor, aor_data)); if (aor_data != NULL) { // Log the bindings. log_bindings(aor, aor_data); } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response LOG_ERROR("Error building REGISTER %d response %s", st_code, PJUtils::pj_status_to_string(status).c_str()); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete aor_data; return; // LCOV_EXCL_STOP } if (st_code != PJSIP_SC_OK) { // LCOV_EXCL_START - we only reject REGISTER if something goes wrong, and // we aren't covering any of those paths so we can't hit this either status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete aor_data; return; // LCOV_EXCL_STOP } // Add supported and require headers for RFC5626. pjsip_generic_string_hdr* gen_hdr; gen_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_SUPPORTED, &STR_OUTBOUND); if (gen_hdr == NULL) { // LCOV_EXCL_START - can't see how this could ever happen LOG_ERROR("Failed to add RFC 5626 headers"); tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR; status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete aor_data; return; // LCOV_EXCL_STOP } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gen_hdr); // Add contact headers for all active bindings. for (RegData::AoR::Bindings::const_iterator i = aor_data->bindings().begin(); i != aor_data->bindings().end(); ++i) { RegData::AoR::Binding* binding = i->second; if (binding->_expires > now) { // The binding hasn't expired. pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool); if (uri != NULL) { // Contact URI is well formed, so include this in the response. pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool); contact->star = 0; contact->uri = uri; contact->q1000 = binding->_priority; contact->expires = binding->_expires - now; pj_list_init(&contact->other_param); for (std::list<std::pair<std::string, std::string> >::iterator j = binding->_params.begin(); j != binding->_params.end(); ++j) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, j->first.c_str()); pj_strdup2(tdata->pool, &new_param->value, j->second.c_str()); pj_list_insert_before(&contact->other_param, new_param); } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)contact); } else { // Contact URI is malformed. Log an error, but otherwise don't try and // fix it. // LCOV_EXCL_START hard to hit - needs bad data in the store LOG_WARNING("Badly formed contact URI %s for address of record %s", binding->_uri.c_str(), aor.c_str()); // LCOV_EXCL_STOP } } } // Deal with path header related fields in the response. pjsip_generic_string_hdr* path_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL); if ((path_hdr != NULL) && (!aor_data->bindings().empty())) { // We have bindings with path headers so we must require outbound. pjsip_require_hdr* require_hdr = pjsip_require_hdr_create(tdata->pool); require_hdr->count = 1; require_hdr->values[0] = STR_OUTBOUND; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)require_hdr); } // Echo back any Path headers as per RFC 3327, section 5.3. We take these // from the request as they may not exist in the bindings any more if the // bindings have expired. while (path_hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, path_hdr)); path_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } // Construct a Service-Route header pointing at the sprout cluster. We don't // care which sprout handles the subsequent requests as they all have access // to all subscriber information. pjsip_sip_uri* service_route_uri = pjsip_sip_uri_create(tdata->pool, false); pj_strdup(tdata->pool, &service_route_uri->host, &stack_data.sprout_cluster_domain); service_route_uri->port = stack_data.trusted_port; service_route_uri->transport_param = pj_str("TCP"); service_route_uri->lr_param = 1; pjsip_route_hdr* service_route = pjsip_route_hdr_create(tdata->pool); service_route->name = STR_SERVICE_ROUTE; service_route->sname = pj_str(""); service_route->name_addr.uri = (pjsip_uri*)service_route_uri; pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)service_route); // Add P-Associated-URI headers for all of the associated URIs. static const pj_str_t p_associated_uri_hdr_name = pj_str("P-Associated-URI"); for (Json::ValueIterator it = uris->begin(); it != uris->end(); it++) { pj_str_t associated_uri = {(char*)(*it).asCString(), strlen((*it).asCString())}; pjsip_hdr* associated_uri_hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &p_associated_uri_hdr_name, &associated_uri); pjsip_msg_add_hdr(tdata->msg, associated_uri_hdr); } delete uris; // Send the response, but prevent the transmitted data from being freed, as we may need to inform the // ASes of the 200 OK response we sent. pjsip_tx_data_add_ref(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); RegistrationUtils::register_with_application_servers(ifchandler, store, rdata, tdata, ""); // Now we can free the tdata. pjsip_tx_data_dec_ref(tdata); LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, SASMarker::END_TIME, 1u); SAS::report_marker(end_marker); delete aor_data; }
/* * create response benchmark */ static int create_response_bench(pj_timestamp *p_elapsed) { enum { COUNT = 100 }; unsigned i, j; pjsip_via_hdr *via; pjsip_rx_data rdata; pjsip_tx_data *request; pjsip_tx_data *tdata[COUNT]; pj_timestamp t1, t2, elapsed; pj_status_t status; /* Create the request first. */ 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; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } /* Create several Via headers */ via = pjsip_via_hdr_create(request->pool); via->sent_by.host = pj_str("192.168.0.7"); via->sent_by.port = 5061; via->transport = pj_str("udp"); via->rport_param = 0; via->branch_param = pj_str("012345678901234567890123456789"); via->recvd_param = pj_str("192.168.0.7"); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via); /* Create "dummy" rdata from the tdata */ pj_bzero(&rdata, sizeof(pjsip_rx_data)); rdata.tp_info.pool = request->pool; rdata.msg_info.msg = request->msg; rdata.msg_info.from = (pjsip_from_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.to = (pjsip_to_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL); rdata.msg_info.cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL); rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.via = via; /* * Now benchmark create_response */ 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_response(endpt, &rdata, 200, 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; pjsip_tx_data_dec_ref(request); return PJ_SUCCESS; on_error: for (i=0; i<COUNT; ++i) { if (tdata[i]) pjsip_tx_data_dec_ref(tdata[i]); } return -400; }
static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) { unsigned i; pjsip_tx_data *request; pjsip_via_hdr *via; pjsip_rx_data rdata; pj_sockaddr_in remote; pjsip_transaction **tsx; pj_timestamp t1, t2, elapsed; char branch_buf[80] = PJSIP_RFC3261_BRANCH_ID "0000000000"; pj_status_t status; /* Create the request first. */ 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; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } /* Create Via */ via = pjsip_via_hdr_create(request->pool); via->sent_by.host = pj_str("192.168.0.7"); via->sent_by.port = 5061; via->transport = pj_str("udp"); via->rport_param = 1; via->recvd_param = pj_str("192.168.0.7"); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via); /* Create "dummy" rdata from the tdata */ pj_bzero(&rdata, sizeof(pjsip_rx_data)); rdata.tp_info.pool = request->pool; rdata.msg_info.msg = request->msg; rdata.msg_info.from = (pjsip_from_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.to = (pjsip_to_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL); rdata.msg_info.cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL); rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.via = via; pj_sockaddr_in_init(&remote, 0, 0); status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, &remote, sizeof(pj_sockaddr_in), NULL, &rdata.tp_info.transport); if (status != PJ_SUCCESS) { app_perror(" error: unable to get loop transport", status); return status; } /* Create transaction array */ tsx = (pjsip_transaction**) pj_pool_zalloc(request->pool, working_set * sizeof(pj_pool_t*)); pj_bzero(&mod_tsx_user, sizeof(mod_tsx_user)); mod_tsx_user.id = -1; /* Benchmark */ elapsed.u64 = 0; pj_get_timestamp(&t1); for (i=0; i<working_set; ++i) { via->branch_param.ptr = branch_buf; via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN + pj_ansi_sprintf(branch_buf+PJSIP_RFC3261_BRANCH_LEN, "-%d", i); status = pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]); if (status != PJ_SUCCESS) goto on_error; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; status = PJ_SUCCESS; on_error: for (i=0; i<working_set; ++i) { if (tsx[i]) { pjsip_tsx_terminate(tsx[i], 601); tsx[i] = NULL; } } pjsip_tx_data_dec_ref(request); flush_events(2000); return status; }
void process_register_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). LOG_ERROR("Rejecting register request using invalid URI scheme"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); // Can't log the public ID as the REGISTER has failed too early std::string public_id = "UNKNOWN"; std::string error_msg = "Rejecting register request using invalid URI scheme"; event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } // Allocate an ACR for this transaction and pass the request to it. ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process REGISTER for public ID %s", public_id.c_str()); // Get the call identifier and the cseq number from the respective headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; pjsip_msg *msg = rdata->msg_info.msg; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; std::string private_id; std::string private_id_for_binding; bool success = get_private_id(rdata, private_id); if (!success) { // There are legitimate cases where we don't have a private ID // here (for example, on a re-registration where Bono has set the // Integrity-Protected header), so this is *not* a failure // condition. // We want the private ID here so that Homestead can use it to // subscribe for updates from the HSS - but on a re-registration, // Homestead should already have subscribed for updates during the // initial registration, so we can just make a request using our // public ID. private_id = ""; // IMS compliant clients will always have the Auth header on all REGISTERs, // including reREGISTERS. Non-IMS clients won't, but their private ID // will always be the public ID with the sip: removed. private_id_for_binding = PJUtils::default_private_id_from_uri(uri); } else { private_id_for_binding = private_id; } SAS::Event event(trail, SASEvent::REGISTER_START, 0); event.add_var_param(public_id); event.add_var_param(private_id); SAS::report_event(event); std::string regstate; std::deque<std::string> ccfs; std::deque<std::string> ecfs; HTTPCode http_code = hss->update_registration_state(public_id, private_id, HSSConnection::REG, regstate, ifc_map, uris, ccfs, ecfs, trail); if ((http_code != HTTP_OK) || (regstate != HSSConnection::STATE_REGISTERED)) { // We failed to register this subscriber at the HSS. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting register request with invalid public/private identity"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid public/private identity"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); int expiry = 0; bool is_initial_registration; // Loop through each contact header. If every registration is an emergency // registration and its expiry is 0 then reject with a 501. // If there are valid registration updates to make then attempt to write to // store, which also stops emergency registrations from being deregistered. bool reject_with_501 = true; bool any_emergency_registrations = false; bool reject_with_400 = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); expiry = (contact_hdr->expires != -1) ? contact_hdr->expires : (expires != NULL) ? expires->ivalue : max_expires; if ((contact_hdr->star) && (expiry != 0)) { // Wildcard contact, which can only be used if the expiry is 0 LOG_ERROR("Attempted to deregister all bindings, but expiry value wasn't 0"); reject_with_400 = true; break; } reject_with_501 = (reject_with_501 && PJUtils::is_emergency_registration(contact_hdr) && (expiry == 0)); any_emergency_registrations = (any_emergency_registrations || PJUtils::is_emergency_registration(contact_hdr)); contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (reject_with_400) { SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid contact header"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_REQUEST, NULL, NULL, NULL); delete acr; return; } if (reject_with_501) { LOG_ERROR("Rejecting register request as attempting to deregister an emergency registration"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting deregister request for emergency registrations"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_IMPLEMENTED, NULL, NULL, NULL); delete acr; return; } // Write to the local store, checking the remote store if there is no entry locally. RegStore::AoR* aor_data = write_to_store(store, aor, rdata, now, expiry, is_initial_registration, NULL, remote_store, true, private_id_for_binding, trail); if (aor_data != NULL) { // Log the bindings. log_bindings(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { int tmp_expiry = 0; bool ignored; RegStore::AoR* remote_aor_data = write_to_store(remote_store, aor, rdata, now, tmp_expiry, ignored, aor_data, NULL, false, private_id_for_binding, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the register with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Unable to access Registration Store"; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response std::string error_msg = "Error building REGISTER " + std::to_string(status) + " response " + PJUtils::pj_status_to_string(status); LOG_ERROR(error_msg.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } if (st_code != PJSIP_SC_OK) { // LCOV_EXCL_START - we only reject REGISTER if something goes wrong, and // we aren't covering any of those paths so we can't hit this either status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "REGISTER failed with status code: " + std::to_string(st_code); event.add_var_param(error_msg); SAS::report_event(event); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add supported and require headers for RFC5626. pjsip_generic_string_hdr* gen_hdr; gen_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_SUPPORTED, &STR_OUTBOUND); if (gen_hdr == NULL) { // LCOV_EXCL_START - can't see how this could ever happen LOG_ERROR("Failed to add RFC 5626 headers"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Failed to add RFC 5636 headers"; event.add_var_param(error_msg); SAS::report_event(event); tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR; pjsip_tx_data_invalidate_msg(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gen_hdr); // Add contact headers for all active bindings. for (RegStore::AoR::Bindings::const_iterator i = aor_data->bindings().begin(); i != aor_data->bindings().end(); ++i) { RegStore::AoR::Binding* binding = i->second; if (binding->_expires > now) { // The binding hasn't expired. Parse the Contact URI from the store, // making sure it is formatted as a name-address. pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool, PJ_TRUE); if (uri != NULL) { // Contact URI is well formed, so include this in the response. pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool); contact->star = 0; contact->uri = uri; contact->q1000 = binding->_priority; contact->expires = binding->_expires - now; pj_list_init(&contact->other_param); for (std::map<std::string, std::string>::iterator j = binding->_params.begin(); j != binding->_params.end(); ++j) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, j->first.c_str()); pj_strdup2(tdata->pool, &new_param->value, j->second.c_str()); pj_list_insert_before(&contact->other_param, new_param); } // The pub-gruu parameter on the Contact header is calculated // from the instance-id, to avoid unnecessary storage in // memcached. std::string gruu = binding->pub_gruu_quoted_string(tdata->pool); if (!gruu.empty()) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, "pub-gruu"); pj_strdup2(tdata->pool, &new_param->value, gruu.c_str()); pj_list_insert_before(&contact->other_param, new_param); } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)contact); } else { // Contact URI is malformed. Log an error, but otherwise don't try and // fix it. // LCOV_EXCL_START hard to hit - needs bad data in the store LOG_WARNING("Badly formed contact URI %s for address of record %s", binding->_uri.c_str(), aor.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Badly formed contact URI - " + binding->_uri; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } } // Deal with path header related fields in the response. pjsip_routing_hdr* path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL); if ((path_hdr != NULL) && (!aor_data->bindings().empty())) { // We have bindings with path headers so we must require outbound. pjsip_require_hdr* require_hdr = pjsip_require_hdr_create(tdata->pool); require_hdr->count = 1; require_hdr->values[0] = STR_OUTBOUND; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)require_hdr); } // Echo back any Path headers as per RFC 3327, section 5.3. We take these // from the request as they may not exist in the bindings any more if the // bindings have expired. while (path_hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, path_hdr)); path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } // Add the Service-Route header. It isn't safe to do this with the // pre-built header from the global pool because the chaining data // structures in the header may get overwritten, but it is safe to do a // shallow clone. pjsip_hdr* clone = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, service_route); pjsip_msg_insert_first_hdr(tdata->msg, clone); // Add P-Associated-URI headers for all of the associated URIs. for (std::vector<std::string>::iterator it = uris.begin(); it != uris.end(); it++) { pjsip_routing_hdr* pau = identity_hdr_create(tdata->pool, STR_P_ASSOCIATED_URI); pau->name_addr.uri = PJUtils::uri_from_string(*it, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pau); } // Add a PCFA header. PJUtils::add_pcfa_header(tdata->msg, tdata->pool, ccfs, ecfs, true); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response, but prevent the transmitted data from being freed, as we may need to inform the // ASes of the 200 OK response we sent. pjsip_tx_data_add_ref(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // TODO in sto397: we should do third-party registration once per // service profile (i.e. once per iFC, using an arbitrary public // ID). hss->get_subscription_data should be enhanced to provide an // appropriate data structure (representing the ServiceProfile // nodes) and we should loop through that. Don't send any register that // contained emergency registrations to the application servers. if (!any_emergency_registrations) { RegistrationUtils::register_with_application_servers(ifc_map[public_id], store, rdata, tdata, expiry, is_initial_registration, public_id, trail); } // Now we can free the tdata. pjsip_tx_data_dec_ref(tdata); LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
int UE::send_message(std::string dest, std::string contents) { timeval before; timeval after; pthread_mutex_lock(&_msg_mutex); pj_str_t to; stra(&to, dest.c_str()); pj_str_t data; stra(&data, contents.c_str()); pj_str_t message; stra(&message, "MESSAGE"); pjsip_tx_data* tdata; pjsip_method msg_method; pjsip_method_init(&msg_method, _pool, &message); pjsip_endpt_create_request(get_global_endpoint(), &msg_method, &to, &_my_uri, &to, &_contact, NULL, -1, &data, &tdata); pjsip_tsx_create_uac(ua_module(), tdata, &_msg_tsx); pjsip_tpselector sel; sel.type = PJSIP_TPSELECTOR_TRANSPORT; sel.u.transport = _transport; pjsip_tsx_set_transport(_msg_tsx, &sel); pjsip_route_hdr* rt_hdr = pjsip_route_hdr_create(tdata->pool); rt_hdr->name_addr.uri = _server_uri; pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)rt_hdr); _msg_tsx->mod_data[ua_module()->id] = this; pj_grp_lock_add_ref(_msg_tsx->grp_lock); gettimeofday(&before, NULL); pj_status_t status = pjsip_tsx_send_msg(_msg_tsx, NULL); if (status != PJ_SUCCESS) { pthread_mutex_unlock(&_msg_mutex); return -1; } while (_msg_tsx->state < PJSIP_TSX_STATE_COMPLETED) { pthread_cond_wait(&_msg_cond, &_msg_mutex); } gettimeofday(&after, NULL); //unsigned long latency = ((after.tv_sec - before.tv_sec) * 1000000) + (after.tv_usec - before.tv_usec); //printf("Message latency is %lu\n", latency); int ret = _msg_tsx->status_code; pj_grp_lock_dec_ref(_msg_tsx->grp_lock); _msg_tsx = NULL; pthread_mutex_unlock(&_msg_mutex); return ret; }