/*! * \brief Store the transport a message came in on, so it can be used for outbound messages to that contact. */ static pj_bool_t websocket_on_rx_msg(pjsip_rx_data *rdata) { static const pj_str_t STR_WS = { "ws", 2 }; static const pj_str_t STR_WSS = { "wss", 3 }; pjsip_contact_hdr *contact; long type = rdata->tp_info.transport->key.type; if (type != (long)transport_type_ws && type != (long)transport_type_wss) { return PJ_FALSE; } if ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); pj_cstr(&uri->host, rdata->pkt_info.src_name); uri->port = rdata->pkt_info.src_port; ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n", (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port); pj_strdup(rdata->tp_info.pool, &uri->transport_param, (type == (long)transport_type_ws) ? &STR_WS : &STR_WSS); } rdata->msg_info.via->rport_param = 0; return PJ_FALSE; }
static int get_endpoint_details(pjsip_rx_data *rdata, char *domain, size_t domain_size) { pjsip_uri *from = rdata->msg_info.from->uri; pjsip_sip_uri *sip_from; if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) { return -1; } sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from); ast_copy_pj_str(domain, &sip_from->host, domain_size); return 0; }
/* * Find buddy. */ static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri) { const pjsip_sip_uri *sip_uri; unsigned i; uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri); if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) return PJSUA_INVALID_ID; sip_uri = (const pjsip_sip_uri*) uri; for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { const pjsua_buddy *b = &pjsua_var.buddy[i]; if (!pjsua_buddy_is_valid(i)) continue; if (pj_stricmp(&sip_uri->user, &b->name)==0 && pj_stricmp(&sip_uri->host, &b->host)==0 && (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060))) { /* Match */ return i; } } return PJSUA_INVALID_ID; }
bool SipAccount::getNumber(pj_str_t* uri, std::string* pDisplay, std::string* pNumber) { pj_pool_t* pool = pjsua_pool_create("", 128, 10); pjsip_name_addr* n = (pjsip_name_addr*)pjsip_parse_uri(pool, uri->ptr, uri->slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (n == NULL) { Logger::warn("pjsip_parse_uri() failed for %s", pj_strbuf(uri)); pj_pool_release(pool); return false; } if (!PJSIP_URI_SCHEME_IS_SIP(n)) { Logger::warn("pjsip_parse_uri() returned unknown schema for %s", pj_strbuf(uri)); pj_pool_release(pool); return false; } *pDisplay = std::string(n->display.ptr, n->display.slen); pjsip_sip_uri *sip = (pjsip_sip_uri*)pjsip_uri_get_uri(n); std::string number = std::string(sip->user.ptr, sip->user.slen); // make number international *pNumber = Helper::makeNumberInternational(&m_settings.base, number); pj_pool_release(pool); return true; }
static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata) { const pj_str_t stateless_user = { "0", 1 }; pjsip_uri *uri; pjsip_sip_uri *sip_uri; uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri); /* Only want to receive SIP/SIPS scheme */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) return PJ_FALSE; sip_uri = (pjsip_sip_uri*) uri; /* Check for matching user part */ if (pj_strcmp(&sip_uri->user, &stateless_user)!=0) return PJ_FALSE; /* * Yes, this is for us. */ /* Ignore ACK request */ if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) return PJ_TRUE; /* * Respond statelessly with 200/OK. */ pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL, NULL, NULL); app.server.cur_state.stateless_cnt++; return PJ_TRUE; }
/// Returns a canonical IMS public user identity from a URI as per TS 23.003 /// 13.4. std::string PJUtils::public_id_from_uri(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri public_id; memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_sip_uri)); public_id.passwd.slen = 0; public_id.port = 0; public_id.user_param.slen = 0; public_id.method_param.slen = 0; public_id.transport_param.slen = 0; public_id.ttl_param = -1; public_id.lr_param = 0; public_id.maddr_param.slen = 0; public_id.other_param.next = NULL; public_id.header_param.next = NULL; return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id); } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { pjsip_tel_uri public_id; memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_tel_uri)); public_id.context.slen = 0; public_id.ext_param.slen = 0; public_id.isub_param.slen = 0; public_id.other_param.next = NULL; return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id); } else { return std::string(); } }
// Utility to determine if URI is local to this host. pj_bool_t PJUtils::is_uri_local(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { int port = (((pjsip_sip_uri*)uri)->port != 0) ? ((pjsip_sip_uri*)uri)->port : 5060; pj_str_t host = ((pjsip_sip_uri*)uri)->host; if ((port == stack_data.trusted_port) || (port == stack_data.untrusted_port)) { // Port matches, check the list of host names. unsigned i; for (i=0; i<stack_data.name_cnt; ++i) { if (pj_stricmp(&host, &stack_data.name[i])==0) { /* Match */ return PJ_TRUE; } } } } /* Doesn't match */ return PJ_FALSE; }
// Determine the default private ID for a public ID contained in a URI. This // is calculated as specified by the 3GPP specs by effectively stripping the // scheme. std::string PJUtils::default_private_id_from_uri(const pjsip_uri* uri) { std::string id; if (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; if (sip_uri->user.slen > 0) { id = PJUtils::pj_str_to_string(&sip_uri->user) + "@" + PJUtils::pj_str_to_string(&sip_uri->host); } else { id = PJUtils::pj_str_to_string(&sip_uri->host); } } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { id = PJUtils::pj_str_to_string(&((pjsip_tel_uri*)uri)->number) + "@" + PJUtils::pj_str_to_string(&stack_data.default_home_domain); } else { const pj_str_t* scheme = pjsip_uri_get_scheme(uri); LOG_WARNING("Unsupported scheme \"%.*s\" in To header when determining private ID - ignoring", scheme->slen, scheme->ptr); } return id; }
/// Apply the mangalgorithm to the specified URI. The user part of the URI is /// always mangled. The domain is mangled separately, and is only mangled if /// mangelwurzel's change_domain flag is set, or if the function's /// force_mangle_domain flag is set (we use this flag to make sure we always /// mangle the domains for Routes and Record-Routes). We don't mangle /// anything else (e.g. port number, SIP parameters). void MangelwurzelTsx::mangle_uri(pjsip_uri* uri, pj_pool_t* pool, bool force_mangle_domain) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; std::string user = PJUtils::pj_str_to_string(&sip_uri->user); mangle_string(user); sip_uri->user = pj_strdup3(pool, user.c_str()); if ((force_mangle_domain) || (_config.change_domain)) { std::string host = PJUtils::pj_str_to_string(&sip_uri->host); mangle_string(host); sip_uri->host = pj_strdup3(pool, host.c_str()); } } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri; std::string number = PJUtils::pj_str_to_string(&tel_uri->number); mangle_string(number); tel_uri->number = pj_strdup3(pool, number.c_str()); } }
/// Utility to determine if URI contains a valid E.164 number pj_bool_t PJUtils::is_e164(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { return PJUtils::is_e164(&((pjsip_sip_uri*)uri)->user); } return PJ_FALSE; }
bool PJUtils::is_emergency_registration(pjsip_contact_hdr* contact_hdr) { // Contact header must be a SIP URI pjsip_sip_uri* uri = (contact_hdr->uri != NULL) ? (pjsip_sip_uri*)pjsip_uri_get_uri(contact_hdr->uri) : NULL; return ((uri != NULL) && (PJSIP_URI_SCHEME_IS_SIP(uri)) && (pjsip_param_find(&uri->other_param, &STR_SOS) != NULL)); }
/// Utility to determine if this URI belongs to the home domain. pj_bool_t PJUtils::is_home_domain(const pjsip_uri* uri) { if ((PJSIP_URI_SCHEME_IS_SIP(uri)) && (pj_stricmp(&((pjsip_sip_uri*)uri)->host, &stack_data.home_domain)==0)) { return PJ_TRUE; } return PJ_FALSE; }
/// Utility to determine if this URI belongs to the home domain. pj_bool_t PJUtils::is_home_domain(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { std::string host = pj_str_to_string(&((pjsip_sip_uri*)uri)->host); return is_home_domain(host); } return PJ_FALSE; }
/*! \brief Helper function which returns the SIP URI of a Contact header */ static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata) { pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { return NULL; } return pjsip_uri_get_uri(contact->uri); }
static int get_from_header(pjsip_rx_data *rdata, char *username, size_t username_size, char *domain, size_t domain_size) { pjsip_uri *from = rdata->msg_info.from->uri; pjsip_sip_uri *sip_from; if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) { return -1; } sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from); ast_copy_pj_str(username, &sip_from->user, username_size); ast_copy_pj_str(domain, &sip_from->host, domain_size); return 0; }
/// Extract the domain from a SIP URI, or if its another type of URI, return /// the default home domain. pj_str_t PJUtils::domain_from_uri(const std::string& uri_str, pj_pool_t* pool) { pjsip_uri* uri = PJUtils::uri_from_string(uri_str, pool); if (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri)) { return ((pjsip_sip_uri*)uri)->host; } else { return stack_data.default_home_domain; } }
static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata) { const pj_str_t stateful_user = { "1", 1 }; pjsip_uri *uri; pjsip_sip_uri *sip_uri; uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri); /* Only want to receive SIP/SIPS scheme */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) return PJ_FALSE; sip_uri = (pjsip_sip_uri*) uri; /* Check for matching user part */ if (pj_strcmp(&sip_uri->user, &stateful_user)!=0) return PJ_FALSE; /* * Yes, this is for us. * Respond statefully with 200/OK. */ switch (rdata->msg_info.msg->line.req.method.id) { case PJSIP_INVITE_METHOD: { pjsip_msg_body *body; if (dummy_sdp_str.slen == 0) dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr); body = pjsip_msg_body_create(rdata->tp_info.pool, &mime_application, &mime_sdp, &dummy_sdp_str); pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata, 200, NULL, NULL, body, NULL); } break; case PJSIP_ACK_METHOD: return PJ_TRUE; default: pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata, 200, NULL, NULL, NULL, NULL); break; } app.server.cur_state.stateful_cnt++; return PJ_TRUE; }
static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs) { pjsip_sip_uri *sip_uri; char host[256]; if (!contact || contact->star) { *addrs = NULL; return 0; } if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { *addrs = NULL; return 0; } sip_uri = pjsip_uri_get_uri(contact->uri); ast_copy_pj_str(host, &sip_uri->host, sizeof(host)); return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC); }
/*! * \internal * \brief Determine where in the dialplan a call should go * * \details This uses the username in the request URI to try to match * an extension in an endpoint's context in order to route the call. * * \param rdata The SIP request * \param context The context to use * \param exten The extension to use */ static enum pjsip_status_code get_destination(const pjsip_rx_data *rdata, const char *context, char *exten) { pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; pjsip_sip_uri *sip_ruri; if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { return PJSIP_SC_UNSUPPORTED_URI_SCHEME; } sip_ruri = pjsip_uri_get_uri(ruri); ast_copy_pj_str(exten, &sip_ruri->user, MAX_EXTEN_SIZE); if (ast_exists_extension(NULL, context, exten, 1, NULL)) { return PJSIP_SC_OK; } return PJSIP_SC_NOT_FOUND; }
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); pjsip_uri *ruri; pjsip_sip_uri *sip_ruri; char exten[AST_MAX_EXTENSION]; if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) { return PJ_FALSE; } if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) { return PJ_FALSE; } ruri = rdata->msg_info.msg->line.req.uri; if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { send_options_response(rdata, 416); return PJ_TRUE; } sip_ruri = pjsip_uri_get_uri(ruri); ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten)); /* * We may want to match in the dialplan without any user * options getting in the way. */ AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(exten); if (ast_shutting_down()) { /* * Not taking any new calls at this time. * Likely a server availability OPTIONS poll. */ send_options_response(rdata, 503); } else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) { send_options_response(rdata, 404); } else { send_options_response(rdata, 200); } return PJ_TRUE; }
/// Utility to determine if URI is local to this host. pj_bool_t PJUtils::is_uri_local(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { // Check the list of host names. pj_str_t host = ((pjsip_sip_uri*)uri)->host; unsigned i; for (i=0; i<stack_data.name_cnt; ++i) { if (pj_stricmp(&host, &stack_data.name[i])==0) { /* Match */ return PJ_TRUE; } } } /* Doesn't match */ return PJ_FALSE; }
static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg) { pjsip_contact_hdr *contact; contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); rewrite_uri(rdata, uri); if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } return 0; } return -1; }
static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) { pjsip_contact_hdr *contact; if (!endpoint) { return PJ_FALSE; } if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); pj_cstr(&uri->host, rdata->pkt_info.src_name); if (strcasecmp("udp", rdata->tp_info.transport->type_name)) { uri->transport_param = pj_str(rdata->tp_info.transport->type_name); } else { uri->transport_param.slen = 0; } uri->port = rdata->pkt_info.src_port; ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n", (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port); /* rewrite the session target since it may have already been pulled from the contact header */ if (dlg && (!dlg->remote.contact || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } } if (endpoint->nat.force_rport) { rdata->msg_info.via->rport_param = rdata->pkt_info.src_port; } return PJ_FALSE; }
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_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_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_URLSCHEME, 0); // Can't log the public ID as the subscribe has failed too early 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_EMERGENCY, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &STR_ALLOW_EVENTS, &STR_REG); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_EVENT, NULL, (pjsip_hdr*)allow_events_hdr, NULL); return; } 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 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); 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; // 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. // 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 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; std::string subscription_id; pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata, now, NULL, remote_store, &tdata_notify, &aor_data, true, subscription_id, 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; std::string ignore; write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL, &tdata_notify, &remote_aor_data, false, ignore, 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); // Add the to tag to the response pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); pj_strdup2(tdata->pool, &to->tag, subscription_id.c_str()); // 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, 0, NULL, NULL, true); 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; }
/// Write to the registration store. pj_status_t write_subscriptions_to_store(RegStore* primary_store, ///<store to write to std::string aor, ///<address of record to write to pjsip_rx_data* rdata, ///<received message to read headers from int now, ///<time now RegStore::AoR* backup_aor, ///<backup data if no entry in store RegStore* backup_store, ///<backup store to read from if no entry in store and no backup data pjsip_tx_data** tdata_notify, ///<tdata to construct a SIP NOTIFY from RegStore::AoR** aor_data, ///<aor_data to write to bool update_notify, ///<whether to generate a SIP NOTIFY std::string& subscription_id, SAS::TrailId trail) { // Parse the 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; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); pjsip_fromto_hdr* from = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL); pjsip_fromto_hdr* to = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL); // The registration store 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. bool backup_aor_alloced = false; int expiry = 0; pj_status_t status = PJ_FALSE; (*aor_data) = NULL; do { // delete NULL is safe, so we can do this on every iteration. delete (*aor_data); // Find the current subscriptions for the AoR. (*aor_data) = primary_store->get_aor_data(aor, trail); 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. // LCOV_EXCL_START - local store (used in testing) never fails LOG_ERROR("Failed to get AoR subscriptions for %s from store", aor.c_str()); break; // LCOV_EXCL_STOP } // If we don't have any subscriptions, try the backup AoR and/or store. if ((*aor_data)->subscriptions().empty()) { if ((backup_aor == NULL) && (backup_store != NULL)) { backup_aor = backup_store->get_aor_data(aor, trail); backup_aor_alloced = (backup_aor != NULL); } if ((backup_aor != NULL) && (!backup_aor->subscriptions().empty())) { for (RegStore::AoR::Subscriptions::const_iterator i = backup_aor->subscriptions().begin(); i != backup_aor->subscriptions().end(); ++i) { RegStore::AoR::Subscription* src = i->second; RegStore::AoR::Subscription* dst = (*aor_data)->get_subscription(i->first); *dst = *src; } } } pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (contact != NULL) { std::string contact_uri; pjsip_uri* uri = (contact->uri != NULL) ? (pjsip_uri*)pjsip_uri_get_uri(contact->uri) : NULL; if ((uri != NULL) && (PJSIP_URI_SCHEME_IS_SIP(uri))) { contact_uri = PJUtils::uri_to_string(PJSIP_URI_IN_CONTACT_HDR, uri); } subscription_id = PJUtils::pj_str_to_string(&to->tag); if (subscription_id == "") { // If there's no to tag, generate an unique one subscription_id = std::to_string(Utils::generate_unique_integer(id_deployment, id_instance)); } LOG_DEBUG("Subscription identifier = %s", subscription_id.c_str()); // Find the appropriate subscription in the subscription list for this AoR. If it can't // be found a new empty subscription is created. RegStore::AoR::Subscription* subscription = (*aor_data)->get_subscription(subscription_id); // Update/create the subscription. subscription->_req_uri = contact_uri; subscription->_route_uris.clear(); pjsip_route_hdr* route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, NULL); while (route_hdr) { std::string route = PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, route_hdr->name_addr.uri); LOG_DEBUG("Route header %s", route.c_str()); // Add the route. subscription->_route_uris.push_back(route); // Look for the next header. route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, route_hdr->next); } subscription->_cid = cid; subscription->_to_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, to->uri); subscription->_to_tag = subscription_id; subscription->_from_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, from->uri); subscription->_from_tag = PJUtils::pj_str_to_string(&from->tag); // Calculate the expiry period for the subscription. expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_expires; } subscription->_expires = now + expiry; std::map<std::string, RegStore::AoR::Binding> bindings; for (RegStore::AoR::Bindings::const_iterator i = (*aor_data)->bindings().begin(); i != (*aor_data)->bindings().end(); ++i) { std::string id = i->first; RegStore::AoR::Binding bind = *(i->second); if (!bind._emergency_registration) { bindings.insert(std::pair<std::string, RegStore::AoR::Binding>(id, bind)); } } if (update_notify) { NotifyUtils::SubscriptionState state = NotifyUtils::SubscriptionState::ACTIVE; if (expiry == 0) { state = NotifyUtils::SubscriptionState::TERMINATED; } status = NotifyUtils::create_notify(tdata_notify, subscription, aor, (*aor_data)->_notify_cseq, bindings, NotifyUtils::DocState::FULL, NotifyUtils::RegistrationState::ACTIVE, NotifyUtils::ContactState::ACTIVE, NotifyUtils::ContactEvent::REGISTERED, state, expiry); } if (analytics != NULL) { // Generate an analytics log for this subscription update. analytics->subscription(aor, subscription_id, contact_uri, expiry); } } } while (!primary_store->set_aor_data(aor, (*aor_data), false, trail)); // If we allocated the backup AoR, tidy up. if (backup_aor_alloced) { delete backup_aor; } return status; }
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; }
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc, const pjsip_rx_data *rdata, unsigned *contact_cnt, unsigned max_contact, pjsip_contact_hdr *contacts[]) { pj_int32_t expiration = NOEXP; const pjsip_msg *msg = rdata->msg_info.msg; const pjsip_hdr *hdr; /* Enumerate all Contact headers in the response */ *contact_cnt = 0; for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { if (hdr->type == PJSIP_H_CONTACT && *contact_cnt < max_contact) { contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr; ++(*contact_cnt); } } if (regc->current_op == REGC_REGISTERING) { pj_bool_t has_our_contact = PJ_FALSE; const pjsip_expires_hdr *expires; /* Get Expires header */ expires = (const pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); /* Try to find the Contact URIs that we register, in the response * to get the expires value. We'll try both with comparing the URI * and comparing the extension param only. */ if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) { unsigned i; for (i=0; i<*contact_cnt; ++i) { const pjsip_contact_hdr *our_hdr; our_hdr = (const pjsip_contact_hdr*) regc->contact_hdr_list.next; /* Match with our Contact header(s) */ while ((void*)our_hdr != (void*)®c->contact_hdr_list) { const pjsip_uri *uri1, *uri2; pj_bool_t matched = PJ_FALSE; /* Exclude the display name when comparing the URI * since server may not return it. */ uri1 = (const pjsip_uri*) pjsip_uri_get_uri(contacts[i]->uri); uri2 = (const pjsip_uri*) pjsip_uri_get_uri(our_hdr->uri); /* First try with exact matching, according to RFC 3261 * Section 19.1.4 URI Comparison */ if (pjsip_cfg()->regc.check_contact) { matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, uri1, uri2)==0; } /* If no match is found, try with matching the extension * parameter only if extension parameter was added. */ if (!matched && regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(uri1) || PJSIP_URI_SCHEME_IS_SIPS(uri1)) && (PJSIP_URI_SCHEME_IS_SIP(uri2) || PJSIP_URI_SCHEME_IS_SIPS(uri2))) { const pjsip_sip_uri *sip_uri1, *sip_uri2; const pjsip_param *p1, *p2; sip_uri1 = (const pjsip_sip_uri*)uri1; sip_uri2 = (const pjsip_sip_uri*)uri2; p1 = pjsip_param_cfind(&sip_uri1->other_param, &XUID_PARAM_NAME); p2 = pjsip_param_cfind(&sip_uri2->other_param, &XUID_PARAM_NAME); matched = p1 && p2 && pj_strcmp(&p1->value, &p2->value)==0; } if (matched) { has_our_contact = PJ_TRUE; if (contacts[i]->expires >= 0 && contacts[i]->expires < expiration) { /* Get the lowest expiration time. */ expiration = contacts[i]->expires; } break; } our_hdr = our_hdr->next; } /* while ((void.. */ } /* for (i=.. */ /* If matching Contact header(s) are found but the * header doesn't contain expires parameter, get the * expiration value from the Expires header. And * if Expires header is not present, get the expiration * value from the request. */ if (has_our_contact && expiration == NOEXP) { if (expires) { expiration = expires->ivalue; } else if (regc->expires_hdr) { expiration = regc->expires_hdr->ivalue; } else { /* We didn't request explicit expiration value, * and server doesn't specify it either. This * shouldn't happen unless we have a broken * registrar. */ expiration = 3600; } } } /* If we still couldn't get matching Contact header(s), it means * there must be something wrong with the registrar (e.g. it may * have modified the URI's in the response, which is prohibited). */ if (expiration==NOEXP) { /* If the number of Contact headers in the response matches * ours, they're all probably ours. Get the expiration * from there if this is the case, or from Expires header * if we don't have exact Contact header count, or * from the request as the last resort. */ pj_size_t our_contact_cnt; our_contact_cnt = pj_list_size(®c->contact_hdr_list); if (*contact_cnt == our_contact_cnt && *contact_cnt && contacts[0]->expires >= 0) { expiration = contacts[0]->expires; } else if (expires) expiration = expires->ivalue; else if (regc->expires_hdr) expiration = regc->expires_hdr->ivalue; else expiration = 3600; } } else { /* Just assume that the unregistration has been successful. */ expiration = 0; } /* Must have expiration value by now */ pj_assert(expiration != NOEXP); return expiration; }
static pj_status_t set_contact( pjsip_regc *regc, int contact_cnt, const pj_str_t contact[] ) { const pj_str_t CONTACT = { "Contact", 7 }; pjsip_contact_hdr *h; int i; /* Save existing contact list to removed_contact_hdr_list and * clear contact_hdr_list. */ pj_list_merge_last(®c->removed_contact_hdr_list, ®c->contact_hdr_list); /* Set the expiration of Contacts in to removed_contact_hdr_list * zero. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { h->expires = 0; h = h->next; } /* Process new contacts */ for (i=0; i<contact_cnt; ++i) { pjsip_contact_hdr *hdr; pj_str_t tmp; pj_strdup_with_null(regc->pool, &tmp, &contact[i]); hdr = (pjsip_contact_hdr*) pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL); if (hdr == NULL) { PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", (int)tmp.slen, tmp.ptr)); return PJSIP_EINVALIDURI; } /* Find the new contact in old contact list. If found, remove * the old header from the old header list. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { int rc; rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, h->uri, hdr->uri); if (rc == 0) { /* Match */ pj_list_erase(h); break; } h = h->next; } /* If add_xuid_param option is enabled and Contact URI is sip/sips, * add xuid parameter to assist matching the Contact URI in the * REGISTER response later. */ if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) || PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) { pjsip_param *xuid_param; pjsip_sip_uri *sip_uri; xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param); xuid_param->name = XUID_PARAM_NAME; pj_create_unique_string(regc->pool, &xuid_param->value); sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri); pj_list_push_back(&sip_uri->other_param, xuid_param); } pj_list_push_back(®c->contact_hdr_list, hdr); } return PJ_SUCCESS; }
// Determine the type of a URI. // // Parameters: // // - uri - the URI to classify // - prefer_sip - for ambiguous URIs like sip:[email protected] (which could be a global phone // number or just a SIP URI), prefer to interpret it as SIP // - check_np - check for the presence of Number Portability parameters and // classify accordingly // URIClass URIClassifier::classify_uri(const pjsip_uri* uri, bool prefer_sip, bool check_np) { URIClass ret = URIClass::UNKNOWN; // First, check to see if this URI has number portability data - this takes priority bool has_rn = false; bool has_npdi = false; if (check_np) { if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // If the URI is a tel URI, pull out the information from the other_params has_rn = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_NPDI) != NULL); } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { // If the URI is a tel URI, pull out the information from the userinfo_params has_rn = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_NPDI) != NULL); } } if (has_rn) { if (has_npdi) { ret = FINAL_NP_DATA; } else { ret = NP_DATA; } } // No number portability data else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // TEL URIs can only represent phone numbers - decide if it's a global (E.164) number or not pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri; std::string user = PJUtils::pj_str_to_string(&tel_uri->number); boost::match_results<std::string::const_iterator> results; if (boost::regex_match(user, results, CHARS_ALLOWED_IN_GLOBAL_NUM)) { ret = GLOBAL_PHONE_NUMBER; } else { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; } } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; pj_str_t host = sip_uri->host; bool home_domain = is_home_domain(host); bool local_to_node = is_local_name(host); bool is_gruu = (pjsip_param_find(&((pjsip_sip_uri*)uri)->other_param, &STR_GR) != NULL); bool treat_number_as_phone = !enforce_user_phone && !prefer_sip; TRC_DEBUG("home domain: %s, local_to_node: %s, is_gruu: %s, enforce_user_phone: %s, prefer_sip: %s, treat_number_as_phone: %s", home_domain ? "true" : "false", local_to_node ? "true" : "false", is_gruu ? "true" : "false", enforce_user_phone ? "true" : "false", prefer_sip ? "true" : "false", treat_number_as_phone ? "true" : "false"); bool classified = false; // SIP URI that's 'really' a phone number - apply the same logic as for TEL URIs if ((!pj_strcmp(&((pjsip_sip_uri*)uri)->user_param, &STR_USER_PHONE) || (home_domain && treat_number_as_phone && !is_gruu))) { // Get the user part minus any parameters. std::string user = PJUtils::pj_str_to_string(&sip_uri->user); if (!user.empty()) { std::vector<std::string> user_tokens; Utils::split_string(user, ';', user_tokens, 0, true); boost::match_results<std::string::const_iterator> results; if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_GLOBAL_NUM)) { ret = GLOBAL_PHONE_NUMBER; classified = true; } else if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_LOCAL_NUM)) { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; classified = true; } } } if (!classified) { // Not a phone number - classify it based on domain ret = (home_domain) ? HOME_DOMAIN_SIP_URI : ((local_to_node) ? NODE_LOCAL_SIP_URI : OFFNET_SIP_URI); } } std::string uri_str = PJUtils::uri_to_string(PJSIP_URI_IN_OTHER, uri); TRC_DEBUG("Classified URI %s as %d", uri_str.c_str(), (int)ret); return ret; }
/* * This is an internal function to find the most appropriate account to be * used to handle incoming calls. */ void on_acc_find_for_incoming_wrapper(const pjsip_rx_data *rdata, pjsua_acc_id* out_acc_id) { pjsip_uri *uri; pjsip_sip_uri *sip_uri; unsigned i; int current_matching_score = 0; int matching_scores[PJSUA_MAX_ACC]; pjsua_acc_id best_matching = pjsua_var.default_acc; /* Check that there's at least one account configured */ PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc); uri = rdata->msg_info.to->uri; /* Just return if To URI is not SIP: */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) { return; } PJSUA_LOCK(); sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri); /* Find account which has matching username and domain. */ for (i=0; i < pjsua_var.acc_cnt; ++i) { unsigned acc_id = pjsua_var.acc_ids[i]; pjsua_acc *acc = &pjsua_var.acc[acc_id]; if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) { /* Match ! */ PJSUA_UNLOCK(); *out_acc_id = acc_id; return; } } /* No exact matching, try fuzzy matching */ pj_bzero(matching_scores, sizeof(matching_scores)); /* No matching account, try match domain part only. */ for (i=0; i < pjsua_var.acc_cnt; ++i) { unsigned acc_id = pjsua_var.acc_ids[i]; pjsua_acc *acc = &pjsua_var.acc[acc_id]; if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) { /* Match ! */ /* We apply 100 weight if account has reg uri * Because in pragmatic case we are more looking * for these one than for the local acc */ matching_scores[i] += (acc->cfg.reg_uri.slen > 0) ? (300 * sip_uri->host.slen) : 1; } } /* No matching account, try match user part (and transport type) only. */ for (i=0; i < pjsua_var.acc_cnt; ++i) { unsigned acc_id = pjsua_var.acc_ids[i]; pjsua_acc *acc = &pjsua_var.acc[acc_id]; if (acc->valid) { /* We apply 100 weight if account has reg uri * Because in pragmatic case we are more looking * for these one than for the local acc */ unsigned weight = (acc->cfg.reg_uri.slen > 0) ? 100 : 1; if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_transport_type_e type; type = pjsip_transport_get_type_from_name(&sip_uri->transport_param); if (type == PJSIP_TRANSPORT_UNSPECIFIED) type = PJSIP_TRANSPORT_UDP; if (pjsua_var.tpdata[acc->cfg.transport_id].type != type) continue; } /* Match ! */ matching_scores[i] += (max_common_substr_len(&acc->user_part, &sip_uri->user) * weight); } } /* Still no match, use default account */ PJSUA_UNLOCK(); for(i=0; i<pjsua_var.acc_cnt; i++) { if(current_matching_score < matching_scores[i]) { best_matching = pjsua_var.acc_ids[i]; current_matching_score = matching_scores[i]; } } *out_acc_id = best_matching; }