예제 #1
1
/*!
 * \brief Store the transport a message came in on, so it can be used for outbound messages to that contact.
 */
static pj_bool_t websocket_on_rx_msg(pjsip_rx_data *rdata)
{
	static const pj_str_t STR_WS = { "ws", 2 };
	static const pj_str_t STR_WSS = { "wss", 3 };
	pjsip_contact_hdr *contact;

	long type = rdata->tp_info.transport->key.type;

	if (type != (long)transport_type_ws && type != (long)transport_type_wss) {
		return PJ_FALSE;
	}

	if ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && !contact->star &&
		(PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

		pj_cstr(&uri->host, rdata->pkt_info.src_name);
		uri->port = rdata->pkt_info.src_port;
		ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
			(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
		pj_strdup(rdata->tp_info.pool, &uri->transport_param, (type == (long)transport_type_ws) ? &STR_WS : &STR_WSS);
	}

	rdata->msg_info.via->rport_param = 0;

	return PJ_FALSE;
}
static int get_endpoint_details(pjsip_rx_data *rdata, char *domain, size_t domain_size)
{
	pjsip_uri *from = rdata->msg_info.from->uri;
	pjsip_sip_uri *sip_from;
	if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) {
		return -1;
	}
	sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from);
	ast_copy_pj_str(domain, &sip_from->host, domain_size);
	return 0;
}
예제 #3
0
/*
 * Find buddy.
 */
static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
{
    const pjsip_sip_uri *sip_uri;
    unsigned i;

    uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)uri);

    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
	return PJSUA_INVALID_ID;

    sip_uri = (const pjsip_sip_uri*) uri;

    for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
	const pjsua_buddy *b = &pjsua_var.buddy[i];

	if (!pjsua_buddy_is_valid(i))
	    continue;

	if (pj_stricmp(&sip_uri->user, &b->name)==0 &&
	    pj_stricmp(&sip_uri->host, &b->host)==0 &&
	    (sip_uri->port==(int)b->port || (sip_uri->port==0 && b->port==5060)))
	{
	    /* Match */
	    return i;
	}
    }

    return PJSUA_INVALID_ID;
}
예제 #4
0
bool SipAccount::getNumber(pj_str_t* uri, std::string* pDisplay, std::string* pNumber) {
  pj_pool_t* pool = pjsua_pool_create("", 128, 10);
  pjsip_name_addr* n = (pjsip_name_addr*)pjsip_parse_uri(pool, uri->ptr, uri->slen, PJSIP_PARSE_URI_AS_NAMEADDR);
  if (n == NULL) {
    Logger::warn("pjsip_parse_uri() failed for %s", pj_strbuf(uri));
    pj_pool_release(pool);
    return false;
  }
  if (!PJSIP_URI_SCHEME_IS_SIP(n)) {
    Logger::warn("pjsip_parse_uri() returned unknown schema for %s", pj_strbuf(uri));
    pj_pool_release(pool);
    return false;
  }

  *pDisplay = std::string(n->display.ptr, n->display.slen);

  pjsip_sip_uri *sip = (pjsip_sip_uri*)pjsip_uri_get_uri(n);
  std::string number = std::string(sip->user.ptr, sip->user.slen);
  
  // make number international
  *pNumber = Helper::makeNumberInternational(&m_settings.base, number);

  pj_pool_release(pool);
  return true;
}
예제 #5
0
static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
{
    const pj_str_t stateless_user = { "0", 1 };
    pjsip_uri *uri;
    pjsip_sip_uri *sip_uri;

    uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);

    /* Only want to receive SIP/SIPS scheme */
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
	return PJ_FALSE;

    sip_uri = (pjsip_sip_uri*) uri;

    /* Check for matching user part */
    if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
	return PJ_FALSE;

    /*
     * Yes, this is for us.
     */

    /* Ignore ACK request */
    if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
	return PJ_TRUE;

    /*
     * Respond statelessly with 200/OK.
     */
    pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
				  NULL, NULL);
    app.server.cur_state.stateless_cnt++;
    return PJ_TRUE;
}
예제 #6
0
/// Returns a canonical IMS public user identity from a URI as per TS 23.003
/// 13.4.
std::string PJUtils::public_id_from_uri(const pjsip_uri* uri)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    pjsip_sip_uri public_id;
    memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_sip_uri));
    public_id.passwd.slen = 0;
    public_id.port = 0;
    public_id.user_param.slen = 0;
    public_id.method_param.slen = 0;
    public_id.transport_param.slen = 0;
    public_id.ttl_param = -1;
    public_id.lr_param = 0;
    public_id.maddr_param.slen = 0;
    public_id.other_param.next = NULL;
    public_id.header_param.next = NULL;
    return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id);
  }
  else if (PJSIP_URI_SCHEME_IS_TEL(uri))
  {
    pjsip_tel_uri public_id;
    memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_tel_uri));
    public_id.context.slen = 0;
    public_id.ext_param.slen = 0;
    public_id.isub_param.slen = 0;
    public_id.other_param.next = NULL;
    return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id);
  }
  else
  {
    return std::string();
  }
}
예제 #7
0
// Utility to determine if URI is local to this host.
pj_bool_t PJUtils::is_uri_local(const pjsip_uri* uri)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    int port = (((pjsip_sip_uri*)uri)->port != 0) ? ((pjsip_sip_uri*)uri)->port : 5060;
    pj_str_t host = ((pjsip_sip_uri*)uri)->host;

    if ((port == stack_data.trusted_port) ||
        (port == stack_data.untrusted_port))
    {
      // Port matches, check the list of host names.
      unsigned i;
      for (i=0; i<stack_data.name_cnt; ++i)
      {
        if (pj_stricmp(&host, &stack_data.name[i])==0)
        {
          /* Match */
          return PJ_TRUE;
        }
      }
    }
  }

  /* Doesn't match */
  return PJ_FALSE;
}
예제 #8
0
파일: pjutils.cpp 프로젝트: oldurecu/sprout
// Determine the default private ID for a public ID contained in a URI.  This
// is calculated as specified by the 3GPP specs by effectively stripping the
// scheme.
std::string PJUtils::default_private_id_from_uri(const pjsip_uri* uri)
{
  std::string id;
  if (PJSIP_URI_SCHEME_IS_SIP(uri) ||
      PJSIP_URI_SCHEME_IS_SIPS(uri))
  {
    pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri;
    if (sip_uri->user.slen > 0)
    {
      id = PJUtils::pj_str_to_string(&sip_uri->user) + "@" + PJUtils::pj_str_to_string(&sip_uri->host);
    }
    else
    {
      id = PJUtils::pj_str_to_string(&sip_uri->host);
    }
  }
  else if (PJSIP_URI_SCHEME_IS_TEL(uri))
  {
    id = PJUtils::pj_str_to_string(&((pjsip_tel_uri*)uri)->number) +
               "@" + PJUtils::pj_str_to_string(&stack_data.default_home_domain);
  }
  else
  {
    const pj_str_t* scheme = pjsip_uri_get_scheme(uri);
    LOG_WARNING("Unsupported scheme \"%.*s\" in To header when determining private ID - ignoring",
                scheme->slen, scheme->ptr);
  }

  return id;
}
예제 #9
0
/// Apply the mangalgorithm to the specified URI. The user part of the URI is
/// always mangled. The domain is mangled separately, and is only mangled if
/// mangelwurzel's change_domain flag is set, or if the function's
/// force_mangle_domain flag is set (we use this flag to make sure we always
/// mangle the domains for Routes and Record-Routes). We don't mangle
/// anything else (e.g. port number, SIP parameters).
void MangelwurzelTsx::mangle_uri(pjsip_uri* uri,
                                 pj_pool_t* pool,
                                 bool force_mangle_domain)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri;
    std::string user = PJUtils::pj_str_to_string(&sip_uri->user);
    mangle_string(user);
    sip_uri->user = pj_strdup3(pool, user.c_str());

    if ((force_mangle_domain) || (_config.change_domain))
    {
      std::string host = PJUtils::pj_str_to_string(&sip_uri->host);
      mangle_string(host);
      sip_uri->host = pj_strdup3(pool, host.c_str());
    }
  }
  else if (PJSIP_URI_SCHEME_IS_TEL(uri))
  {
    pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri;
    std::string number = PJUtils::pj_str_to_string(&tel_uri->number);
    mangle_string(number);
    tel_uri->number = pj_strdup3(pool, number.c_str());
  }
}
예제 #10
0
/// Utility to determine if URI contains a valid E.164 number
pj_bool_t PJUtils::is_e164(const pjsip_uri* uri)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    return PJUtils::is_e164(&((pjsip_sip_uri*)uri)->user);
  }
  return PJ_FALSE;
}
예제 #11
0
파일: pjutils.cpp 프로젝트: oldurecu/sprout
bool PJUtils::is_emergency_registration(pjsip_contact_hdr* contact_hdr)
{
  // Contact header must be a SIP URI
  pjsip_sip_uri* uri = (contact_hdr->uri != NULL) ?
                     (pjsip_sip_uri*)pjsip_uri_get_uri(contact_hdr->uri) : NULL;
  return ((uri != NULL) && (PJSIP_URI_SCHEME_IS_SIP(uri)) &&
          (pjsip_param_find(&uri->other_param, &STR_SOS) != NULL));
}
예제 #12
0
/// Utility to determine if this URI belongs to the home domain.
pj_bool_t PJUtils::is_home_domain(const pjsip_uri* uri)
{
  if ((PJSIP_URI_SCHEME_IS_SIP(uri)) &&
      (pj_stricmp(&((pjsip_sip_uri*)uri)->host, &stack_data.home_domain)==0))
  {
    return PJ_TRUE;
  }
  return PJ_FALSE;
}
예제 #13
0
파일: pjutils.cpp 프로젝트: oldurecu/sprout
/// Utility to determine if this URI belongs to the home domain.
pj_bool_t PJUtils::is_home_domain(const pjsip_uri* uri)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    std::string host = pj_str_to_string(&((pjsip_sip_uri*)uri)->host);
    return is_home_domain(host);
  }

  return PJ_FALSE;
}
예제 #14
0
/*! \brief Helper function which returns the SIP URI of a Contact header */
static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata)
{
	pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);

	if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		return NULL;
	}

	return pjsip_uri_get_uri(contact->uri);
}
static int get_from_header(pjsip_rx_data *rdata, char *username, size_t username_size, char *domain, size_t domain_size)
{
	pjsip_uri *from = rdata->msg_info.from->uri;
	pjsip_sip_uri *sip_from;
	if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) {
		return -1;
	}
	sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from);
	ast_copy_pj_str(username, &sip_from->user, username_size);
	ast_copy_pj_str(domain, &sip_from->host, domain_size);
	return 0;
}
예제 #16
0
파일: pjutils.cpp 프로젝트: oldurecu/sprout
/// Extract the domain from a SIP URI, or if its another type of URI, return
/// the default home domain.
pj_str_t PJUtils::domain_from_uri(const std::string& uri_str, pj_pool_t* pool)
{
  pjsip_uri* uri = PJUtils::uri_from_string(uri_str, pool);
  if (PJSIP_URI_SCHEME_IS_SIP(uri) ||
      PJSIP_URI_SCHEME_IS_SIPS(uri))
  {
    return ((pjsip_sip_uri*)uri)->host;
  }
  else
  {
    return stack_data.default_home_domain;
  }
}
예제 #17
0
static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
{
    const pj_str_t stateful_user = { "1", 1 };
    pjsip_uri *uri;
    pjsip_sip_uri *sip_uri;

    uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);

    /* Only want to receive SIP/SIPS scheme */
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
	return PJ_FALSE;

    sip_uri = (pjsip_sip_uri*) uri;

    /* Check for matching user part */
    if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
	return PJ_FALSE;

    /*
     * Yes, this is for us.
     * Respond statefully with 200/OK.
     */
    switch (rdata->msg_info.msg->line.req.method.id) {
    case PJSIP_INVITE_METHOD:
	{
	    pjsip_msg_body *body;

	    if (dummy_sdp_str.slen == 0)
		dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);

	    body = pjsip_msg_body_create(rdata->tp_info.pool, 
					 &mime_application, &mime_sdp, 
					 &dummy_sdp_str);
	    pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
				200, NULL, NULL, body, NULL);
	}
	break;
    case PJSIP_ACK_METHOD:
	return PJ_TRUE;
    default:
	pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
			    200, NULL, NULL, NULL, NULL);
	break;
    }

    app.server.cur_state.stateful_cnt++;
    return PJ_TRUE;
}
예제 #18
0
static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs)
{
	pjsip_sip_uri *sip_uri;
	char host[256];

	if (!contact || contact->star) {
		*addrs = NULL;
		return 0;
	}
	if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
		*addrs = NULL;
		return 0;
	}
	sip_uri = pjsip_uri_get_uri(contact->uri);
	ast_copy_pj_str(host, &sip_uri->host, sizeof(host));
	return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
}
예제 #19
0
/*!
 * \internal
 * \brief Determine where in the dialplan a call should go
 *
 * \details This uses the username in the request URI to try to match
 * an extension in an endpoint's context in order to route the call.
 *
 * \param rdata The SIP request
 * \param context The context to use
 * \param exten The extension to use
 */
static enum pjsip_status_code get_destination(const pjsip_rx_data *rdata, const char *context, char *exten)
{
	pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri;
	pjsip_sip_uri *sip_ruri;

	if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
		return PJSIP_SC_UNSUPPORTED_URI_SCHEME;
	}

	sip_ruri = pjsip_uri_get_uri(ruri);
	ast_copy_pj_str(exten, &sip_ruri->user, MAX_EXTEN_SIZE);

	if (ast_exists_extension(NULL, context, exten, 1, NULL)) {
		return PJSIP_SC_OK;
	}
	return PJSIP_SC_NOT_FOUND;
}
예제 #20
0
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
{
	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
	pjsip_uri *ruri;
	pjsip_sip_uri *sip_ruri;
	char exten[AST_MAX_EXTENSION];

	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) {
		return PJ_FALSE;
	}

	if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
		return PJ_FALSE;
	}

	ruri = rdata->msg_info.msg->line.req.uri;
	if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
		send_options_response(rdata, 416);
		return PJ_TRUE;
	}

	sip_ruri = pjsip_uri_get_uri(ruri);
	ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));

	/*
	 * We may want to match in the dialplan without any user
	 * options getting in the way.
	 */
	AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(exten);

	if (ast_shutting_down()) {
		/*
		 * Not taking any new calls at this time.
		 * Likely a server availability OPTIONS poll.
		 */
		send_options_response(rdata, 503);
	} else if (!ast_strlen_zero(exten)
		&& !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
		send_options_response(rdata, 404);
	} else {
		send_options_response(rdata, 200);
	}
	return PJ_TRUE;
}
예제 #21
0
/// Utility to determine if URI is local to this host.
pj_bool_t PJUtils::is_uri_local(const pjsip_uri* uri)
{
  if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    // Check the list of host names.
    pj_str_t host = ((pjsip_sip_uri*)uri)->host;
    unsigned i;
    for (i=0; i<stack_data.name_cnt; ++i)
    {
      if (pj_stricmp(&host, &stack_data.name[i])==0)
      {
        /* Match */
        return PJ_TRUE;
      }
    }
  }

  /* Doesn't match */
  return PJ_FALSE;
}
예제 #22
0
static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
{
	pjsip_contact_hdr *contact;

	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
	if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

		rewrite_uri(rdata, uri);

		if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
			|| pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
			dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
			dlg->target = dlg->remote.contact->uri;
		}
		return 0;
	}

	return -1;
}
예제 #23
0
static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
{
	pjsip_contact_hdr *contact;

	if (!endpoint) {
		return PJ_FALSE;
	}

	if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
		!contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
		pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);

		pj_cstr(&uri->host, rdata->pkt_info.src_name);
		if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
			uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
		} else {
			uri->transport_param.slen = 0;
		}
		uri->port = rdata->pkt_info.src_port;
		ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
			(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);

		/* rewrite the session target since it may have already been pulled from the contact header */
		if (dlg && (!dlg->remote.contact
			|| pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
			dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
			dlg->target = dlg->remote.contact->uri;
		}
	}

	if (endpoint->nat.force_rport) {
		rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
	}

	return PJ_FALSE;
}
예제 #24
0
void process_subscription_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);
  pjsip_msg *msg = rdata->msg_info.msg;
  pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
  int expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES;

  if (expiry > max_expires)
  {
    // Expiry is too long, set it to the maximum.
    expiry = max_expires;
  }

  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 subscribe request using invalid URI scheme");

    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0);
    // Can't log the public ID as the subscribe has failed too early
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_NOT_FOUND,
                               NULL,
                               NULL,
                               NULL);
    return;
  }

  bool emergency_subscription = false;

  pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)
                 pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);

  while (contact_hdr != NULL)
  {
    emergency_subscription = PJUtils::is_emergency_registration(contact_hdr);

    if (!emergency_subscription)
    {
      break;
    }

    contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
                                                          PJSIP_H_CONTACT,
                                                          contact_hdr->next);
  }

  if (emergency_subscription)
  {
    // Reject a subscription with a Contact header containing a contact address
    // that's been registered for emergency service.
    LOG_ERROR("Rejecting subscribe request from emergency registration");

    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0);
    // Can't log the public ID as the subscribe has failed too early
    SAS::report_event(event);

    // Allow-Events is a mandatory header on 489 responses.
    pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &STR_ALLOW_EVENTS, &STR_REG);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_BAD_EVENT,
                               NULL,
                               (pjsip_hdr*)allow_events_hdr,
                               NULL);
    return;
  }

  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 SUBSCRIBE for public ID %s", public_id.c_str());

  // Get the call identifier from the headers.
  std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);;

  // Add SAS markers to the trail attached to the message so the trail
  // becomes searchable.
  SAS::Event event(trail, SASEvent::SUBSCRIBE_START, 0);
  event.add_var_param(public_id);
  SAS::report_event(event);

  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;

  // Subscriber must have already registered to be making a subscribe
  std::string state;
  HTTPCode http_code = hss->get_registration_data(public_id, state, ifc_map, uris, trail);
  if ((http_code != HTTP_OK) || (state != "REGISTERED"))
  {
    // 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.

    // 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 SUBSCRIBE request");

    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("aor = %s", aor.c_str());
  LOG_DEBUG("SUBSCRIBE 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);

  // Write to the local store, checking the remote store if there is no entry locally. If the write to the local store succeeds, then write to the remote store.
  pjsip_tx_data* tdata_notify = NULL;
  RegStore::AoR* aor_data = NULL;
  std::string subscription_id;
  pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata,
                                                           now, NULL, remote_store,
                                                           &tdata_notify, &aor_data,
                                                           true, subscription_id,
                                                           trail);

  if (aor_data != NULL)
  {
    // Log the subscriptions.
    log_subscriptions(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)
    {
      RegStore::AoR* remote_aor_data = NULL;
      std::string ignore;
      write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL,
                                   &tdata_notify, &remote_aor_data, false, ignore,
                                   trail);
      delete remote_aor_data;
    }
  }
  else
  {
    // Failed to connect to the local store.  Reject the subscribe 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;
    // 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
    LOG_ERROR("Error building SUBSCRIBE %d response %s", st_code,
              PJUtils::pj_status_to_string(status).c_str());

    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Error building SUBSCRIBE (" + std::to_string(st_code) + ") " + PJUtils::pj_status_to_string(status);
    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
  }

  // Add expires headers
  pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(tdata->pool, expiry);
  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);

  // Add the to tag to the response
  pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(tdata->msg,
                                                        PJSIP_H_TO,
                                                        NULL);
  pj_strdup2(tdata->pool, &to->tag, subscription_id.c_str());

  // Pass the response to the ACR.
  acr->tx_response(tdata->msg);

  // Send the response.
  status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL);

  // Send the ACR and delete it.
  acr->send_message();
  delete acr;

  // Send the Notify
  if (tdata_notify != NULL && notify_status == PJ_SUCCESS)
  {
    set_trail(tdata_notify, trail);
    status = PJUtils::send_request(tdata_notify, 0, NULL, NULL, true);

    if (status != PJ_SUCCESS)
    {
      // LCOV_EXCL_START
      SAS::Event event(trail, SASEvent::NOTIFICATION_FAILED, 0);
      std::string error_msg = "Failed to send NOTIFY - error code: " + std::to_string(status);
      event.add_var_param(error_msg);
      SAS::report_event(event);
      // LCOV_EXCL_STOP
    }
  }

  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;
}
예제 #25
0
/// Write to the registration store.
pj_status_t write_subscriptions_to_store(RegStore* primary_store,      ///<store to write to
                                         std::string aor,              ///<address of record to write to
                                         pjsip_rx_data* rdata,         ///<received message to read headers from
                                         int now,                      ///<time now
                                         RegStore::AoR* backup_aor,    ///<backup data if no entry in store
                                         RegStore* backup_store,       ///<backup store to read from if no entry in store and no backup data
                                         pjsip_tx_data** tdata_notify, ///<tdata to construct a SIP NOTIFY from
                                         RegStore::AoR** aor_data,     ///<aor_data to write to
                                         bool update_notify,           ///<whether to generate a SIP NOTIFY
                                         std::string& subscription_id,
                                         SAS::TrailId trail)
{
  // Parse the 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;
  pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
  pjsip_fromto_hdr* from = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL);
  pjsip_fromto_hdr* to = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL);

  // The registration store 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.
  bool backup_aor_alloced = false;
  int expiry = 0;
  pj_status_t status = PJ_FALSE;
  (*aor_data) = NULL;

  do
  {
    // delete NULL is safe, so we can do this on every iteration.
    delete (*aor_data);

    // Find the current subscriptions for the AoR.
    (*aor_data) = primary_store->get_aor_data(aor, trail);
    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.
      // LCOV_EXCL_START - local store (used in testing) never fails
      LOG_ERROR("Failed to get AoR subscriptions for %s from store", aor.c_str());
      break;
      // LCOV_EXCL_STOP
    }

    // If we don't have any subscriptions, try the backup AoR and/or store.
    if ((*aor_data)->subscriptions().empty())
    {
      if ((backup_aor == NULL) &&
          (backup_store != NULL))
      {
        backup_aor = backup_store->get_aor_data(aor, trail);
        backup_aor_alloced = (backup_aor != NULL);
      }

      if ((backup_aor != NULL) &&
          (!backup_aor->subscriptions().empty()))
      {
        for (RegStore::AoR::Subscriptions::const_iterator i = backup_aor->subscriptions().begin();
             i != backup_aor->subscriptions().end();
             ++i)
        {
          RegStore::AoR::Subscription* src = i->second;
          RegStore::AoR::Subscription* dst = (*aor_data)->get_subscription(i->first);
          *dst = *src;
        }
      }
    }

    pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
    if (contact != NULL)
    {
      std::string contact_uri;
      pjsip_uri* uri = (contact->uri != NULL) ?
                       (pjsip_uri*)pjsip_uri_get_uri(contact->uri) :
                       NULL;

      if ((uri != NULL) &&
          (PJSIP_URI_SCHEME_IS_SIP(uri)))
      {
        contact_uri = PJUtils::uri_to_string(PJSIP_URI_IN_CONTACT_HDR, uri);
      }

      subscription_id = PJUtils::pj_str_to_string(&to->tag);

      if (subscription_id == "")
      {
        // If there's no to tag, generate an unique one
        subscription_id = std::to_string(Utils::generate_unique_integer(id_deployment, id_instance));
      }

      LOG_DEBUG("Subscription identifier = %s", subscription_id.c_str());

      // Find the appropriate subscription in the subscription list for this AoR. If it can't
      // be found a new empty subscription is created.
      RegStore::AoR::Subscription* subscription = (*aor_data)->get_subscription(subscription_id);

      // Update/create the subscription.
      subscription->_req_uri = contact_uri;

      subscription->_route_uris.clear();
      pjsip_route_hdr* route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, NULL);

      while (route_hdr)
      {
        std::string route = PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, route_hdr->name_addr.uri);
        LOG_DEBUG("Route header %s", route.c_str());
        // Add the route.
        subscription->_route_uris.push_back(route);
        // Look for the next header.
        route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, route_hdr->next);
      }

      subscription->_cid = cid;
      subscription->_to_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, to->uri);
      subscription->_to_tag = subscription_id;
      subscription->_from_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, from->uri);
      subscription->_from_tag = PJUtils::pj_str_to_string(&from->tag);

      // Calculate the expiry period for the subscription.
      expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES;

      if (expiry > max_expires)
      {
        // Expiry is too long, set it to the maximum.
        expiry = max_expires;
      }

      subscription->_expires = now + expiry;
      std::map<std::string, RegStore::AoR::Binding> bindings;

      for (RegStore::AoR::Bindings::const_iterator i = (*aor_data)->bindings().begin();
           i != (*aor_data)->bindings().end();
           ++i)
      {
        std::string id = i->first;
        RegStore::AoR::Binding bind = *(i->second);

        if (!bind._emergency_registration)
        {
          bindings.insert(std::pair<std::string, RegStore::AoR::Binding>(id, bind));
        }
      }

      if (update_notify)
      {
        NotifyUtils::SubscriptionState state = NotifyUtils::SubscriptionState::ACTIVE;

        if (expiry == 0)
        {
          state = NotifyUtils::SubscriptionState::TERMINATED;
        }

        status = NotifyUtils::create_notify(tdata_notify, subscription, aor,
                                            (*aor_data)->_notify_cseq, bindings,
                                            NotifyUtils::DocState::FULL,
                                            NotifyUtils::RegistrationState::ACTIVE,
                                            NotifyUtils::ContactState::ACTIVE,
                                            NotifyUtils::ContactEvent::REGISTERED,
                                            state, expiry);
      }

      if (analytics != NULL)
      {
        // Generate an analytics log for this subscription update.
        analytics->subscription(aor, subscription_id, contact_uri, expiry);
      }
    }
  }
  while (!primary_store->set_aor_data(aor, (*aor_data), false, trail));

  // If we allocated the backup AoR, tidy up.
  if (backup_aor_alloced)
  {
    delete backup_aor;
  }

  return status;
}
예제 #26
0
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;
}
예제 #27
0
파일: sip_reg.c 프로젝트: iamroger/voip
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
					        const pjsip_rx_data *rdata,
						unsigned *contact_cnt,
						unsigned max_contact,
						pjsip_contact_hdr *contacts[])
{
    pj_int32_t expiration = NOEXP;
    const pjsip_msg *msg = rdata->msg_info.msg;
    const pjsip_hdr *hdr;

    /* Enumerate all Contact headers in the response */
    *contact_cnt = 0;
    for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
	if (hdr->type == PJSIP_H_CONTACT && 
	    *contact_cnt < max_contact) 
	{
	    contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
	    ++(*contact_cnt);
	}
    }

    if (regc->current_op == REGC_REGISTERING) {
	pj_bool_t has_our_contact = PJ_FALSE;
	const pjsip_expires_hdr *expires;

	/* Get Expires header */
	expires = (const pjsip_expires_hdr*)
		  pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);

	/* Try to find the Contact URIs that we register, in the response
	 * to get the expires value. We'll try both with comparing the URI
	 * and comparing the extension param only.
	 */
	if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
	    unsigned i;
	    for (i=0; i<*contact_cnt; ++i) {
		const pjsip_contact_hdr *our_hdr;

		our_hdr = (const pjsip_contact_hdr*) 
			  regc->contact_hdr_list.next;

		/* Match with our Contact header(s) */
		while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {

		    const pjsip_uri *uri1, *uri2;
		    pj_bool_t matched = PJ_FALSE;

		    /* Exclude the display name when comparing the URI 
		     * since server may not return it.
		     */
		    uri1 = (const pjsip_uri*)
			   pjsip_uri_get_uri(contacts[i]->uri);
		    uri2 = (const pjsip_uri*)
			   pjsip_uri_get_uri(our_hdr->uri);

		    /* First try with exact matching, according to RFC 3261
		     * Section 19.1.4 URI Comparison
		     */
		    if (pjsip_cfg()->regc.check_contact) {
			matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
						uri1, uri2)==0;
		    }

		    /* If no match is found, try with matching the extension
		     * parameter only if extension parameter was added.
		     */
		    if (!matched && regc->add_xuid_param &&
			(PJSIP_URI_SCHEME_IS_SIP(uri1) ||
			 PJSIP_URI_SCHEME_IS_SIPS(uri1)) && 
			(PJSIP_URI_SCHEME_IS_SIP(uri2) ||
			 PJSIP_URI_SCHEME_IS_SIPS(uri2))) 
		    {
			const pjsip_sip_uri *sip_uri1, *sip_uri2;
			const pjsip_param *p1, *p2;
		
			sip_uri1 = (const pjsip_sip_uri*)uri1;
			sip_uri2 = (const pjsip_sip_uri*)uri2;

			p1 = pjsip_param_cfind(&sip_uri1->other_param,
					       &XUID_PARAM_NAME);
			p2 = pjsip_param_cfind(&sip_uri2->other_param,
					       &XUID_PARAM_NAME);
			matched = p1 && p2 &&
				  pj_strcmp(&p1->value, &p2->value)==0;

		    }

		    if (matched) {
			has_our_contact = PJ_TRUE;

			if (contacts[i]->expires >= 0 && 
			    contacts[i]->expires < expiration) 
			{
			    /* Get the lowest expiration time. */
			    expiration = contacts[i]->expires;
			}

			break;
		    }

		    our_hdr = our_hdr->next;

		} /* while ((void.. */

	    }  /* for (i=.. */

	    /* If matching Contact header(s) are found but the
	     * header doesn't contain expires parameter, get the
	     * expiration value from the Expires header. And
	     * if Expires header is not present, get the expiration
	     * value from the request.
	     */
	    if (has_our_contact && expiration == NOEXP) {
		if (expires) {
		    expiration = expires->ivalue;
		} else if (regc->expires_hdr) {
		    expiration = regc->expires_hdr->ivalue;
		} else {
		    /* We didn't request explicit expiration value,
		     * and server doesn't specify it either. This 
		     * shouldn't happen unless we have a broken
		     * registrar.
		     */
		    expiration = 3600;
		}
	    }

	}

	/* If we still couldn't get matching Contact header(s), it means
	 * there must be something wrong with the  registrar (e.g. it may
	 * have modified the URI's in the response, which is prohibited).
	 */
	if (expiration==NOEXP) {
	    /* If the number of Contact headers in the response matches 
	     * ours, they're all probably ours. Get the expiration
	     * from there if this is the case, or from Expires header
	     * if we don't have exact Contact header count, or
	     * from the request as the last resort.
	     */
	    pj_size_t our_contact_cnt;

	    our_contact_cnt = pj_list_size(&regc->contact_hdr_list);

	    if (*contact_cnt == our_contact_cnt && *contact_cnt &&
		contacts[0]->expires >= 0) 
	    {
		expiration = contacts[0]->expires;
	    } else if (expires)
		expiration = expires->ivalue;
	    else if (regc->expires_hdr)
		expiration = regc->expires_hdr->ivalue;
	    else
		expiration = 3600;
	}

    } else {
	/* Just assume that the unregistration has been successful. */
	expiration = 0;
    }

    /* Must have expiration value by now */
    pj_assert(expiration != NOEXP);

    return expiration;
}
예제 #28
0
파일: sip_reg.c 프로젝트: iamroger/voip
static pj_status_t set_contact( pjsip_regc *regc,
			        int contact_cnt,
				const pj_str_t contact[] )
{
    const pj_str_t CONTACT = { "Contact", 7 };
    pjsip_contact_hdr *h;
    int i;
    
    /* Save existing contact list to removed_contact_hdr_list and
     * clear contact_hdr_list.
     */
    pj_list_merge_last(&regc->removed_contact_hdr_list, 
		       &regc->contact_hdr_list);

    /* Set the expiration of Contacts in to removed_contact_hdr_list 
     * zero.
     */
    h = regc->removed_contact_hdr_list.next;
    while (h != &regc->removed_contact_hdr_list) {
	h->expires = 0;
	h = h->next;
    }

    /* Process new contacts */
    for (i=0; i<contact_cnt; ++i) {
	pjsip_contact_hdr *hdr;
	pj_str_t tmp;

	pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
	hdr = (pjsip_contact_hdr*)
              pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
	if (hdr == NULL) {
	    PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", 
		     (int)tmp.slen, tmp.ptr));
	    return PJSIP_EINVALIDURI;
	}

	/* Find the new contact in old contact list. If found, remove
	 * the old header from the old header list.
	 */
	h = regc->removed_contact_hdr_list.next;
	while (h != &regc->removed_contact_hdr_list) {
	    int rc;

	    rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, 
			       h->uri, hdr->uri);
	    if (rc == 0) {
		/* Match */
		pj_list_erase(h);
		break;
	    }

	    h = h->next;
	}

	/* If add_xuid_param option is enabled and Contact URI is sip/sips,
	 * add xuid parameter to assist matching the Contact URI in the 
	 * REGISTER response later.
	 */
	if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
				     PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) 
	{
	    pjsip_param *xuid_param;
	    pjsip_sip_uri *sip_uri;

	    xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
	    xuid_param->name = XUID_PARAM_NAME;
	    pj_create_unique_string(regc->pool, &xuid_param->value);

	    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
	    pj_list_push_back(&sip_uri->other_param, xuid_param);
	}

	pj_list_push_back(&regc->contact_hdr_list, hdr);
    }

    return PJ_SUCCESS;
}
예제 #29
0
// Determine the type of a URI.
//
// Parameters:
//
// - uri - the URI to classify
// - prefer_sip - for ambiguous URIs like sip:[email protected] (which could be a global phone
// number or just a SIP URI), prefer to interpret it as SIP
// - check_np - check for the presence of Number Portability parameters and
// classify accordingly
//
URIClass URIClassifier::classify_uri(const pjsip_uri* uri, bool prefer_sip, bool check_np)
{
  URIClass ret = URIClass::UNKNOWN;

  // First, check to see if this URI has number portability data - this takes priority
  bool has_rn = false;
  bool has_npdi = false;

  if (check_np)
  {
    if (PJSIP_URI_SCHEME_IS_TEL(uri))
    {
      // If the URI is a tel URI, pull out the information from the other_params
      has_rn = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_RN) != NULL);
      has_npdi = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_NPDI) != NULL);
    }
    else if (PJSIP_URI_SCHEME_IS_SIP(uri))
    {
      // If the URI is a tel URI, pull out the information from the userinfo_params
      has_rn = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_RN) != NULL);
      has_npdi = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_NPDI) != NULL);
    }
  }

  if (has_rn)
  {
    if (has_npdi)
    {
      ret = FINAL_NP_DATA;
    }
    else
    {
      ret = NP_DATA;
    }
  }
  // No number portability data
  else if (PJSIP_URI_SCHEME_IS_TEL(uri))
  {
    // TEL URIs can only represent phone numbers - decide if it's a global (E.164) number or not
    pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri;
    std::string user = PJUtils::pj_str_to_string(&tel_uri->number);
    boost::match_results<std::string::const_iterator> results;
    if (boost::regex_match(user, results, CHARS_ALLOWED_IN_GLOBAL_NUM))
    {
      ret = GLOBAL_PHONE_NUMBER;
    }
    else
    {
      ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER;
    }
  }
  else if (PJSIP_URI_SCHEME_IS_SIP(uri))
  {
    pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri;
    pj_str_t host = sip_uri->host;
    bool home_domain = is_home_domain(host);
    bool local_to_node = is_local_name(host);
    bool is_gruu = (pjsip_param_find(&((pjsip_sip_uri*)uri)->other_param, &STR_GR) != NULL);
    bool treat_number_as_phone = !enforce_user_phone && !prefer_sip;

    TRC_DEBUG("home domain: %s, local_to_node: %s, is_gruu: %s, enforce_user_phone: %s, prefer_sip: %s, treat_number_as_phone: %s",
              home_domain ? "true" : "false",
              local_to_node ? "true" : "false",
              is_gruu ? "true" : "false",
              enforce_user_phone ? "true" : "false",
              prefer_sip ? "true" : "false",
              treat_number_as_phone ? "true" : "false");

    bool classified = false;
    // SIP URI that's 'really' a phone number - apply the same logic as for TEL URIs
    if ((!pj_strcmp(&((pjsip_sip_uri*)uri)->user_param, &STR_USER_PHONE) ||
         (home_domain && treat_number_as_phone && !is_gruu)))
    {
      // Get the user part minus any parameters.
      std::string user = PJUtils::pj_str_to_string(&sip_uri->user);
      if (!user.empty())
      {
        std::vector<std::string> user_tokens;
        Utils::split_string(user, ';', user_tokens, 0, true);
        boost::match_results<std::string::const_iterator> results;
        if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_GLOBAL_NUM))
        {
          ret = GLOBAL_PHONE_NUMBER;
          classified = true;
        }
        else if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_LOCAL_NUM))
        {
          ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER;
          classified = true;
        }
      }
    }
    if (!classified)
    {
      // Not a phone number - classify it based on domain
      ret = (home_domain) ? HOME_DOMAIN_SIP_URI : ((local_to_node) ? NODE_LOCAL_SIP_URI : OFFNET_SIP_URI);
    }
  }

  std::string uri_str = PJUtils::uri_to_string(PJSIP_URI_IN_OTHER, uri);
  TRC_DEBUG("Classified URI %s as %d", uri_str.c_str(), (int)ret);
  return ret;
}
예제 #30
0
/*
 * This is an internal function to find the most appropriate account to be
 * used to handle incoming calls.
 */
void on_acc_find_for_incoming_wrapper(const pjsip_rx_data *rdata, pjsua_acc_id* out_acc_id)
{
    pjsip_uri *uri;
    pjsip_sip_uri *sip_uri;
    unsigned i;
    int current_matching_score = 0;
    int matching_scores[PJSUA_MAX_ACC];
    pjsua_acc_id best_matching = pjsua_var.default_acc;

    /* Check that there's at least one account configured */
    PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);

    uri = rdata->msg_info.to->uri;

    /* Just return if To URI is not SIP: */
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
    !PJSIP_URI_SCHEME_IS_SIPS(uri))
    {
    return;
    }


    PJSUA_LOCK();

    sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);

    /* Find account which has matching username and domain. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
        pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
    {
        /* Match ! */
        PJSUA_UNLOCK();
        *out_acc_id = acc_id;
        return;
    }
    }

    /* No exact matching, try fuzzy matching */
    pj_bzero(matching_scores, sizeof(matching_scores));

    /* No matching account, try match domain part only. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
        /* Match ! */
        /* We apply 100 weight if account has reg uri
         * Because in pragmatic case we are more looking
         * for these one than for the local acc
         */
        matching_scores[i] += (acc->cfg.reg_uri.slen > 0) ? (300 * sip_uri->host.slen) : 1;
    }
    }

    /* No matching account, try match user part (and transport type) only. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid) {
        /* We apply 100 weight if account has reg uri
         * Because in pragmatic case we are more looking
         * for these one than for the local acc
         */
        unsigned weight = (acc->cfg.reg_uri.slen > 0) ? 100 : 1;

        if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
        pjsip_transport_type_e type;
        type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
        if (type == PJSIP_TRANSPORT_UNSPECIFIED)
            type = PJSIP_TRANSPORT_UDP;

        if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
            continue;
        }
        /* Match ! */
        matching_scores[i] += (max_common_substr_len(&acc->user_part, &sip_uri->user) * weight);
    }
    }

    /* Still no match, use default account */
    PJSUA_UNLOCK();
    for(i=0; i<pjsua_var.acc_cnt; i++) {
        if(current_matching_score < matching_scores[i])
        {
            best_matching = pjsua_var.acc_ids[i];
            current_matching_score = matching_scores[i];
        }
    }
    *out_acc_id = best_matching;
}