/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY * requests. */ static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) { pjsip_msg *msg; pjsip_rx_data *rdata; /* Only want incoming message events. */ if (event->src_type != PJSIP_EVENT_RX_MSG) return; rdata = event->src.rdata; msg = rdata->msg; /* Only want to process request messages. */ if (msg->type != PJSIP_REQUEST_MSG) return; /* Only want the first notification. */ if (event->obj.tsx && event->obj.tsx->status_code >= 100) return; if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { /* Process incoming SUBSCRIBE request. */ on_subscribe_request( event->obj.tsx, rdata ); } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { /* Process incoming NOTIFY request. */ on_notify_request( event->obj.tsx, rdata ); } }
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; }
static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata) { if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) && pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) || !session->channel) { return; } rfc3326_add_reason_header(session, tdata); }
static int rfc3326_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { if ((pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method) && pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)) || !session->channel) { return 0; } rfc3326_use_reason_header(session, rdata); return 0; }
static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata) { if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) && pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) || !session->channel /* * The session->channel has been seen to go away on us between * checks so we must also be running under the call's serializer * thread. */ || session->serializer != ast_threadpool_serializer_get_current()) { return; } rfc3326_add_reason_header(session, tdata); }
static int rewrite_route_set(pjsip_rx_data *rdata, pjsip_dialog *dlg) { pjsip_rr_hdr *rr = NULL; pjsip_sip_uri *uri; if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) { pjsip_hdr *iter; for (iter = rdata->msg_info.msg->hdr.prev; iter != &rdata->msg_info.msg->hdr; iter = iter->prev) { if (iter->type == PJSIP_H_RECORD_ROUTE) { rr = (pjsip_rr_hdr *)iter; break; } } } else if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method)) { rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, NULL); } if (rr) { uri = pjsip_uri_get_uri(&rr->name_addr); rewrite_uri(rdata, uri); if (dlg && !pj_list_empty(&dlg->route_set) && !dlg->route_set_frozen) { pjsip_routing_hdr *route = dlg->route_set.next; uri = pjsip_uri_get_uri(&route->name_addr); rewrite_uri(rdata, uri); } return 0; } return -1; }
/* 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(); }
/* Notification on incoming request */ static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata) { /* Simple registrar */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) == 0) { simple_registrar(rdata); return PJ_TRUE; } return PJ_FALSE; }
/* Find subscription in the hash table. * If found, lock the subscription before returning to caller. */ static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) { pj_str_t key; pjsip_role_e role; pjsip_event_sub *sub; pjsip_method *method = &rdata->msg->line.req.method; pj_str_t *tag; if (rdata->msg->type == PJSIP_REQUEST_MSG) { if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { role = PJSIP_ROLE_UAS; tag = &rdata->to_tag; } else { pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); role = PJSIP_ROLE_UAC; tag = &rdata->to_tag; } } else { if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { role = PJSIP_ROLE_UAC; tag = &rdata->from_tag; } else { pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); role = PJSIP_ROLE_UAS; tag = &rdata->from_tag; } } create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); pj_mutex_lock(mgr.mutex); sub = pj_hash_get(mgr.ht, key.ptr, key.slen); if (sub) pj_mutex_lock(sub->mutex); pj_mutex_unlock(mgr.mutex); return sub; }
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; }
void UE::on_rx_request(pjsip_rx_data *rdata) { pjsip_msg* msg = rdata->msg_info.msg; pj_str_t message; stra(&message, "MESSAGE"); pjsip_method msg_method; pjsip_method_init(&msg_method, _pool, &message); if (pjsip_method_cmp(&msg_method, &msg->line.req.method) == 0) { std::string body((char*)msg->body->data, msg->body->len); pjsip_transaction* tsx; pjsip_endpt_respond(get_global_endpoint(), NULL, rdata, 200, NULL, NULL, NULL, &tsx); } else { pjsip_endpt_respond_stateless(get_global_endpoint(), rdata, 500, NULL, NULL, NULL); } }
/* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; content_type_e content_type = CONTENT_TYPE_NONE; pjsip_evsub *sub; pjsip_pres *pres; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "presence" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; i<accept->count; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { content_type = CONTENT_TYPE_PIDF; break; } else if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { content_type = CONTENT_TYPE_XPIDF; break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Treat as "application/pidf+xml" */ content_type = CONTENT_TYPE_PIDF; } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server presence subscription */ pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); pres->dlg = dlg; pres->sub = sub; pres->content_type = content_type; if (user_cb) pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
/* * Handler to receive incoming MESSAGE */ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) { pj_str_t from, to; pjsip_accept_hdr *accept_hdr; pjsip_msg *msg; msg = rdata->msg_info.msg; /* Only want to handle MESSAGE requests. */ if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) { return PJ_FALSE; } /* Should not have any transaction attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE); /* Should not have any dialog attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE); /* Check if we can accept the message. */ if (!pjsua_im_accept_pager(rdata, &accept_hdr)) { pjsip_hdr hdr_list; pj_list_init(&hdr_list); pj_list_push_back(&hdr_list, accept_hdr); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, &hdr_list, NULL); return PJ_TRUE; } /* Respond with 200 first, so that remote doesn't retransmit in case * the UI takes too long to process the message. */ pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL, NULL, NULL, NULL); /* For the source URI, we use Contact header if present, since * Contact header contains the port number information. If this is * not available, then use From header. */ from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from.ptr, PJSIP_MAX_URL_SIZE); if (from.slen < 1) from = pj_str("<--URI is too long-->"); /* Build the To text. */ to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.to->uri, to.ptr, PJSIP_MAX_URL_SIZE); if (to.slen < 1) to = pj_str("<--URI is too long-->"); /* Process pager. */ pjsua_im_process_pager(-1, &from, &to, rdata); /* Done. */ return PJ_TRUE; }
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) { RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup); struct rx_task_data *task_data; RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); pjsip_sip_uri *uri; char user_name[64], domain_name[64]; char *configured_aors, *aor_name; if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { return PJ_FALSE; } if (ast_strlen_zero(endpoint->aors)) { /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors"); ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received"); ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); ast_copy_pj_str(user_name, &uri->user, sizeof(user_name)); ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name)); configured_aors = ast_strdupa(endpoint->aors); /* Iterate the configured AORs to see if the user or the user+domain match */ while ((aor_name = strsep(&configured_aors, ","))) { char id[AST_UUID_STR_LEN]; RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup); snprintf(id, sizeof(id), "%s@%s", user_name, domain_name); if (!strcmp(aor_name, id)) { break; } if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain); if (!strcmp(aor_name, id)) { break; } } if (!strcmp(aor_name, user_name)) { break; } } if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!aor->max_contacts) { /* Registration is not permitted for this AOR */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted"); ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n", ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!(ser = serializer_find_or_create(aor_name))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (ast_sip_push_task(ser->serializer, rx_task, task_data)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); ao2_ref(task_data, -1); } 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, ®info)) { 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; }
static int msg_send(void *data) { RAII_VAR(struct msg_data *, mdata, data, ao2_cleanup); const struct ast_sip_body body = { .type = "text", .subtype = "plain", .body_text = ast_msg_get_body(mdata->msg) }; pjsip_tx_data *tdata; RAII_VAR(char *, uri, NULL, ast_free); RAII_VAR(struct ast_sip_endpoint *, endpoint, get_outbound_endpoint( mdata->to, &uri), ao2_cleanup); if (!endpoint) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint '%s' and " "no default outbound endpoint configured\n", mdata->to); return -1; } if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n"); return -1; } update_to(tdata, mdata->to); update_from(tdata, mdata->from); if (ast_sip_add_body(tdata, &body)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n"); return -1; } vars_to_headers(mdata->msg, tdata); ast_debug(1, "Sending message to '%s' (via endpoint %s) from '%s'\n", mdata->to, ast_sorcery_object_get_id(endpoint), mdata->from); if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n"); return -1; } return PJ_SUCCESS; } static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from) { struct msg_data *mdata; if (ast_strlen_zero(to)) { ast_log(LOG_ERROR, "SIP MESSAGE - a 'To' URI must be specified\n"); return -1; } if (!(mdata = msg_data_create(msg, to, from)) || ast_sip_push_task(message_serializer, msg_send, mdata)) { ao2_ref(mdata, -1); return -1; } return 0; } static const struct ast_msg_tech msg_tech = { .name = "pjsip", .msg_send = sip_msg_send, }; static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code code, pjsip_dialog *dlg, pjsip_transaction *tsx) { pjsip_tx_data *tdata; pj_status_t status; status = ast_sip_create_response(rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create response (%d)\n", status); return status; } if (dlg && tsx) { status = pjsip_dlg_send_response(dlg, tsx, tdata); } else { struct ast_sip_endpoint *endpoint; endpoint = ast_pjsip_rdata_get_endpoint(rdata); status = ast_sip_send_stateful_response(rdata, tdata, endpoint); ao2_cleanup(endpoint); } if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send response (%d)\n", status); } return status; } static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata) { enum pjsip_status_code code; struct ast_msg *msg; /* if not a MESSAGE, don't handle */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_message_method)) { return PJ_FALSE; } code = check_content_type(rdata); if (code != PJSIP_SC_OK) { send_response(rdata, code, NULL, NULL); return PJ_TRUE; } msg = ast_msg_alloc(); if (!msg) { send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL); return PJ_TRUE; } code = rx_data_to_ast_msg(rdata, msg); if (code != PJSIP_SC_OK) { send_response(rdata, code, NULL, NULL); ast_msg_destroy(msg); return PJ_TRUE; } if (!ast_msg_has_destination(msg)) { ast_debug(1, "MESSAGE request received, but no handler wanted it\n"); send_response(rdata, PJSIP_SC_NOT_FOUND, NULL, NULL); ast_msg_destroy(msg); return PJ_TRUE; } /* Send it to the messaging core. * * If we are unable to send a response, the most likely reason is that we * are handling a retransmission of an incoming MESSAGE and were unable to * create a transaction due to a duplicate key. If we are unable to send * a response, we should not queue the message to the dialplan */ if (!send_response(rdata, PJSIP_SC_ACCEPTED, NULL, NULL)) { ast_msg_queue(msg); } return PJ_TRUE; } static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { char buf[MAX_BODY_SIZE]; enum pjsip_status_code code; struct ast_frame f; pjsip_dialog *dlg = session->inv_session->dlg; pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); if (!session->channel) { send_response(rdata, PJSIP_SC_NOT_FOUND, dlg, tsx); return 0; } if ((code = check_content_type(rdata)) != PJSIP_SC_OK) { send_response(rdata, code, dlg, tsx); return 0; } if (print_body(rdata, buf, sizeof(buf)-1) < 1) { /* invalid body size */ send_response(rdata, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, dlg, tsx); return 0; } ast_debug(3, "Received in dialog SIP message\n"); memset(&f, 0, sizeof(f)); f.frametype = AST_FRAME_TEXT; f.subclass.integer = 0; f.offset = 0; f.data.ptr = buf; f.datalen = strlen(buf) + 1; ast_queue_frame(session->channel, &f); send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx); return 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; }
// 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, ®info)) { 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; }
/* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { const pjsip_method *method; pjsip_cached_auth *auth; pjsip_hdr added; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Init list */ pj_list_init(&added); /* Get the method. */ method = &tdata->msg->line.req.method; auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { /* Reset stale counter */ auth->stale_cnt = 0; if (auth->qop_value == PJSIP_AUTH_QOP_NONE) { # if defined(PJSIP_AUTH_HEADER_CACHING) && \ PJSIP_AUTH_HEADER_CACHING!=0 { pjsip_cached_auth_hdr *entry = auth->cached_hdr.next; while (entry != &auth->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); break; } entry = entry->next; } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { if (entry == &auth->cached_hdr) new_auth_for_req( tdata, sess, auth, NULL); } # endif } # elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { new_auth_for_req( tdata, sess, auth, NULL); } # endif } # if defined(PJSIP_AUTH_QOP_SUPPORT) && \ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; cred = auth_find_cred(sess, &auth->realm, &auth->last_chal->scheme); if (!cred) { auth = auth->next; continue; } status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ auth = auth->next; } if (sess->pref.initial_auth == PJ_FALSE) { pjsip_hdr *h; /* Don't want to send initial empty Authorization header, so * just send whatever available in the list (maybe empty). */ h = added.next; while (h != &added) { pjsip_hdr *next = h->next; pjsip_msg_add_hdr(tdata->msg, h); h = next; } } else { /* For each realm, add either the cached authorization header * or add an empty authorization header. */ unsigned i; char *uri_str; int len; uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, uri_str, PJSIP_MAX_URL_SIZE); if (len < 1 || len >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; i<sess->cred_cnt; ++i) { pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; h = get_header_for_realm(&added, &c->realm); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); } else { pjsip_authorization_hdr *hs; hs = pjsip_authorization_hdr_create(tdata->pool); pj_strdup(tdata->pool, &hs->scheme, &c->scheme); pj_strdup(tdata->pool, &hs->credential.digest.username, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup2(tdata->pool, &hs->credential.digest.uri, uri_str); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } return PJ_SUCCESS; }
/* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; }
/* * Handler when a transaction within a call has changed state. */ static void on_call_tsx_state(pjsua_call_id call_id, pjsip_transaction *tsx, pjsip_event *e) { const pjsip_method info_method = { PJSIP_OTHER_METHOD, { "INFO", 4 } }; if (pjsip_method_cmp(&tsx->method, &info_method)==0) { /* * Handle INFO method. */ if (tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || (tsx->state == PJSIP_TSX_STATE_TERMINATED && e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED))) { /* Status of outgoing INFO request */ if (tsx->status_code >= 200 && tsx->status_code < 300) { PJ_LOG(4,(THIS_FILE, "Call %d: DTMF sent successfully with INFO", call_id)); } else if (tsx->status_code >= 300) { PJ_LOG(4,(THIS_FILE, "Call %d: Failed to send DTMF with INFO: %d/%.*s", call_id, tsx->status_code, (int)tsx->status_text.slen, tsx->status_text.ptr)); } } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING) { /* Answer incoming INFO with 200/OK */ pjsip_rx_data *rdata; pjsip_tx_data *tdata; pj_status_t status; rdata = e->body.tsx_state.src.rdata; if (rdata->msg_info.msg->body) { status = pjsip_endpt_create_response(tsx->endpt, rdata, 200, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_tsx_send_msg(tsx, tdata); PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s", call_id, (int)rdata->msg_info.msg->body->len, rdata->msg_info.msg->body->data)); } else { status = pjsip_endpt_create_response(tsx->endpt, rdata, 400, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_tsx_send_msg(tsx, tdata); } } } }
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; }
static pj_bool_t distributor(pjsip_rx_data *rdata) { pjsip_dialog *dlg; struct distributor_dialog_data *dist = NULL; struct ast_taskprocessor *serializer = NULL; pjsip_rx_data *clone; if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) { /* * Ignore everything until we are fully booted. Let the * peer retransmit messages until we are ready. */ return PJ_TRUE; } dlg = find_dialog(rdata); if (dlg) { ast_debug(3, "Searching for serializer associated with dialog %s for %s\n", dlg->obj_name, pjsip_rx_data_get_info(rdata)); dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY); if (dist) { ao2_lock(dist); serializer = ao2_bump(dist->serializer); ao2_unlock(dist); if (serializer) { ast_debug(3, "Found serializer %s associated with dialog %s\n", ast_taskprocessor_name(serializer), dlg->obj_name); } } } if (serializer) { /* We have a serializer so we know where to send the message. */ } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) { ast_debug(3, "No dialog serializer for %s. Using request transaction as basis.\n", pjsip_rx_data_get_info(rdata)); serializer = find_request_serializer(rdata); if (!serializer) { /* * Pick a serializer for the unmatched response. * We couldn't determine what serializer originally * sent the request or the serializer is gone. */ serializer = ast_sip_get_distributor_serializer(rdata); } } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) { /* We have a BYE or CANCEL request without a serializer. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); ao2_cleanup(dist); return PJ_TRUE; } else { if (ast_taskprocessor_alert_get()) { /* * When taskprocessors get backed up, there is a good chance that * we are being overloaded and need to defer adding new work to * the system. To defer the work we will ignore the request and * rely on the peer's transport layer to retransmit the message. * We usually work off the overload within a few seconds. The * alternative is to send back a 503 response to these requests * and be done with it. */ ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n", pjsip_rx_data_get_info(rdata)); ao2_cleanup(dist); return PJ_TRUE; } /* Pick a serializer for the out-of-dialog request. */ serializer = ast_sip_get_distributor_serializer(rdata); } if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) { ast_taskprocessor_unreference(serializer); ao2_cleanup(dist); return PJ_TRUE; } if (dist) { ao2_lock(dist); clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint); ao2_unlock(dist); ao2_cleanup(dist); } if (ast_sip_push_task(serializer, distribute, clone)) { ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]); pjsip_rx_data_free_cloned(clone); } ast_taskprocessor_unreference(serializer); return PJ_TRUE; }
// 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, ®info)) { 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; }
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); } } }
/* * Create transferee (receiver of REFER request). * */ PJ_DEF(pj_status_t) pjsip_xfer_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_evsub *sub; pjsip_xfer *xfer; const pj_str_t STR_EVENT = {"Event", 5 }; pjsip_event_hdr *event_hdr; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is REFER */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())==0, PJSIP_ENOTREFER); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* The evsub framework expects an Event header in the request, * while a REFER request conveniently doesn't have one (pun intended!). * So create a dummy Event header. */ if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL)==NULL) { event_hdr = pjsip_event_hdr_create(rdata->tp_info.pool); event_hdr->event_type = STR_REFER; pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr*)event_hdr); } /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &xfer_user, rdata, PJSIP_EVSUB_NO_EVENT_ID, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server xfer subscription */ xfer = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_xfer); xfer->dlg = dlg; xfer->sub = sub; if (user_cb) pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
/* * Initialize new request message with authorization headers. * This function will put Authorization/Proxy-Authorization headers to the * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) * and the session has previously sent Authorization/Proxy-Authorization header * with the same method, then the same Authorization/Proxy-Authorization header * will be resent from the cache only if qop is not present. If the stack is * configured to automatically generate next Authorization/Proxy-Authorization * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- * Authorization headers are calculated and generated when they are not present * in the case or if authorization session has qop. * * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag * are not set, this function will do nothing. The stack then will only send * Authorization/Proxy-Authorization to respond 401/407 response. */ PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, pjsip_tx_data *tdata, pjsip_auth_session *sess_list, int cred_count, const pjsip_cred_info cred_info[]) { pjsip_auth_session *sess; pjsip_method *method = &tdata->msg->line.req.method; pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); if (!sess_list) return 0; sess = sess_list->next; while (sess != sess_list) { if (sess->qop_value == PJSIP_AUTH_QOP_NONE) { # if (PJSIP_AUTH_HEADER_CACHING) { pjsip_cached_auth_hdr *entry = sess->cached_hdr.next; while (entry != &sess->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); } else { # if (PJSIP_AUTH_AUTO_SEND_NEXT) { new_auth_for_req( tdata, sess_pool, sess, cred_count, cred_info); } # else { PJ_UNUSED_ARG(sess_pool); PJ_UNUSED_ARG(cred_count); PJ_UNUSED_ARG(cred_info); } # endif /* PJSIP_AUTH_AUTO_SEND_NEXT */ } entry = entry->next; } } # elif (PJSIP_AUTH_AUTO_SEND_NEXT) { new_auth_for_req( tdata, sess_pool, sess, cred_count, cred_info); } # else { PJ_UNUSED_ARG(sess_pool); PJ_UNUSED_ARG(cred_count); PJ_UNUSED_ARG(cred_info); } # endif /* PJSIP_AUTH_HEADER_CACHING */ } # if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm, &sess->last_chal->scheme); if (!cred) { sess = sess->next; continue; } hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess_pool, sess ); if (hauth) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); } } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ sess = sess->next; } return 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; }
/* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; pjsip_evsub *sub; pjsip_mwi *mwi; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "mwi" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_MWI) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; i<accept->count; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) { break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Assume client supports "application/simple-message-summary" */ } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server mwi subscription */ mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi); mwi->dlg = dlg; mwi->sub = sub; if (user_cb) pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool); mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
static int msg_send(void *data) { RAII_VAR(struct msg_data *, mdata, data, ao2_cleanup); const struct ast_sip_body body = { .type = "text", .subtype = "plain", .body_text = ast_msg_get_body(mdata->msg) }; pjsip_tx_data *tdata; RAII_VAR(char *, uri, NULL, ast_free); RAII_VAR(struct ast_sip_endpoint *, endpoint, get_outbound_endpoint( mdata->to, &uri), ao2_cleanup); if (!endpoint) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint and " "no default outbound endpoint configured\n"); return -1; } if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n"); return -1; } update_to(tdata, mdata->to); update_from(tdata, mdata->from); if (ast_sip_add_body(tdata, &body)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n"); return -1; } vars_to_headers(mdata->msg, tdata); if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n"); return -1; } return PJ_SUCCESS; } static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from) { struct msg_data *mdata; if (ast_strlen_zero(to)) { ast_log(LOG_ERROR, "SIP MESSAGE - a 'To' URI must be specified\n"); return -1; } if (!(mdata = msg_data_create(msg, to, from)) || ast_sip_push_task(NULL, msg_send, mdata)) { ao2_ref(mdata, -1); return -1; } return 0; } static const struct ast_msg_tech msg_tech = { .name = "pjsip", .msg_send = sip_msg_send, }; static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code code, pjsip_dialog *dlg, pjsip_transaction *tsx) { pjsip_tx_data *tdata; pj_status_t status; pjsip_response_addr res_addr; status = ast_sip_create_response(rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create response (%d)\n", status); return status; } if (dlg && tsx) { status = pjsip_dlg_send_response(dlg, tsx, tdata); } else { /* Get where to send request. */ status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status); return status; } status = ast_sip_send_response(&res_addr, tdata, ast_pjsip_rdata_get_endpoint(rdata)); } if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send response (%d)\n", status); } return status; } static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata) { enum pjsip_status_code code; struct ast_msg *msg; /* if not a MESSAGE, don't handle */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_message_method)) { return PJ_FALSE; } msg = ast_msg_alloc(); if (!msg) { send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL); return PJ_TRUE; } if ((code = check_content_type(rdata)) != PJSIP_SC_OK) { send_response(rdata, code, NULL, NULL); return PJ_TRUE; } if ((code = rx_data_to_ast_msg(rdata, msg)) == PJSIP_SC_OK) { /* send it to the dialplan */ ast_msg_queue(msg); code = PJSIP_SC_ACCEPTED; } send_response(rdata, code, NULL, NULL); return PJ_TRUE; } static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { char buf[MAX_BODY_SIZE]; enum pjsip_status_code code; struct ast_frame f; pjsip_dialog *dlg = session->inv_session->dlg; pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); if ((code = check_content_type(rdata)) != PJSIP_SC_OK) { send_response(rdata, code, dlg, tsx); return 0; } if (print_body(rdata, buf, sizeof(buf)-1) < 1) { /* invalid body size */ return 0; } ast_debug(3, "Received in dialog SIP message\n"); memset(&f, 0, sizeof(f)); f.frametype = AST_FRAME_TEXT; f.subclass.integer = 0; f.offset = 0; f.data.ptr = buf; f.datalen = strlen(buf) + 1; ast_queue_frame(session->channel, &f); send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx); return 0; }