static int pjsip_name_addr_compare( pjsip_uri_context_e context, const pjsip_name_addr *naddr1, const pjsip_name_addr *naddr2) { int d; /* Check that naddr2 is also a name_addr */ if (naddr1->vptr != naddr2->vptr) return -1; /* I'm not sure whether display name is included in the comparison. */ if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { return -1; } pj_assert( naddr1->uri != NULL ); pj_assert( naddr2->uri != NULL ); /* Compare name-addr as URL */ d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); if (d) return d; return 0; }
/*! \brief Callback function for finding a contact */ static int registrar_find_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; const struct registrar_contact_details *details = arg; pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0); return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0; }
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; }
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; }
/* * Test one test entry. */ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry) { pj_status_t status; int len; char *input; pjsip_uri *parsed_uri, *ref_uri; pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; pj_timestamp t1, t2; if (entry->len == 0) entry->len = pj_ansi_strlen(entry->str); #if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 input = pj_pool_alloc(pool, entry->len + 1); pj_memcpy(input, entry->str, entry->len); input[entry->len] = '\0'; #else input = entry->str; #endif /* Parse URI text. */ pj_get_timestamp(&t1); var.parse_len = var.parse_len + entry->len; parsed_uri = pjsip_parse_uri(pool, input, entry->len, 0); if (!parsed_uri) { /* Parsing failed. If the entry says that this is expected, then * return OK. */ status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri parse error!\n" " uri='%s'\n", input)); } goto on_return; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.parse_time, &t2); /* Create the reference URI. */ ref_uri = entry->creator(pool); /* Print both URI. */ s1.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); s2.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); pj_get_timestamp(&t1); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -20; goto on_return; } s1.ptr[len] = '\0'; s1.slen = len; var.print_len = var.print_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.print_time, &t2); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -30; goto on_return; } s2.ptr[len] = '\0'; s2.slen = len; /* Full comparison of parsed URI with reference URI. */ pj_get_timestamp(&t1); status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri); if (status != 0) { /* Not equal. See if this is the expected status. */ status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri comparison mismatch, status=%d:\n" " uri1='%s'\n" " uri2='%s'", status, s1.ptr, s2.ptr)); } goto on_return; } else { /* Equal. See if this is the expected status. */ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50; if (status != PJ_SUCCESS) { goto on_return; } } var.cmp_len = var.cmp_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.cmp_time, &t2); /* Compare text. */ if (entry->printed) { if (pj_strcmp2(&s1, entry->printed) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " printed='%s'\n" " expectd='%s'", s1.ptr, entry->printed)); status = -60; } } else { if (pj_strcmp(&s1, &s2) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " uri1='%s'\n" " uri2='%s'", s1.ptr, s2.ptr)); status = -70; } } on_return: return status; }
void ICSCFSproutletTsx::on_rx_initial_request(pjsip_msg* req) { pj_pool_t* pool = get_pool(req); pjsip_route_hdr* hroute = (pjsip_route_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_ROUTE, NULL); // TS 24.229 says I-CSCF processing shouldn't be done if a message has more than one Route header. // We've stripped one off in Sproutlet processing, so check for a second and just forward the // message if it's there. if (hroute != NULL) { send_request(req); return; } pjsip_uri* next_hop = PJUtils::next_hop(req); URIClass next_hop_class = URIClassifier::classify_uri(next_hop); if (req->line.req.method.id == PJSIP_ACK_METHOD && next_hop == req->line.req.uri && ((next_hop_class == NODE_LOCAL_SIP_URI) || (next_hop_class == HOME_DOMAIN_SIP_URI))) { // Ignore ACK messages with no Route headers and a local Request-URI, as: // - the I-CSCF should not be handling these // - we've seen ACKs matching this descrption being generated at overload and looping repeatedly // // This is a fairly targeted fix for https://github.com/Metaswitch/sprout/issues/1091. // TODO: remove this code when #1091 is fixed by other means. free_msg(req); return; } // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for non-REGISTER request"); // Before we clone the request for retries, remove the P-Profile-Key header // if present. PJUtils::remove_hdr(req, &STR_P_PROFILE_KEY); // Determine orig/term and the served user's name. const pjsip_route_hdr* route = route_hdr(); std::string impu; if ((route != NULL) && (pjsip_param_find(&((pjsip_sip_uri*)route->name_addr.uri)->other_param, &STR_ORIG) != NULL)) { // Originating request. TRC_DEBUG("Originating request"); _originating = true; impu = PJUtils::public_id_from_uri(PJUtils::orig_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_ORIG_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } else { // Terminating request. TRC_DEBUG("Terminating request"); _originating = false; pjsip_uri* uri = PJUtils::term_served_user(req); // If the Req URI is a SIP URI with the user=phone parameter set, is not a // GRUU and the user part starts with '+' (i.e. is a global phone number), // we should replace it with a tel URI, as per TS24.229 5.3.2.1. if (PJSIP_URI_SCHEME_IS_SIP(uri)) { URIClass uri_class = URIClassifier::classify_uri(uri); pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; if (uri_class == GLOBAL_PHONE_NUMBER) { TRC_DEBUG("Change request URI from SIP URI to tel URI"); req->line.req.uri = PJUtils::translate_sip_uri_to_tel_uri(sip_uri, pool); } } impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_TERM_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } // Create an LIR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFLIRouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impu, _originating); pjsip_sip_uri* scscf_sip_uri = NULL; // Use the router we just created to query the HSS for an S-CSCF to use. // TS 32.260 Table 5.2.1.1 says an EVENT ACR should be generated on the // completion of a Cx query issued in response to a SIP INVITE bool do_billing = (req->line.req.method.id == PJSIP_INVITE_METHOD); std::string wildcard; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); if ((!_originating) && (scscf_not_found(status_code))) { TRC_DEBUG("Couldn't find an S-CSCF, attempt to translate the URI"); pjsip_uri* uri = PJUtils::term_served_user(req); URIClass uri_class = URIClassifier::classify_uri(uri, false); // For terminating processing, if the HSS indicates that the user does not // exist, and if the request URI is a tel URI, try an ENUM translation. If // this succeeds, go back to the HSS. See TS24.229, 5.3.2.1. // // Before doing that we should check whether the enforce_user_phone flag is // set. If it isn't, and we have a numeric SIP URI, it is possible that // this should have been a tel URI, so translate it and do the HSS lookup // again. Once again, only do this for global numbers. if (PJSIP_URI_SCHEME_IS_SIP(uri) && (uri_class == GLOBAL_PHONE_NUMBER)) { TRC_DEBUG("enforce_user_phone set to false, try using a tel URI"); uri = PJUtils::translate_sip_uri_to_tel_uri((pjsip_sip_uri*)uri, pool); req->line.req.uri = uri; // We need to change the IMPU stored on our LIR router so that when // we do a new LIR we look up the new IMPU. impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); ((ICSCFLIRouter *)_router)->change_impu(impu); status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } if (_icscf->_enum_service) { // If we still haven't found an S-CSCF, we can now try an ENUM lookup. // We put this processing in a loop because in theory we may go round // several times before finding an S-CSCF. In reality this is unlikely // so we set MAX_ENUM_LOOKUPS to 2. for (int ii = 0; (ii < MAX_ENUM_LOOKUPS) && (scscf_not_found(status_code)); ++ii) { if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // Do an ENUM lookup and see if we should translate the TEL URI pjsip_uri* original_req_uri = req->line.req.uri; _icscf->translate_request_uri(req, get_pool(req), trail()); uri = req->line.req.uri; URIClass uri_class = URIClassifier::classify_uri(uri, false, true); std::string rn; if ((uri_class == NP_DATA) || (uri_class == FINAL_NP_DATA)) { // We got number portability information from ENUM - drop out and route to the BGCF. route_to_bgcf(req); return; } else if (pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, original_req_uri, req->line.req.uri) != PJ_SUCCESS) { // The URI has changed, so make sure we do a LIR lookup on it. impu = PJUtils::public_id_from_uri(req->line.req.uri); ((ICSCFLIRouter *)_router)->change_impu(impu); } // If we successfully translate the req URI and end up with either another TEL URI or a // local SIP URI, we should look for an S-CSCF again. if ((uri_class == LOCAL_PHONE_NUMBER) || (uri_class == GLOBAL_PHONE_NUMBER) || (uri_class == HOME_DOMAIN_SIP_URI)) { // TEL or local SIP URI. Look up the S-CSCF again. status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } else { // Number translated to off-switch. Drop out of the loop. ii = MAX_ENUM_LOOKUPS; } } else { // Can't translate the number, skip to the end of the loop. ii = MAX_ENUM_LOOKUPS; } } } else { // The user is not in the HSS and ENUM is not configured. TS 24.229 // says that, as an alternative to ENUM, we can "forward the request to // the transit functionality for subsequent routeing". Let's do that // (currently, we assume the BGCF is the transit functionality, but that // may be made configurable in future). TRC_DEBUG("No ENUM service available - outing request directly to transit function (BGCF)"); route_to_bgcf(req); return; } } URIClass uri_class = URIClassifier::classify_uri(req->line.req.uri); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for non-REGISTER"); if (_originating) { // Add the `orig` parameter. pjsip_param* orig_param = PJ_POOL_ALLOC_T(get_pool(req), pjsip_param); pj_strdup(get_pool(req), &orig_param->name, &STR_ORIG); orig_param->value.slen = 0; pj_list_insert_after(&scscf_sip_uri->other_param, orig_param); } // Add the P-Profile-Key header here if we've got a wildcard if (wildcard != "") { add_p_profile_header(wildcard, req); } PJUtils::add_route_header(req, scscf_sip_uri, get_pool(req)); send_request(req); } else if ((uri_class == OFFNET_SIP_URI) || (uri_class == GLOBAL_PHONE_NUMBER)) { // Target is a TEL URI or not in our home domain. Pass to the BGCF. route_to_bgcf(req); } else { // Target is in our home domain, but we failed to find an S-CSCF. This is the final response. pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }