/// Mangelwurzel can be configured to generate originating or terminating /// requests, and out of the blue requests. This requires manipulation of the /// S-CSCF Route header, which will be the top Route header. Mangelwurzel /// adds the orig parameter for originating requests, removes it for /// terminating requests and removes the ODI token from the URI for out of /// the blue requests. void MangelwurzelTsx::edit_scscf_route_hdr(pjsip_msg* req, pj_pool_t* pool) { pjsip_route_hdr* route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_ROUTE, NULL); if (route_hdr != NULL) { pjsip_sip_uri* scscf_uri = (pjsip_sip_uri*)route_hdr->name_addr.uri; pjsip_param* orig_param = pjsip_param_find(&scscf_uri->other_param, &STR_ORIG); if ((_config.orig) && (orig_param == NULL)) { TRC_DEBUG("Add orig param to S-CSCF Route header"); orig_param = PJ_POOL_ALLOC_T(pool, pjsip_param); pj_strdup(pool, &orig_param->name, &STR_ORIG); orig_param->value.slen = 0; pj_list_insert_after(&scscf_uri->other_param, orig_param); } else if ((!_config.orig) && (orig_param != NULL)) { TRC_DEBUG("Remove orig param from S-CSCF Route header"); pj_list_erase(orig_param); } // Ensure there is no ODI token by clearing the user part of the URI. if (_config.ootb) { TRC_DEBUG("Remove ODI token from S-CSCF Route header"); scscf_uri->user.ptr = NULL; scscf_uri->user.slen = 0; } } }
/* * Create new block. * Create a new big chunk of memory block, from which user allocation will be * taken from. */ static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) { pj_pool_block *block; PJ_CHECK_STACK(); pj_assert(size >= sizeof(pj_pool_block)); LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", size, pool->capacity, pj_pool_get_used_size(pool))); /* Request memory from allocator. */ block = (pj_pool_block*) (*pool->factory->policy.block_alloc)(pool->factory, size); if (block == NULL) { (*pool->callback)(pool, size); return NULL; } /* Add capacity. */ pool->capacity += size; /* Set start and end of buffer. */ block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); block->end = ((unsigned char*)block) + size; /* Set the start pointer, aligning it as needed */ block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); /* Insert in the front of the list. */ pj_list_insert_after(&pool->block_list, block); LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); return block; }
/* * Create new memory pool. */ PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback) { pj_pool_t *pool; pj_pool_block *block; pj_uint8_t *buffer; PJ_CHECK_STACK(); /* Size must be at least sizeof(pj_pool)+sizeof(pj_pool_block) */ PJ_ASSERT_RETURN(initial_size >= sizeof(pj_pool_t)+sizeof(pj_pool_block), NULL); /* If callback is NULL, set calback from the policy */ if (callback == NULL) callback = f->policy.callback; /* Allocate initial block */ buffer = (pj_uint8_t*) (*f->policy.block_alloc)(f, initial_size); if (!buffer) return NULL; /* Set pool administrative data. */ pool = (pj_pool_t*)buffer; pj_bzero(pool, sizeof(*pool)); pj_list_init(&pool->block_list); pool->factory = f; /* Create the first block from the memory. */ block = (pj_pool_block*) (buffer + sizeof(*pool)); block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); block->end = buffer + initial_size; /* Set the start pointer, aligning it as needed */ block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); pj_list_insert_after(&pool->block_list, block); pj_pool_init_int(pool, name, increment_size, callback); /* Pool initial capacity and used size */ pool->capacity = initial_size; LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); return pool; }
/// Calculate targets for incoming requests by querying HSS. int ICSCFProxy::UASTsx::calculate_targets() { // Invoke the router to select an S-CSCF. std::string scscf; int status_code = _router->get_scscf(scscf); if (status_code == PJSIP_SC_OK) { // Found a suitable S-CSCF. if (_case == SessionCase::REGISTER) { // REGISTER request, so add a target with this S-CSCF as the Request-URI. LOG_DEBUG("Route REGISTER to S-CSCF %s", scscf.c_str()); Target* target = new Target; target->uri = PJUtils::uri_from_string(scscf, _req->pool); add_target(target); // Don't add a P-User-Database header - as per 5.3.1.2/TS24.229 Note 3 // this can only be added if we have local configuration that the S-CSCF // can process P-User-Database. } else { // Non-register request, so add a Route header for the destination S-CSCF. LOG_DEBUG("Route Non-REGISTER to S-CSCF %s", scscf.c_str()); Target* target = new Target; pjsip_sip_uri* route_uri = (pjsip_sip_uri*)PJUtils::uri_from_string(scscf, _req->pool); route_uri->lr_param = 1; if (_case == SessionCase::ORIGINATING) { // Add the "orig" parameter. pjsip_param* p = PJ_POOL_ALLOC_T(_req->pool, pjsip_param); pj_strdup(_req->pool, &p->name, &STR_ORIG); p->value.slen = 0; pj_list_insert_after(&route_uri->other_param, p); } target->paths.push_back((pjsip_uri*)route_uri); add_target(target); // Remove the P-Profile-Key header if present. PJUtils::remove_hdr(_req->msg, &STR_P_PROFILE_KEY); } } return status_code; }
pj_status_t init_registrar(RegStore* registrar_store, RegStore* remote_reg_store, HSSConnection* hss_connection, AnalyticsLogger* analytics_logger, ACRFactory* rfacr_factory, int cfg_max_expires) { pj_status_t status; store = registrar_store; remote_store = remote_reg_store; hss = hss_connection; analytics = analytics_logger; max_expires = cfg_max_expires; acr_factory = rfacr_factory; // Construct a Service-Route header pointing at the S-CSCF ready to be added // to REGISTER 200 OK response. pjsip_sip_uri* service_route_uri = (pjsip_sip_uri*) pjsip_parse_uri(stack_data.pool, stack_data.scscf_uri.ptr, stack_data.scscf_uri.slen, 0); service_route_uri->lr_param = 1; // Add the orig parameter. The UE must provide this back on future messages // to ensure we perform originating processing. pjsip_param *orig_param = PJ_POOL_ALLOC_T(stack_data.pool, pjsip_param); pj_strdup(stack_data.pool, &orig_param->name, &STR_ORIG); pj_strdup2(stack_data.pool, &orig_param->value, ""); pj_list_insert_after(&service_route_uri->other_param, orig_param); service_route = pjsip_route_hdr_create(stack_data.pool); service_route->name = STR_SERVICE_ROUTE; service_route->sname = pj_str(""); service_route->name_addr.uri = (pjsip_uri*)service_route_uri; status = pjsip_endpt_register_module(stack_data.endpt, &mod_registrar); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); return status; }
int pjsip_p_c_f_a_hdr_print_on(void *h, char* buf, pj_size_t len) { const pjsip_parser_const_t *pc = pjsip_parser_const(); pjsip_p_c_f_a_hdr* hdr = (pjsip_p_c_f_a_hdr*)h; char* p = buf; // Check that at least the header name will fit. int needed = 0; needed += hdr->name.slen; // Header name needed += 2; // : and space if (needed > (pj_ssize_t)len) { return -1; } // Now write the header name out. pj_memcpy(p, hdr->name.ptr, hdr->name.slen); p += hdr->name.slen; *p++ = ':'; *p++ = ' '; // Now try to write out the three parameter lists. Annoyingly, // pjsip_param_print_on() will always print the separator before each // parameter, including the first parameter in this case. // // The P-Charging-Function-Addresses header has no body (technically // invalid SIP) and thus we need to print the first parameter without the // separator. Since this first parameter could be in any of the parameter // lists, we have to track (with the found_first_param flag) when we've // handled it. bool found_first_param = false; int printed; pjsip_param* param_list = NULL; for (int i = 0; i < 3; i++) { switch (i) { case 0: param_list = &hdr->ccf; break; case 1: param_list = &hdr->ecf; break; case 2: param_list = &hdr->other_param; break; } if (pj_list_empty(param_list)) { continue; // LCOV_EXCL_LINE } if (found_first_param) { // Simply write out the parameters printed = pjsip_param_print_on(param_list, p, buf+len-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) { return -1; } p += printed; } else { // We print the first parameter manually then print the rest. pjsip_param* first_param = param_list->next; pj_list_erase(first_param); // Check we have space for the first param before printing it out. needed = pj_strlen(&first_param->name); if (first_param->value.slen) { needed += 1 + pj_strlen(&first_param->value); } if (needed > buf+len-p) { pj_list_insert_after(param_list, first_param); return -1; } pj_memcpy(p, first_param->name.ptr, first_param->name.slen); p += first_param->name.slen; if (first_param->value.slen) { *p++ = '='; pj_memcpy(p, first_param->value.ptr, first_param->value.slen); p += first_param->value.slen; } // Now print the rest of this parameter list (may be empty). printed = pjsip_param_print_on(param_list, p, buf+len-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) { pj_list_insert_after(param_list, first_param); return -1; } p += printed; // Finally, restore the first param to the head of the parameter list. pj_list_insert_after(param_list, first_param); // We've found the first parameter, everything else is simple. found_first_param = true; } } *p = '\0'; return p - buf; }
static pj_status_t create_request(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); /* Create the request. */ status = pjsip_endpt_create_request_from_hdr( regc->endpt, pjsip_get_register_method(), regc->srv_url, regc->from_hdr, regc->to_hdr, NULL, regc->cid_hdr, regc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ pjsip_auth_clt_init_req( ®c->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ if (!pj_list_empty(®c->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; route_pos = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (!route_pos) route_pos = &tdata->msg->hdr; route = regc->route_set.next; while (route != ®c->route_set) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; route = route->next; } } /* Add additional request headers */ if (!pj_list_empty(®c->hdr_list)) { const pjsip_hdr *hdr; hdr = regc->hdr_list.next; while (hdr != ®c->hdr_list) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, new_hdr); hdr = hdr->next; } } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; }
int list_test() { list_node nodes[4]; // must be even number of nodes list_node list; list_node list2; list_node *p; int i; // don't change to unsigned! // // Test insert_before(). // list.value = (unsigned)-1; pj_list_init(&list); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { nodes[i].value = i; pj_list_insert_before(&list, &nodes[i]); } // check. for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) { pj_assert(p->value == i); if (p->value != i) { return -1; } } // // Test insert_after() // pj_list_init(&list); for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) { pj_list_insert_after(&list, &nodes[i]); } // check. for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) { pj_assert(p->value == i); if (p->value != i) { return -1; } } // // Test merge_last() // // Init lists pj_list_init(&list); pj_list_init(&list2); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) { pj_list_insert_before(&list, &nodes[i]); } for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { pj_list_insert_before(&list2, &nodes[i]); } // merge pj_list_merge_last(&list, &list2); // check. for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) { pj_assert(p->value == i); if (p->value != i) { return -1; } } // check list is empty pj_assert( pj_list_empty(&list2) ); if (!pj_list_empty(&list2)) { return -1; } // // Check merge_first() // pj_list_init(&list); pj_list_init(&list2); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) { pj_list_insert_before(&list, &nodes[i]); } for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { pj_list_insert_before(&list2, &nodes[i]); } // merge pj_list_merge_first(&list2, &list); // check (list2). for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) { pj_assert(p->value == i); if (p->value != i) { return -1; } } // check list is empty pj_assert( pj_list_empty(&list) ); if (!pj_list_empty(&list)) { return -1; } // // Test insert_nodes_before() // // init list pj_list_init(&list); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) { pj_list_insert_before(&list, &nodes[i]); } // chain remaining nodes pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]); for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]); } // insert nodes pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]); // check for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) { pj_assert(p->value == i); if (p->value != i) { return -1; } } // erase test. pj_list_init(&list); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { nodes[i].value = i; pj_list_insert_before(&list, &nodes[i]); } for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) { int j; pj_list_erase(&nodes[i]); for (j=0, p=list.next; j<i; ++j, p=p->next) { pj_assert(p->value == j); if (p->value != j) { return -1; } } } // find and search pj_list_init(&list); for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { nodes[i].value = i; pj_list_insert_before(&list, &nodes[i]); } for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) { p = (list_node*) pj_list_find_node(&list, &nodes[i]); pj_assert( p == &nodes[i] ); if (p != &nodes[i]) { return -1; } p = (list_node*) pj_list_search(&list, (void*)(pj_ssize_t)i, &compare_node); pj_assert( p == &nodes[i] ); if (p != &nodes[i]) { return -1; } } return 0; }
static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) { pj_caching_pool *cp = (pj_caching_pool*)pf; unsigned pool_capacity; unsigned i; PJ_CHECK_STACK(); PJ_ASSERT_ON_FAIL(pf && pool, return); pj_lock_acquire(cp->lock); #if PJ_SAFE_POOL /* Make sure pool is still in our used list */ if (pj_list_find_node(&cp->used_list, pool) != pool) { pj_assert(!"Attempt to destroy pool that has been destroyed before"); return; } #endif /* Erase from the used list. */ pj_list_erase(pool); /* Decrement used count. */ --cp->used_count; pool_capacity = pj_pool_get_capacity(pool); /* Destroy the pool if the size is greater than our size or if the total * capacity in our recycle list (plus the size of the pool) exceeds * maximum capacity. . */ if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || cp->capacity + pool_capacity > cp->max_capacity) { pj_pool_destroy_int(pool); pj_lock_release(cp->lock); return; } /* Reset pool. */ PJ_LOG(6, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", pool_capacity, pj_pool_get_used_size(pool), pj_pool_get_used_size(pool)*100/pool_capacity)); pj_pool_reset(pool); pool_capacity = pj_pool_get_capacity(pool); /* * Otherwise put the pool in our recycle list. */ i = (unsigned) (unsigned long) pool->factory_data; pj_assert(i<PJ_CACHING_POOL_ARRAY_SIZE); if (i >= PJ_CACHING_POOL_ARRAY_SIZE ) { /* Something has gone wrong with the pool. */ pj_pool_destroy_int(pool); pj_lock_release(cp->lock); return; } pj_list_insert_after(&cp->free_list[i], pool); cp->capacity += pool_capacity; pj_lock_release(cp->lock); }
// // Insert a node in the back. // void push_back(List_Node *node) { pj_list_insert_after(root_.prev, node); }
// // Insert node. // void insert_after(iterator &pos, List_Node *node) { pj_list_insert_after(*pos, node); }
void ICSCFSproutletTsx::on_rx_response(pjsip_msg* rsp, int fork_id) { if (_acr != NULL) { // Pass the received response to the ACR. // @TODO - timestamp from response??? _acr->rx_response(rsp); } // Check if this response is one that we are allowed to retry the HSS lookup // for. See TS 24.229 - section 5.3.2.2. // // Note we support service restoration, so integrity-protected settings in // Authorization header are immaterial. // // Note also that we can never retry once we've routed to the BGCF. pjsip_status_code rsp_status = (pjsip_status_code)rsp->line.status.code; const ForkState& fork_status = fork_state(fork_id); TRC_DEBUG("Check retry conditions for non-REGISTER, S-CSCF %sresponsive", (fork_status.error_state != NONE) ? "not " : ""); if ((!_routed_to_bgcf) && (fork_status.error_state != NONE)) { // Indeed it it, first log to SAS. TRC_DEBUG("Attempt retry to alternate S-CSCF for non-REGISTER request"); std::string st_code = std::to_string(rsp_status); SAS::Event event(trail(), SASEvent::SCSCF_RETRY, 0); std::string method = "non-REGISTER"; event.add_var_param(method); event.add_var_param(st_code); SAS::report_event(event); // Now we can simply reuse the UA router we made on the initial request. pjsip_sip_uri* scscf_sip_uri = NULL; pjsip_msg* req = original_request(); pj_pool_t* pool = get_pool(req); // TS 32.260 Table 5.2.1.1 says an EVENT ACR should be generated on the // completion of a Cx query issued in response to a SIP INVITE. It's // ambiguous on whether this should be sent on each Cx query completion // so we err on the side of over-sending events. bool do_billing = (rsp->line.req.method.id == PJSIP_INVITE_METHOD); std::string wildcard; int status_code = _router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for non-REGISTER"); if (_originating) { // Add the `orig` parameter. pjsip_param* orig_param = PJ_POOL_ALLOC_T(pool, pjsip_param); pj_strdup(pool, &orig_param->name, &STR_ORIG); orig_param->value.slen = 0; pj_list_insert_after(&scscf_sip_uri->other_param, orig_param); } // Add the P-Profile-Key header here if we've got a wildcard if (wildcard != "") { add_p_profile_header(wildcard, req); } PJUtils::add_route_header(req, scscf_sip_uri, pool); send_request(req); // We're not forwarding this response upstream. free_msg(rsp); } else { free_msg(req); send_response(rsp); } } else { // Provisional, successful or non-retryable response, simply forward on // upstream. send_response(rsp); } }
void ICSCFSproutletTsx::on_rx_initial_request(pjsip_msg* req) { pj_pool_t* pool = get_pool(req); pjsip_route_hdr* hroute = (pjsip_route_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_ROUTE, NULL); // TS 24.229 says I-CSCF processing shouldn't be done if a message has more than one Route header. // We've stripped one off in Sproutlet processing, so check for a second and just forward the // message if it's there. if (hroute != NULL) { send_request(req); return; } pjsip_uri* next_hop = PJUtils::next_hop(req); URIClass next_hop_class = URIClassifier::classify_uri(next_hop); if (req->line.req.method.id == PJSIP_ACK_METHOD && next_hop == req->line.req.uri && ((next_hop_class == NODE_LOCAL_SIP_URI) || (next_hop_class == HOME_DOMAIN_SIP_URI))) { // Ignore ACK messages with no Route headers and a local Request-URI, as: // - the I-CSCF should not be handling these // - we've seen ACKs matching this descrption being generated at overload and looping repeatedly // // This is a fairly targeted fix for https://github.com/Metaswitch/sprout/issues/1091. // TODO: remove this code when #1091 is fixed by other means. free_msg(req); return; } // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for non-REGISTER request"); // Before we clone the request for retries, remove the P-Profile-Key header // if present. PJUtils::remove_hdr(req, &STR_P_PROFILE_KEY); // Determine orig/term and the served user's name. const pjsip_route_hdr* route = route_hdr(); std::string impu; if ((route != NULL) && (pjsip_param_find(&((pjsip_sip_uri*)route->name_addr.uri)->other_param, &STR_ORIG) != NULL)) { // Originating request. TRC_DEBUG("Originating request"); _originating = true; impu = PJUtils::public_id_from_uri(PJUtils::orig_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_ORIG_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } else { // Terminating request. TRC_DEBUG("Terminating request"); _originating = false; pjsip_uri* uri = PJUtils::term_served_user(req); // If the Req URI is a SIP URI with the user=phone parameter set, is not a // GRUU and the user part starts with '+' (i.e. is a global phone number), // we should replace it with a tel URI, as per TS24.229 5.3.2.1. if (PJSIP_URI_SCHEME_IS_SIP(uri)) { URIClass uri_class = URIClassifier::classify_uri(uri); pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; if (uri_class == GLOBAL_PHONE_NUMBER) { TRC_DEBUG("Change request URI from SIP URI to tel URI"); req->line.req.uri = PJUtils::translate_sip_uri_to_tel_uri(sip_uri, pool); } } impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_TERM_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } // Create an LIR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFLIRouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impu, _originating); pjsip_sip_uri* scscf_sip_uri = NULL; // Use the router we just created to query the HSS for an S-CSCF to use. // TS 32.260 Table 5.2.1.1 says an EVENT ACR should be generated on the // completion of a Cx query issued in response to a SIP INVITE bool do_billing = (req->line.req.method.id == PJSIP_INVITE_METHOD); std::string wildcard; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); if ((!_originating) && (scscf_not_found(status_code))) { TRC_DEBUG("Couldn't find an S-CSCF, attempt to translate the URI"); pjsip_uri* uri = PJUtils::term_served_user(req); URIClass uri_class = URIClassifier::classify_uri(uri, false); // For terminating processing, if the HSS indicates that the user does not // exist, and if the request URI is a tel URI, try an ENUM translation. If // this succeeds, go back to the HSS. See TS24.229, 5.3.2.1. // // Before doing that we should check whether the enforce_user_phone flag is // set. If it isn't, and we have a numeric SIP URI, it is possible that // this should have been a tel URI, so translate it and do the HSS lookup // again. Once again, only do this for global numbers. if (PJSIP_URI_SCHEME_IS_SIP(uri) && (uri_class == GLOBAL_PHONE_NUMBER)) { TRC_DEBUG("enforce_user_phone set to false, try using a tel URI"); uri = PJUtils::translate_sip_uri_to_tel_uri((pjsip_sip_uri*)uri, pool); req->line.req.uri = uri; // We need to change the IMPU stored on our LIR router so that when // we do a new LIR we look up the new IMPU. impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); ((ICSCFLIRouter *)_router)->change_impu(impu); status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } if (_icscf->_enum_service) { // If we still haven't found an S-CSCF, we can now try an ENUM lookup. // We put this processing in a loop because in theory we may go round // several times before finding an S-CSCF. In reality this is unlikely // so we set MAX_ENUM_LOOKUPS to 2. for (int ii = 0; (ii < MAX_ENUM_LOOKUPS) && (scscf_not_found(status_code)); ++ii) { if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // Do an ENUM lookup and see if we should translate the TEL URI pjsip_uri* original_req_uri = req->line.req.uri; _icscf->translate_request_uri(req, get_pool(req), trail()); uri = req->line.req.uri; URIClass uri_class = URIClassifier::classify_uri(uri, false, true); std::string rn; if ((uri_class == NP_DATA) || (uri_class == FINAL_NP_DATA)) { // We got number portability information from ENUM - drop out and route to the BGCF. route_to_bgcf(req); return; } else if (pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, original_req_uri, req->line.req.uri) != PJ_SUCCESS) { // The URI has changed, so make sure we do a LIR lookup on it. impu = PJUtils::public_id_from_uri(req->line.req.uri); ((ICSCFLIRouter *)_router)->change_impu(impu); } // If we successfully translate the req URI and end up with either another TEL URI or a // local SIP URI, we should look for an S-CSCF again. if ((uri_class == LOCAL_PHONE_NUMBER) || (uri_class == GLOBAL_PHONE_NUMBER) || (uri_class == HOME_DOMAIN_SIP_URI)) { // TEL or local SIP URI. Look up the S-CSCF again. status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } else { // Number translated to off-switch. Drop out of the loop. ii = MAX_ENUM_LOOKUPS; } } else { // Can't translate the number, skip to the end of the loop. ii = MAX_ENUM_LOOKUPS; } } } else { // The user is not in the HSS and ENUM is not configured. TS 24.229 // says that, as an alternative to ENUM, we can "forward the request to // the transit functionality for subsequent routeing". Let's do that // (currently, we assume the BGCF is the transit functionality, but that // may be made configurable in future). TRC_DEBUG("No ENUM service available - outing request directly to transit function (BGCF)"); route_to_bgcf(req); return; } } URIClass uri_class = URIClassifier::classify_uri(req->line.req.uri); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for non-REGISTER"); if (_originating) { // Add the `orig` parameter. pjsip_param* orig_param = PJ_POOL_ALLOC_T(get_pool(req), pjsip_param); pj_strdup(get_pool(req), &orig_param->name, &STR_ORIG); orig_param->value.slen = 0; pj_list_insert_after(&scscf_sip_uri->other_param, orig_param); } // Add the P-Profile-Key header here if we've got a wildcard if (wildcard != "") { add_p_profile_header(wildcard, req); } PJUtils::add_route_header(req, scscf_sip_uri, get_pool(req)); send_request(req); } else if ((uri_class == OFFNET_SIP_URI) || (uri_class == GLOBAL_PHONE_NUMBER)) { // Target is a TEL URI or not in our home domain. Pass to the BGCF. route_to_bgcf(req); } else { // Target is in our home domain, but we failed to find an S-CSCF. This is the final response. pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }
static pj_status_t create_request(pjsip_publishc *pubc, pjsip_tx_data **p_tdata) { const pj_str_t STR_EVENT = { "Event", 5 }; pj_status_t status; pjsip_generic_string_hdr *hdr; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); /* Create the request. */ status = pjsip_endpt_create_request_from_hdr( pubc->endpt, &pjsip_publish_method, pubc->target_uri, pubc->from_hdr, pubc->to_hdr, NULL, pubc->cid_hdr, pubc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ pjsip_auth_clt_init_req( &pubc->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ if (!pj_list_empty(&pubc->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; route_pos = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (!route_pos) route_pos = &tdata->msg->hdr; route = pubc->route_set.next; while (route != &pubc->route_set) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; route = route->next; } } /* Add Event header */ hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT, &pubc->event); if (hdr) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); /* Add SIP-If-Match if we have etag */ if (pubc->etag.slen) { const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &pubc->etag); if (hdr) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; }
static pj_status_t lb_put_frame( pjmedia_port *this_port, const pjmedia_frame *frame) { pj_status_t status; struct leaky_bucket_port *lb = (struct leaky_bucket_port*)this_port; struct leaky_bucket_item *item = PJ_POOL_ZALLOC_T(lb->pool, struct leaky_bucket_item); PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); pj_memcpy(&item->frame, frame, sizeof(pjmedia_frame)); PJ_LOG(6, (THIS_FILE, "packet: sz=%d ts=%llu", frame->size/sizeof(pj_uint16_t), frame->timestamp.u64)); /* if frame is not empty, push all previous frames into downstream port */ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO){ status = lb_push_frames_till(lb, frame->timestamp); if (status != PJ_SUCCESS) return status; } /* update bucket and received packet states */ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO ) { if (lb->bucket_size > lb->items){ void *buf; unsigned sent_delay; if (lb->sent_delay) { /* sent delay or pps is set */ sent_delay = lb->sent_delay; } else { /* bps is set, compute sent delay on the fly */ int hdr_sz = 20 + 8 + 12; sent_delay = 8.0 * (hdr_sz + frame->size) * \ lb->base.info.clock_rate / lb->bits_per_second; PJ_LOG(6, (THIS_FILE, "Sent delay: %u. Pack sz: %u. Samples: %u", sent_delay, frame->size, lb->base.info.samples_per_frame)); } /* update timestamps */ if (lb->frames == 0){ lb->last_ts = item->frame.timestamp; } else if ( lb->last_ts.u64 + sent_delay < item->frame.timestamp.u64 ) { lb->last_ts = item->frame.timestamp; } else { lb->last_ts.u64 += sent_delay; item->frame.timestamp = lb->last_ts; } buf = pj_pool_zalloc(lb->pool, frame->size); pj_memcpy(buf, frame->buf, frame->size); item->frame.buf = buf; PJ_LOG(6, (THIS_FILE, "packet in buf: sz=%d ts=%llu", item->frame.size/sizeof(pj_uint16_t), item->frame.timestamp.u64)); lb->items++; } else { pj_bzero(item, sizeof(*item)); item->frame.type = PJMEDIA_FRAME_TYPE_NONE; PJ_LOG(6, (THIS_FILE, "bucket size %u exhausted, packet dropped", lb->bucket_size)); } } else { PJ_LOG(6, (THIS_FILE, "received empty frame")); } /* put frame at the end of list or init an empty one */ if (lb->last_item == NULL){ pj_list_init(item); lb->first_item = lb->last_item = item; } else { pj_list_insert_after(lb->last_item, item); lb->last_item = item; } lb->frames++; return PJ_SUCCESS; }
/** * @isFirstPacket first packet in a frame * @isMarketPacket last packet in a frame */ PJ_DEF(int) jitter_buffer_insert_packet(Jitter_Buffer *jitter_buffer, pj_uint16_t seq, pj_uint32_t ts, pjmedia_frame_type frame_type, char *payload, int size, pj_bool_t isFirstPacket, pj_bool_t isMarketPacket) { int ret = 0; if(pj_mutex_lock(jitter_buffer->jb_mutex) != PJ_SUCCESS) { return -1; } pj_ssize_t current; JTPacket *packet = NULL; Frame_Buffer *frame; // is running if(!jitter_buffer->running) { ret = -2; goto ON_RET; } // sanity check if(ts == 0 || (frame_type != PJMEDIA_FRAME_TYPE_EMPTY && size == 0)) { ret = -3; goto ON_RET; } if(!jitter_buffer->first_packet) { isFirstPacket = PJ_TRUE; jitter_buffer->first_packet = PJ_TRUE; } //clean old frames //CleanOldFrames(jitter_buffer); //update jitter current = GetCurrentTimeMs(); if(frame_type != PJMEDIA_FRAME_TYPE_EMPTY && frame_type != PJMEDIA_FRAME_TYPE_NONE) { if(jitter_buffer->waiting_for_completed_frame.timestamp == ts) { jitter_buffer->waiting_for_completed_frame.frame_size += size; jitter_buffer->waiting_for_completed_frame.latest_packet_timestamp = current; } else if(jitter_buffer->waiting_for_completed_frame.latest_packet_timestamp > 0 && current - jitter_buffer->waiting_for_completed_frame.latest_packet_timestamp > 2000) { //too old UpdateJitterEstimatorForWaitingFrame(jitter_buffer, &jitter_buffer->waiting_for_completed_frame); jitter_buffer->waiting_for_completed_frame.frame_size = 0; jitter_buffer->waiting_for_completed_frame.latest_packet_timestamp = -1; jitter_buffer->waiting_for_completed_frame.timestamp = 0; } } //create packet packet = NULL; if(jt_packet_create(&packet, &jitter_buffer->packet_alloc, seq, ts, frame_type, isFirstPacket, isMarketPacket, payload, size) != 0) { if(packet) list_alloc_insert(packet, &jitter_buffer->packet_alloc); ret = -1; goto ON_RET; } //GetCurrentTimeInLocal(packet->time_in_jb, 60); if(!isFirstPacket) { //is first packet in frame isFirstPacket = first_packet_in_frame(jitter_buffer, packet); packet->isFirst = isFirstPacket; } if(isMarketPacket) { //check if next packet is first packet Frame_Buffer *next_frame = findFrame(&jitter_buffer->frameList, packet->ts, '>'); if(next_frame != NULL) { JTPacket * first_packet = next_frame->session_info.packetList.next; if(packet != &next_frame->session_info.packetList) { if(InSequence(packet->seq, first_packet->seq)) { first_packet->isFirst = PJ_TRUE; } } } } //clean old frames CleanOldFrames(jitter_buffer); // is old packet if(decode_state_isOldPacket(packet, &jitter_buffer->decode_state)) { decode_state_updateOldPacket(packet, &jitter_buffer->decode_state); list_alloc_insert(packet, &jitter_buffer->packet_alloc); ret = -1; goto ON_RET; } // find or alloc a frame frame = findFrame(&jitter_buffer->frameList, packet->ts, '='); if(frame == NULL) { //alloc one //if(jitter_buffer->number_of_frames > jitter_buffer->max_number_of_frames) { if(jitter_buffer->number_of_frames > jitter_buffer->max_number_of_frames) { //clean old frames at least one Frame_Buffer *oldestFrame = jitter_buffer->frameList.next; if(oldestFrame != &jitter_buffer->frameList) RealseFrame(jitter_buffer, oldestFrame); } list_alloc_alloc(Frame_Buffer, &frame, &jitter_buffer->frame_alloc); //init frame_buffer_init(frame, &jitter_buffer->packet_alloc); } //insert packet into the frame ret = frame_buffer_insert_packet(frame, packet); if(ret > 0) { list_alloc_insert(packet, &jitter_buffer->packet_alloc); frame_buffer_reset(frame); list_alloc_insert(frame, &jitter_buffer->frame_alloc); ret = -1; goto ON_RET; } else if (ret < 0) { frame_buffer_reset(frame); list_alloc_insert(frame, &jitter_buffer->frame_alloc); ret = -1; goto ON_RET; } else { event_set(jitter_buffer->packet_event); if(packet->isRetrans) frame_buffer_IncrementNackCount(frame); } //insert frame to frame list if(findFrame(&jitter_buffer->frameList, frame->ts, '=') == NULL) { Frame_Buffer *prev_frame = findFrame(&jitter_buffer->frameList, frame->ts, '<'); prev_frame = (prev_frame == NULL ? &jitter_buffer->frameList : prev_frame); pj_list_insert_after(prev_frame, frame); event_set(jitter_buffer->frame_event); jitter_buffer->number_of_frames++; } ON_RET: pj_mutex_unlock(jitter_buffer->jb_mutex); return ret; }