/** @internal Create a handle for processing incoming request */ nua_handle_t *nua_stack_incoming_handle(nua_t *nua, nta_incoming_t *irq, sip_t const *sip, int create_dialog) { nua_handle_t *nh; url_t const *url; sip_to_t to[1]; sip_from_t from[1]; assert(sip && sip->sip_from && sip->sip_to); if (sip->sip_contact) url = sip->sip_contact->m_url; else url = sip->sip_from->a_url; /* Strip away parameters */ sip_from_init(from)->a_display = sip->sip_to->a_display; *from->a_url = *sip->sip_to->a_url; sip_to_init(to)->a_display = sip->sip_from->a_display; *to->a_url = *sip->sip_from->a_url; nh = nh_create(nua, NUTAG_URL((url_string_t *)url), /* Remote target */ SIPTAG_TO(to), /* Local AoR */ SIPTAG_FROM(from), /* Remote AoR */ TAG_END()); if (nh && nua_stack_init_handle(nua, nh, NULL) < 0) nh_destroy(nua, nh), nh = NULL; if (nh && create_dialog) { struct nua_dialog_state *ds = nh->nh_ds; nua_dialog_store_peer_info(nh, ds, sip); ds->ds_leg = nta_leg_tcreate(nua->nua_nta, nua_stack_process_request, nh, SIPTAG_CALL_ID(sip->sip_call_id), SIPTAG_FROM(sip->sip_to), SIPTAG_TO(sip->sip_from), NTATAG_REMOTE_CSEQ(sip->sip_cseq->cs_seq), TAG_END()); if (!ds->ds_leg || !nta_leg_tag(ds->ds_leg, nta_incoming_tag(irq, NULL))) nh_destroy(nua, nh), nh = NULL; } if (nh) nua_dialog_uas_route(nh, nh->nh_ds, sip, 1); return nh; }
/** * Create a new leg object for incoming request message. * * @param agent agent object * @param callback function which is called for each * incoming request belonging to this leg * @param magic call leg context * @param msg a request message * * @note The ownership of @a msg will pass back to NTA upon successful call * to the function nta_msg_leg(). In other words, if the call to @a * nta_msg_leg() is successful, the application may not do anything with @a * msg anymore. Instead of that, NTA will create of a new incoming request * object for the @a msg and eventually return the request to application by * calling the @a callback function. * * @deprecated Use nta_leg_stateful() instead. */ nta_leg_t *nta_msg_leg(nta_agent_t *agent, msg_t *msg, nta_request_f *callback, nta_leg_magic_t *magic, ...) { nta_leg_t *leg; sip_t *sip = sip_object(msg); SU_DEBUG_9(("\tnta_msg_leg(): called\n")); assert(msg && sip && sip->sip_request); if (!msg || !sip || !sip->sip_request || !callback) return NULL; leg = nta_leg_tcreate(agent, callback, magic, SIPTAG_CALL_ID(sip->sip_call_id), SIPTAG_FROM(sip->sip_to), /* local address */ SIPTAG_TO(sip->sip_from), /* remote address */ TAG_END()); if (!leg) /* xyzzy */; else if (nta_leg_server_route(leg, sip->sip_record_route, sip->sip_contact) < 0) nta_leg_destroy(leg), leg = NULL; else if (nta_leg_stateful(leg, msg) < 0) nta_leg_destroy(leg), leg = NULL; SU_DEBUG_9(("\tnta_msg_leg(): returns %p\n", leg)); return leg; }
/** * Create a new leg object * * @param agent agent object * @param callback function which is called for each * incoming request belonging to this leg * @param magic call leg context * @param i optional @CallID * (if @c NULL, an ID generated by @b NTA is used) * @param from optional @From (local address) * @param to optional @To (remote address) * @param extra optional extra header * @param headers va_list of optional extra headers * * @deprecated Use nta_leg_tcreate() instead. */ nta_leg_t *nta_leg_vcreate(nta_agent_t *agent, nta_request_f *callback, nta_leg_magic_t *magic, sip_call_id_t const *i, sip_from_t const *from, sip_to_t const *to, void const *extra, va_list headers) { sip_route_t const *route = NULL; sip_cseq_t const *cseq = NULL; for (; extra ; extra = va_arg(headers, void *)) { sip_header_t const *h = (sip_header_t const *)extra; if (h == SIP_NONE) continue; else if (sip_call_id_p(h)) { if (i == NULL) i = h->sh_call_id; } else if (sip_from_p(h)) { if (from == NULL) from = h->sh_from; } else if (sip_to_p(h)) { if (to == NULL) to = h->sh_to; } else if (sip_route_p(h)) { route = h->sh_route; } else if (sip_cseq_p(h)) { cseq = h->sh_cseq; } else { SU_DEBUG_3(("nta_leg_create: extra header %s\n", sip_header_name(h, 0))); } } return nta_leg_tcreate(agent, callback, magic, NTATAG_NO_DIALOG(i == SIP_NONE->sh_call_id), TAG_IF(i != SIP_NONE->sh_call_id, SIPTAG_CALL_ID(i)), TAG_IF(from != SIP_NONE->sh_from, SIPTAG_FROM(from)), TAG_IF(to != SIP_NONE->sh_to, SIPTAG_TO(to)), SIPTAG_ROUTE(route), SIPTAG_CSEQ(cseq), TAG_END()); }
/** Send a BYE to an INVITE. * * @deprecated * This function should used only if application requires * RFC2543 compatibility. */ nta_outgoing_t *nta_outgoing_tbye(nta_outgoing_t *orq, nta_response_f *callback, nta_outgoing_magic_t *magic, url_string_t const *route_url, tag_type_t tag, tag_value_t value, ...) { msg_t *msg; sip_t *sip, *inv; sip_cseq_t *cs; sip_request_t *rq; su_home_t *home; url_string_t *url; if (orq == NULL || orq->orq_method != sip_method_invite) return NULL; inv = sip_object(orq->orq_request); msg = nta_msg_create(orq->orq_agent, 0); home = msg_home(msg); sip = sip_object(msg); if (inv == NULL || sip == NULL) { msg_destroy(msg); return NULL; } sip_add_tl(msg, sip, SIPTAG_TO(inv->sip_to), SIPTAG_FROM(inv->sip_from), SIPTAG_CALL_ID(inv->sip_call_id), SIPTAG_ROUTE(inv->sip_route), TAG_END()); url = (url_string_t *)inv->sip_request->rq_url; rq = sip_request_create(home, SIP_METHOD_BYE, url, NULL); sip_header_insert(msg, sip, (sip_header_t*)rq); cs = sip_cseq_create(home, inv->sip_cseq->cs_seq + 1, SIP_METHOD_BYE); sip_header_insert(msg, sip, (sip_header_t*)cs); return nta_outgoing_mcreate(orq->orq_agent, callback, magic, route_url, msg); }
/** @internal Create a message template for keepalive. */ static int create_keepalive_message(outbound_t *ob, sip_t const *regsip) { msg_t *msg = nta_msg_create(ob->ob_nta, MSG_FLG_COMPACT), *previous; sip_t *osip = sip_object(msg); sip_contact_t *m = ob->ob_rcontact; unsigned d = ob->ob_keepalive.interval; if (msg == NULL) return -1; assert(regsip); assert(regsip->sip_request); if (m && m->m_params) { sip_accept_contact_t *ac; size_t i; int features = 0; ac = sip_accept_contact_make(msg_home(msg), "*;require;explicit"); for (i = 0; m->m_params[i]; i++) { char const *s = m->m_params[i]; if (!sip_is_callerpref(s)) continue; features++; s = su_strdup(msg_home(msg), s); msg_header_add_param(msg_home(msg), ac->cp_common, s); } if (features) msg_header_insert(msg, NULL, (void *)ac); else msg_header_free(msg_home(msg), (void *)ac); } if (0 > /* Duplicate essential headers from REGISTER request: */ sip_add_tl(msg, osip, SIPTAG_TO(regsip->sip_to), SIPTAG_FROM(regsip->sip_from), /* XXX - we should only use loose routing here */ /* XXX - if we used strict routing, the route header/request_uri must be restored */ SIPTAG_ROUTE(regsip->sip_route), /* Add Max-Forwards 0 */ TAG_IF(d, SIPTAG_MAX_FORWARDS_STR("0")), TAG_IF(d, SIPTAG_SUBJECT_STR("KEEPALIVE")), SIPTAG_CALL_ID_STR(ob->ob_cookie), SIPTAG_ACCEPT_STR(outbound_content_type), TAG_END()) || /* Create request-line, Call-ID, CSeq */ nta_msg_request_complete(msg, nta_default_leg(ob->ob_nta), SIP_METHOD_OPTIONS, (void *)regsip->sip_to->a_url) < 0 || msg_serialize(msg, (void *)osip) < 0 || msg_prepare(msg) < 0) return msg_destroy(msg), -1; previous = ob->ob_keepalive.msg; ob->ob_keepalive.msg = msg; msg_destroy(previous); return 0; }
/**Send a request message. * * @retval 0 if request is pending * @retval >=1 if error event has been sent * @retval < 0 if no error event has been sent */ static int nua_client_request_sendmsg(nua_client_request_t *cr) { nua_handle_t *nh = cr->cr_owner; nua_dialog_state_t *ds = nh->nh_ds; sip_method_t method = cr->cr_method; char const *name = cr->cr_method_name; url_string_t const *url = (url_string_t *)cr->cr_target; nta_leg_t *leg; msg_t *msg; sip_t *sip; int error; assert(cr->cr_orq == NULL); cr->cr_offer_sent = cr->cr_answer_recv = 0; cr->cr_offer_recv = cr->cr_answer_sent = 0; if (!ds->ds_leg && cr->cr_dialog) { ds->ds_leg = nta_leg_tcreate(nh->nh_nua->nua_nta, nua_stack_process_request, nh, SIPTAG_CALL_ID(cr->cr_sip->sip_call_id), SIPTAG_FROM(cr->cr_sip->sip_from), SIPTAG_TO(cr->cr_sip->sip_to), SIPTAG_CSEQ(cr->cr_sip->sip_cseq), TAG_END()); if (!ds->ds_leg) return -1; } if (cr->cr_sip->sip_from && ds->ds_leg) { if (cr->cr_sip->sip_from->a_tag == NULL) { if (sip_from_tag(msg_home(cr->cr_msg), cr->cr_sip->sip_from, nta_leg_tag(ds->ds_leg, NULL)) < 0) { return -1; } } } cr->cr_retry_count++; if (ds->ds_leg) leg = ds->ds_leg; else leg = nh->nh_nua->nua_dhandle->nh_ds->ds_leg; /* Default leg */ msg = msg_copy(cr->cr_msg), sip = sip_object(msg); if (msg == NULL) return -1; if (nua_dialog_is_established(ds)) { while (sip->sip_route) sip_route_remove(msg, sip); } else if (!ds->ds_route) { sip_route_t *initial_route = NH_PGET(nh, initial_route); if (initial_route) { initial_route = sip_route_dup(msg_home(msg), initial_route); if (!initial_route) return -1; msg_header_prepend(msg, (msg_pub_t*)sip, /* This should be (msg_header_t **)&sip->sip_route * but directly casting pointer &sip->sip_route gives * spurious type-punning warning */ (msg_header_t **)((char *)sip + offsetof(sip_t, sip_route)), (msg_header_t *)initial_route); } } /** * For in-dialog requests, the request URI is taken from the @Contact * header received from the remote party during dialog establishment, * and the NUTAG_URL() is ignored. * * Also, the @CallID and @CSeq headers and @From and @To tags are * generated based on the dialog information and added to the request. * If the dialog has a route, it is added to the request, too. */ if (nta_msg_request_complete(msg, leg, method, name, url) < 0) { msg_destroy(msg); return -1; } /**@MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is * also added now, if it does not exist. */ if (!ds->ds_remote) ds->ds_remote = sip_to_dup(nh->nh_home, sip->sip_to); if (!ds->ds_local) ds->ds_local = sip_from_dup(nh->nh_home, sip->sip_from); /** * Next, values previously set with nua_set_params() or nua_set_hparams() * are used: @Allow, @Supported, @Organization, @UserAgent and * @AllowEvents headers are added to the request if they are not already * set. */ if (!sip->sip_allow) sip_add_dup(msg, sip, (sip_header_t*)NH_PGET(nh, allow)); if (!sip->sip_supported && NH_PGET(nh, supported)) sip_add_dup(msg, sip, (sip_header_t *)NH_PGET(nh, supported)); if (method == sip_method_register && NH_PGET(nh, path_enable) && !sip_has_feature(sip->sip_supported, "path") && !sip_has_feature(sip->sip_require, "path")) sip_add_make(msg, sip, sip_supported_class, "path"); if (!sip->sip_organization && NH_PGET(nh, organization)) sip_add_make(msg, sip, sip_organization_class, NH_PGET(nh, organization)); if (!sip->sip_user_agent && NH_PGET(nh, user_agent)) sip_add_make(msg, sip, sip_user_agent_class, NH_PGET(nh, user_agent)); /** Any node implementing one or more event packages SHOULD include an * appropriate @AllowEvents header indicating all supported events in * all methods which initiate dialogs and their responses (such as * INVITE) and OPTIONS responses. */ if (!sip->sip_allow_events && NH_PGET(nh, allow_events) && (method == sip_method_notify || /* Always in NOTIFY */ (!ds->ds_remote_tag && /* And in initial requests */ (method == sip_method_subscribe || method == sip_method_refer || method == sip_method_options || method == sip_method_invite)))) sip_add_dup(msg, sip, (void *)NH_PGET(nh, allow_events)); /** * Next, the stack generates a @Contact header for the request (unless * the application already gave a @Contact header or it does not want to * use @Contact and indicates that by including SIPTAG_CONTACT(NULL) or * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the * application has registered the URI in @From header, the @Contact * header used with registration is used. Otherwise, the @Contact header * is generated from the local IP address and port number. */ /**For the initial requests, @ServiceRoute set that was received from the * registrar is also added to the request message. */ if (cr->cr_method != sip_method_register) { if (cr->cr_contactize && cr->cr_has_contact) { sip_contact_t *ltarget = sip_contact_dup(nh->nh_home, sip->sip_contact); if (ds->ds_ltarget) msg_header_free(nh->nh_home, (msg_header_t *)ds->ds_ltarget); ds->ds_ltarget = ltarget; } if (ds->ds_ltarget && !cr->cr_has_contact) sip_add_dup(msg, sip, (sip_header_t *)ds->ds_ltarget); if (nua_registration_add_contact_to_request(nh, msg, sip, cr->cr_contactize && !cr->cr_has_contact && !ds->ds_ltarget, !ds->ds_route) < 0) { msg_destroy(msg); return -1; } } cr->cr_wait_for_cred = 0; if (cr->cr_methods->crm_send) error = cr->cr_methods->crm_send(cr, msg, sip, NULL); else error = nua_base_client_request(cr, msg, sip, NULL); if (error == -1) msg_destroy(msg); return error; }
int DrachtioController::processRequestOutsideDialog( nta_leg_t* defaultLeg, nta_incoming_t* irq, sip_t const *sip) { DR_LOG(log_debug) << "processRequestOutsideDialog" << endl ; int rc = validateSipMessage( sip ) ; if( 0 != rc ) { return rc ; } switch (sip->sip_request->rq_method ) { case sip_method_invite: { /* TODO: should support optional config to only allow invites from defined addresses */ /* system-wide minimum session-expires is 90 seconds */ if( sip->sip_session_expires && sip->sip_session_expires->x_delta < 90 ) { nta_incoming_treply( irq, SIP_422_SESSION_TIMER_TOO_SMALL, SIPTAG_MIN_SE_STR("90"), TAG_END() ) ; return 0; } string transactionId ; generateUuid( transactionId ) ; nta_incoming_treply( irq, SIP_100_TRYING, TAG_END() ) ; if( !m_pClientController->route_request_outside_dialog( irq, sip, transactionId ) ) { DR_LOG(log_error) << "No providers available for invite" << endl ; return 503 ; } nta_leg_t* leg = nta_leg_tcreate(m_nta, legCallback, this, SIPTAG_CALL_ID(sip->sip_call_id), SIPTAG_CSEQ(sip->sip_cseq), SIPTAG_TO(sip->sip_from), SIPTAG_FROM(sip->sip_to), TAG_END()); if( NULL == leg ) { DR_LOG(log_error) << "Error creating a leg for origination" << endl ; //TODO: we got a client out there with a dead INVITE now... return 500 ; } boost::shared_ptr<SipDialog> dlg = boost::make_shared<SipDialog>( leg, irq, sip ) ; dlg->setTransactionId( transactionId ) ; string contactStr ; generateOutgoingContact( sip->sip_contact, contactStr ) ; nta_leg_server_route( leg, sip->sip_record_route, sip->sip_contact ) ; m_pDialogController->addIncomingInviteTransaction( leg, irq, sip, transactionId, dlg ) ; } break ; case sip_method_ack: /* success case: call has been established */ nta_incoming_destroy( irq ) ; return 0 ; case sip_method_register: case sip_method_message: case sip_method_options: case sip_method_notify: { string transactionId ; generateUuid( transactionId ) ; if( !m_pClientController->route_request_outside_dialog( irq, sip, transactionId ) ) { DR_LOG(log_error) << "No providers available for register" << endl ; return 503 ; } m_pDialogController->addIncomingRequestTransaction( irq, transactionId ) ; return 0 ; } case sip_method_bye: case sip_method_cancel: DR_LOG(log_error) << "Received BYE or CANCEL for unknown dialog: " << sip->sip_call_id->i_id << endl ; return 481 ; default: DR_LOG(log_error) << "DrachtioController::processRequestOutsideDialog - unsupported method type: " << sip->sip_request->rq_method_name << ": " << sip->sip_call_id->i_id << endl ; return 501 ; break ; } return 0 ; }