📄 nua_register.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE nua_register.c * @brief REGISTER and registrations * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * * @date Created: Wed Mar 8 11:48:49 EET 2006 ppessi */#include "config.h"#include <sofia-sip/string0.h>#include <sofia-sip/su_strlst.h>#include <sofia-sip/su_uniqueid.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/sip_protos.h>#include <sofia-sip/sip_util.h>#include <sofia-sip/sip_status.h>#define NTA_LEG_MAGIC_T struct nua_handle_s#define NTA_OUTGOING_MAGIC_T struct nua_handle_s#define NTA_UPDATE_MAGIC_T struct nua_s#include "nua_stack.h"#include <sofia-sip/hostdomain.h>#include <sofia-sip/nta_tport.h>#include <sofia-sip/tport.h>#include <sofia-sip/tport_tag.h>#define OUTBOUND_OWNER_T struct nua_handle_s#include "outbound.h"#if HAVE_SIGCOMP#include <sigcomp.h>#endif#include <stddef.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <assert.h>#if !defined(random) && defined(_WIN32)#define random rand#endif/* ======================================================================== *//* Registrations and contacts */int nua_registration_from_via(nua_registration_t **list, su_home_t *home, sip_via_t const *via, int public);int nua_registration_add(nua_registration_t **list, nua_registration_t *nr);void nua_registration_remove(nua_registration_t *nr);int nua_registration_set_aor(su_home_t *, nua_registration_t *nr, sip_from_t const *aor);int nua_registration_set_contact(su_home_t *, nua_registration_t *nr, sip_contact_t const *m, int terminating);void nua_registration_set_ready(nua_registration_t *nr, int ready);/* ====================================================================== *//* REGISTER usage */static char const *nua_register_usage_name(nua_dialog_usage_t const *du);static int nua_register_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static void nua_register_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du);static void nua_register_usage_peer_info(nua_dialog_usage_t *du, nua_dialog_state_t const *ds, sip_t const *sip);/** REGISTER usage, aka nua_registration_t */struct register_usage { nua_registration_t *nr_next, **nr_prev, **nr_list; /* Doubly linked list and its head */ sip_from_t *nr_aor; /**< AoR for this registration, NULL if none */ sip_contact_t *nr_contact; /**< Our Contact */ /** Status of registration */ unsigned nr_ready:1; /** Kind of registration. * * If nr_default is true, this is not a real registration but placeholder * for Contact header derived from a transport address. * * If nr_secure is true, this registration supports SIPS/TLS. * * If nr_public is true, transport should have public address. */ unsigned nr_default:1, nr_secure:1, nr_public:1; /** Stack-generated contact */ unsigned nr_by_stack:1, :0; sip_route_t *nr_route; /**< Outgoing Service-Route */ sip_path_t *nr_path; /**< Incoming Path */ tport_t *nr_tport; /**< Transport to be used when registered */ nua_dialog_state_t *nr_dialogs; /**< List of our dialogs */#if HAVE_SIGCOMP struct sigcomp_compartment *nr_compartment;#endif outbound_t *nr_ob; /**< Outbound connection */};nua_usage_class const nua_register_usage[1] = { { sizeof (struct register_usage), (sizeof nua_register_usage), nua_register_usage_add, nua_register_usage_remove, nua_register_usage_name, nua_register_usage_peer_info, }};static char const *nua_register_usage_name(nua_dialog_usage_t const *du){ return "register";}static int nua_register_usage_add(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du){ nua_registration_t *nr = nua_dialog_usage_private(du); if (ds->ds_has_register) return -1; /* There can be only one usage */ ds->ds_has_register = 1; nr->nr_public = 1; /* */ return 0;}static void nua_register_usage_remove(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du){ nua_registration_t *nr = nua_dialog_usage_private(du); if (nr->nr_list) nua_registration_remove(nr); /* Remove from list of registrations */ if (nr->nr_ob) outbound_unref(nr->nr_ob);#if HAVE_SIGCOMP if (nr->nr_compartment) sigcomp_compartment_unref(nr->nr_compartment); nr->nr_compartment = NULL;#endif ds->ds_has_register = 0; /* There can be only one */}/** @internal Store information about registrar. */static void nua_register_usage_peer_info(nua_dialog_usage_t *du, nua_dialog_state_t const *ds, sip_t const *sip){ nua_registration_t *nr = nua_dialog_usage_private(du); if (nr->nr_ob) outbound_peer_info(nr->nr_ob, sip);}/* ======================================================================== *//* REGISTER */static void restart_register(nua_handle_t *nh, tagi_t *tags);static void refresh_register(nua_handle_t *, nua_dialog_usage_t *, sip_time_t);static int process_response_to_register(nua_handle_t *nh, nta_outgoing_t *orq, sip_t const *sip);static void unregister_expires_contacts(msg_t *msg, sip_t *sip);/* Interface towards outbound_t */static int nua_stack_outbound_features(nua_handle_t *nh, outbound_t *ob);static int nua_stack_outbound_refresh(nua_handle_t *, outbound_t *ob);static int nua_stack_outbound_status(nua_handle_t *, outbound_t *ob, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...);static int nua_stack_outbound_failed(nua_handle_t *, outbound_t *ob, int status, char const *phrase, tag_type_t tag, tag_value_t value, ...);static int nua_stack_outbound_credentials(nua_handle_t *, auth_client_t **auc);outbound_owner_vtable nua_stack_outbound_callbacks = { sizeof nua_stack_outbound_callbacks, nua_stack_outbound_refresh, nua_stack_outbound_status, nua_stack_outbound_failed, nua_stack_outbound_failed, nua_stack_outbound_credentials };/**@fn void nua_register(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); * * Send SIP REGISTER request to the registrar. * * Request status will be delivered to the application using #nua_r_register * event. When successful the registration will be updated periodically. * * The handle used for registration cannot be used for any other purposes. * * @param nh Pointer to operation handle * @param tag, value, ... List of tagged parameters * * @return * nothing * * @par Related tags: * NUTAG_REGISTRAR(), NUTAG_INSTANCE(), NUTAG_OUTBOUND(), * NUTAG_KEEPALIVE(), NUTAG_KEEPALIVE_STREAM(), * * @par Events: * #nua_r_register, #nua_i_outbound * * @par NAT, Firewall and Outbound Support * * If the application did not include the Contact header in the tags, * nua_register() will generate one and start a protocol engine for outbound * connections used for NAT and firewall traversal and connectivity checks. * * First, nua_register() will probe for NATs in between UA and registrar. It * will send a REGISTER request as usual. Upon receiving the response it * checks for the presence of valid "received" and "rport" parameters in the * Via header returned by registrar. The presence of NAT is determined from * the "received" parameter in a Via header. When a REGISTER request was * sent, the stack inserted the source IP address in the Via header: if that * is different from the source IP address seen by the registrar, the * registrar inserts the source IP address it sees into the "received" * parameter. * * Please note that an ALG (application-level gateway) modifying the Via * headers in outbound requests and again in incoming responses will make * the above-described NAT check to fail. * * The response to the initial REGISTER should also include feature tags * indicating whether registrar supports various SIP extensions: @e * outbound, @e pref, @e path, @e gruu. * * Basically, @e outbound means that instead of registering its contact URI * with a particular address-of-record URI, the user-agent registers a * transport-level connection. Such a connection is identified on the * Contact header field with an instance identifier, application-provided * @ref NUTAG_INSTANCE() "unique string" identifying the user-agent instance * and a stack-generated numeric index identifying the transport-level * connection. * * If the @e outbound extension is supported, NUTAG_OUTBOUND() contains * option string "outbound" and the application has provided an instance * identifer to the stack with NUTAG_INSTANCE(), the nua_register() will try * to use outbound. * * If @e outbound is not supported, nua_register() has to generate a URI * that can be used to reach it from outside. It will check for public * transport addresses detected by underlying stack with, e.g., STUN, UPnP * or SOCKS. If there are public addresses, nua_register() will use them. If * there is no public address, it will try to generate a Contact URI from * the "received" and "rport" parameters found in the Via header of the * response message. * * @todo Actually generate public addresses. * * You can disable this kind of NAT traversal by setting "no-natify" into * NUTAG_OUTBOUND() options string. * * @par GRUU and Service-Route * * After a successful response to the REGISTER request has been received, * nua_register() will update the information about the registration based * on it. If there is a "gruu" parameter included in the response, * nua_register() will save it and use the gruu URI in the Contact header * fields of dialog-establishing messages, such as INVITE or SUBSCRIBE. * Also, if the registrar has included a Service-Route header in the * response, and the service route feature has not been disabled using * NUTAG_SERVICE_ROUTE_ENABLE(), the route URIs from the Service-Route * header will be used for initial non-REGISTER requests. * * The #nua_r_register message will include the contact header and route * used in with the registration. * * @par Registration Keep-Alive * * After the registration has successfully completed the nua_register() will * validate the registration and initiate the keepalive mechanism, too. The * user-agent validates the registration by sending a OPTIONS requests to * itself. If there is an error, nua_register() will indicate that to the * application using nua_i_outbound event, and start unregistration * procedure (unless that has been explicitly disabled). * * You can disable validation by inserting "no-validate" into * NUTAG_OUTBOUND() string. * * The keepalive mechanism depends on the network features detected earlier. * If @a outbound extension is used, the STUN keepalives will be used. * Otherwise, NUA stack will repeatedly send OPTIONS requests to itself. In * order to save bandwidth, it will include Max-Forwards: 0 in the * keep-alive requests, however. The keepalive interval is determined by * NUTAG_KEEPALIVE() parameter. If the interval is 0, no keepalive messages * is sent. * * You can disable keepalive OPTIONS by inserting "no-options-keepalive" * into NUTAG_OUTBOUND() string. Currently there are no other keepalive * mechanisms available. * * The value of NUTAG_KEEPALIVE_STREAM(), if specified, is used to indicate * the desired transport-layer keepalive interval for stream-based * transports like TLS and TCP. * * @sa NUTAG_OUTBOUND() and tags. *//** @var nua_event_e::nua_r_register * * Answer to outgoing REGISTER. * * The REGISTER may be sent explicitly by nua_register() or implicitly by * NUA state machines. The @a status may be 100 even if the real response * status returned is different if the REGISTER request has been restarted. * * @param nh operation handle associated with the call * @param hmagic operation magic associated with the call * @param status registration status * @param sip response to REGISTER request or NULL upon an error * (error code and message are in status an phrase parameters) * @param tags empty *//** @var nua_event_e::nua_i_outbound * * Answer to outgoing REGISTER. * * The REGISTER may be sent explicitly by nua_register() or * implicitly by NUA state machine. * * @param nh operation handle associated with the call * @param hmagic operation magic associated with the call * @param sip response to REGISTER request or NULL upon an error * (error code and message are in status an phrase parameters) * @param tags empty *//**@fn void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); * Unregister. * * Send a REGISTER request with expiration time 0. This removes the * registration from the registrar. If the handle was earlier used * with nua_register() the periodic updates will be terminated. * * If a SIPTAG_CONTACT_STR() with argument "*" is used, all the * registrations will be removed from the registrar otherwise only the * contact address belonging to the NUA stack is removed. * * @param nh Pointer to operation handle * @param tag, value, ... List of tagged parameters * * @return * nothing * * @par Related tags: * NUTAG_REGISTRAR() \n * Tags in <sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR() * * @par Events: * #nua_r_unregister *//** @var nua_event_e::nua_r_unregister * * Answer to outgoing un-REGISTER. * * @param nh operation handle associated with the call * @param hmagic operation magic associated with the call * @param sip response to REGISTER request or NULL upon an error * (error code and message are in status and phrase parameters) * @param tags empty */intnua_stack_register(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags){ nua_dialog_usage_t *du; nua_registration_t *nr = NULL; outbound_t *ob = NULL; struct nua_client_request *cr = nh->nh_cr; msg_t *msg = NULL; sip_t *sip; int terminating = e != nua_r_register; if (nh->nh_special && nh->nh_special != nua_r_register) return UA_EVENT2(e, 900, "Invalid handle for REGISTER"); if (cr->cr_orq) return UA_EVENT2(e, 900, "Request already in progress"); nua_stack_init_handle(nua, nh, nh_has_register, "", TAG_NEXT(tags)); nh->nh_special = nua_r_register; du = nua_dialog_usage_add(nh, nh->nh_ds, nua_register_usage, NULL); if (!du) return UA_EVENT1(e, NUA_INTERNAL_ERROR); nr = nua_dialog_usage_private(du); assert(nr); nua_registration_add(&nh->nh_nua->nua_registrations, nr); if (!terminating && du->du_terminating) return UA_EVENT2(e, 900, "Unregister in progress"); if (cr->cr_msg) msg_destroy(cr->cr_msg), cr->cr_msg = NULL; /* Use original message as template when unregistering */ if (terminating) cr->cr_msg = msg_ref_create(du->du_msg); msg = nua_creq_msg(nua, nh, cr, cr->cr_msg != NULL, SIP_METHOD_REGISTER, TAG_IF(!terminating, NUTAG_USE_DIALOG(1)), TAG_NEXT(tags)); sip = sip_object(msg); if (!msg || !sip) goto error; if (!nr->nr_aor) { if (nua_registration_set_aor(nh->nh_home, nr, sip->sip_to) < 0) goto error; } du->du_terminating = terminating; if (du->du_msg == NULL) du->du_msg = msg_ref_create(cr->cr_msg); /* Save original message */ if (terminating) /* Add Expires: 0 and remove the expire parameters from contacts */ unregister_expires_contacts(msg, sip); if (nua_registration_set_contact(nh->nh_home, nr, sip->sip_contact, terminating) < 0) goto error; ob = nr->nr_ob; if (!ob && (NH_PGET(nh, outbound) || NH_PGET(nh, instance))) { nr->nr_ob = ob = outbound_new(nh, &nua_stack_outbound_callbacks, nh->nh_nua->nua_root, nh->nh_nua->nua_nta,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -