Example #1
0
/* Callback when transaction state has changed. */
static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub, 
				     pjsip_transaction *tsx,
				     pjsip_event *event)
{
    pjsua_buddy *buddy;
    pjsip_contact_hdr *contact_hdr;

    PJSUA_LOCK();

    buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
    if (!buddy) {
	PJSUA_UNLOCK();
	return;
    }

    /* We only use this to update buddy's Contact, when it's not
     * set.
     */
    if (buddy->contact.slen != 0) {
	/* Contact already set */
	PJSUA_UNLOCK();
	return;
    }
    
    /* Only care about 2xx response to outgoing SUBSCRIBE */
    if (tsx->status_code/100 != 2 ||
	tsx->role != PJSIP_UAC_ROLE ||
	event->type != PJSIP_EVENT_RX_MSG || 
	pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
    {
	PJSUA_UNLOCK();
	return;
    }

    /* Find contact header. */
    contact_hdr = (pjsip_contact_hdr*)
		  pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
				     PJSIP_H_CONTACT, NULL);
    if (!contact_hdr) {
	PJSUA_UNLOCK();
	return;
    }

    buddy->contact.ptr = (char*)
			 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
    buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
					   contact_hdr->uri,
					   buddy->contact.ptr, 
					   PJSIP_MAX_URL_SIZE);
    if (buddy->contact.slen < 0)
	buddy->contact.slen = 0;

    PJSUA_UNLOCK();
}
Example #2
0
// Reject request unless it's a SUBSCRIBE targeted at the home domain / this node.
pj_bool_t subscription_on_rx_request(pjsip_rx_data *rdata)
{
  SAS::TrailId trail = get_trail(rdata);

  if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port)
  {
    // Not an S-CSCF, so don't handle SUBSCRIBEs.
    return PJ_FALSE; // LCOV_EXCL_LINE
  }

  if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()))
  {
    // This isn't a SUBSCRIBE, so this module can't process it.
    return PJ_FALSE;
  }

  if (!((PJUtils::is_home_domain(rdata->msg_info.msg->line.req.uri) ||
         (PJUtils::is_uri_local(rdata->msg_info.msg->line.req.uri))) &&
        PJUtils::check_route_headers(rdata)))
  {
    LOG_DEBUG("Rejecting subscription request not targeted at this domain or node");
    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0);
    SAS::report_event(event);
    return PJ_FALSE;
  }

  // SUBSCRIBE request targeted at the home domain or specifically at this node. Check
  // whether it should be processed by this module or passed up to an AS.
  pjsip_msg *msg = rdata->msg_info.msg;

  // A valid subscription must have the Event header set to "reg". This is case-sensitive
  pj_str_t event_name = pj_str("Event");
  pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(msg, &event_name, NULL);

  if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg"))
  {
    // The Event header is missing or doesn't match "reg"
    LOG_DEBUG("Rejecting subscription request with invalid event header");

    SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0);
    if (event)
    {
      char event_hdr_str[256];
      memset(event_hdr_str, 0, 256);
      pjsip_hdr_print_on(event, event_hdr_str, 255);
      sas_event.add_var_param(event_hdr_str);
    }
    SAS::report_event(sas_event);

    return PJ_FALSE;
  }

  // Accept header may be present - if so must include the application/reginfo+xml
  pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ACCEPT, NULL);
  if (accept)
  {
    bool found = false;
    pj_str_t reginfo = pj_str("application/reginfo+xml");
    for (uint32_t i = 0; i < accept->count; i++)
    {
      if (!pj_strcmp(accept->values + i, &reginfo))
      {
        found = true;
      }
    }

    if (!found)
    {
      // The Accept header (if it exists) doesn't contain "application/reginfo+xml"
      LOG_DEBUG("Rejecting subscription request with invalid accept header");
      char accept_hdr_str[256];
      memset(accept_hdr_str, 0, 256);
      pjsip_hdr_print_on(accept, accept_hdr_str, 255);
      SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0);
      event.add_var_param(accept_hdr_str);
      SAS::report_event(event);

      return PJ_FALSE;
    }
  }

  process_subscription_request(rdata);
  return PJ_TRUE;
}
// Check whether this request should be absorbed by the subscription module
bool SubscriptionSproutlet::handle_request(pjsip_msg* req,
                                           SAS::TrailId trail)
{
  if (pjsip_method_cmp(&req->line.req.method, pjsip_get_subscribe_method()))
  {
    // This isn't a SUBSCRIBE, so this module can't process it.
    return false;
  }

  URIClass uri_class = URIClassifier::classify_uri(req->line.req.uri);

  if (((uri_class != NODE_LOCAL_SIP_URI) &&
       (uri_class != HOME_DOMAIN_SIP_URI) &&
       (uri_class != GLOBAL_PHONE_NUMBER) &&
       (uri_class != LOCAL_PHONE_NUMBER)) ||
      !PJUtils::check_route_headers(req))
  {
    TRC_DEBUG("Not processing subscription request not targeted at this domain "
              "or node");

    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0);
    SAS::report_event(event);

    return false;
  }

  // We now know we have a SUBSCRIBE request targeted at the home domain
  // or specifically at this node. Check whether it should be processed
  // by this module or passed up to an AS.

  // A valid subscription must have the Event header set to "reg". This is
  // case-sensitive.
  pj_str_t event_name = pj_str((char*)"Event");
  pjsip_event_hdr* event =
           (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(req, &event_name, NULL);

  if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg"))
  {
    // The Event header is missing or doesn't match "reg"
    TRC_DEBUG("Not processing subscribe that's not for the 'reg' package");

    SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0);
    if (event)
    {
      char event_hdr_str[256];
      memset(event_hdr_str, 0, 256);
      pjsip_hdr_print_on(event, event_hdr_str, 255);
      sas_event.add_var_param(event_hdr_str);
    }
    SAS::report_event(sas_event);

    return false;
  }

  // Accept header may be present - if so must include application/reginfo+xml
  pjsip_accept_hdr* accept =
               (pjsip_accept_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_ACCEPT, NULL);

  if (accept)
  {
    bool found = false;
    pj_str_t reginfo = pj_str((char*)"application/reginfo+xml");
    for (uint32_t i = 0; i < accept->count; i++)
    {
      if (!pj_strcmp(accept->values + i, &reginfo))
      {
        found = true;
        break;
      }
    }

    if (!found)
    {
      // The Accept header (if it exists) doesn't contain
      // "application/reginfo+xml"
      TRC_DEBUG("Not processing subscription request that doesn't "
                "accept reginfo notifications");
      char accept_hdr_str[256];
      memset(accept_hdr_str, 0, 256);
      pjsip_hdr_print_on(accept, accept_hdr_str, 255);

      SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0);
      event.add_var_param(accept_hdr_str);
      SAS::report_event(event);

      return false;
    }
  }

  return true;
}
Example #4
0
// Reject request unless it's a SUBSCRIBE targeted at the home domain / this node.
pj_bool_t subscription_on_rx_request(pjsip_rx_data *rdata)
{
  SAS::TrailId trail = get_trail(rdata);

  if ((rdata->tp_info.transport->local_name.port == stack_data.scscf_port) &&
      !(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) &&
      ((PJUtils::is_home_domain(rdata->msg_info.msg->line.req.uri)) ||
       (PJUtils::is_uri_local(rdata->msg_info.msg->line.req.uri))) &&
      (PJUtils::check_route_headers(rdata)))
  {
    // SUBSCRIBE request targeted at the home domain or specifically at this node. Check
    // whether it should be processed by this module or passed up to an AS.
    pjsip_msg *msg = rdata->msg_info.msg;

    // A valid subscription must have the Event header set to "reg". This is case-sensitive
    pj_str_t event_name = pj_str("Event");
    pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(msg, &event_name, NULL);

    if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg"))
    {
      // The Event header is missing or doesn't match "reg"
      LOG_DEBUG("Rejecting subscription request with invalid event header");

      SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY, 0);
      std::string error_msg = "SUBSCRIBE rejected by the S-CSCF as the Event header is invalid or missing - it should be 'reg'";
      event.add_var_param(error_msg);
      SAS::report_event(event);

      return PJ_FALSE;
    }

    // Accept header may be present - if so must include the application/reginfo+xml
    pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ACCEPT, NULL);
    if (accept)
    {
      bool found = false;
      pj_str_t reginfo = pj_str("application/reginfo+xml");
      for (uint32_t i = 0; i < accept->count; i++)
      {
        if (!pj_strcmp(accept->values + i, &reginfo))
        {
          found = true;
        }
      }

      if (!found)
      {
        // The Accept header (if it exists) doesn't contain "application/reginfo+xml"
        LOG_DEBUG("Rejecting subscription request with invalid accept header");

        SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY, 0);
        std::string error_msg = "SUBSCRIBE rejected by the S-CSCF as the Accepts header is invalid - if it's present it should be 'application/reginfo+xml'";
        event.add_var_param(error_msg);
        SAS::report_event(event);

        return PJ_FALSE;
      }
    }

    process_subscription_request(rdata);
    return PJ_TRUE;
  }

  SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY, 0);
  std::string error_msg = "SUBSCRIBE rejected by the S-CSCF as it wasn't targeted at the home domain or this node";
  event.add_var_param(error_msg);
  SAS::report_event(event);

  return PJ_FALSE;
}
Example #5
0
/* This is called when request is received. 
 * We need to check for incoming SUBSCRIBE request.
 */
static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
{
    int acc_id;
    pjsua_acc *acc;
    pj_str_t contact;
    pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
    pjsua_srv_pres *uapres;
    pjsip_evsub *sub;
    pjsip_evsub_user pres_cb;
    pjsip_dialog *dlg;
    pjsip_status_code st_code;
    pj_str_t reason;
    pjsip_expires_hdr *expires_hdr;
    pjsua_msg_data msg_data;
    pj_status_t status;

    if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
	return PJ_FALSE;

    /* Incoming SUBSCRIBE: */

    PJSUA_LOCK();

    /* Find which account for the incoming request. */
    acc_id = pjsua_acc_find_for_incoming(rdata);
    acc = &pjsua_var.acc[acc_id];

    PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
	      acc_id));
    
    /* Create suitable Contact header */
    if (acc->contact.slen) {
	contact = acc->contact;
    } else {
	status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
					      acc_id, rdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Unable to generate Contact header", 
			 status);
	    PJSUA_UNLOCK();
	    pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
					  NULL, NULL);
	    return PJ_TRUE;
	}
    }

    /* Create UAS dialog: */
    status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, 
				  &contact, &dlg);
    if (status != PJ_SUCCESS) {
	pjsua_perror(THIS_FILE, 
		     "Unable to create UAS dialog for subscription", 
		     status);
	PJSUA_UNLOCK();
	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
				      NULL, NULL);
	return PJ_TRUE;
    }

    /* Set credentials and preference. */
    pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
    pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);

    /* Init callback: */
    pj_bzero(&pres_cb, sizeof(pres_cb));
    pres_cb.on_evsub_state = &pres_evsub_on_srv_state;

    /* Create server presence subscription: */
    status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
    if (status != PJ_SUCCESS) {
	int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
	pjsip_tx_data *tdata;

	pjsua_perror(THIS_FILE, "Unable to create server subscription", 
		     status);

	if (code==599 || code > 699 || code < 300) {
	    code = 400;
	}

	status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
	if (status == PJ_SUCCESS) {
	    status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
					     tdata);
	}

	PJSUA_UNLOCK();
	return PJ_TRUE;
    }

    /* If account is locked to specific transport, then lock dialog
     * to this transport too.
     */
    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
	pjsip_tpselector tp_sel;

	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
	pjsip_dlg_set_transport(dlg, &tp_sel);
    }

    /* Attach our data to the subscription: */
    uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
    uapres->sub = sub;
    uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
    uapres->acc_id = acc_id;
    uapres->dlg = dlg;
    status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
			     uapres->remote, PJSIP_MAX_URL_SIZE);
    if (status < 1)
	pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
    else
	uapres->remote[status] = '\0';

    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);

    /* Add server subscription to the list: */
    pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);


    /* Capture the value of Expires header. */
    expires_hdr = (pjsip_expires_hdr*)
    		  pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
				     NULL);
    if (expires_hdr)
	uapres->expires = expires_hdr->ivalue;
    else
	uapres->expires = -1;

    st_code = (pjsip_status_code)200;
    reason = pj_str("OK");
    pjsua_msg_data_init(&msg_data);

    /* Notify application callback, if any */
    if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
	pjsua_buddy_id buddy_id;

	buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);

	(*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
						     &dlg->remote.info_str, 
						     rdata, &st_code, &reason,
						     &msg_data);
    }

    /* Handle rejection case */
    if (st_code >= 300) {
	pjsip_tx_data *tdata;

	/* Create response */
	status = pjsip_dlg_create_response(dlg, rdata, st_code, 
					   &reason, &tdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error creating response",  status);
	    pj_list_erase(uapres);
	    pjsip_pres_terminate(sub, PJ_FALSE);
	    PJSUA_UNLOCK();
	    return PJ_FALSE;
	}

	/* Add header list, if any */
	pjsua_process_msg_data(tdata, &msg_data);

	/* Send the response */
	status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
					 tdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error sending response",  status);
	    /* This is not fatal */
	}

	/* Terminate presence subscription */
	pj_list_erase(uapres);
	pjsip_pres_terminate(sub, PJ_FALSE);
	PJSUA_UNLOCK();
	return PJ_TRUE;
    }

    /* Create and send 2xx response to the SUBSCRIBE request: */
    status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
    if (status != PJ_SUCCESS) {
	pjsua_perror(THIS_FILE, "Unable to accept presence subscription", 
		     status);
	pj_list_erase(uapres);
	pjsip_pres_terminate(sub, PJ_FALSE);
	PJSUA_UNLOCK();
	return PJ_FALSE;
    }

    /* If code is 200, send NOTIFY now */
    if (st_code == 200) {
	pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE, 
			  NULL, NULL, PJ_TRUE, &msg_data);
    }

    /* Done: */

    PJSUA_UNLOCK();

    return PJ_TRUE;
}
Example #6
0
static pj_bool_t on_rx_msg(pjsip_rx_data* rdata)
{
  // Do logging.
  local_log_rx_msg(rdata);
  sas_log_rx_msg(rdata);

  requests_counter->increment();

  // Check whether the request should be processed
  if (!(load_monitor->admit_request()) &&
      (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) &&
      (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD))
  {
    // Discard non-ACK requests if there are no available tokens.
    // Respond statelessly with a 503 Service Unavailable, including a
    // Retry-After header with a zero length timeout.
    LOG_DEBUG("Rejected request due to overload");

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

    SAS::TrailId trail = get_trail(rdata);

    SAS::Marker start_marker(trail, MARKER_ID_START, 1u);
    SAS::report_marker(start_marker);

    SAS::Event event(trail, SASEvent::SIP_OVERLOAD, 0);
    event.add_static_param(load_monitor->get_target_latency());
    event.add_static_param(load_monitor->get_current_latency());
    event.add_static_param(load_monitor->get_rate_limit());
    SAS::report_event(event);

    PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg);

    if ((rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) ||
        ((pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) == 0) ||
        ((pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_notify_method())) == 0))
    {
      // Omit the Call-ID for these requests, as the same Call-ID can be
      // reused over a long period of time and produce huge SAS trails.
      PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg);
    }
    else
    {
      PJUtils::mark_sas_call_branch_ids(trail, cid, rdata->msg_info.msg);
    }

    SAS::Marker end_marker(trail, MARKER_ID_END, 1u);
    SAS::report_marker(end_marker);

    pjsip_retry_after_hdr* retry_after = pjsip_retry_after_hdr_create(rdata->tp_info.pool, 0);
    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_SERVICE_UNAVAILABLE,
                               NULL,
                               (pjsip_hdr*)retry_after,
                               NULL);

    // We no longer terminate TCP connections on overload as the shutdown has
    // to wait for existing transactions to end and therefore it takes too
    // long to get feedback to the downstream node.  We expect downstream nodes
    // to rebalance load if possible triggered by receipt of the 503 responses.

    overload_counter->increment();
    return PJ_TRUE;
  }

  // Check that the worker threads are not all deadlocked.
  if (rx_msg_q.is_deadlocked())
  {
    // The queue has not been serviced for sufficiently long to imply that
    // all the worker threads are deadlock, so exit the process so it will be
    // restarted.
    LOG_ERROR("Detected worker thread deadlock - exiting");
    abort();
  }

  // Before we start, get a timestamp.  This will track the time from
  // receiving a message to forwarding it on (or rejecting it).
  struct rx_msg_qe qe;
  qe.stop_watch.start();

  // Notify the connection tracker that the transport is active.
  connection_tracker->connection_active(rdata->tp_info.transport);

  // Clone the message and queue it to a scheduler thread.
  pjsip_rx_data* clone_rdata;
  pj_status_t status = pjsip_rx_data_clone(rdata, 0, &clone_rdata);

  if (status != PJ_SUCCESS)
  {
    // Failed to clone the message, so drop it.
    LOG_ERROR("Failed to clone incoming message (%s)", PJUtils::pj_status_to_string(status).c_str());
    return PJ_TRUE;
  }

  // Make sure the trail identifier is passed across.
  set_trail(clone_rdata, get_trail(rdata));

  // @TODO - need to think about back-pressure mechanisms.  For example,
  // should we have a maximum depth of queue and drop messages after that?
  // May be better to hold on to the message until the queue has space - this
  // will force back pressure on the particular TCP connection.  Or should we
  // have a queue per transport and round-robin them?

  LOG_DEBUG("Queuing cloned received message %p for worker threads", clone_rdata);
  qe.rdata = clone_rdata;

  // Track the current queue size
  queue_size_accumulator->accumulate(rx_msg_q.size());
  rx_msg_q.push(qe);

  // return TRUE to flag that we have absorbed the incoming message.
  return PJ_TRUE;
}