PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg; pjsip_hdr *hdr; pj_status_t status; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); pj_lock_acquire(regc->lock); if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } regc->expires_requested = 0; status = create_request(regc, &tdata); if (status != PJ_SUCCESS) { pj_lock_release(regc->lock); return status; } msg = tdata->msg; /* Add Contact headers. */ hdr = (pjsip_hdr*)regc->contact_hdr_list.next; while ((void*)hdr != (void*)®c->contact_hdr_list) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); hdr = hdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next; pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); pj_list_erase(hdr); } /* Add Expires:0 header */ hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0); pjsip_msg_add_hdr(msg, hdr); pj_lock_release(regc->lock); *p_tdata = tdata; return PJ_SUCCESS; }
static pj_status_t send_options_response(pjsip_rx_data *rdata, int code) { pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata); pjsip_tx_data *tdata; const pjsip_hdr *hdr; pj_status_t status; /* Make the response object */ status = ast_sip_create_response(rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create response (%d)\n", status); return status; } /* Add appropriate headers */ if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } /* * XXX TODO: pjsip doesn't care a lot about either of these headers - * while it provides specific methods to create them, they are defined * to be the standard string header creation. We never did add them * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here. */ ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING); ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE); if (dlg && trans) { status = pjsip_dlg_send_response(dlg, trans, tdata); } else { struct ast_sip_endpoint *endpoint; endpoint = ast_pjsip_rdata_get_endpoint(rdata); status = ast_sip_send_stateful_response(rdata, tdata, endpoint); ao2_cleanup(endpoint); } if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send response (%d)\n", status); } return status; }
pjsip_route_hdr * createRouteSet(const std::string &route, pj_pool_t *hdr_pool) { pjsip_route_hdr *route_set = pjsip_route_hdr_create(hdr_pool); std::string host; int port = 0; size_t found = route.find(":"); if (found != std::string::npos) { host = route.substr(0, found); port = atoi(route.substr(found + 1, route.length() - found).c_str()); } else host = route; pjsip_route_hdr *routing = pjsip_route_hdr_create(hdr_pool); pjsip_sip_uri *url = pjsip_sip_uri_create(hdr_pool, 0); url->lr_param = 1; routing->name_addr.uri = (pjsip_uri*) url; pj_strdup2(hdr_pool, &url->host, host.c_str()); url->port = port; RING_DBG("Adding route %s", host.c_str()); pj_list_push_back(route_set, pjsip_hdr_clone(hdr_pool, routing)); return route_set; }
void SIPPresence::fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data) { if (tdata->msg->type == PJSIP_REQUEST_MSG) { const pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent"); std::string useragent(acc_->getUserAgentName()); pj_str_t pJuseragent = pj_str((char*) useragent.c_str()); pjsip_hdr *h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent); pjsip_msg_add_hdr(tdata->msg, h); } if (msg_data == NULL) return; const pjsip_hdr *hdr; hdr = msg_data->hdr_list.next; while (hdr && hdr != &msg_data->hdr_list) { pjsip_hdr *new_hdr; new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr); DEBUG("adding header", new_hdr->name.ptr); pjsip_msg_add_hdr(tdata->msg, new_hdr); hdr = hdr->next; } if (msg_data->content_type.slen && msg_data->msg_body.slen) { pjsip_msg_body *body; const pj_str_t type = CONST_PJ_STR("application"); const pj_str_t subtype = CONST_PJ_STR("pidf+xml"); body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &msg_data->msg_body); tdata->msg->body = body; } }
/// This is a clone of the PJSIP pjsip_endpt_respond_stateless function, /// with the addition of code to reflect the trail on the request on to the /// response. All sprout application code should use this method instead. pj_status_t PJUtils::respond_stateless(pjsip_endpoint* endpt, pjsip_rx_data* rdata, int st_code, const pj_str_t* st_text, const pjsip_hdr* hdr_list, const pjsip_msg_body* body) { pj_status_t status; pjsip_response_addr res_addr; pjsip_tx_data* tdata; // Create response message status = create_response(endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) { return status; } // Add the message headers, if any if (hdr_list) { const pjsip_hdr* hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } // Add the message body, if any. if (body) { tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } // Get where to send request. status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } // Send! status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } return PJ_SUCCESS; }
void PJUtils::clone_header(const pj_str_t* hdr_name, pjsip_msg* old_msg, pjsip_msg* new_msg, pj_pool_t* pool) { pjsip_hdr* original_hdr = NULL; pjsip_hdr* last_hdr = NULL; while ((original_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(old_msg, hdr_name, original_hdr)) && (last_hdr != original_hdr)) { LOG_INFO("Cloning header! %ld", (long int)original_hdr); pjsip_hdr* new_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, original_hdr); pjsip_msg_add_hdr(new_msg, new_hdr); last_hdr = original_hdr; } }
static int options_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata) { pjsip_tx_data *tdata; pj_status_t status; const pjsip_hdr *hdr; pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); status = pjsip_dlg_create_response(session->inv_session->dlg, rdata, 200, NULL,&tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create response (%d)\n", status); return status; } /* Add appropriate headers */ if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); } /* * XXX TODO: pjsip doesn't care a lot about either of these headers - * while it provides specific methods to create them, they are defined * to be the standard string header creation. We never did add them * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here. */ ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING); ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE); status = pjsip_dlg_send_response(session->inv_session->dlg, pjsip_rdata_get_tsx(rdata), tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send response (%d)\n", status); } return status; }
/*! * \internal * \brief Insert the header pointers into the linked list. * * For each header in the message, allocate a list entry, * clone the header, then insert the entry. */ static int insert_headers(pj_pool_t * pool, struct hdr_list *list, pjsip_msg * msg) { pjsip_hdr *hdr = msg->hdr.next; struct hdr_list_entry *le; while (hdr && hdr != &msg->hdr) { le = pj_pool_zalloc(pool, sizeof(struct hdr_list_entry)); le->hdr = pjsip_hdr_clone(pool, hdr); AST_LIST_INSERT_TAIL(list, le, nextptr); hdr = hdr->next; } return 0; }
/* * Update authentication session with a challenge. */ static void update_digest_session( pj_pool_t *ses_pool, pjsip_cached_auth *cached_auth, const pjsip_www_authenticate_hdr *hdr ) { if (hdr->challenge.digest.qop.slen == 0) return; /* Initialize cnonce and qop if not present. */ if (cached_auth->cnonce.slen == 0) { /* Save the whole challenge */ cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); /* Create cnonce */ pj_create_unique_string( ses_pool, &cached_auth->cnonce ); /* Initialize nonce-count */ cached_auth->nc = 1; /* Save realm. */ pj_assert(cached_auth->realm.slen != 0); if (cached_auth->realm.slen == 0) { pj_strdup(ses_pool, &cached_auth->realm, &hdr->challenge.digest.realm); } } else { /* Update last_nonce and nonce-count */ if (!pj_strcmp(&hdr->challenge.digest.nonce, &cached_auth->last_chal->challenge.digest.nonce)) { /* Same nonce, increment nonce-count */ ++cached_auth->nc; } else { /* Server gives new nonce. */ pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce, &hdr->challenge.digest.nonce); /* Has the opaque changed? */ if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque)) { pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque); } cached_auth->nc = 1; } } }
/* * A simple registrar, invoked by default_mod_on_rx_request() */ static void simple_registrar(pjsip_rx_data *rdata) { pjsip_tx_data *tdata; const pjsip_expires_hdr *exp; const pjsip_hdr *h; unsigned cnt = 0; pjsip_generic_string_hdr *srv; pj_status_t status; status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(), rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) return; exp = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); h = rdata->msg_info.msg->hdr.next; while (h != &rdata->msg_info.msg->hdr) { if (h->type == PJSIP_H_CONTACT) { const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h; int e = c->expires; if (e < 0) { if (exp) e = exp->ivalue; else e = 3600; } if (e > 0) { pjsip_contact_hdr *nc = (pjsip_contact_hdr*) pjsip_hdr_clone(tdata->pool, h); nc->expires = e; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc); ++cnt; } } h = h->next; } srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL); srv->name = pj_str((char*)"Server"); srv->hvalue = pj_str((char*)"pjsua simple registrar"); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv); pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata, NULL, NULL); }
PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc, const pjsip_hdr *hdr_list) { const pjsip_hdr *h; PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL); pj_list_init(&pubc->usr_hdr); h = hdr_list->next; while (h != hdr_list) { pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h)); h = h->next; } return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc, const pjsip_route_hdr *route_set) { const pjsip_route_hdr *chdr; PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL); pj_list_init(®c->route_set); chdr = route_set->next; while (chdr != route_set) { pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr)); chdr = chdr->next; } return PJ_SUCCESS; }
pjsip_multipart_clone_part(pj_pool_t *pool, const pjsip_multipart_part *src) { pjsip_multipart_part *dst; const pjsip_hdr *hdr; dst = pjsip_multipart_create_part(pool); hdr = src->hdr.next; while (hdr != &src->hdr) { pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr)); hdr = hdr->next; } dst->body = pjsip_msg_body_clone(pool, src->body); return dst; }
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc, const pjsip_hdr *hdr_list) { const pjsip_hdr *hdr; PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL); //This is "add" operation, so don't remove headers. //pj_list_init(®c->hdr_list); hdr = hdr_list->next; while (hdr != hdr_list) { pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr)); hdr = hdr->next; } return PJ_SUCCESS; }
/// Add the Via headers that we removed from the request back on the response. /// We do this by looking at the original request. void MangelwurzelTsx::add_via_hdrs(pjsip_msg* rsp, pj_pool_t* pool) { // Copy all the via headers from the original request back onto the response // in the correct order. pjsip_via_hdr* via_hdr = (pjsip_via_hdr*)pjsip_msg_find_hdr(_unmodified_request, PJSIP_H_VIA, NULL); while (via_hdr != NULL) { pjsip_via_hdr* cloned_via_hdr = (pjsip_via_hdr*)pjsip_hdr_clone(pool, via_hdr); pjsip_msg_add_hdr(rsp, (pjsip_hdr*)cloned_via_hdr); via_hdr = (pjsip_via_hdr*)pjsip_msg_find_hdr(_unmodified_request, PJSIP_H_VIA, via_hdr->next); } }
/// This is analogous to respond_stateless, although in this case to /// respond statefully on an existing transaction. Strangely there is /// no equivalent PJSIP API. pj_status_t PJUtils::respond_stateful(pjsip_endpoint* endpt, pjsip_transaction* uas_tsx, pjsip_rx_data* rdata, int st_code, const pj_str_t* st_text, const pjsip_hdr* hdr_list, const pjsip_msg_body* body) { pj_status_t status; pjsip_tx_data* tdata; status = create_response(stack_data.endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) { return status; } // Add the message headers, if any if (hdr_list) { const pjsip_hdr* hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } // Add the message body, if any. if (body) { tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } status = pjsip_tsx_send_msg(uas_tsx, tdata); return status; }
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; }
/* * Set route-set. */ PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, const pjsip_route_hdr *route_set ) { const pjsip_route_hdr *hdr; pj_mutex_lock(sub->mutex); /* Clear existing route set. */ pj_list_init(&sub->route_set); /* Duplicate route headers. */ hdr = route_set->next; while (hdr != route_set) { pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); pj_list_insert_before(&sub->route_set, new_hdr); hdr = hdr->next; } pj_mutex_unlock(sub->mutex); return 0; }
static void* multipart_clone_data(pj_pool_t *pool, const void *data, unsigned len) { const struct multipart_data *src; struct multipart_data *dst; const pjsip_multipart_part *src_part; PJ_UNUSED_ARG(len); src = (const struct multipart_data*) data; dst = PJ_POOL_ALLOC_T(pool, struct multipart_data); pj_list_init(&dst->part_head); pj_strdup(pool, &dst->boundary, &src->boundary); src_part = src->part_head.next; while (src_part != &src->part_head) { pjsip_multipart_part *dst_part; const pjsip_hdr *src_hdr; dst_part = pjsip_multipart_create_part(pool); src_hdr = src_part->hdr.next; while (src_hdr != &src_part->hdr) { pjsip_hdr *dst_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, src_hdr); pj_list_push_back(&dst_part->hdr, dst_hdr); src_hdr = src_hdr->next; } dst_part->body = pjsip_msg_body_clone(pool, src_part->body); pj_list_push_back(&dst->part_head, dst_part); src_part = src_part->next; } return (void*)dst; }
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_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; }
PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, pjsip_tx_data **p_tdata) { pjsip_msg *msg; pjsip_contact_hdr *hdr; const pjsip_hdr *h_allow; pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); pj_lock_acquire(regc->lock); status = create_request(regc, &tdata); if (status != PJ_SUCCESS) { pj_lock_release(regc->lock); return status; } msg = tdata->msg; /* Add Contact headers. */ hdr = regc->contact_hdr_list.next; while (hdr != ®c->contact_hdr_list) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); hdr = hdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { hdr = regc->removed_contact_hdr_list.next; pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); pj_list_erase(hdr); } if (regc->expires_hdr) pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, regc->expires_hdr)); if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */ h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL); if (h_allow) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, h_allow)); } regc->auto_reg = autoreg; pj_lock_release(regc->lock); /* Done */ *p_tdata = tdata; return PJ_SUCCESS; }
/* * Update authentication session with a challenge. */ static void update_digest_session( pj_pool_t *ses_pool, pjsip_cached_auth *cached_auth, const pjsip_www_authenticate_hdr *hdr ) { if (hdr->challenge.digest.qop.slen == 0) { #if PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (!cached_auth->last_chal || pj_stricmp2(&hdr->scheme, "digest")) { cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); } else { /* Only update if the new challenge is "significantly different" * than the one in the cache, to reduce memory usage. */ const pjsip_digest_challenge *d1 = &cached_auth->last_chal->challenge.digest; const pjsip_digest_challenge *d2 = &hdr->challenge.digest; if (pj_strcmp(&d1->domain, &d2->domain) || pj_strcmp(&d1->realm, &d2->realm) || pj_strcmp(&d1->nonce, &d2->nonce) || pj_strcmp(&d1->opaque, &d2->opaque) || pj_strcmp(&d1->algorithm, &d2->algorithm) || pj_strcmp(&d1->qop, &d2->qop)) { cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); } } #endif return; } /* Initialize cnonce and qop if not present. */ if (cached_auth->cnonce.slen == 0) { /* Save the whole challenge */ cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); /* Create cnonce */ pj_create_unique_string( ses_pool, &cached_auth->cnonce ); /* Initialize nonce-count */ cached_auth->nc = 1; /* Save realm. */ /* Note: allow empty realm (http://trac.pjsip.org/repos/ticket/1061) pj_assert(cached_auth->realm.slen != 0); */ if (cached_auth->realm.slen == 0) { pj_strdup(ses_pool, &cached_auth->realm, &hdr->challenge.digest.realm); } } else { /* Update last_nonce and nonce-count */ if (!pj_strcmp(&hdr->challenge.digest.nonce, &cached_auth->last_chal->challenge.digest.nonce)) { /* Same nonce, increment nonce-count */ ++cached_auth->nc; } else { /* Server gives new nonce. */ pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce, &hdr->challenge.digest.nonce); /* Has the opaque changed? */ if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque)) { pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque); } cached_auth->nc = 1; } } }
/* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; }
static void regc_tsx_callback(void *token, pjsip_event *event) { pj_status_t status; pjsip_regc *regc = (pjsip_regc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; pj_bool_t handled = PJ_TRUE; pj_bool_t update_contact = PJ_FALSE; pj_atomic_inc(regc->busy_ctr); pj_lock_acquire(regc->lock); /* Decrement pending transaction counter. */ pj_assert(regc->has_tsx); regc->has_tsx = PJ_FALSE; /* Add reference to the transport */ if (tsx->transport != regc->last_transport) { if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } if (tsx->transport) { regc->last_transport = tsx->transport; pjsip_transport_add_ref(regc->last_transport); } } if (regc->_delete_flag == 0 && regc->tsx_cb && regc->current_op == REGC_REGISTERING) { struct pjsip_regc_tsx_cb_param param; param.contact_cnt = -1; cbparam_init(¶m.cbparam, regc, PJ_SUCCESS, tsx->status_code, &tsx->status_text, (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL, -1, 0, NULL); /* Call regc tsx callback before handling any response */ pj_lock_release(regc->lock); (*regc->tsx_cb)(¶m); pj_lock_acquire(regc->lock); if (param.contact_cnt >= 0) { /* Since we receive non-2xx response, it means that (some) contact * bindings haven't been established so we can safely remove these * contact headers. This is to avoid removing non-existent contact * bindings later. */ if (tsx->status_code/100 != 2) { pjsip_contact_hdr *h; h = regc->contact_hdr_list.next; while (h != ®c->contact_hdr_list) { pjsip_contact_hdr *next = h->next; if (h->expires == -1) { pj_list_erase(h); } h = next; } } /* Update contact address */ pjsip_regc_update_contact(regc, param.contact_cnt, param.contact); update_contact = PJ_TRUE; } } /* Handle 401/407 challenge (even when _delete_flag is set) */ if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; /* reset current op */ regc->current_op = REGC_IDLE; if (update_contact) { pjsip_msg *msg; pjsip_hdr *hdr, *ins_hdr; pjsip_contact_hdr *chdr; /* Delete Contact headers, but we shouldn't delete headers * which are supposed to remove contact bindings since * we cannot reconstruct those headers. */ msg = tsx->last_tx->msg; hdr = msg->hdr.next; ins_hdr = &msg->hdr; while (hdr != &msg->hdr) { pjsip_hdr *next = hdr->next; if (hdr->type == PJSIP_H_CONTACT) { chdr = (pjsip_contact_hdr *)hdr; if (chdr->expires != 0) { pj_list_erase(hdr); ins_hdr = next; } } hdr = next; } /* Add Contact headers. */ chdr = regc->contact_hdr_list.next; while (chdr != ®c->contact_hdr_list) { pj_list_insert_before(ins_hdr, (pjsip_hdr*) pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr)); chdr = chdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { chdr = regc->removed_contact_hdr_list.next; pj_list_insert_before(ins_hdr, (pjsip_hdr*) pjsip_hdr_clone(tsx->last_tx->pool, chdr)); pj_list_erase(chdr); } } status = pjsip_auth_clt_reinit_req( ®c->auth_sess, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS) { /* Only call callback if application is still interested * in it. */ if (regc->_delete_flag == 0) { /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1, 0, NULL); pj_lock_acquire(regc->lock); } } } else if (regc->_delete_flag) { /* User has called pjsip_regc_destroy(), so don't call callback. * This regc will be destroyed later in this function. */ /* Just reset current op */ regc->current_op = REGC_IDLE; } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF && regc->current_op == REGC_REGISTERING) { /* Handle 423 response automatically: * - set requested expiration to Min-Expires header, ONLY IF * the original request is a registration (as opposed to * unregistration) and the requested expiration was indeed * lower than Min-Expires) * - resend the request */ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_min_expires_hdr *me_hdr; pjsip_tx_data *tdata; pj_int32_t min_exp; /* reset current op */ regc->current_op = REGC_IDLE; /* Update requested expiration */ me_hdr = (pjsip_min_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL); if (me_hdr) { min_exp = me_hdr->ivalue; } else { /* Broken server, Min-Expires doesn't exist. * Just guestimate then, BUT ONLY if if this is the * first time we received such response. */ enum { /* Note: changing this value would require changing couple of * Python test scripts. */ UNSPECIFIED_MIN_EXPIRES = 3601 }; if (!regc->expires_hdr || regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES) { min_exp = UNSPECIFIED_MIN_EXPIRES; } else { handled = PJ_FALSE; PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response " "without Min-Expires header is invalid")); goto handle_err; } } if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) { /* But we already send with greater expiration time, why does * the server send us with 423? Oh well, just fail the request. */ handled = PJ_FALSE; PJ_LOG(4,(THIS_FILE, "Registration failed: invalid " "Min-Expires header value in response")); goto handle_err; } set_expires(regc, min_exp); status = pjsip_regc_register(regc, regc->auto_reg, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS) { /* Only call callback if application is still interested * in it. */ if (!regc->_delete_flag) { /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1, 0, NULL); pj_lock_acquire(regc->lock); } } } else { handled = PJ_FALSE; } handle_err: if (!handled) { pjsip_rx_data *rdata; pj_int32_t expiration = NOEXP; unsigned contact_cnt = 0; pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; if (tsx->status_code/100 == 2) { rdata = event->body.tsx_state.src.rdata; /* Calculate expiration */ expiration = calculate_response_expiration(regc, rdata, &contact_cnt, PJSIP_REGC_MAX_CONTACT, contact); /* Schedule next registration */ schedule_registration(regc, expiration); } else { rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL; } /* Update registration */ if (expiration==NOEXP) expiration=-1; regc->expires = expiration; /* Mark operation as complete */ regc->current_op = REGC_IDLE; /* Call callback. */ /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : &tsx->status_text), rdata, expiration, contact_cnt, contact); pj_lock_acquire(regc->lock); } pj_lock_release(regc->lock); /* Delete the record if user destroy regc during the callback. */ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) { pjsip_regc_destroy(regc); } }
/* * Verify that incoming request with Replaces header can be processed. */ PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata, pjsip_dialog **p_dlg, pj_bool_t lock_dlg, pjsip_tx_data **p_tdata) { const pj_str_t STR_REPLACES = { "Replaces", 8 }; pjsip_replaces_hdr *rep_hdr; int code = 200; const char *warn_text = NULL; pjsip_hdr res_hdr_list; pjsip_dialog *dlg = NULL; pjsip_inv_session *inv; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL); /* Check that pjsip_replaces_init_module() has been called. */ PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP); /* Init output arguments */ *p_dlg = NULL; if (p_tdata) *p_tdata = NULL; pj_list_init(&res_hdr_list); /* Find Replaces header */ rep_hdr = (pjsip_replaces_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, NULL); if (!rep_hdr) { /* No Replaces header. No further processing is necessary. */ return PJ_SUCCESS; } /* Check that there's no other Replaces header and return 400 Bad Request * if not. */ if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, rep_hdr->next)) { code = PJSIP_SC_BAD_REQUEST; warn_text = "Found multiple Replaces headers"; goto on_return; } /* Find the dialog identified by Replaces header (and always lock the * dialog no matter what application wants). */ dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag, &rep_hdr->from_tag, PJ_TRUE); /* Respond with 481 "Call/Transaction Does Not Exist" response if * no dialog is found. */ if (dlg == NULL) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "No dialog found for Replaces request"; goto on_return; } /* Get the invite session within the dialog */ inv = pjsip_dlg_get_inv_session(dlg); /* Return 481 if no invite session is present. */ if (inv == NULL) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "No INVITE session found for Replaces request"; goto on_return; } /* Return 603 Declined response if invite session has already * terminated */ if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) { code = PJSIP_SC_DECLINE; warn_text = "INVITE session already terminated"; goto on_return; } /* If "early-only" flag is present, check that the invite session * has not been confirmed yet. If the session has been confirmed, * return 486 "Busy Here" response. */ if (rep_hdr->early_only && inv->state >= PJSIP_INV_STATE_CONNECTING) { code = PJSIP_SC_BUSY_HERE; warn_text = "INVITE session already established"; goto on_return; } /* If the Replaces header field matches an early dialog that was not * initiated by this UA, it returns a 481 (Call/Transaction Does Not * Exist) response to the new INVITE. */ if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "Found early INVITE session but not initiated by this UA"; goto on_return; } /* * Looks like everything is okay!! */ *p_dlg = dlg; status = PJ_SUCCESS; code = 200; on_return: /* Create response if necessary */ if (code != 200) { /* If we have dialog we must unlock it */ if (dlg) pjsip_dlg_dec_lock(dlg); /* Create response */ if (p_tdata) { pjsip_tx_data *tdata; const pjsip_hdr *h; status = pjsip_endpt_create_response(the_endpt, rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add response headers. */ h = res_hdr_list.next; while (h != &res_hdr_list) { pjsip_hdr *cloned; cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h); PJ_ASSERT_RETURN(cloned, PJ_ENOMEM); pjsip_msg_add_hdr(tdata->msg, cloned); h = h->next; } /* Add warn text, if any */ if (warn_text) { pjsip_warning_hdr *warn_hdr; pj_str_t warn_value = pj_str((char*)warn_text); warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399, pjsip_endpt_name(the_endpt), &warn_value); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr); } *p_tdata = tdata; } /* Can not return PJ_SUCCESS when response message is produced. * Ref: PROTOS test ~#2490 */ if (status == PJ_SUCCESS) status = PJSIP_ERRNO_FROM_SIP_STATUS(code); } else { /* If application doesn't want to lock the dialog, unlock it */ if (!lock_dlg) pjsip_dlg_dec_lock(dlg); } return status; }
/* * create response benchmark */ static int create_response_bench(pj_timestamp *p_elapsed) { enum { COUNT = 100 }; unsigned i, j; pjsip_via_hdr *via; pjsip_rx_data rdata; pjsip_tx_data *request; pjsip_tx_data *tdata[COUNT]; pj_timestamp t1, t2, elapsed; pj_status_t status; /* Create the request first. */ pj_str_t str_target = pj_str("sip:[email protected]"); pj_str_t str_from = pj_str("\"Local User\" <sip:[email protected]>"); pj_str_t str_to = pj_str("\"Remote User\" <sip:[email protected]>"); pj_str_t str_contact = str_from; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } /* Create several Via headers */ via = pjsip_via_hdr_create(request->pool); via->sent_by.host = pj_str("192.168.0.7"); via->sent_by.port = 5061; via->transport = pj_str("udp"); via->rport_param = 0; via->branch_param = pj_str("012345678901234567890123456789"); via->recvd_param = pj_str("192.168.0.7"); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via); /* Create "dummy" rdata from the tdata */ pj_bzero(&rdata, sizeof(pjsip_rx_data)); rdata.tp_info.pool = request->pool; rdata.msg_info.msg = request->msg; rdata.msg_info.from = (pjsip_from_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.to = (pjsip_to_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL); rdata.msg_info.cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL); rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.via = via; /* * Now benchmark create_response */ elapsed.u64 = 0; for (i=0; i<LOOP; i+=COUNT) { pj_bzero(tdata, sizeof(tdata)); pj_get_timestamp(&t1); for (j=0; j<COUNT; ++j) { status = pjsip_endpt_create_response(endpt, &rdata, 200, NULL, &tdata[j]); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); goto on_error; } } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); for (j=0; j<COUNT; ++j) pjsip_tx_data_dec_ref(tdata[j]); } p_elapsed->u64 = elapsed.u64; pjsip_tx_data_dec_ref(request); return PJ_SUCCESS; on_error: for (i=0; i<COUNT; ++i) { if (tdata[i]) pjsip_tx_data_dec_ref(tdata[i]); } return -400; }
static 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_bool_t on_rx_request( pjsip_rx_data *rdata ) { pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; pjsip_dialog *dlg; pjsip_rdata_sdp_info *sdp_info; pjmedia_sdp_session *answer = NULL; pjsip_tx_data *tdata = NULL; call_t *call = NULL; unsigned i; pj_status_t status; PJ_LOG(3,(THIS_FILE, "RX %.*s from %s", (int)rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, rdata->pkt_info.src_name)); if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { /* Let me be a registrar! */ pjsip_hdr hdr_list, *h; pjsip_msg *msg; int expires = -1; pj_list_init(&hdr_list); msg = rdata->msg_info.msg; h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (h) { expires = ((pjsip_expires_hdr*)h)->ivalue; pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); PJ_LOG(3,(THIS_FILE, " Expires=%d", expires)); } if (expires != 0) { h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (h) pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); } pjsip_endpt_respond(app.sip_endpt, &mod_sipecho, rdata, 200, NULL, &hdr_list, NULL, NULL); return PJ_TRUE; } if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_str_t reason = pj_str("Go away"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } return PJ_TRUE; } sdp_info = pjsip_rdata_get_sdp_info(rdata); if (!sdp_info || !sdp_info->sdp) { pj_str_t reason = pj_str("Require valid offer"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } for (i=0; i<MAX_CALLS; ++i) { if (app.call[i].inv == NULL) { call = &app.call[i]; break; } } if (i==MAX_CALLS) { pj_str_t reason = pj_str("We're full"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, PJSIP_SC_BUSY_HERE, &reason, NULL, NULL); return PJ_TRUE; } /* Generate Contact URI */ status = pj_gethostip(AF, &hostaddr); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return PJ_TRUE; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, SIP_PORT); local_uri = pj_str(temp); status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, &local_uri, &dlg); if (status == PJ_SUCCESS) answer = create_answer(call-app.call, dlg->pool, sdp_info->sdp); if (status == PJ_SUCCESS) status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv); if (status == PJ_SUCCESS) status = pjsip_inv_initial_answer(call->inv, rdata, 100, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 180, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 500, NULL, NULL, NULL); destroy_call(call); } else { call->inv->mod_data[mod_sipecho.id] = call; } return PJ_TRUE; }
/* * Send response statefully. */ PJ_DEF(pj_status_t) pjsip_endpt_respond( pjsip_endpoint *endpt, pjsip_module *tsx_user, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body, pjsip_transaction **p_tsx ) { pj_status_t status; pjsip_tx_data *tdata; pjsip_transaction *tsx; /* Validate arguments. */ PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL); if (p_tsx) *p_tsx = NULL; /* Create response message */ status = pjsip_endpt_create_response( endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; /* Add the message headers, if any */ if (hdr_list) { const pjsip_hdr *hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } /* Add the message body, if any. */ if (body) { tdata->msg->body = pjsip_msg_body_clone( tdata->pool, body ); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } /* Create UAS transaction. */ status = pjsip_tsx_create_uas(tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } /* Feed the request to the transaction. */ pjsip_tsx_recv_msg(tsx, rdata); /* Send the message. */ status = pjsip_tsx_send_msg(tsx, tdata); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } else if (p_tsx) { *p_tsx = tsx; } return status; }