void SessionExpiresHelper::process_request(pjsip_msg* req,
                                           pj_pool_t* pool,
                                           SAS::TrailId trail)
{
  // Session expires is only allowed on INVITE and UPDATE methods.
  pjsip_method* method = &req->line.req.method;

  if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) &&
      (pjsip_method_cmp(method, &METHOD_UPDATE) != 0))
  {
    return;
  }

  // Store if this is the initial transaction on a dialog, and if the UAC
  // supports session timers. We need both of these when processing the
  // response.
  _initial_request = (PJSIP_MSG_TO_HDR(req)->tag.slen == 0);
  _uac_supports_timer = timer_supported(req);

  // Find the session-expires header (if present) and the minimum
  // session-expires. Note that the latter has a default value.
  pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*)
    pjsip_msg_find_hdr_by_name(req, &STR_SESSION_EXPIRES, NULL);

  pjsip_min_se_hdr* min_se_hdr = (pjsip_min_se_hdr*)
    pjsip_msg_find_hdr_by_name(req, &STR_MIN_SE, NULL);

  SessionInterval min_se = (min_se_hdr != NULL) ?
                            min_se_hdr->expires :
                            DEFAULT_MIN_SE;

  if ((se_hdr != NULL) && (se_hdr->expires < _target_se))
  {
    // The request already has a session expires that is below our target. We
    // don't need to change the value.
    TRC_DEBUG("Session expires already set to %d", se_hdr->expires);
  }
  else
  {
    // No pre-existing session expires, or the current value is greater than
    // our target. Set it to as close to our target as possible, but don't set
    // it below the min-SE.
    if (se_hdr == NULL)
    {
      se_hdr = pjsip_session_expires_hdr_create(pool);
      pjsip_msg_add_hdr(req, (pjsip_hdr*)se_hdr);
    }

    se_hdr->expires = std::max(_target_se, min_se);

    TRC_DEBUG("Set session expires to %d", se_hdr->expires);
  }

  // Make a note of the session expires (we may need it when processing the
  // response)
  _se_on_req = se_hdr->expires;
}
void SessionExpiresHelper::process_response(pjsip_msg* rsp,
                                            pj_pool_t* pool,
                                            SAS::TrailId trail)
{
  // Session expires is only allowed on INVITE and UPDATE methods.
  pjsip_method* method = &PJSIP_MSG_CSEQ_HDR(rsp)->method;

  if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) &&
      (pjsip_method_cmp(method, &METHOD_UPDATE) != 0))
  {
    return;
  }

  // We only need to process successful final responses.
  if (!PJSIP_IS_STATUS_IN_CLASS(rsp->line.status.code, 200))
  {
    return;
  }

  pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*)
    pjsip_msg_find_hdr_by_name(rsp, &STR_SESSION_EXPIRES, NULL);

  if (se_hdr == NULL)
  {
    // There is no session-expires header. This means we are most downstream
    // device that supports session timers, and in particular the UAS does not
    // support them.
    //
    // If the UAC does not support session timers, there's nothing more we can
    // do - session timers will not be used for this dialog.
    //
    // If the UAC *does* support session timers, re-add a session-expires header
    // that instructs the UAC to be the refresher.
    if (_uac_supports_timer)
    {
      se_hdr = pjsip_session_expires_hdr_create(pool);
      pjsip_msg_add_hdr(rsp, (pjsip_hdr*)se_hdr);
      se_hdr->expires = _se_on_req;
      se_hdr->refresher = SESSION_REFRESHER_UAC;

      // Also update (or add) the require header to force the UAC to do session
      // refreshes.
      pjsip_require_hdr* require_hdr = (pjsip_require_hdr*)
        pjsip_msg_find_hdr(rsp, PJSIP_H_REQUIRE, NULL);

      if (require_hdr == NULL)
      {
        require_hdr = (pjsip_require_hdr*)pjsip_require_hdr_create(pool);
        pjsip_msg_add_hdr(rsp, (pjsip_hdr*)require_hdr);
      }

      pj_strdup(pool, &require_hdr->values[require_hdr->count], &STR_TIMER);
      require_hdr->count++;
    }
  }

  if (_initial_request)
  {
    if (se_hdr == NULL)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_NO_UA_SUPPORT, 0);
      SAS::report_event(event);
    }
    else if (se_hdr->expires > _target_se)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_INTERVAL_TOO_LONG, 0);
      event.add_static_param(_target_se);
      event.add_static_param(se_hdr->expires);
      SAS::report_event(event);
    }
  }
}
Example #3
0
static void sas_log_rx_msg(pjsip_rx_data* rdata)
{
  SAS::TrailId trail = 0;

  if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG)
  {
    // Message is a response, so try to correlate to an existing UAC
    // transaction using the top-most Via header.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC,
                         &rdata->msg_info.cseq->method, rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
    if (tsx)
    {
      // Found the UAC transaction, so get the trail if there is one.
      trail = get_trail(tsx);

      // Unlock tsx because it is locked in find_tsx()
      pj_grp_lock_release(tsx->grp_lock);
    }
  }
  else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
  {
    // Message is an ACK, so try to correlate it to the existing UAS
    // transaction using the top-most Via header.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
                         &rdata->msg_info.cseq->method, rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
    if (tsx)
    {
      // Found the UAS transaction, so get the trail if there is one.
      trail = get_trail(tsx);

      // Unlock tsx because it is locked in find_tsx()
      pj_grp_lock_release(tsx->grp_lock);
    }
  }
  else if (rdata->msg_info.msg->line.req.method.id == PJSIP_CANCEL_METHOD)
  {
    // Message is a CANCEL request chasing an INVITE, so we want to try to
    // correlate it to the INVITE trail for the purposes of SAS tracing.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
                         pjsip_get_invite_method(), rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
    if (tsx)
    {
      // Found the INVITE UAS transaction, so get the trail if there is one.
      trail = get_trail(tsx);

      // Unlock tsx because it is locked in find_tsx()
      pj_grp_lock_release(tsx->grp_lock);
    }
  }

  if (trail == 0)
  {
    // The message doesn't correlate to an existing trail, so create a new
    // one.
    trail = SAS::new_trail(1u);
  }

  // Store the trail in the message as it gets passed up the stack.
  set_trail(rdata, trail);

  // Log the message event.
  SAS::Event event(trail, SASEvent::RX_SIP_MSG, 1u);
  event.add_static_param(pjsip_transport_get_type_from_flag(rdata->tp_info.transport->flag));
  event.add_static_param(rdata->pkt_info.src_port);
  event.add_var_param(rdata->pkt_info.src_name);
  event.add_var_param(rdata->msg_info.len, rdata->msg_info.msg_buf);
  SAS::report_event(event);
}
/* Callback to be called to handle new incoming requests. */
static pj_bool_t proxy_on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_transaction *uas_tsx, *uac_tsx;
    struct uac_data *uac_data;
    struct uas_data *uas_data;
    pjsip_tx_data *tdata;
    pj_status_t status;

    if (rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) {

	/* Verify incoming request */
	status = proxy_verify_request(rdata);
	if (status != PJ_SUCCESS) {
	    app_perror("RX invalid request", status);
	    return PJ_TRUE;
	}

	/*
	 * Request looks sane, next clone the request to create transmit data.
	 */
	status = pjsip_endpt_create_request_fwd(global.endpt, rdata, NULL,
						NULL, 0, &tdata);
	if (status != PJ_SUCCESS) {
	    pjsip_endpt_respond_stateless(global.endpt, rdata,
					  PJSIP_SC_INTERNAL_SERVER_ERROR, 
					  NULL, NULL, NULL);
	    return PJ_TRUE;
	}


	/* Process routing */
	status = proxy_process_routing(tdata);
	if (status != PJ_SUCCESS) {
	    app_perror("Error processing route", status);
	    return PJ_TRUE;
	}

	/* Calculate target */
	status = proxy_calculate_target(rdata, tdata);
	if (status != PJ_SUCCESS) {
	    app_perror("Error calculating target", status);
	    return PJ_TRUE;
	}

	/* Everything is set to forward the request. */

	/* If this is an ACK request, forward statelessly.
	 * This happens if the proxy records route and this ACK
	 * is sent for 2xx response. An ACK that is sent for non-2xx
	 * final response will be absorbed by transaction layer, and
	 * it will not be received by on_rx_request() callback.
	 */
	if (tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
	    status = pjsip_endpt_send_request_stateless(global.endpt, tdata, 
							NULL, NULL);
	    if (status != PJ_SUCCESS) {
		app_perror("Error forwarding request", status);
		return PJ_TRUE;
	    }

	    return PJ_TRUE;
	}

	/* Create UAC transaction for forwarding the request. 
	 * Set our module as the transaction user to receive further
	 * events from this transaction.
	 */
	status = pjsip_tsx_create_uac(&mod_tu, tdata, &uac_tsx);
	if (status != PJ_SUCCESS) {
	    pjsip_tx_data_dec_ref(tdata);
	    pjsip_endpt_respond_stateless(global.endpt, rdata, 
					  PJSIP_SC_INTERNAL_SERVER_ERROR, 
					  NULL, NULL, NULL);
	    return PJ_TRUE;
	}

	/* Create UAS transaction to handle incoming request */
	status = pjsip_tsx_create_uas(&mod_tu, rdata, &uas_tsx);
	if (status != PJ_SUCCESS) {
	    pjsip_tx_data_dec_ref(tdata);
	    pjsip_endpt_respond_stateless(global.endpt, rdata, 
					  PJSIP_SC_INTERNAL_SERVER_ERROR, 
					  NULL, NULL, NULL);
	    pjsip_tsx_terminate(uac_tsx, PJSIP_SC_INTERNAL_SERVER_ERROR);
	    return PJ_TRUE;
	}

	/* Feed the request to the UAS transaction to drive it's state 
	 * out of NULL state. 
	 */
	pjsip_tsx_recv_msg(uas_tsx, rdata);

	/* Attach a data to the UAC transaction, to be used to find the
	 * UAS transaction when we receive response in the UAC side.
	 */
	uac_data = (struct uac_data*)
		   pj_pool_alloc(uac_tsx->pool, sizeof(struct uac_data));
	uac_data->uas_tsx = uas_tsx;
	uac_tsx->mod_data[mod_tu.id] = (void*)uac_data;

	/* Attach data to the UAS transaction, to find the UAC transaction
	 * when cancelling INVITE request.
	 */
	uas_data = (struct uas_data*)
		    pj_pool_alloc(uas_tsx->pool, sizeof(struct uas_data));
	uas_data->uac_tsx = uac_tsx;
	uas_tsx->mod_data[mod_tu.id] = (void*)uas_data;

	/* Everything is setup, forward the request */
	status = pjsip_tsx_send_msg(uac_tsx, tdata);
	if (status != PJ_SUCCESS) {
	    pjsip_tx_data *err_res;

	    /* Fail to send request, for some reason */

	    /* Destroy transmit data */
	    pjsip_tx_data_dec_ref(tdata);

	    /* I think UAC transaction should have been destroyed when
	     * it fails to send request, so no need to destroy it.
	    pjsip_tsx_terminate(uac_tsx, PJSIP_SC_INTERNAL_SERVER_ERROR);
	     */

	    /* Send 500/Internal Server Error to UAS transaction */
	    pjsip_endpt_create_response(global.endpt, rdata,
					500, NULL, &err_res);
	    pjsip_tsx_send_msg(uas_tsx, err_res);

	    return PJ_TRUE;
	}

	/* Send 100/Trying if this is an INVITE */
	if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
	    pjsip_tx_data *res100;

	    pjsip_endpt_create_response(global.endpt, rdata, 100, NULL, 
					&res100);
	    pjsip_tsx_send_msg(uas_tsx, res100);
	}

    } else {
	/* This is CANCEL request */
	pjsip_transaction *invite_uas;
	struct uas_data *uas_data;
	pj_str_t key;
	
	/* Find the UAS INVITE transaction */
	pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
			     pjsip_get_invite_method(), rdata);
	invite_uas = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
	if (!invite_uas) {
	    /* Invite transaction not found, respond CANCEL with 481 */
	    pjsip_endpt_respond_stateless(global.endpt, rdata, 481, NULL,
					  NULL, NULL);
	    return PJ_TRUE;
	}

	/* Respond 200 OK to CANCEL */
	pjsip_endpt_respond(global.endpt, NULL, rdata, 200, NULL, NULL,
			    NULL, NULL);

	/* Send CANCEL to cancel the UAC transaction.
	 * The UAS INVITE transaction will get final response when
	 * we receive final response from the UAC INVITE transaction.
	 */
	uas_data = (struct uas_data*) invite_uas->mod_data[mod_tu.id];
	if (uas_data->uac_tsx && uas_data->uac_tsx->status_code < 200) {
	    pjsip_tx_data *cancel;

	    pj_mutex_lock(uas_data->uac_tsx->mutex);

	    pjsip_endpt_create_cancel(global.endpt, uas_data->uac_tsx->last_tx,
				      &cancel);
	    pjsip_endpt_send_request(global.endpt, cancel, -1, NULL, NULL);

	    pj_mutex_unlock(uas_data->uac_tsx->mutex);
	}

	/* Unlock UAS tsx because it is locked in find_tsx() */
	pj_mutex_unlock(invite_uas->mutex);
    }

    return PJ_TRUE;
}
// LCOV_EXCL_START - can't meaningfully test SAS in UT
static void sas_log_rx_msg(pjsip_rx_data* rdata)
{
  bool first_message_in_trail = false;
  SAS::TrailId trail = 0;

  // Look for the SAS Trail ID for the corresponding transaction object.
  //
  // Note that we are NOT locking the transaction object before we fetch the
  // trail ID from it.  This is deliberate - we cannot get a group lock from
  // this routine as we may already have obtained the IO lock (which is lower
  // in the locking hierarchy) higher up the stack.
  // (e.g. from ioqueue_common_abs::ioqueue_dispatch_read_event) and grabbing
  // the group lock here may cause us to deadlock with a thread using the locks
  // in the right order.
  //
  // This is safe for the following reasons
  // - The transaction objects are only ever invalidated by the current thread
  //   (i.e. the transport thread), so we don't need to worry about the tsx
  //   pointers being invalid.
  // - In principle, the trail IDs (which are 64 bit numbers stored as void*s
  //   since thats the format of the generic PJSIP user data area) might be
  //   being written to as we are reading them, thereby invalidating them.
  //   However, the chances of this happening are exceedingly remote and, if it
  //   ever happened, the worst that could happen is that the trail ID would be
  //   invalid and the log we're about to make unreachable by SAS.  This is
  //   assumed to be sufficiently low impact as to be ignorable for practical
  //   purposes.
  if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG)
  {
    // Message is a response, so try to correlate to an existing UAC
    // transaction using the top-most Via header.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC,
                         &rdata->msg_info.cseq->method, rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
    if (tsx)
    {
      // Found the UAC transaction, so get the trail if there is one.
      trail = get_trail(tsx);
    }
  }
  else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
  {
    // Message is an ACK, so try to correlate it to the existing UAS
    // transaction using the top-most Via header.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
                         &rdata->msg_info.cseq->method, rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
    if (tsx)
    {
      // Found the UAS transaction, so get the trail if there is one.
      trail = get_trail(tsx);
    }
  }
  else if (rdata->msg_info.msg->line.req.method.id == PJSIP_CANCEL_METHOD)
  {
    // Message is a CANCEL request chasing an INVITE, so we want to try to
    // correlate it to the INVITE trail for the purposes of SAS tracing.
    pj_str_t key;
    pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_UAS_ROLE,
                         pjsip_get_invite_method(), rdata);
    pjsip_transaction* tsx = pjsip_tsx_layer_find_tsx(&key, PJ_FALSE);
    if (tsx)
    {
      // Found the INVITE UAS transaction, so get the trail if there is one.
      trail = get_trail(tsx);
    }
  }
  else if ((rdata->msg_info.msg->line.req.method.id == PJSIP_OPTIONS_METHOD) &&
           (URIClassifier::classify_uri(rdata->msg_info.msg->line.req.uri) == NODE_LOCAL_SIP_URI))
  {
    // This is an OPTIONS poll directed at this node. Don't log it to SAS, and set the trail ID to a sentinel value so we don't log the response either.
    TRC_DEBUG("Skipping SAS logging for OPTIONS request");
    set_trail(rdata, DONT_LOG_TO_SAS);
    return;
  }

  if (trail == 0)
  {
    // The message doesn't correlate to an existing trail, so create a new
    // one.

    // If SAS::new_trail returns 0 or DONT_LOG_TO_SAS, keep going.
    while ((trail == 0) || (trail == DONT_LOG_TO_SAS))
    {
      trail = SAS::new_trail(1u);
    }
    first_message_in_trail = true;
  }

  // Store the trail in the message as it gets passed up the stack.
  set_trail(rdata, trail);

  // Raise SAS markers on the first message in a trail only - subsequent
  // messages with the same trail ID don't need additional markers
  if (first_message_in_trail)
  {
    PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg);

    pjsip_cid_hdr* cid = (pjsip_cid_hdr*)rdata->msg_info.cid;

    PJUtils::mark_sas_call_branch_ids(trail, cid, rdata->msg_info.msg);
  }

  // Log the message event.
  SAS::Event event(trail, SASEvent::RX_SIP_MSG, 0);
  event.add_static_param(pjsip_transport_get_type_from_flag(rdata->tp_info.transport->flag));
  event.add_static_param(rdata->pkt_info.src_port);
  event.add_var_param(rdata->pkt_info.src_name);
  event.add_compressed_param(rdata->msg_info.len, rdata->msg_info.msg_buf, &SASEvent::PROFILE_SIP);
  SAS::report_event(event);
}
Example #6
0
static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
{
	pj_str_t tsx_key;
	pjsip_transaction *tsx;
	pjsip_dialog *dlg;
	pj_str_t *local_tag;
	pj_str_t *remote_tag;

	if (!rdata->msg_info.msg) {
		return NULL;
	}

	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
		local_tag = &rdata->msg_info.to->tag;
		remote_tag = &rdata->msg_info.from->tag;
	} else {
		local_tag = &rdata->msg_info.from->tag;
		remote_tag = &rdata->msg_info.to->tag;
	}

	/* We can only call the convenient method for
	 *  1) responses
	 *  2) non-CANCEL requests
	 *  3) CANCEL requests with a to-tag
	 */
	if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG ||
			pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
			rdata->msg_info.to->tag.slen != 0) {
		dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
				remote_tag, PJ_FALSE);
		if (dlg) {
			return dlg;
		}
	}

	/*
	 * There may still be a matching dialog if this is
	 * 1) an incoming CANCEL request without a to-tag
	 * 2) an incoming response to a dialog-creating request.
	 */
	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
		/* CANCEL requests will need to match the INVITE we initially received. Any
		 * other request type will either have been matched already or is not in
		 * dialog
		 */
		pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS,
				pjsip_get_invite_method(), rdata);
	} else {
		pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC,
				&rdata->msg_info.cseq->method, rdata);
	}

	tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
	if (!tsx) {
		ast_debug(3, "Could not find matching transaction for %s\n",
			pjsip_rx_data_get_info(rdata));
		return NULL;
	}

	dlg = pjsip_tsx_get_dlg(tsx);

#ifdef HAVE_PJ_TRANSACTION_GRP_LOCK
	pj_grp_lock_release(tsx->grp_lock);
#else
	pj_mutex_unlock(tsx->mutex);
#endif

	return dlg;
}