/* Helper function to add pending notification callback. */ static pj_status_t add_notification( struct loop_transport *loop, pjsip_tx_data *tdata, pj_ssize_t sent, void *token, void (*callback)(pjsip_transport*, void*, pj_ssize_t)) { struct send_list *sent_status; pjsip_tx_data_add_ref(tdata); pj_lock_acquire(tdata->lock); sent_status = pj_pool_alloc(tdata->pool, sizeof(struct send_list)); pj_lock_release(tdata->lock); sent_status->sent = sent; sent_status->tdata = tdata; sent_status->token = token; sent_status->callback = callback; pj_gettimeofday(&sent_status->sent_time); sent_status->sent_time.msec += loop->send_delay; pj_time_val_normalize(&sent_status->sent_time); pj_lock_acquire(loop->base.lock); pj_list_push_back(&loop->send_list, sent_status); pj_lock_release(loop->base.lock); return PJ_SUCCESS; }
/// Callback used for PJUtils::send_request_stateless static void stateless_send_cb(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont) { *cont = PJ_FALSE; bool retrying = false; StatelessSendState* sss = (StatelessSendState*)st->token; if ((sent <= 0) && (!sss->servers.empty())) { // Request to a resolved server failed. When sending statelessly // this means we couldn't get a transport, so couldn't connect to the // selected target, so we always blacklist. PJUtils::blacklist_server(sss->servers[sss->current_server]); // Can we do a retry? pj_status_t status = PJ_ENOTFOUND; ++sss->current_server; if (sss->current_server < (int)sss->servers.size()) { pjsip_tx_data* tdata = st->tdata; // According to RFC3263 we should generate a new branch identifier for // the message so there is no possibility of it being confused with // previous attempts. Not clear this is really necessary in this case, // but just in case ... PJUtils::generate_new_branch_id(tdata); // Add a reference to the tdata to stop PJSIP releasing it when we // return the callback. pjsip_tx_data_add_ref(tdata); // Set up destination info for the new server and resend the request. PJUtils::set_dest_info(tdata, sss->servers[sss->current_server]); status = pjsip_endpt_send_request_stateless(stack_data.endpt, tdata, (void*)sss, &stateless_send_cb); if (status == PJ_SUCCESS) { retrying = true; } else { pjsip_tx_data_dec_ref(tdata); } } } if ((sent > 0) || (!retrying)) { // Either the request was sent successfully, or we couldn't retry. delete sss; } }
pjsip_tx_data* PJUtils::clone_tdata(pjsip_tx_data* tdata) { pjsip_tx_data* cloned_tdata; pj_status_t status; status = pjsip_endpt_create_tdata(stack_data.endpt, &cloned_tdata); if (status != PJ_SUCCESS) { return NULL; } // Always increment ref counter to 1. pjsip_tx_data_add_ref(cloned_tdata); // Clone the message from the supplied tdata. cloned_tdata->msg = pjsip_msg_clone(cloned_tdata->pool, tdata->msg); if (cloned_tdata->msg == NULL) { pjsip_tx_data_dec_ref(cloned_tdata); cloned_tdata = NULL; } // Copy the trail identifier to the cloned message. set_trail(cloned_tdata, get_trail(tdata)); if (tdata->msg->type == PJSIP_REQUEST_MSG) { // Substitute the branch value in the top Via header with a unique // branch identifier. generate_new_branch_id(cloned_tdata); } // If the original message already had a specified transport set this // on the clone. (Must use pjsip_tx_data_set_transport to ensure // reference counts get updated.) if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { pjsip_tx_data_set_transport(cloned_tdata, &tdata->tp_sel); } // If the message has any addr in dest_info, copy that if (tdata->dest_info.addr.count != 0) { pj_memcpy(&cloned_tdata->dest_info, &tdata->dest_info, sizeof(cloned_tdata->dest_info)); } return cloned_tdata; }
pjsip_tx_data* PJUtils::clone_tdata(pjsip_tx_data* tdata) { pjsip_tx_data* cloned_tdata; pj_status_t status; status = pjsip_endpt_create_tdata(stack_data.endpt, &cloned_tdata); if (status != PJ_SUCCESS) { return NULL; } // Always increment ref counter to 1. pjsip_tx_data_add_ref(cloned_tdata); // Clone the message from the supplied tdata. cloned_tdata->msg = pjsip_msg_clone(cloned_tdata->pool, tdata->msg); if (cloned_tdata->msg == NULL) { pjsip_tx_data_dec_ref(cloned_tdata); cloned_tdata = NULL; } // Copy the trail identifier to the cloned message. set_trail(cloned_tdata, get_trail(tdata)); if (tdata->msg->type == PJSIP_REQUEST_MSG) { // Substitute the branch value in the top Via header with a unique // branch identifier. pjsip_via_hdr* via = (pjsip_via_hdr*) pjsip_msg_find_hdr(cloned_tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.ptr = (char*) pj_pool_alloc(cloned_tdata->pool, PJSIP_MAX_BRANCH_LEN); via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN; pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); pj_str_t tmp; tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; // I have absolutely no idea what the following two lines do, but it // doesn't seem to work without them! *(tmp.ptr-2) = (pj_int8_t)(via->branch_param.slen+73); *(tmp.ptr-1) = (pj_int8_t)(via->branch_param.slen+99); pj_generate_unique_string( &tmp ); via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; } // If the original message already had a specified transport set this // on the clone. (Must use pjsip_tx_data_set_transport to ensure // reference counts get updated.) if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { pjsip_tx_data_set_transport(cloned_tdata, &tdata->tp_sel); } // If the message has any addr in dest_info, copy that if (tdata->dest_info.addr.count != 0) { pj_memcpy(&cloned_tdata->dest_info, &tdata->dest_info, sizeof(cloned_tdata->dest_info)); } return cloned_tdata; }
/* Callback to be called to handle transaction state changed. */ static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { struct uac_data *uac_data; pj_status_t status; if (tsx->role == PJSIP_ROLE_UAS) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { struct uas_data *uas_data; uas_data = (struct uas_data*) tsx->mod_data[mod_tu.id]; if (uas_data->uac_tsx) { uac_data = (struct uac_data*) uas_data->uac_tsx->mod_data[mod_tu.id]; uac_data->uas_tsx = NULL; } } return; } /* Get the data that we attached to the UAC transaction previously */ uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id]; /* Handle incoming response */ if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_rx_data *rdata; pjsip_response_addr res_addr; pjsip_via_hdr *hvia; pjsip_tx_data *tdata; rdata = event->body.tsx_state.src.rdata; /* Do not forward 100 response for INVITE (we already responded * INVITE with 100) */ if (tsx->method.id == PJSIP_INVITE_METHOD && rdata->msg_info.msg->line.status.code == 100) { return; } /* Create response to be forwarded upstream * (Via will be stripped here) */ status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, &tdata); if (status != PJ_SUCCESS) { app_perror("Error creating response", status); return; } /* Get topmost Via header of the new response */ hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (hvia == NULL) { /* Invalid response! Just drop it */ pjsip_tx_data_dec_ref(tdata); return; } /* Calculate the address to forward the response */ pj_bzero(&res_addr, sizeof(res_addr)); res_addr.dst_host.type = PJSIP_TRANSPORT_UDP; res_addr.dst_host.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP); /* Destination address is Via's received param */ res_addr.dst_host.addr.host = hvia->recvd_param; if (res_addr.dst_host.addr.host.slen == 0) { /* Someone has messed up our Via header! */ res_addr.dst_host.addr.host = hvia->sent_by.host; } /* Destination port is the rport */ if (hvia->rport_param != 0 && hvia->rport_param != -1) res_addr.dst_host.addr.port = hvia->rport_param; if (res_addr.dst_host.addr.port == 0) { /* Ugh, original sender didn't put rport! * At best, can only send the response to the port in Via. */ res_addr.dst_host.addr.port = hvia->sent_by.port; } /* Forward response with the UAS transaction */ pjsip_tsx_send_msg(uac_data->uas_tsx, tdata); } /* If UAC transaction is terminated, terminate the UAS as well. * This could happen because of: * - timeout on the UAC side * - receipt of 2xx response to INVITE */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data && uac_data->uas_tsx) { pjsip_transaction *uas_tsx; struct uas_data *uas_data; uas_tsx = uac_data->uas_tsx; uas_data = (struct uas_data*) uas_tsx->mod_data[mod_tu.id]; uas_data->uac_tsx = NULL; if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) { /* Send 408/Timeout if this is an INVITE transaction, since * we must have sent provisional response before. For non * INVITE transaction, just destroy it. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { pjsip_tx_data *tdata = uas_tsx->last_tx; tdata->msg->line.status.code = PJSIP_SC_REQUEST_TIMEOUT; tdata->msg->line.status.reason = pj_str("Request timed out"); tdata->msg->body = NULL; pjsip_tx_data_add_ref(tdata); pjsip_tx_data_invalidate_msg(tdata); pjsip_tsx_send_msg(uas_tsx, tdata); } else { /* For non-INVITE, just destroy the UAS transaction */ pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT); } } else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) { pjsip_msg *msg; int code; msg = event->body.tsx_state.src.rdata->msg_info.msg; code = msg->line.status.code; uac_data->uas_tsx = NULL; pjsip_tsx_terminate(uas_tsx, code); } } } }
/* Double terminate test. */ static int double_terminate(void) { pj_str_t target, from, tsx_key; pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_status_t status; PJ_LOG(3,(THIS_FILE, " double terminate test")); target = pj_str(TARGET_URI); from = pj_str(FROM_URI); /* Create request. */ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return -10; } /* Create transaction. */ status = pjsip_tsx_create_uac(NULL, tdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); return -20; } /* Save transaction key for later. */ pj_strdup_with_null(tdata->pool, &tsx_key, &tsx->transaction_key); /* Add reference to transmit buffer (tsx_send_msg() will dec txdata). */ pjsip_tx_data_add_ref(tdata); /* Send message to start timeout timer. */ status = pjsip_tsx_send_msg(tsx, NULL); /* Terminate transaction. */ status = pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); if (status != PJ_SUCCESS) { app_perror(" error: unable to terminate transaction", status); return -30; } tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { /* Terminate transaction again. */ pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); if (status != PJ_SUCCESS) { app_perror(" error: unable to terminate transaction", status); return -40; } pj_mutex_unlock(tsx->mutex); } flush_events(500); if (pjsip_tx_data_dec_ref(tdata) != PJSIP_EBUFDESTROYED) { return -50; } return PJ_SUCCESS; }
/* * The generic test framework, used by most of the tests. */ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method) { pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_str_t target, from, tsx_key; pjsip_via_hdr *via; pj_time_val timeout; pj_status_t status; PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ recv_count = 0; test_complete = 0; /* Init headers. */ target = pj_str(target_uri); from = pj_str(from_uri); /* Create request. */ status = pjsip_endpt_create_request( endpt, method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); return -100; } /* Set the branch param for test 1. */ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(branch_param); /* Add additional reference to tdata to prevent transaction from * deleting it. */ pjsip_tx_data_add_ref(tdata); /* Create transaction. */ status = pjsip_tsx_create_uac( &tsx_user, tdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create UAC transaction", status); pjsip_tx_data_dec_ref(tdata); return -110; } /* Get transaction key. */ pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); /* Send the message. */ status = pjsip_tsx_send_msg(tsx, NULL); // Ignore send result. Some tests do deliberately triggers error // when sending message. if (status != PJ_SUCCESS) { // app_perror(" Error: unable to send request", status); pjsip_tx_data_dec_ref(tdata); // return -120; } /* Set test completion time. */ pj_gettimeofday(&timeout); timeout.sec += test_time; /* Wait until test complete. */ while (!test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); pj_gettimeofday(&now); if (now.sec > timeout.sec) { PJ_LOG(3,(THIS_FILE, " Error: test has timed out")); pjsip_tx_data_dec_ref(tdata); return -130; } } if (test_complete < 0) { tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); return test_complete; } else { pj_time_val now; /* Allow transaction to destroy itself */ flush_events(500); /* Wait until test completes */ pj_gettimeofday(&now); if (PJ_TIME_VAL_LT(now, timeout)) { pj_time_val interval; interval = timeout; PJ_TIME_VAL_SUB(interval, now); flush_events(PJ_TIME_VAL_MSEC(interval)); } } /* Make sure transaction has been destroyed. */ if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -140; } /* Check tdata reference counter. */ if (pj_atomic_get(tdata->ref_cnt) != 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d", pj_atomic_get(tdata->ref_cnt))); pjsip_tx_data_dec_ref(tdata); return -150; } /* Destroy txdata */ pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; }
/* Reinitialize outgoing request after 401/407 response is received. * The purpose of this function is: * - to add a Authorization/Proxy-Authorization header. * - to put the newly created Authorization/Proxy-Authorization header * in cached_list. */ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, const pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request ) { pjsip_tx_data *tdata; const pjsip_hdr *hdr; unsigned chal_cnt; pjsip_via_hdr *via; pj_status_t status; PJ_ASSERT_RETURN(sess && rdata && old_request && new_request, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); PJ_ASSERT_RETURN(old_request->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_ASSERT_RETURN(rdata->msg_info.msg->line.status.code == 401 || rdata->msg_info.msg->line.status.code == 407, PJSIP_EINVALIDSTATUS); tdata = old_request; tdata->auth_retry = PJ_FALSE; /* * Respond to each authentication challenge. */ hdr = rdata->msg_info.msg->hdr.next; chal_cnt = 0; while (hdr != &rdata->msg_info.msg->hdr) { pjsip_cached_auth *cached_auth; const pjsip_www_authenticate_hdr *hchal; pjsip_authorization_hdr *hauth; /* Find WWW-Authenticate or Proxy-Authenticate header. */ while (hdr != &rdata->msg_info.msg->hdr && hdr->type != PJSIP_H_WWW_AUTHENTICATE && hdr->type != PJSIP_H_PROXY_AUTHENTICATE) { hdr = hdr->next; } if (hdr == &rdata->msg_info.msg->hdr) break; hchal = (const pjsip_www_authenticate_hdr*) hdr; ++chal_cnt; /* Find authentication session for this realm, create a new one * if not present. */ cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm ); if (!cached_auth) { cached_auth = PJ_POOL_ZALLOC_T( sess->pool, pjsip_cached_auth); pj_strdup( sess->pool, &cached_auth->realm, &hchal->challenge.common.realm); cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); # if (PJSIP_AUTH_HEADER_CACHING) { pj_list_init(&cached_auth->cached_hdr); } # endif pj_list_insert_before( &sess->cached_auth, cached_auth ); } /* Create authorization header for this challenge, and update * authorization session. */ status = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, tdata, sess, cached_auth, &hauth); if (status != PJ_SUCCESS) return status; /* Add to the message. */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); /* Process next header. */ hdr = hdr->next; } /* Check if challenge is present */ if (chal_cnt == 0) return PJSIP_EAUTHNOCHAL; /* Remove branch param in Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.slen = 0; /* Restore strict route set. * See http://trac.pjsip.org/repos/ticket/492 */ pjsip_restore_strict_route_set(tdata); /* Must invalidate the message! */ pjsip_tx_data_invalidate_msg(tdata); /* Retrying.. */ tdata->auth_retry = PJ_TRUE; /* Increment reference counter. */ pjsip_tx_data_add_ref(tdata); /* Done. */ *new_request = tdata; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt, pjsip_rx_data *rdata, unsigned options, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_USE_EXCEPTION; PJ_UNUSED_ARG(options); status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; pjsip_tx_data_add_ref(tdata); 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_RESPONSE_MSG); /* Clone the status line */ dst->line.status.code = src->line.status.code; pj_strdup(tdata->pool, &dst->line.status.reason, &src->line.status.reason); /* Duplicate all headers */ hsrc = src->hdr.next; while (hsrc != &src->hdr) { /* Skip Content-Type and Content-Length as these would be * generated when the the message is printed. */ if (hsrc->type == PJSIP_H_CONTENT_LENGTH || hsrc->type == PJSIP_H_CONTENT_TYPE) { hsrc = hsrc->next; continue; } /* Remove the first Via header */ else if (hsrc == (pjsip_hdr*) rdata->msg_info.via) { hsrc = hsrc->next; continue; } pjsip_msg_add_hdr(dst, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hsrc)); hsrc = hsrc->next; } /* Clone message 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; *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; }
/// This provides function similar to the pjsip_endpt_send_request method /// but includes setting the SAS trail. pj_status_t PJUtils::send_request(pjsip_tx_data* tdata, int retries, void* token, pjsip_endpt_send_callback cb) { pjsip_transaction* tsx; pj_status_t status = PJ_SUCCESS; LOG_DEBUG("Sending standalone request statefully"); // Allocate temporary storage for the request. StatefulSendState* sss = new StatefulSendState; // Store the user supplied callback and token. sss->user_token = token; sss->user_cb = cb; if (tdata->tp_sel.type != PJSIP_TPSELECTOR_TRANSPORT) { // No transport determined, so resolve the next hop for the message. resolve_next_hop(tdata, retries, sss->servers, get_trail(tdata)); if (!sss->servers.empty()) { // Set up the destination information for the first server. sss->current_server = 0; set_dest_info(tdata, sss->servers[sss->current_server]); } else { // No servers found. status = PJ_ENOTFOUND; } } if (status == PJ_SUCCESS) { // We have servers to send the request to, so allocate a transaction. status = pjsip_tsx_create_uac(&mod_sprout_util, tdata, &tsx); if (status == PJ_SUCCESS) { // Set the trail ID in the transaction from the message. set_trail(tsx, get_trail(tdata)); // Set up the module data for the new transaction to reference // the state information. tsx->mod_data[mod_sprout_util.id] = sss; if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { // Transport has already been determined, so copy it across to the // transaction. LOG_DEBUG("Transport already determined"); pjsip_tsx_set_transport(tsx, &tdata->tp_sel); } // Store the message and add a reference to prevent the transaction layer // freeing it. sss->tdata = tdata; pjsip_tx_data_add_ref(tdata); LOG_DEBUG("Sending request"); status = pjsip_tsx_send_msg(tsx, tdata); } } if (status != PJ_SUCCESS) { // The assumption here is that, if pjsip_tsx_send_msg returns an error // the on_tsx_state callback will not get called, so it is safe to free // off the state data and request here. Also, this is an unexpected // error rather than an indication that the destination server is down, // so we don't blacklist. LOG_ERROR("Failed to send request to %s", PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, PJUtils::next_hop(tdata->msg)).c_str()); // Only free the state data if there are no more references to it pj_status_t dec_status = pjsip_tx_data_dec_ref(tdata); if (dec_status == PJSIP_EBUFDESTROYED) { delete sss; } } return status; }
static void on_tsx_state(pjsip_transaction* tsx, pjsip_event* event) { StatefulSendState* sss; bool retrying = false; if ((mod_sprout_util.id < 0) || (event->type != PJSIP_EVENT_TSX_STATE)) { return; } sss = (StatefulSendState*)tsx->mod_data[mod_sprout_util.id]; if (sss == NULL) { return; } if (!sss->servers.empty()) { // The target for the request came from the resolver, so check to see // if the request failed. if ((tsx->state == PJSIP_TSX_STATE_COMPLETED) || (tsx->state == PJSIP_TSX_STATE_TERMINATED)) { // Transaction has completed or terminated. We need to look at both // states as // - timeouts and transport errors cause an immediate transition // to terminated state, bypassing completed state // - a 5xx response causes a transition to completed state, with a // possible delay until the transition to terminated state (5 seconds // for UDP transport), which would needlessly delay any retry. if ((event->body.tsx_state.type == PJSIP_EVENT_TIMER) || (event->body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR) || (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 500))) { // Either transaction failed on a timeout, transport error or received // 5xx error, so blacklist the failed target. LOG_DEBUG("Transaction failed with retriable error"); if ((event->body.tsx_state.type == PJSIP_EVENT_TIMER) || (event->body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR)) { // Either the connection failed, or the server didn't respond within // the timeout, so blacklist it. We don't blacklist servers that // return 5xx errors as this may indicate a transient overload. PJUtils::blacklist_server(sss->servers[sss->current_server]); } // Can we do a retry? ++sss->current_server; if (sss->current_server < (int)sss->servers.size()) { // More servers to try, so allocate a new branch ID and transaction. LOG_DEBUG("Attempt to resend request to next destination server"); pjsip_tx_data* tdata = sss->tdata; pjsip_transaction* retry_tsx; PJUtils::generate_new_branch_id(tdata); pj_status_t status = pjsip_tsx_create_uac(&mod_sprout_util, tdata, &retry_tsx); if (status == PJ_SUCCESS) { // The new transaction has been set up. // Set the trail ID in the transaction from the message. set_trail(retry_tsx, get_trail(tdata)); // Set up the module data for the new transaction to reference // the state information. retry_tsx->mod_data[mod_sprout_util.id] = sss; // Increment the reference count of the request as we are passing // it to a new transaction. pjsip_tx_data_add_ref(tdata); // Copy across the destination information for a retry and try to // resend the request. PJUtils::set_dest_info(tdata, sss->servers[sss->current_server]); status = pjsip_tsx_send_msg(retry_tsx, tdata); if (status == PJ_SUCCESS) { // Successfully sent a retry. Make sure this callback isn't // invoked again for the previous transaction. tsx->mod_data[mod_sprout_util.id] = NULL; retrying = true; } } } } } } if ((!retrying) && (tsx->status_code >= 200)) { // Call the user callback, if any, and prevent the callback to be called again // by clearing the transaction's module_data. LOG_DEBUG("Request transaction completed, status code = %d", tsx->status_code); tsx->mod_data[mod_sprout_util.id] = NULL; if (sss->user_cb != NULL) { (*sss->user_cb)(sss->user_token, event); } // The transaction has completed, so decrement our reference to the tx_data // and free the state data. pjsip_tx_data_dec_ref(sss->tdata); delete sss; } }
void process_register_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). LOG_ERROR("Rejecting register request using invalid URI scheme"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); // Can't log the public ID as the REGISTER has failed too early std::string public_id = "UNKNOWN"; std::string error_msg = "Rejecting register request using invalid URI scheme"; event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } // Allocate an ACR for this transaction and pass the request to it. ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process 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);; pjsip_msg *msg = rdata->msg_info.msg; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; std::string private_id; std::string private_id_for_binding; bool success = get_private_id(rdata, private_id); if (!success) { // There are legitimate cases where we don't have a private ID // here (for example, on a re-registration where Bono has set the // Integrity-Protected header), so this is *not* a failure // condition. // We want the private ID here so that Homestead can use it to // subscribe for updates from the HSS - but on a re-registration, // Homestead should already have subscribed for updates during the // initial registration, so we can just make a request using our // public ID. private_id = ""; // IMS compliant clients will always have the Auth header on all REGISTERs, // including reREGISTERS. Non-IMS clients won't, but their private ID // will always be the public ID with the sip: removed. private_id_for_binding = PJUtils::default_private_id_from_uri(uri); } else { private_id_for_binding = private_id; } SAS::Event event(trail, SASEvent::REGISTER_START, 0); event.add_var_param(public_id); event.add_var_param(private_id); SAS::report_event(event); std::string regstate; std::deque<std::string> ccfs; std::deque<std::string> ecfs; HTTPCode http_code = hss->update_registration_state(public_id, private_id, HSSConnection::REG, regstate, ifc_map, uris, ccfs, ecfs, trail); if ((http_code != HTTP_OK) || (regstate != HSSConnection::STATE_REGISTERED)) { // We failed to register this subscriber at the HSS. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting register request with invalid public/private identity"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid public/private identity"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); int expiry = 0; bool is_initial_registration; // Loop through each contact header. If every registration is an emergency // registration and its expiry is 0 then reject with a 501. // If there are valid registration updates to make then attempt to write to // store, which also stops emergency registrations from being deregistered. bool reject_with_501 = true; bool any_emergency_registrations = false; bool reject_with_400 = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); expiry = (contact_hdr->expires != -1) ? contact_hdr->expires : (expires != NULL) ? expires->ivalue : max_expires; if ((contact_hdr->star) && (expiry != 0)) { // Wildcard contact, which can only be used if the expiry is 0 LOG_ERROR("Attempted to deregister all bindings, but expiry value wasn't 0"); reject_with_400 = true; break; } reject_with_501 = (reject_with_501 && PJUtils::is_emergency_registration(contact_hdr) && (expiry == 0)); any_emergency_registrations = (any_emergency_registrations || PJUtils::is_emergency_registration(contact_hdr)); contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (reject_with_400) { SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid contact header"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_REQUEST, NULL, NULL, NULL); delete acr; return; } if (reject_with_501) { LOG_ERROR("Rejecting register request as attempting to deregister an emergency registration"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting deregister request for emergency registrations"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_IMPLEMENTED, NULL, NULL, NULL); delete acr; return; } // Write to the local store, checking the remote store if there is no entry locally. RegStore::AoR* aor_data = write_to_store(store, aor, rdata, now, expiry, is_initial_registration, NULL, remote_store, true, private_id_for_binding, trail); if (aor_data != NULL) { // Log the bindings. log_bindings(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { int tmp_expiry = 0; bool ignored; RegStore::AoR* remote_aor_data = write_to_store(remote_store, aor, rdata, now, tmp_expiry, ignored, aor_data, NULL, false, private_id_for_binding, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the register with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Unable to access Registration Store"; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response std::string error_msg = "Error building REGISTER " + std::to_string(status) + " response " + PJUtils::pj_status_to_string(status); LOG_ERROR(error_msg.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } 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); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "REGISTER failed with status code: " + std::to_string(st_code); event.add_var_param(error_msg); SAS::report_event(event); delete acr; 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"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Failed to add RFC 5636 headers"; event.add_var_param(error_msg); SAS::report_event(event); tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR; pjsip_tx_data_invalidate_msg(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete acr; 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 (RegStore::AoR::Bindings::const_iterator i = aor_data->bindings().begin(); i != aor_data->bindings().end(); ++i) { RegStore::AoR::Binding* binding = i->second; if (binding->_expires > now) { // The binding hasn't expired. Parse the Contact URI from the store, // making sure it is formatted as a name-address. pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool, PJ_TRUE); 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::map<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); } // The pub-gruu parameter on the Contact header is calculated // from the instance-id, to avoid unnecessary storage in // memcached. std::string gruu = binding->pub_gruu_quoted_string(tdata->pool); if (!gruu.empty()) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, "pub-gruu"); pj_strdup2(tdata->pool, &new_param->value, gruu.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()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Badly formed contact URI - " + binding->_uri; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } } // Deal with path header related fields in the response. pjsip_routing_hdr* path_hdr = (pjsip_routing_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_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } // Add the Service-Route header. It isn't safe to do this with the // pre-built header from the global pool because the chaining data // structures in the header may get overwritten, but it is safe to do a // shallow clone. pjsip_hdr* clone = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, service_route); pjsip_msg_insert_first_hdr(tdata->msg, clone); // Add P-Associated-URI headers for all of the associated URIs. for (std::vector<std::string>::iterator it = uris.begin(); it != uris.end(); it++) { pjsip_routing_hdr* pau = identity_hdr_create(tdata->pool, STR_P_ASSOCIATED_URI); pau->name_addr.uri = PJUtils::uri_from_string(*it, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pau); } // Add a PCFA header. PJUtils::add_pcfa_header(tdata->msg, tdata->pool, ccfs, ecfs, true); // Pass the response to the ACR. acr->tx_response(tdata->msg); // 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); // Send the ACR and delete it. acr->send_message(); delete acr; // TODO in sto397: we should do third-party registration once per // service profile (i.e. once per iFC, using an arbitrary public // ID). hss->get_subscription_data should be enhanced to provide an // appropriate data structure (representing the ServiceProfile // nodes) and we should loop through that. Don't send any register that // contained emergency registrations to the application servers. if (!any_emergency_registrations) { RegistrationUtils::register_with_application_servers(ifc_map[public_id], store, rdata, tdata, expiry, is_initial_registration, public_id, trail); } // 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, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
/* * 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; }
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pjsip_expires_hdr *expires_hdr; pj_uint32_t cseq; pj_atomic_inc(regc->busy_ctr); pj_lock_acquire(regc->lock); /* Make sure we don't have pending transaction. */ if (regc->has_tsx) { PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another " "transaction pending")); pjsip_tx_data_dec_ref( tdata ); pj_lock_release(regc->lock); pj_atomic_dec(regc->busy_ctr); return PJSIP_EBUSY; } pj_assert(regc->current_op == REGC_IDLE); /* Invalidate message buffer. */ pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ cseq = ++regc->cseq_hdr->cseq; cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Find Expires header */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL); /* Bind to transport selector */ pjsip_tx_data_set_transport(tdata, ®c->tp_sel); regc->has_tsx = PJ_TRUE; /* Set current operation based on the value of Expires header */ if (expires_hdr && expires_hdr->ivalue==0) regc->current_op = REGC_UNREGISTERING; else regc->current_op = REGC_REGISTERING; /* Prevent deletion of tdata, e.g: when something wrong in sending, * we need tdata to retrieve the transport. */ pjsip_tx_data_add_ref(tdata); /* If via_addr is set, use this address for the Via header. */ if (regc->via_addr.host.slen > 0) { tdata->via_addr = regc->via_addr; tdata->via_tp = regc->via_tp; } /* Need to unlock the regc temporarily while sending the message to * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247). * It should be safe to do this since the regc's refcount has been * incremented. */ pj_lock_release(regc->lock); /* Now send the message */ status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT, regc, ®c_tsx_callback); if (status!=PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } /* Reacquire the lock */ pj_lock_acquire(regc->lock); /* Get last transport used and add reference to it */ if (tdata->tp_info.transport != regc->last_transport && status==PJ_SUCCESS) { if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } if (tdata->tp_info.transport) { regc->last_transport = tdata->tp_info.transport; pjsip_transport_add_ref(regc->last_transport); } } /* Release tdata */ pjsip_tx_data_dec_ref(tdata); 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); } return status; }
/* * The generic test framework, used by most of the tests. */ static int perform_test( char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method, int request_cnt, int request_interval_msec, int expecting_timeout) { pjsip_tx_data *tdata; pj_str_t target, from; pjsip_via_hdr *via; pj_time_val timeout, next_send; int sent_cnt; pj_status_t status; PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ recv_count = 0; test_complete = 0; tsx_key.slen = 0; /* Init headers. */ target = pj_str(target_uri); from = pj_str(from_uri); /* Create request. */ status = pjsip_endpt_create_request( endpt, method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); return -10; } /* Set the branch param for test 1. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(branch_param); /* Schedule first send. */ sent_cnt = 0; pj_gettimeofday(&next_send); pj_time_val_normalize(&next_send); /* Set test completion time. */ pj_gettimeofday(&timeout); timeout.sec += test_time; /* Wait until test complete. */ while (!test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); pj_gettimeofday(&now); if (sent_cnt < request_cnt && PJ_TIME_VAL_GTE(now, next_send)) { /* Add additional reference to tdata to prevent transaction from * deleting it. */ pjsip_tx_data_add_ref(tdata); /* (Re)Send the request. */ PJ_LOG(4,(THIS_FILE, " (re)sending request %d", sent_cnt)); status = pjsip_endpt_send_request_stateless(endpt, tdata, 0, 0); if (status != PJ_SUCCESS) { app_perror(" Error: unable to send request", status); pjsip_tx_data_dec_ref(tdata); return -20; } /* Schedule next send, if any. */ sent_cnt++; if (sent_cnt < request_cnt) { pj_gettimeofday(&next_send); next_send.msec += request_interval_msec; pj_time_val_normalize(&next_send); } } if (now.sec > timeout.sec) { if (!expecting_timeout) PJ_LOG(3,(THIS_FILE, " Error: test has timed out")); pjsip_tx_data_dec_ref(tdata); return TEST_TIMEOUT_ERROR; } } if (test_complete < 0) { pjsip_transaction *tsx; tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); return test_complete; } /* Allow transaction to destroy itself */ flush_events(500); /* Make sure transaction has been destroyed. */ if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -40; } /* Check tdata reference counter. */ if (pj_atomic_get(tdata->ref_cnt) != 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d", pj_atomic_get(tdata->ref_cnt))); pjsip_tx_data_dec_ref(tdata); return -50; } /* Destroy txdata */ pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; }
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; }
/* Reinitialize outgoing request after 401/407 response is received. * The purpose of this function is: * - to add a Authorization/Proxy-Authorization header. * - to put the newly created Authorization/Proxy-Authorization header * in cached_list. */ PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, pj_pool_t *ses_pool, pjsip_auth_session *sess_list, int cred_count, const pjsip_cred_info cred_info[], pjsip_tx_data *tdata, const pjsip_rx_data *rdata) { const pjsip_hdr *hdr; pjsip_via_hdr *via; PJ_UNUSED_ARG(endpt) pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG); pj_assert(rdata->msg->line.status.code == 401 || rdata->msg->line.status.code == 407 ); /* * Respond to each authentication challenge. */ hdr = rdata->msg->hdr.next; while (hdr != &rdata->msg->hdr) { pjsip_auth_session *sess; const pjsip_www_authenticate_hdr *hchal; pjsip_authorization_hdr *hauth; /* Find WWW-Authenticate or Proxy-Authenticate header. */ while (hdr->type != PJSIP_H_WWW_AUTHENTICATE && hdr->type != PJSIP_H_PROXY_AUTHENTICATE && hdr != &rdata->msg->hdr) { hdr = hdr->next; } if (hdr == &rdata->msg->hdr) break; hchal = (const pjsip_www_authenticate_hdr*) hdr; /* Find authentication session for this realm, create a new one * if not present. */ sess = find_session(sess_list, &hchal->challenge.common.realm ); if (!sess) { sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess)); pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm); sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); # if (PJSIP_AUTH_HEADER_CACHING) { pj_list_init(&sess->cached_hdr); } # endif pj_list_insert_before( sess_list, sess ); } /* Create authorization header for this challenge, and update * authorization session. */ hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, tdata, cred_count, cred_info, ses_pool, sess ); if (!hauth) return NULL; /* Add to the message. */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); /* Process next header. */ hdr = hdr->next; } /* Remove branch param in Via header. */ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (via) via->branch_param.slen = 0; /* Increment reference counter. */ pjsip_tx_data_add_ref(tdata); /* Done. */ return tdata; }