void AuthTimeoutHandler::run() { if (_req.method() != htp_method_POST) { send_http_reply(HTTP_BADMETHOD); delete this; return; } SAS::Marker start_marker(trail(), MARKER_ID_START, 1u); SAS::report_marker(start_marker); SAS::Marker calling_dn(trail(), MARKER_ID_CALLING_DN, 1u); calling_dn.add_var_param(_impu); SAS::report_marker(calling_dn); HTTPCode rc = handle_response(_req.body()); SAS::Marker end_marker(trail(), MARKER_ID_END, 1u); SAS::report_marker(end_marker); if (rc != HTTP_OK) { LOG_DEBUG("Unable to handle callback from Chronos"); send_http_reply(rc); delete this; return; } send_http_reply(HTTP_OK); delete this; }
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; }
void process_subscription_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); pjsip_msg *msg = rdata->msg_info.msg; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPION_EXPIRES; 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 subscribe request using invalid URI scheme"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY, 0); // Can't log the public ID as the subscribe has failed too early std::string error_msg = "Subscribe failed as using invalid URI Scheme"; 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; } bool emergency_subscription = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { emergency_subscription = PJUtils::is_emergency_registration(contact_hdr); if (!emergency_subscription) { break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_subscription) { // Reject a subscription with a Contact header containing a contact address // that's been registered for emergency service. LOG_ERROR("Rejecting subscribe request from emergency registration"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY, 0); // Can't log the public ID as the subscribe has failed too early std::string error_msg = "Subscribe failed as using emergency registration"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_EVENT, NULL, NULL, NULL); return; } ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY); 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 SUBSCRIBE for public ID %s", public_id.c_str()); // Get the call identifier from the headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); SAS::Marker calling_dn(trail, MARKER_ID_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); PJUtils::mark_sas_call_branch_ids(trail, rdata->msg_info.cid, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; // Subscriber must have already registered to be making a subscribe std::string state; HTTPCode http_code = hss->get_registration_data(public_id, state, ifc_map, uris, trail); if ((http_code != HTTP_OK) || (state != "REGISTERED")) { // 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. st_code = PJSIP_SC_SERVICE_UNAVAILABLE; // If the client shouldn't retry (when the subscriber isn't present in the HSS) // reject with a 403, otherwise reject with a 503. if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting SUBSCRIBE request"); 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("aor = %s", aor.c_str()); LOG_DEBUG("SUBSCRIBE 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); // Write to the local store, checking the remote store if there is no entry locally. If the write to the local store succeeds, then write to the remote store. pjsip_tx_data* tdata_notify = NULL; RegStore::AoR* aor_data = NULL; pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata, now, NULL, remote_store, &tdata_notify, &aor_data, true, trail); if (aor_data != NULL) { // Log the subscriptions. log_subscriptions(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) { RegStore::AoR* remote_aor_data = NULL; write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL, &tdata_notify, &remote_aor_data, false, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the subscribe 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; // 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 LOG_ERROR("Error building SUBSCRIBE %d response %s", st_code, PJUtils::pj_status_to_string(status).c_str()); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Error building SUBSCRIBE (" + std::to_string(st_code) + ") " + PJUtils::pj_status_to_string(status); 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 } // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(tdata->pool, expiry); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response. status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // Send the Notify if (tdata_notify != NULL && notify_status == PJ_SUCCESS) { set_trail(tdata_notify, trail); status = PJUtils::send_request(tdata_notify); if (status != PJ_SUCCESS) { // LCOV_EXCL_START SAS::Event event(trail, SASEvent::NOTIFICATION_FAILED, 0); std::string error_msg = "Failed to send NOTIFY - error code: " + std::to_string(status); event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } 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; }