/** Process response to REGISTER request */ int outbound_register_response(outbound_t *ob, int terminating, sip_t const *request, sip_t const *response) { int status, reregister; if (!ob) return 0; if (terminating) { ob->ob_registering = ob->ob_registered = 0; return 0; /* Cleanup is done separately */ } if (!response || !request) return 0; assert(request->sip_request); assert(response->sip_status); status = response->sip_status->st_status; if (status < 300) { if (request->sip_contact && response->sip_contact) { sip_contact_t *m; if (ob->ob_rcontact != NULL) { msg_header_free(ob->ob_home, (msg_header_t *)ob->ob_rcontact); ob->ob_rcontact = NULL; } /* Ignore the contacts that were unregistered, if any */ for (m = request->sip_contact; m; m = m->m_next) { if (!m->m_expires || strtoul(m->m_expires, NULL, 10) != 0) break; } assert (!ob->ob_registering || m != NULL); if (m) ob->ob_rcontact = (sip_contact_t *) msg_header_dup_one(ob->ob_home, (const msg_header_t *)m); ob->ob_registered = ob->ob_registering; } else ob->ob_registered = 0; } reregister = outbound_check_for_nat(ob, request, response); if (reregister) return reregister; if (ob->ob_previous && status < 300) { msg_header_free(ob->ob_home, (void *)ob->ob_previous); ob->ob_previous = NULL; } return 0; }
static int nua_refer_server_preprocess(nua_server_request_t *sr) { nua_handle_t *nh = sr->sr_owner; sip_t const *sip = sr->sr_request.sip; struct notifier_usage *nu; sip_event_t *o; if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id)) o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq); else o = sip_event_make(nh->nh_home, "refer"); if (o) { sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o); msg_header_free(nh->nh_home, (msg_header_t *)o); } if (!sr->sr_usage) return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); nu = nua_dialog_usage_private(sr->sr_usage); nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires); return 0; }
int s2_save_register(struct message *rm) { sip_contact_t *contact, *m, **m_prev; sip_expires_t const *ex; sip_date_t const *date; sip_time_t now = rm->when.tv_sec, expires; msg_header_free_all(s2->home, (msg_header_t *)s2->registration->aor); msg_header_free_all(s2->home, (msg_header_t *)s2->registration->contact); tport_unref(s2->registration->tport); s2->registration->aor = NULL; s2->registration->contact = NULL; s2->registration->tport = NULL; if (rm == NULL) return 0; assert(rm && rm->sip && rm->sip->sip_request); assert(rm->sip->sip_request->rq_method == sip_method_register); ex = rm->sip->sip_expires; date = rm->sip->sip_date; contact = sip_contact_dup(s2->home, rm->sip->sip_contact); for (m_prev = &contact; *m_prev;) { m = *m_prev; expires = sip_contact_expires(m, ex, date, s2_default_registration_duration, now); if (expires) { char *p = su_sprintf(s2->home, "expires=%lu", (unsigned long)expires); msg_header_add_param(s2->home, m->m_common, p); m_prev = &m->m_next; } else { *m_prev = m->m_next; m->m_next = NULL; msg_header_free(s2->home, (msg_header_t *)m); } } if (contact == NULL) return 0; s2->registration->aor = sip_to_dup(s2->home, rm->sip->sip_to); s2->registration->contact = contact; s2->registration->tport = tport_ref(rm->tport); s2sip->sut.aor = s2->registration->aor; s2sip->sut.contact = s2->registration->contact; s2sip->sut.tport = s2->registration->tport; return 0; }
/** Remove dialog information. */ int nua_dialog_zap(nua_owner_t *own, nua_dialog_state_t *ds) { /* zap peer info */ nua_dialog_store_peer_info(own, ds, NULL); /* Local Contact */ msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL; /* Leg */ nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL; /* Remote tag */ su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL; /* Ready to set route/remote target */ ds->ds_route = 0; return 0; }
/** @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; }
/** Convert "gruu" parameter returned by registrar to Contact header. */ int outbound_gruuize(outbound_t *ob, sip_t const *sip) { sip_contact_t *m = NULL; char *gruu; if (!ob) return 0; if (ob->ob_rcontact == NULL) return -1; if (!ob->ob_prefs.gruuize && ob->ob_instance) { char const *my_instance, *my_reg_id = NULL; char const *instance, *reg_id; m = ob->ob_rcontact; my_instance = msg_header_find_param(m->m_common, "+sip.instance="); if (my_instance) my_reg_id = msg_header_find_param(m->m_common, "reg-id="); for (m = sip->sip_contact; m; m = m->m_next) { if (my_instance) { instance = msg_header_find_param(m->m_common, "+sip.instance="); if (!instance || strcmp(instance, my_instance)) continue; if (my_reg_id) { reg_id = msg_header_find_param(m->m_common, "reg-id="); if (!reg_id || strcmp(reg_id, my_reg_id)) continue; } } if (url_cmp_all(ob->ob_rcontact->m_url, m->m_url) == 0) break; } } if (m == NULL) { if (ob->ob_gruu) msg_header_free(ob->ob_home, (void *)ob->ob_gruu), ob->ob_gruu = NULL; return 0; } gruu = (char *)msg_header_find_param(m->m_common, "pub-gruu="); if (gruu == NULL || gruu[0] == '\0') gruu = (char *)msg_header_find_param(m->m_common, "gruu="); if (gruu == NULL || gruu[0] == '\0') return 0; gruu = msg_unquote_dup(NULL, gruu); m = gruu ? sip_contact_format(ob->ob_home, "<%s>", gruu) : NULL; su_free(NULL, gruu); if (!m) return -1; if (ob->ob_gruu) msg_header_free(ob->ob_home, (void *)ob->ob_gruu); ob->ob_gruu = m; return 0; }
/**Set new contact. * * @retval 0 when successful * @retval -1 error setting contact */ int outbound_set_contact(outbound_t *ob, sip_contact_t const *application_contact, sip_via_t const *v, int terminating) { su_home_t *home = ob->ob_home; sip_contact_t *rcontact = NULL, *dcontact = NULL, *previous = NULL; sip_contact_t *m1, *m2, *m3; int contact_uri_changed = 0; m1 = ob->ob_rcontact; m2 = ob->ob_dcontact; m3 = ob->ob_previous; if (terminating) { if (ob->ob_by_stack && application_contact == NULL) return 0; if (ob->ob_contacts) previous = ob->ob_rcontact; } else if (application_contact) { rcontact = sip_contact_dup(home, application_contact); if (!ob->ob_rcontact || url_cmp_all(ob->ob_rcontact->m_url, application_contact->m_url)) { contact_uri_changed = 1; previous = ob->ob_contacts ? ob->ob_rcontact : NULL; } } else if (ob->ob_by_stack) { return 0; /* Xyzzy - nothing happens */ } else if (v) { char const *tport = !v->v_next ? v->v_protocol : NULL; char reg_id_param[20]; dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1, v, tport, NULL); if (!dcontact) return -1; if (ob->ob_instance && ob->ob_reg_id != 0) snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id); rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0, v, v->v_protocol, ob->ob_instance, reg_id_param, NULL); if (!rcontact) return -1; if (!ob->ob_rcontact || url_cmp_all(ob->ob_rcontact->m_url, rcontact->m_url)) { contact_uri_changed = 1; previous = ob->ob_contacts ? ob->ob_rcontact : NULL; } } if (previous) msg_header_replace_param(home, (msg_common_t *)previous, "expires=0"); ob->ob_by_stack = application_contact == NULL; ob->ob_contacts = rcontact != NULL; ob->ob_rcontact = rcontact; ob->ob_dcontact = dcontact; ob->ob_previous = previous; if (contact_uri_changed) { ob->ob_registering = 0; ob->ob_registered = 0; ob->ob_validated = 0; ob->ob_once_validated = 0; } if (m1 != previous) msg_header_free(home, (void *)m1); if (m2 != m1 && m2 != m3) msg_header_free(home, (void *)m2); msg_header_free(home, (void *)m3); return 0; }
/**@internal * Create contacts for outbound. * * There are two contacts: * one suitable for registrations (ob_rcontact) and another that can be used * in dialogs (ob_dcontact). */ int outbound_contacts_from_via(outbound_t *ob, sip_via_t const *via) { su_home_t *home = ob->ob_home; sip_contact_t *rcontact, *dcontact; char reg_id_param[20] = ""; sip_contact_t *previous_previous, *previous_rcontact, *previous_dcontact; sip_via_t *v, v0[1], *previous_via; int contact_uri_changed; if (!via) return -1; v = v0; *v0 = *via; v0->v_next = NULL; dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1, v, v->v_protocol, NULL); if (ob->ob_instance && ob->ob_reg_id != 0) snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id); rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0, v, v->v_protocol, ob->ob_instance, reg_id_param, NULL); v = sip_via_dup(home, v); if (!rcontact || !dcontact || !v) { msg_header_free(home, (void *)dcontact); if (rcontact != dcontact) msg_header_free(home, (void *)rcontact); msg_header_free(home, (void *)v); return -1; } contact_uri_changed = !ob->ob_rcontact || url_cmp_all(ob->ob_rcontact->m_url, rcontact->m_url); if (contact_uri_changed) { previous_previous = ob->ob_previous; previous_dcontact = ob->ob_dcontact; previous_via = ob->ob_via; if (ob->ob_registered /* && (ob->ob_reg_id == 0 || ob->ob_info.outbound < outbound_feature_supported) * XXX - multiple connections not yet supported */) previous_rcontact = NULL, ob->ob_previous = ob->ob_rcontact; else previous_rcontact = ob->ob_rcontact, ob->ob_previous = NULL; if (ob->ob_previous) msg_header_replace_param(home, (void*)ob->ob_previous, "expires=0"); } else { previous_previous = ob->ob_rcontact; previous_rcontact = NULL; previous_dcontact = ob->ob_dcontact; previous_via = ob->ob_via; } ob->ob_contacts = 1; ob->ob_rcontact = rcontact; ob->ob_dcontact = dcontact; ob->ob_via = v; if (contact_uri_changed) { ob->ob_registering = 0; ob->ob_registered = 0; ob->ob_validated = 0; } msg_header_free(home, (void *)previous_rcontact); msg_header_free(home, (void *)previous_previous); if (previous_dcontact != ob->ob_previous && previous_dcontact != previous_rcontact && previous_dcontact != previous_previous) msg_header_free(home, (void *)previous_dcontact); msg_header_free(home, (void *)previous_via); 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; }