/// Initialise the UAS transaction object. pj_status_t ICSCFProxy::UASTsx::init(pjsip_rx_data* rdata) { // Do the BasicProxy initialization first. pj_status_t status = BasicProxy::UASTsx::init(rdata); pjsip_msg* msg = rdata->msg_info.msg; // Create an ACR if ACR generation is enabled. _acr = create_acr(); // Parse interesting parameters from the request for the later lookups. if (msg->line.req.method.id == PJSIP_REGISTER_METHOD) { // REGISTER request. LOG_DEBUG("I-CSCF initialize transaction for REGISTER request"); _case = SessionCase::REGISTER; std::string impu; std::string impi; std::string visited_network; std::string auth_type; // Get the public identity from the To: header. pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(msg); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri); impu = PJUtils::public_id_from_uri(to_uri); // Get the private identity from the Authentication header, or generate // a default if there is no Authentication header or no username in the // header. pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // Get the IMPI from the username. impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); } else { // Create a default IMPI from the IMPU by removing the sip: prefix. impi = impu.substr(4); } // Get the visted network identification if present. If not, homestead will // default it. pjsip_generic_string_hdr* vn_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_P_V_N_I, NULL); if (vn_hdr != NULL) { visited_network = PJUtils::pj_str_to_string(&vn_hdr->hvalue); } else if (PJSIP_URI_SCHEME_IS_SIP(to_uri) || PJSIP_URI_SCHEME_IS_SIPS(to_uri)) { // Use the domain of the IMPU as the visited network. visited_network = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)to_uri)->host); } // Work out what authorization type to use by looking at the expiry // values in the request. (Use a default of 1 because if there is no // expires header or expires values in the contact headers this will // be a registration not a deregistration.) auth_type = (PJUtils::max_expires(msg, 1) > 0) ? "REG" : "DEREG"; // Create an UAR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFUARouter(((ICSCFProxy*)_proxy)->_hss, ((ICSCFProxy*)_proxy)->_scscf_selector, trail(), _acr, impi, impu, visited_network, auth_type); } else { // Non-register request. LOG_DEBUG("I-CSCF initialize transaction for non-REGISTER request"); // Check for a route header containing the orig parameter; pjsip_route_hdr* route = rdata->msg_info.route; std::string impu; if ((route != NULL) && (pjsip_param_find(&((pjsip_sip_uri*)route->name_addr.uri)->other_param, &STR_ORIG) != NULL)) { // Originating request. LOG_DEBUG("Originating request"); _case = SessionCase::ORIGINATING; impu = PJUtils::public_id_from_uri(PJUtils::orig_served_user(msg)); } else { // Terminating request. LOG_DEBUG("Terminating request"); _case = SessionCase::TERMINATING; impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(msg)); } // Create an LIR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFLIRouter(((ICSCFProxy*)_proxy)->_hss, ((ICSCFProxy*)_proxy)->_scscf_selector, trail(), _acr, impu, (_case == SessionCase::ORIGINATING)); } // Pass the received request to the ACR. _acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Record whether or not this is an in-dialog request. This is needed // to determine whether or not to send interim ACRs on provisional // responses. _in_dialog = (rdata->msg_info.msg->line.req.method.id != PJSIP_BYE_METHOD) && (rdata->msg_info.to->tag.slen != 0); return status; }
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) { pjsip_tpmgr_fla2_param prm; pjsip_cseq_hdr *cseq; pjsip_via_hdr *via; /* Use the destination information to determine what local interface this message will go out on */ pjsip_tpmgr_fla2_param_default(&prm); prm.tp_type = tdata->tp_info.transport->key.type; pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); prm.local_if = PJ_TRUE; /* If we can't get the local address use best effort and let it pass */ if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) { return PJ_SUCCESS; } /* The port in the message should always be that of the original transport */ prm.ret_port = tdata->tp_info.transport->local_name.port; /* If the IP source differs from the existing transport see if we need to update it */ if (pj_strcmp(&prm.ret_addr, &tdata->tp_info.transport->local_name.host)) { /* If the transport it is going out on is different reflect it in the message */ if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) { pjsip_transport *transport; transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port); if (transport) { tdata->tp_info.transport = transport; } } /* If the chosen transport is not bound to any we can't use the source address as it won't get back to us */ if (!multihomed_bound_any(tdata->tp_info.transport)) { pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host); } } else { /* The transport chosen will deliver this but ensure it is updated with the right information */ pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host); } /* If the message needs to be updated with new address do so */ if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) || pj_strcmp2(&cseq->method.name, "REGISTER")) { 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)) && !(tdata->msg->type == PJSIP_RESPONSE_MSG && tdata->msg->line.status.code / 100 == 3)) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); /* prm.ret_addr is allocated from the tdata pool OR the transport so it is perfectly fine to just do an assignment like this */ pj_strassign(&uri->host, &prm.ret_addr); uri->port = prm.ret_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); pjsip_tx_data_invalidate_msg(tdata); } } if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) { pj_strassign(&via->sent_by.host, &prm.ret_addr); via->sent_by.port = prm.ret_port; pjsip_tx_data_invalidate_msg(tdata); } /* Update the SDP if it is present */ if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") && multihomed_rewrite_sdp(tdata->msg->body->data)) { struct pjmedia_sdp_session *sdp = tdata->msg->body->data; int stream; pj_strassign(&sdp->conn->addr, &prm.ret_addr); for (stream = 0; stream < sdp->media_count; ++stream) { if (sdp->media[stream]->conn) { pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr); } } pjsip_tx_data_invalidate_msg(tdata); } return PJ_SUCCESS; }
static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata) { const pj_str_t call_user = { "2", 1 }; pjsip_uri *uri; pjsip_sip_uri *sip_uri; struct call *call; pjsip_dialog *dlg; pjmedia_sdp_session *sdp; pjsip_tx_data *tdata; pj_bool_t has_initial = PJ_FALSE; pj_status_t status; 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; /* Only want to handle INVITE requests. */ if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { return PJ_FALSE; } /* Check for matching user part. Incoming requests will be handled * call-statefully if: * - user part is "2", or * - user part is not "0" nor "1" and method is INVITE. */ if (pj_strcmp(&sip_uri->user, &call_user) == 0 || sip_uri->user.slen != 1 || (*sip_uri->user.ptr != '0' && *sip_uri->user.ptr != '1')) { /* Match */ } else { return PJ_FALSE; } /* Verify that we can handle the request. */ if (app.real_sdp) { unsigned options = 0; status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, app.sip_endpt, &tdata); if (status != PJ_SUCCESS) { /* * No we can't handle the incoming INVITE request. */ if (tdata) { pjsip_response_addr res_addr; pjsip_get_response_addr(tdata->pool, rdata, &res_addr); pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata, NULL, NULL); } else { /* Respond with 500 (Internal Server Error) */ pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL, NULL, NULL); } return PJ_TRUE; } } /* Create UAS dialog */ status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, &app.local_contact, &dlg); if (status != PJ_SUCCESS) { const pj_str_t reason = pj_str("Unable to create dialog"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 500, &reason, NULL, NULL); return PJ_TRUE; } /* Alloc call structure. */ call = pj_pool_zalloc(dlg->pool, sizeof(struct call)); /* Create SDP from PJMEDIA */ if (app.real_sdp) { status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool, app.skinfo_cnt, app.skinfo, &sdp); } else { sdp = app.dummy_sdp; } /* Create UAS invite session */ status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv); if (status != PJ_SUCCESS) { pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata); pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); return PJ_TRUE; } /* Send 100/Trying if needed */ if (app.server.send_trying) { status = send_response(call->inv, rdata, 100, &has_initial); if (status != PJ_SUCCESS) return PJ_TRUE; } /* Send 180/Ringing if needed */ if (app.server.send_ringing) { status = send_response(call->inv, rdata, 180, &has_initial); if (status != PJ_SUCCESS) return PJ_TRUE; } /* Simulate call processing delay */ if (app.server.delay) { pj_time_val delay; call->ans_timer.id = 1; call->ans_timer.user_data = call; call->ans_timer.cb = &answer_timer_cb; delay.sec = 0; delay.msec = app.server.delay; pj_time_val_normalize(&delay); pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay); } else { /* Send the 200 response immediately . */ status = send_response(call->inv, rdata, 200, &has_initial); PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE); } /* Done */ app.server.cur_state.call_cnt++; return PJ_TRUE; }
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. */ unsigned 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_bool_t endpoint_lookup(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint; struct unidentified_request *unid; int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; if (endpoint) { /* * ao2_find with OBJ_UNLINK always write locks the container before even searching * for the object. Since the majority case is that the object won't be found, do * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink * if needed. */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } apply_acls(rdata); return PJ_FALSE; } endpoint = ast_sip_identify_endpoint(rdata); if (endpoint) { unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } } if (!endpoint) { /* always use an artificial endpoint - per discussion no reason to have "alwaysauthreject" as an option. It is felt using it was a bug fix and it is not needed since we are not worried about breaking old stuff and we really don't want to enable the discovery of SIP accounts */ endpoint = ast_sip_get_artificial_endpoint(); } /* endpoint ref held by mod_data[] */ rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint; if (endpoint == artificial_endpoint && !is_ack) { char name[AST_UUID_STR_LEN] = ""; pjsip_uri *from = rdata->msg_info.from->uri; if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) { pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from); ast_copy_pj_str(name, &sip_from->user, sizeof(name)); } unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { check_endpoint(rdata, unid, name); ao2_ref(unid, -1); } else if (using_auth_username) { ao2_wrlock(unidentified_requests); /* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (unid) { check_endpoint(rdata, unid, name); } else { unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1, NULL, AO2_ALLOC_OPT_LOCK_RWLOCK); if (!unid) { ao2_unlock(unidentified_requests); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */ unid->first_seen = ast_tvnow(); unid->count = 1; ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK); } ao2_ref(unid, -1); ao2_unlock(unidentified_requests); } else { log_failed_request(rdata, "No matching endpoint found", 0, 0); ast_sip_report_invalid_endpoint(name, rdata); } } apply_acls(rdata); return PJ_FALSE; }
static pj_bool_t regs_rx_request(pjsip_rx_data *rdata) { pjsip_msg *msg = rdata->msg_info.msg; pjsip_hdr hdr_list; int code; pj_status_t status; if (msg->line.req.method.id != PJSIP_REGISTER_METHOD) return PJ_FALSE; if (!registrar.cfg.respond) return PJ_TRUE; pj_list_init(&hdr_list); if (registrar.cfg.authenticate && pjsip_msg_find_hdr(msg, PJSIP_H_AUTHORIZATION, NULL)==NULL) { pjsip_generic_string_hdr *hwww; const pj_str_t hname = pj_str("WWW-Authenticate"); const pj_str_t hvalue = pj_str("Digest realm=\"test\""); hwww = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname, &hvalue); pj_list_push_back(&hdr_list, hwww); code = 401; } else { if (registrar.cfg.contact_op == EXACT || registrar.cfg.contact_op == MODIFIED) { pjsip_hdr *hsrc; for (hsrc=msg->hdr.next; hsrc!=&msg->hdr; hsrc=hsrc->next) { pjsip_contact_hdr *hdst; if (hsrc->type != PJSIP_H_CONTACT) continue; hdst = (pjsip_contact_hdr*) pjsip_hdr_clone(rdata->tp_info.pool, hsrc); if (hdst->expires==0) continue; if (registrar.cfg.contact_op == MODIFIED) { if (PJSIP_URI_SCHEME_IS_SIP(hdst->uri) || PJSIP_URI_SCHEME_IS_SIPS(hdst->uri)) { pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdst->uri); sip_uri->host = pj_str("x-modified-host"); sip_uri->port = 1; } } if (registrar.cfg.expires_param) hdst->expires = registrar.cfg.expires_param; pj_list_push_back(&hdr_list, hdst); } } if (registrar.cfg.more_contacts.slen) { pjsip_generic_string_hdr *hcontact; const pj_str_t hname = pj_str("Contact"); hcontact = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname, ®istrar.cfg.more_contacts); pj_list_push_back(&hdr_list, hcontact); } if (registrar.cfg.expires) { pjsip_expires_hdr *hexp; hexp = pjsip_expires_hdr_create(rdata->tp_info.pool, registrar.cfg.expires); pj_list_push_back(&hdr_list, hexp); } registrar.response_cnt++; code = registrar.cfg.status_code; } status = pjsip_endpt_respond(endpt, NULL, rdata, code, NULL, &hdr_list, NULL, NULL); pj_assert(status == PJ_SUCCESS); return PJ_TRUE; }
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; }
/* * Create new request message to be forwarded upstream to new destination URI * in uri. */ PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt, pjsip_rx_data *rdata, const pjsip_uri *uri, const pj_str_t *branch, unsigned options, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_UNUSED_ARG(options); /* Request forwarding rule in RFC 3261 section 16.6: * * For each target, the proxy forwards the request following these * steps: * * 1. Make a copy of the received request * 2. Update the Request-URI * 3. Update the Max-Forwards header field * 4. Optionally add a Record-route header field value * 5. Optionally add additional header fields * 6. Postprocess routing information * 7. Determine the next-hop address, port, and transport * 8. Add a Via header field value * 9. Add a Content-Length header field if necessary * 10. Forward the new request * * Of these steps, we only do step 1-3, since the later will be * done by application. */ status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Always increment ref counter to 1 */ pjsip_tx_data_add_ref(tdata); /* Duplicate the request */ PJ_TRY { pjsip_msg *dst; const pjsip_msg *src = rdata->msg_info.msg; const pjsip_hdr *hsrc; /* Create the request */ tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); /* Duplicate request method */ pjsip_method_copy(tdata->pool, &tdata->msg->line.req.method, &src->line.req.method); /* Set request URI */ if (uri) { dst->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, uri); } else { dst->line.req.uri= (pjsip_uri*) pjsip_uri_clone(tdata->pool, src->line.req.uri); } /* Clone ALL headers */ hsrc = src->hdr.next; while (hsrc != &src->hdr) { pjsip_hdr *hdst; /* If this is the top-most Via header, insert our own before * cloning the header. */ if (hsrc == (pjsip_hdr*)rdata->msg_info.via) { pjsip_via_hdr *hvia; hvia = pjsip_via_hdr_create(tdata->pool); if (branch) pj_strdup(tdata->pool, &hvia->branch_param, branch); else { pj_str_t new_branch = pjsip_calculate_branch_id(rdata); pj_strdup(tdata->pool, &hvia->branch_param, &new_branch); } pjsip_msg_add_hdr(dst, (pjsip_hdr*)hvia); } /* Skip Content-Type and Content-Length as these would be * generated when the the message is printed. */ else if (hsrc->type == PJSIP_H_CONTENT_LENGTH || hsrc->type == PJSIP_H_CONTENT_TYPE) { hsrc = hsrc->next; continue; } #if 0 /* If this is the top-most Route header and it indicates loose * route, remove the header. */ else if (hsrc == (pjsip_hdr*)rdata->msg_info.route) { const pjsip_route_hdr *hroute = (const pjsip_route_hdr*) hsrc; const pjsip_sip_uri *sip_uri; if (!PJSIP_URI_SCHEME_IS_SIP(hroute->name_addr.uri) && !PJSIP_URI_SCHEME_IS_SIPS(hroute->name_addr.uri)) { /* This is a bad request! */ status = PJSIP_EINVALIDHDR; goto on_error; } sip_uri = (pjsip_sip_uri*) hroute->name_addr.uri; if (sip_uri->lr_param) { /* Yes lr param is present, skip this Route header */ hsrc = hsrc->next; continue; } } #endif /* Clone the header */ hdst = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hsrc); /* If this is Max-Forward header, decrement the value */ if (hdst->type == PJSIP_H_MAX_FORWARDS) { pjsip_max_fwd_hdr *hmaxfwd = (pjsip_max_fwd_hdr*)hdst; --hmaxfwd->ivalue; } /* Append header to new request */ pjsip_msg_add_hdr(dst, hdst); hsrc = hsrc->next; } /* 16.6.3: * If the copy does not contain a Max-Forwards header field, the * proxy MUST add one with a field value, which SHOULD be 70. */ if (rdata->msg_info.max_fwd == NULL) { pjsip_max_fwd_hdr *hmaxfwd = pjsip_max_fwd_hdr_create(tdata->pool, 70); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hmaxfwd); } /* Clone request body */ if (src->body) { dst->body = pjsip_msg_body_clone(tdata->pool, src->body); } } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END /* Done */ *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; }
void ICSCFSproutletRegTsx::on_rx_initial_request(pjsip_msg* req) { // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for REGISTER request"); // Extract relevant fields from the message std::string impu; std::string impi; std::string visited_network; std::string auth_type; // Get the public identity from the To: header. pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri); impu = PJUtils::public_id_from_uri(to_uri); SAS::Event reg_event(trail(), SASEvent::ICSCF_RCVD_REGISTER, 0); reg_event.add_var_param(impu); SAS::report_event(reg_event); // Get the private identity from the Authentication header, or generate // a default if there is no Authentication header or no username in the // header. pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // Get the IMPI from the username. impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); } else { // Create a default IMPI from the IMPU by removing the sip: prefix. impi = impu.substr(4); } // Get the visited network identification if present. If not, warn // (because a spec-compliant P-CSCF should default it) and use a // sensible default. pjsip_generic_string_hdr* vn_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(req, &STR_P_V_N_I, NULL); if (vn_hdr != NULL) { visited_network = PJUtils::pj_str_to_string(&vn_hdr->hvalue); } else if (PJSIP_URI_SCHEME_IS_SIP(to_uri) || PJSIP_URI_SCHEME_IS_SIPS(to_uri)) { // Use the domain of the IMPU as the visited network. visited_network = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)to_uri)->host); TRC_WARNING("No P-Visited-Network-ID in REGISTER - using %s as a default", visited_network.c_str()); } // Work out what authorization type to use by looking at the expiry // values in the request. (Use a default of 1 because if there is no // expires header or expires values in the contact headers this will // be a registration not a deregistration.) auth_type = (PJUtils::max_expires(req, 1) > 0) ? "REG" : "DEREG"; // Remove any Route headers present on the request as we're re-routing the // message. pj_str_t route_hdr_name = pj_str((char *)"Route"); PJUtils::remove_hdr(req, &route_hdr_name); // Create an UAR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFUARouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impi, impu, visited_network, auth_type); // We have a router, query it for an S-CSCF to use. pjsip_sip_uri* scscf_sip_uri = NULL; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(get_pool(req), scscf_sip_uri); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for REGISTER"); req->line.req.uri = (pjsip_uri*)scscf_sip_uri; send_request(req); } else { pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }
static int rx_task(void *data) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; const char *aor_name = ast_sorcery_object_get_id(task_data->aor); RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; /* Retrieve the current contacts, we'll need to know whether to update or not */ contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* So we don't count static contacts against max_contacts we prune them out from the container */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if (registrar_validate_path(task_data, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 420, NULL, NULL, NULL); ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts); return PJ_TRUE; } if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } user_agent_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { size_t alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; char contact_uri[PJSIP_MAX_URL_SIZE]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } if (ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, task_data->endpoint)) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = task_data->aor->qualify_frequency; contact_update->authenticate_qualify = task_data->aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sorcery_delete(ast_sip_get_sorcery(), contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_cleanup(contact_update); } else { /* We want to report the user agent that was actually in the removed contact */ user_agent = ast_strdupa(contact->user_agent); ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER * do so */ if (task_data->aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Update the contacts as things will probably have changed */ ao2_cleanup(contacts); contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(task_data->rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); return PJ_TRUE; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(task_data->aor, NULL, task_data->rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint); return PJ_TRUE; }
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) { RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup); struct rx_task_data *task_data; RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); pjsip_sip_uri *uri; char *domain_name; char *configured_aors, *aor_name; RAII_VAR(struct ast_str *, id, NULL, ast_free); if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { return PJ_FALSE; } if (ast_strlen_zero(endpoint->aors)) { /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors"); ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received"); ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); domain_name = ast_alloca(uri->host.slen + 1); ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1); if (endpoint->bypass_auth) { uri->user = pj_str((char *) ast_sorcery_object_get_id(endpoint)); } configured_aors = ast_strdupa(endpoint->aors); /* Iterate the configured AORs to see if the user or the user+domain match */ while ((aor_name = strsep(&configured_aors, ","))) { struct ast_sip_domain_alias *alias = NULL; if (!pj_strcmp2(&uri->user, aor_name)) { break; } if (!id && !(id = ast_str_create(uri->user.slen + uri->host.slen + 2))) { return PJ_TRUE; } ast_str_set(&id, 0, "%.*s@", (int)uri->user.slen, uri->user.ptr); if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { ast_str_append(&id, 0, "%s", alias->domain); ao2_cleanup(alias); } else { ast_str_append(&id, 0, "%s", domain_name); } if (!strcmp(aor_name, ast_str_buffer(id))) { ast_free(id); break; } } if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); ast_log(LOG_WARNING, "AOR '%.*s' not found for endpoint '%s'\n", (int)uri->user.slen, uri->user.ptr, ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!aor->max_contacts) { /* Registration is not permitted for this AOR */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted"); ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n", ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!(ser = serializer_find_or_create(aor_name))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (ast_sip_push_task(ser->serializer, rx_task, task_data)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); ao2_ref(task_data, -1); } return PJ_TRUE; }
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted) { pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256), }; if (!details.pool) { return -1; } while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { int expiration = registrar_get_expiration(aor, contact, rdata); RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ if ((expiration != 0) || previous) { pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } continue; } else if (previous && previous->star) { /* If there is a previous contact and it is a '*' this is a deal breaker */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } previous = contact; if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { continue; } details.uri = pjsip_uri_get_uri(contact->uri); /* Determine if this is an add, update, or delete for policy enforcement purposes */ if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { if (expiration) { (*added)++; } } else if (expiration) { (*updated)++; } else { (*deleted)++; } } /* The provided contacts are acceptable, huzzah! */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return 0; } /*! \brief Callback function which prunes static contacts */ static int registrar_prune_static(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0; } /*! \brief Internal function used to delete a contact from an AOR */ static int registrar_delete_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; const char *aor_name = arg; ast_sip_location_delete_contact(contact); if (!ast_strlen_zero(aor_name)) { ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact->uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact->uri, aor_name, contact->user_agent); } return 0; } /*! \brief Internal function which adds a contact to a response */ static int registrar_add_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; pjsip_tx_data *tdata = arg; pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool); pj_str_t uri; pj_strdup2_with_null(tdata->pool, &uri, contact->uri); hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR); hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); return 0; } /*! \brief Helper function which adds a Date header to a response */ static void registrar_add_date_header(pjsip_tx_data *tdata) { char date[256]; struct tm tm; time_t t = time(NULL); gmtime_r(&t, &tm); strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm); ast_sip_add_header(tdata, "Date", date); }
/* * Initialize a new account (after configuration is set). */ static pj_status_t initialize_acc(unsigned acc_id) { pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_name_addr *name_addr; pjsip_sip_uri *sip_uri, *sip_reg_uri; pj_status_t status; unsigned i; /* Need to parse local_uri to get the elements: */ name_addr = (pjsip_name_addr*) pjsip_parse_uri(acc->pool, acc_cfg->id.ptr, acc_cfg->id.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (name_addr == NULL) { pjsua_perror(THIS_FILE, "Invalid local URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } /* Local URI MUST be a SIP or SIPS: */ if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) && !PJSIP_URI_SCHEME_IS_SIPS(name_addr)) { pjsua_perror(THIS_FILE, "Invalid local URI", PJSIP_EINVALIDSCHEME); return PJSIP_EINVALIDSCHEME; } /* Get the SIP URI object: */ sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr); /* Parse registrar URI, if any */ if (acc_cfg->reg_uri.slen) { pjsip_uri *reg_uri; reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr, acc_cfg->reg_uri.slen, 0); if (reg_uri == NULL) { pjsua_perror(THIS_FILE, "Invalid registrar URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } /* Registrar URI MUST be a SIP or SIPS: */ if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) && !PJSIP_URI_SCHEME_IS_SIPS(reg_uri)) { pjsua_perror(THIS_FILE, "Invalid registar URI", PJSIP_EINVALIDSCHEME); return PJSIP_EINVALIDSCHEME; } sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri); } else { sip_reg_uri = NULL; } /* Save the user and domain part. These will be used when finding an * account for incoming requests. */ acc->display = name_addr->display; acc->user_part = sip_uri->user; acc->srv_domain = sip_uri->host; acc->srv_port = 0; if (sip_reg_uri) { acc->srv_port = sip_reg_uri->port; } /* Create Contact header if not present. */ //if (acc_cfg->contact.slen == 0) { // acc_cfg->contact = acc_cfg->id; //} /* Build account route-set from outbound proxies and route set from * account configuration. */ pj_list_init(&acc->route_set); for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) { pj_str_t hname = { "Route", 5}; pjsip_route_hdr *r; pj_str_t tmp; pj_strdup_with_null(acc->pool, &tmp, &pjsua_var.ua_cfg.outbound_proxy[i]); r = (pjsip_route_hdr*) pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL); if (r == NULL) { pjsua_perror(THIS_FILE, "Invalid outbound proxy URI", PJSIP_EINVALIDURI); return PJSIP_EINVALIDURI; } pj_list_push_back(&acc->route_set, r); } for (i=0; i<acc_cfg->proxy_cnt; ++i) { pj_str_t hname = { "Route", 5}; pjsip_route_hdr *r; pj_str_t tmp; pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]); r = (pjsip_route_hdr*) pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL); if (r == NULL) { pjsua_perror(THIS_FILE, "Invalid URI in account route set", PJ_EINVAL); return PJ_EINVAL; } pj_list_push_back(&acc->route_set, r); } /* Concatenate credentials from account config and global config */ acc->cred_cnt = 0; for (i=0; i<acc_cfg->cred_count; ++i) { acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i]; } for (i=0; i<pjsua_var.ua_cfg.cred_count && acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i) { acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i]; } status = pjsua_pres_init_acc(acc_id); if (status != PJ_SUCCESS) return status; /* Mark account as valid */ pjsua_var.acc[acc_id].valid = PJ_TRUE; /* Insert account ID into account ID array, sorted by priority */ for (i=0; i<pjsua_var.acc_cnt; ++i) { if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority < pjsua_var.acc[acc_id].cfg.priority) { break; } } pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]), pjsua_var.acc_cnt, i, &acc_id); return PJ_SUCCESS; }
static void register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts, struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0; int updated = 0; int deleted = 0; int permanent = 0; int contact_count; struct ao2_container *existing_contacts = NULL; pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* We create a single pool and use it throughout this function where we need one */ details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 1024, 256); if (!details.pool) { response->code = 500; return; } /* If there are any permanent contacts configured on the AOR we need to take them * into account when counting contacts. */ if (aor->permanent_contacts) { permanent = ao2_container_count(aor->permanent_contacts); } if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); response->code = 400; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); response->code = 420; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (aor->remove_existing) { /* Cumulative number of contacts affected by this registration */ contact_count = MAX(updated + added - deleted, 0); /* We need to keep track of only existing contacts so we can later * remove them if need be. */ existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, ast_sorcery_object_id_compare); if (!existing_contacts) { response->code = 500; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts); } else { /* Total contacts after this registration */ contact_count = ao2_container_count(contacts) - permanent + added - deleted; } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); response->code = 403; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); ao2_cleanup(existing_contacts); return; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); /* If we are keeping track of existing contacts for removal then, well, there is * absolutely nothing left so no need to try to remove any. */ if (existing_contacts) { ao2_ref(existing_contacts, -1); existing_contacts = NULL; } break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); /* If a contact was returned and we need to keep track of existing contacts then it * should be removed. */ if (contact && existing_contacts) { ao2_unlink(existing_contacts, contact); } if (!contact) { int prune_on_boot; /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata); contact = ast_sip_location_create_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint); if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } if (prune_on_boot) { const char *contact_name; struct contact_transport_monitor *monitor; /* * Monitor the transport in case it gets disconnected because * the contact won't be valid anymore if that happens. */ contact_name = ast_sorcery_object_get_id(contact); monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (monitor) { strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_register(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor); ao2_ref(monitor, -1); } } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); ao2_link(contacts, contact); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_link(contacts, contact_update); ao2_cleanup(contact_update); } else { if (contact->prune_on_boot) { struct contact_transport_monitor *monitor; const char *contact_name = ast_sorcery_object_get_id(contact); monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name)); strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_unregister(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher); } /* We want to report the user agent that was actually in the removed contact */ ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* * If the AOR is configured to remove any contacts over max_contacts * that have not been updated/added/deleted as a result of this * REGISTER do so. * * The existing contacts container holds all contacts that were not * involved in this REGISTER. * The contacts container holds the current contacts of the AOR. */ if (aor->remove_existing && existing_contacts) { /* Total contacts after this registration */ contact_count = ao2_container_count(existing_contacts) + updated + added; if (contact_count > aor->max_contacts) { /* Remove excess existing contacts that expire the soonest */ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts); } ao2_ref(existing_contacts, -1); } response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); response->code = 500; return; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } response->tdata = tdata; }
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts, struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted) { pjsip_contact_hdr *previous = NULL; pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { .pool = pool, }; for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) { int expiration = registrar_get_expiration(aor, contact, rdata); struct ast_sip_contact *existing; char contact_uri[pjsip_max_url_size]; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ if (expiration != 0 || previous) { return -1; } /* Count all contacts to delete */ *deleted = ao2_container_count(contacts) - permanent; previous = contact; continue; } else if (previous && previous->star) { /* If there is a previous contact and it is a '*' this is a deal breaker */ return -1; } previous = contact; if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { continue; } details.uri = pjsip_uri_get_uri(contact->uri); /* pjsip_uri_print returns -1 if there's not enough room in the buffer */ if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) { /* If the total length of the uri is greater than pjproject can handle, go no further */ return -1; } if (details.uri->host.slen >= pj_max_hostname) { /* If the length of the hostname is greater than pjproject can handle, go no further */ return -1; } /* Determine if this is an add, update, or delete for policy enforcement purposes */ existing = ao2_callback(contacts, 0, registrar_find_contact, &details); ao2_cleanup(existing); if (!existing) { if (expiration) { ++*added; } } else if (expiration) { ++*updated; } else { ++*deleted; } } return 0; }
/* * Add new buddy. */ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg, pjsua_buddy_id *p_buddy_id) { pjsip_name_addr *url; pjsua_buddy *buddy; pjsip_sip_uri *sip_uri; int index; pj_str_t tmp; PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <= PJ_ARRAY_SIZE(pjsua_var.buddy), PJ_ETOOMANY); PJSUA_LOCK(); /* Find empty slot */ for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) { if (pjsua_var.buddy[index].uri.slen == 0) break; } /* Expect to find an empty slot */ if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) { PJSUA_UNLOCK(); /* This shouldn't happen */ pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)"); return PJ_ETOOMANY; } buddy = &pjsua_var.buddy[index]; /* Create pool for this buddy */ if (buddy->pool) { pj_pool_reset(buddy->pool); } else { char name[PJ_MAX_OBJ_NAME]; pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index); buddy->pool = pjsua_pool_create(name, 512, 256); } /* Init buffers for presence subscription status */ buddy->term_reason.ptr = (char*) pj_pool_alloc(buddy->pool, PJSUA_BUDDY_SUB_TERM_REASON_LEN); /* Get name and display name for buddy */ pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri); url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (url == NULL) { pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI); pj_pool_release(buddy->pool); buddy->pool = NULL; PJSUA_UNLOCK(); return PJSIP_EINVALIDURI; } /* Only support SIP schemes */ if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) { pj_pool_release(buddy->pool); buddy->pool = NULL; PJSUA_UNLOCK(); return PJSIP_EINVALIDSCHEME; } /* Reset buddy, to make sure everything is cleared with default * values */ reset_buddy(index); /* Save URI */ pjsua_var.buddy[index].uri = tmp; sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri); pjsua_var.buddy[index].name = sip_uri->user; pjsua_var.buddy[index].display = url->display; pjsua_var.buddy[index].host = sip_uri->host; pjsua_var.buddy[index].port = sip_uri->port; pjsua_var.buddy[index].monitor = cfg->subscribe; if (pjsua_var.buddy[index].port == 0) pjsua_var.buddy[index].port = 5060; /* Save user data */ pjsua_var.buddy[index].user_data = (void*)cfg->user_data; if (p_buddy_id) *p_buddy_id = index; pjsua_var.buddy_cnt++; PJSUA_UNLOCK(); pjsua_buddy_subscribe_pres(index, cfg->subscribe); return PJ_SUCCESS; }