nua_register.c 60 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * 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>
Martti Mela's avatar
Martti Mela committed
29
 * @author Martti Mela <Martti.Mela@nokia.com>
30
 * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
31 32 33 34 35 36
 *
 * @date Created: Wed Mar  8 11:48:49 EET 2006 ppessi
 */

#include "config.h"

37 38 39
/** @internal SU network changed detector argument pointer type */
#define SU_NETWORK_CHANGED_MAGIC_T struct nua_s

40 41
#define TP_CLIENT_T          struct register_usage

42
#include <sofia-sip/string0.h>
43
#include <sofia-sip/su_strlst.h>
44 45 46
#include <sofia-sip/su_uniqueid.h>
#include <sofia-sip/su_tagarg.h>

47
#include <sofia-sip/sip_protos.h>
48
#include <sofia-sip/sip_util.h>
49
#include <sofia-sip/sip_status.h>
50

51
#define NTA_UPDATE_MAGIC_T   struct nua_s
52

53
#include "nua_stack.h"
54

55
#include <sofia-sip/hostdomain.h>
56
#include <sofia-sip/nta_tport.h>
57
#include <sofia-sip/tport.h>
Pekka Pessi's avatar
Pekka Pessi committed
58
#include <sofia-sip/tport_tag.h>
59

60 61 62 63
#define OUTBOUND_OWNER_T struct nua_handle_s

#include "outbound.h"

64 65 66 67
#if HAVE_SIGCOMP
#include <sigcomp.h>
#endif

68 69 70 71 72 73 74
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include <assert.h>

75 76
/* ======================================================================== */
/* Registrations and contacts */
77

78
int nua_registration_from_via(nua_registration_t **list,
79
			      nua_handle_t *nh,
80 81
			      sip_via_t const *via,
			      int public);
82

83
int nua_registration_add(nua_registration_t **list, nua_registration_t *nr);
84

85
void nua_registration_remove(nua_registration_t *nr);
86

87 88
int nua_registration_set_aor(su_home_t *, nua_registration_t *nr,
			     sip_from_t const *aor);
89

90
int nua_registration_set_contact(nua_handle_t *,
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
				 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,
107 108 109
				      nua_dialog_usage_t *du,
				      nua_client_request_t *cr,
				      nua_server_request_t *sr);
110 111 112 113
static void nua_register_usage_update_params(nua_dialog_usage_t const *du,
					     nua_handle_preferences_t const *,
					     nua_handle_preferences_t const *,
					     nua_handle_preferences_t const *);
114 115 116
static void nua_register_usage_peer_info(nua_dialog_usage_t *du,
					 nua_dialog_state_t const *ds,
					 sip_t const *sip);
117 118 119
static void nua_register_usage_refresh(nua_handle_t *,
				       nua_dialog_state_t *,
				       nua_dialog_usage_t *,
120
				       sip_time_t);
121 122 123
static int nua_register_usage_shutdown(nua_handle_t *,
				       nua_dialog_state_t *,
				       nua_dialog_usage_t *);
124

Pekka Pessi's avatar
Pekka Pessi committed
125
/** @internal @brief REGISTER usage, aka nua_registration_t. */
126 127 128 129
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 */
130
  sip_contact_t nr_dcontact[1];	/**< Contact in dialog */
131
  sip_via_t *nr_via;		/**< Corresponding Via headers */
132

133 134
  unsigned long nr_min_expires;	/**< Value from 423 negotiation */

135 136
  /** Status of registration */
  unsigned nr_ready:1;
137

138 139 140 141 142 143 144 145 146
  /** 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.
   */
147
  unsigned nr_default:1, nr_secure:1, nr_public:1, nr_ip4:1, nr_ip6:1;
148

149
  /** Stack-generated contact */
150 151 152
  unsigned nr_by_stack:1;

  unsigned:0;
153

154
  int nr_error_report_id;	/**< ID used to ask for error reports from tport */
155

156 157
  sip_route_t *nr_route;	/**< Outgoing Service-Route */
  sip_path_t *nr_path;		/**< Incoming Path */
158

159 160
  tport_t *nr_tport;		/**< Transport to be used when registered */
  nua_dialog_state_t *nr_dialogs; /**< List of our dialogs */
161

162 163 164
#if HAVE_SIGCOMP
  struct sigcomp_compartment *nr_compartment;
#endif
165

166 167
  outbound_t *nr_ob;	/**< Outbound connection */
};
168

169 170 171 172 173 174 175
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,
176
    nua_register_usage_update_params,
177
    nua_register_usage_peer_info,
178 179
    nua_register_usage_refresh,
    nua_register_usage_shutdown
180
  }};
181

182 183 184 185
static char const *nua_register_usage_name(nua_dialog_usage_t const *du)
{
  return "register";
}
186

187 188 189 190
static int nua_register_usage_add(nua_handle_t *nh,
				  nua_dialog_state_t *ds,
				  nua_dialog_usage_t *du)
{
191
  nua_registration_t *nr = NUA_DIALOG_USAGE_PRIVATE(du);
192

193 194
  if (ds->ds_has_register)
    return -1;			/* There can be only one usage */
195

196
  ds->ds_has_register = 1;
197

198
  nr->nr_public = 1;		/* */
199

200 201
  return 0;
}
202

203

204 205
static void nua_register_usage_remove(nua_handle_t *nh,
				      nua_dialog_state_t *ds,
206 207 208
				      nua_dialog_usage_t *du,
				      nua_client_request_t *cr,
				      nua_server_request_t *sr)
209
{
210
  nua_registration_t *nr = NUA_DIALOG_USAGE_PRIVATE(du);
211

212 213 214 215 216
  if (nr->nr_list)
    nua_registration_remove(nr);	/* Remove from list of registrations */

  if (nr->nr_ob)
    outbound_unref(nr->nr_ob);
217

218
#if HAVE_SIGCOMP
219 220 221
  if (nr->nr_compartment)
    sigcomp_compartment_unref(nr->nr_compartment);
  nr->nr_compartment = NULL;
222
#endif
223

224 225 226 227 228 229
  if (nr->nr_error_report_id)
    tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0);

  if (nr->nr_tport)
    tport_unref(nr->nr_tport), nr->nr_tport = NULL;

230 231 232 233 234 235 236 237 238
  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)
{
239
  nua_registration_t *nr = NUA_DIALOG_USAGE_PRIVATE(du);
240 241 242
  if (nr->nr_ob)
    outbound_peer_info(nr->nr_ob, sip);
}
243

244 245 246
/* ======================================================================== */
/* REGISTER */

247 248 249 250 251 252
static void nua_register_connection_closed(tp_stack_t *sip_stack,
					   nua_registration_t *nr,
					   tport_t *tport,
					   msg_t *msg,
					   int error);

253
/* Interface towards outbound_t */
254 255
sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
					 su_home_t *home,
256
					 int in_dialog,
257 258 259 260 261
					 sip_via_t const *v,
					 char const *transport,
					 char const *m_param,
					 ...);

262 263
static int nua_stack_outbound_refresh(nua_handle_t *,
				      outbound_t *ob);
264

265 266
static int nua_stack_outbound_status(nua_handle_t *,
				     outbound_t *ob,
267 268 269
				     int status, char const *phrase,
				     tag_type_t tag, tag_value_t value, ...);

270 271
static int nua_stack_outbound_failed(nua_handle_t *,
				     outbound_t *ob,
272 273 274
				     int status, char const *phrase,
				     tag_type_t tag, tag_value_t value, ...);

275
static int nua_stack_outbound_credentials(nua_handle_t *, auth_client_t **auc);
276

277 278
outbound_owner_vtable nua_stack_outbound_callbacks = {
    sizeof nua_stack_outbound_callbacks,
279 280 281 282 283 284
    /* oo_contact */ nua_handle_contact_by_via,
    /* oo_refresh */ nua_stack_outbound_refresh,
    /* oo_status */  nua_stack_outbound_status,
    /* oo_probe_error */     nua_stack_outbound_failed,
    /* oo_keepalive_error */ nua_stack_outbound_failed,
    /* oo_credentials */     nua_stack_outbound_credentials
285 286
  };

Pekka Pessi's avatar
Pekka Pessi committed
287
/**@fn void nua_register(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
288 289
 *
 * Send SIP REGISTER request to the registrar.
Pekka Pessi's avatar
Pekka Pessi committed
290 291 292 293 294 295 296 297 298 299 300 301 302
 *
 * 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:
303
 *     NUTAG_REGISTRAR(), NUTAG_INSTANCE(), NUTAG_OUTBOUND(),
304
 *     NUTAG_KEEPALIVE(), NUTAG_KEEPALIVE_STREAM(), NUTAG_M_USERNAME(),
Pekka Pessi's avatar
Pekka Pessi committed
305
 *     NUTAG_M_DISPLAY(), NUTAG_M_PARAMS(), NUTAG_M_FEATURES()
Pekka Pessi's avatar
Pekka Pessi committed
306 307 308
 *
 * @par Events:
 *     #nua_r_register, #nua_i_outbound
309
 *
310 311 312 313 314 315 316
 * @par Generating Contact Header
 *
 * If the application did not specify the Contact header in the tags,
 * nua_register() will generate one. It will obtain the schema, IP address
 * for the host and port number for the Contact URI from the transport
 * socket. The diplay name is taken from NUTAG_M_DISPLAY(), URL username
 * part is taken from NUTAG_M_USERNAME(), URI parameters from
317
 * NUTAG_M_PARAMS(), and Contact header parameters from NUTAG_M_FEATURES().
318 319
 * If NUTAG_CALLEE_CAPS(1) is specified, additional Contact header
 * parameters are generated based on SDP capabilities and SIP @Allow header.
320
 *
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
 * Note that @b nua may append a identifier of its own to the @Contact URI
 * username. Such nua-generated identifier trailer always starts with "="
 * (equal sign), rest of the nua-generated identifier may contain any
 * url-unreserved characters except "=".
 *
 * Likewise, nua may add transport parameters (such as "transport=tcp" or
 * "maddr") to the @Contact URI. It can add addtional header parameters, like
 * "+sip.instance" or "reg-id", too.
 *
 * For instance, if application uses tags like
 * @code
 *   nua_register(nh,
 *                NUTAG_M_DISPLAY("1"),
 *                NUTAG_M_USERNAME("line-1"),
 *                NUTAG_M_PARAMS("user=phone"),
 *                NUTAG_M_FEATURES("audio"),
 *                NUTAG_CALLEE_CAPS(0),
 *                TAG_END())
 * @endcode
 * @b nua can generate a Contact header like
 * @code
 * Contact: 1 <sip:line-1=SSQAIbjv@192.168.1.200;transport=tcp;user=phone>
 *   ;audio;reg-id=1
 *   ;+sip.instance=urn:uuid:97701ad9-39df-1229-1083-dbc0a85f029c
 * @endcode
 *
 * The incoming request from the proxy should contain the registered contact
 * URI as the request URI. The application can use the username prefix set
 * by NUTAG_M_USERNAME() and the non-transport parameters of the request URI
 * set by NUTAG_M_PARAMS() when determining to which registration the
 * incoming request belongs.
352
 *
353 354 355 356 357 358 359 360
 * For example, a request line correspoding to the @Contact in above example
 * may look like:
 * @code
 * INVITE sip:line-1=SSQAIbjv@192.168.1.200;user=phone SIP/2.0
 * @endcode
 *
 * @sa NUTAG_M_DISPLAY(), NUTAG_M_USERNAME(), NUTAG_M_PARAMS(),
 * NUTAG_M_FEATURES(), NUTAG_CALLEE_CAPS().
Pekka Pessi's avatar
Pekka Pessi committed
361 362 363
 *
 * @par NAT, Firewall and Outbound Support
 *
364 365
 * Normally, @b nua will start start a protocol engine for outbound
 * connections used for NAT and firewall traversal and connectivity checks
366
 * when registering.
367
 *
368 369 370 371
 * @note If the application provides @b nua with a
 * @Contact header of its own (or includes a SIPTAG_CONTACT(NULL) tag in
 * nua_register() tags), the outbound protocol engine is not started. It is
 * assumed that the application knows better what it is doing when it sets
372
 * the @Contact, or it is using experimental CPL upload as specified in
373 374 375
 * <a href="http://www.ietf.org/internet-drafts/draft-lennox-sip-reg-payload-01.txt">
 * draft-lennox-sip-reg-payload-01.txt</a>.
 *
376
 * First, outbound engine will probe for NATs in between UA and registrar.
377 378 379 380 381 382
 * It will send a REGISTER request as usual. Upon receiving the response it
 * checks for the presence of "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 actual source IP address in the Via header: if
 * that is different from the source IP address seen by the registrar, the
Pekka Pessi's avatar
Pekka Pessi committed
383 384 385 386 387 388 389
 * 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.
 *
390 391
 * The response to the initial REGISTER should also include option tags
 * indicating whether registrar supports various SIP extension options: @e
392 393 394 395 396 397 398 399 400 401 402 403 404 405
 * 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.
Pekka Pessi's avatar
Pekka Pessi committed
406 407 408 409 410 411 412 413 414
 *
 * 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.
 *
415 416 417 418
 * @todo Actually generate public addresses.
 *
 * You can disable this kind of NAT traversal by setting "no-natify" into
 * NUTAG_OUTBOUND() options string.
419
 *
Pekka Pessi's avatar
Pekka Pessi committed
420 421 422 423 424 425
 * @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
426
 * fields of dialog-establishing messages, such as INVITE or SUBSCRIBE.
Pekka Pessi's avatar
Pekka Pessi committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440
 * 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
441
 * application using #nua_i_outbound event, and start unregistration
Pekka Pessi's avatar
Pekka Pessi committed
442 443
 * procedure (unless that has been explicitly disabled).
 *
444 445 446
 * You can disable validation by inserting "no-validate" into
 * NUTAG_OUTBOUND() string.
 *
447 448
 * The keepalive mechanism depends on the network features detected earlier.
 * If @a outbound extension is used, the STUN keepalives will be used.
Pekka Pessi's avatar
Pekka Pessi committed
449 450
 * Otherwise, NUA stack will repeatedly send OPTIONS requests to itself. In
 * order to save bandwidth, it will include Max-Forwards: 0 in the
451 452 453 454 455 456 457 458 459 460 461 462
 * 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.
 *
463 464
 * As alternative to OPTIONS/STUN keepalives, the client can propose
 * a more frequent registration refresh interval with
465
 * NUTAG_M_FEATURES() (e.g. NUTAG_M_FEATURES("expires=120") given as
466
 * parameter to nua_register()).
467 468
 *
 * @sa #nua_r_register, nua_unregister(), #nua_r_unregister,
469 470 471 472 473
 * #nua_i_register,
 * @RFC3261 section 10,
 * @Expires, @Contact, @CallID, @CSeq,
 * @Path, @RFC3327, @ServiceRoute, @RFC3608, @RFC3680,
 *     NUTAG_REGISTRAR(), NUTAG_INSTANCE(), NUTAG_OUTBOUND(),
474
 *     NUTAG_KEEPALIVE(), NUTAG_KEEPALIVE_STREAM(),
475
 *     SIPTAG_CONTACT(), SIPTAG_CONTACT_STR(), NUTAG_M_USERNAME(),
476
 *     NUTAG_M_DISPLAY(), NUTAG_M_PARAMS(), NUTAG_M_FEATURES(),
Pekka Pessi's avatar
Pekka Pessi committed
477 478
 */

479
/** @NUA_EVENT nua_r_register
Pekka Pessi's avatar
Pekka Pessi committed
480
 *
481
 * Response to an outgoing REGISTER.
Pekka Pessi's avatar
Pekka Pessi committed
482 483
 *
 * The REGISTER may be sent explicitly by nua_register() or implicitly by
484 485
 * NUA state machines.
 *
486 487
 * When REGISTER request has been restarted the @a status may be 100 even
 * while the real response status returned is different.
Pekka Pessi's avatar
Pekka Pessi committed
488
 *
489 490 491 492 493
 * @param status response status code
 *               (if the request is retried, @a status is 100, the @a
 *               sip->sip_status->st_status contain the real status code
 *               from the response message, e.g., 302, 401, or 407)
 * @param phrase a short textual description of @a status code
494
 * @param nh     operation handle associated with the registration
495
 * @param hmagic application context associated with the registration
496
 * @param sip    response message to REGISTER request or NULL upon an error
497
 *               (status code is in @a status and
498
 *                descriptive message in @a phrase parameters)
Pekka Pessi's avatar
Pekka Pessi committed
499
 * @param tags   empty
500 501 502 503
 *
 * @sa nua_register(), nua_unregister(), #nua_r_unregister,
 * @Contact, @CallID, @CSeq, @RFC3261 section 10,
 * @Path, @RFC3327, @ServiceRoute, @RFC3608, @RFC3680
504
 *
505
 * @END_NUA_EVENT
Pekka Pessi's avatar
Pekka Pessi committed
506 507 508
 */

/**@fn void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
509
 * Unregister.
Pekka Pessi's avatar
Pekka Pessi committed
510
 *
511 512 513
 * 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.
Pekka Pessi's avatar
Pekka Pessi committed
514 515 516 517 518 519 520 521 522 523 524 525 526
 *
 * 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
527
 *     Header tags defined in <sofia-sip/sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR()
Pekka Pessi's avatar
Pekka Pessi committed
528 529 530
 *
 * @par Events:
 *     #nua_r_unregister
531 532 533 534 535 536
 *
 * @sa nua_register(), #nua_r_register, nua_handle_destroy(), nua_shutdown(),
 * #nua_i_register,
 * @Expires, @Contact, @CallID, @CSeq, @RFC3261 section 10,
 * @Path, @RFC3327, @ServiceRoute, @RFC3608, @RFC3680,
 *     NUTAG_REGISTRAR(), NUTAG_INSTANCE(), NUTAG_OUTBOUND(),
537
 *     NUTAG_KEEPALIVE(), NUTAG_KEEPALIVE_STREAM(),
538
 *     SIPTAG_CONTACT(), SIPTAG_CONTACT_STR(), NUTAG_M_USERNAME(),
539
 *     NUTAG_M_DISPLAY(), NUTAG_M_PARAMS(), NUTAG_M_FEATURES(),
Pekka Pessi's avatar
Pekka Pessi committed
540 541
 */

542
/** @NUA_EVENT nua_r_unregister
Pekka Pessi's avatar
Pekka Pessi committed
543 544 545
 *
 * Answer to outgoing un-REGISTER.
 *
546 547 548 549 550
 * @param status response status code
 *               (if the request is retried, @a status is 100, the @a
 *               sip->sip_status->st_status contain the real status code
 *               from the response message, e.g., 302, 401, or 407)
 * @param phrase a short textual description of @a status code
551
 * @param nh     operation handle associated with the registration
552
 * @param hmagic application context associated with the registration
553
 * @param sip    response message to REGISTER request or NULL upon an error
554
 *               (status code is in @a status and
555
 *                descriptive message in @a phrase parameters)
Pekka Pessi's avatar
Pekka Pessi committed
556
 * @param tags   empty
557 558 559 560
 *
 * @sa nua_unregister(), nua_register(), #nua_r_register,
 * @Contact, @CallID, @CSeq, @RFC3261 section 10,
 * @Path, @RFC3327, @ServiceRoute, @RFC3608, @RFC3680
561
 *
562
 * @END_NUA_EVENT
Pekka Pessi's avatar
Pekka Pessi committed
563 564
 */

Pekka Pessi's avatar
Pekka Pessi committed
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
static int nua_register_client_template(nua_client_request_t *cr,
					msg_t **return_msg,
					tagi_t const *tags);
static int nua_register_client_init(nua_client_request_t *cr,
				    msg_t *, sip_t *,
				    tagi_t const *tags);
static int nua_register_client_request(nua_client_request_t *cr,
				       msg_t *, sip_t *,
				       tagi_t const *tags);
static int nua_register_client_check_restart(nua_client_request_t *cr,
					     int status, char const *phrase,
					     sip_t const *sip);
static int nua_register_client_response(nua_client_request_t *cr,
					int status, char const *phrase,
					sip_t const *sip);

581
static nua_client_methods_t const nua_register_client_methods = {
582 583 584
  SIP_METHOD_REGISTER,		/* crm_method, crm_method_name */
  0,				/* crm_extra */
  {				/* crm_flags */
Pekka Pessi's avatar
Pekka Pessi committed
585 586 587 588
    /* create_dialog */ 1,
    /* in_dialog */ 0,
    /* target refresh */ 0
  },
589 590 591 592 593 594 595 596
  nua_register_client_template,	/* crm_template */
  nua_register_client_init,	/* crm_init */
  nua_register_client_request,	/* crm_send */
  nua_register_client_check_restart, /* crm_check_restart */
  nua_register_client_response,	/* crm_recv */
  NULL,				/* crm_preliminary */
  NULL,				/* crm_report */
  NULL,				/* crm_complete */
Pekka Pessi's avatar
Pekka Pessi committed
597 598 599 600 601 602 603 604 605 606 607 608 609 610
};

/**@internal Send REGISTER. */
int nua_stack_register(nua_t *nua,
		       nua_handle_t *nh,
		       nua_event_t e,
		       tagi_t const *tags)
{
  return nua_client_create(nh, e, &nua_register_client_methods, tags);
}

static int nua_register_client_template(nua_client_request_t *cr,
					msg_t **return_msg,
					tagi_t const *tags)
611 612 613
{
  nua_dialog_usage_t *du;

Pekka Pessi's avatar
Pekka Pessi committed
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
  if (cr->cr_event == nua_r_register)
    return 0;

  /* Use a copy of REGISTER message as the template for un-REGISTER */
  du = nua_dialog_usage_get(cr->cr_owner->nh_ds, nua_register_usage, NULL);
  if (du && du->du_cr) {
    if (nua_client_set_target(cr, du->du_cr->cr_target) < 0)
      return -1;
    *return_msg = msg_copy(du->du_cr->cr_msg);
    return 1;
  }

  return 0;
}

static int nua_register_client_init(nua_client_request_t *cr,
				    msg_t *msg, sip_t *sip,
				    tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du;
  nua_registration_t *nr;
  sip_to_t const *aor = sip->sip_to;

  int unreg;
639

Pekka Pessi's avatar
Pekka Pessi committed
640 641 642 643 644 645 646 647
  /* Explicit empty (NULL) contact - used for CPL store/remove? */
  if (!sip->sip_contact && cr->cr_has_contact)
    /* Do not create any usage */
    return 0;

  unreg = cr->cr_event != nua_r_register ||
    (sip->sip_expires && sip->sip_expires->ex_delta == 0);
  if (unreg)
648
    nua_client_set_terminating(cr, 1);
649

650
  du = nua_dialog_usage_add(nh, nh->nh_ds, nua_register_usage, NULL);
Pekka Pessi's avatar
Pekka Pessi committed
651 652 653 654 655 656
  if (du == NULL)
    return -1;
  nr = nua_dialog_usage_private(du);

  if (nua_client_bind(cr, du) < 0)
    return -1;
657

658 659
  if (!nr->nr_list) {
    nua_registration_add(&nh->nh_nua->nua_registrations, nr);
660

661 662 663 664 665 666 667 668
    if (aor == NULL)
      aor = sip->sip_from;
    if (aor == NULL)
      aor = nh->nh_nua->nua_from;

    if (nua_registration_set_aor(nh->nh_home, nr, aor) < 0)
      return -1;
  }
669

Pekka Pessi's avatar
Pekka Pessi committed
670 671
  if (nua_registration_set_contact(nh, nr, sip->sip_contact, unreg) < 0)
    return -1;
672

Pekka Pessi's avatar
Pekka Pessi committed
673 674 675 676 677 678 679
  if (!nr->nr_ob && (NH_PGET(nh, outbound) || NH_PGET(nh, instance))) {
    nr->nr_ob = outbound_new(nh, &nua_stack_outbound_callbacks,
			     nh->nh_nua->nua_root,
			     nh->nh_nua->nua_nta,
			     NH_PGET(nh, instance));
    if (!nr->nr_ob)
      return nua_client_return(cr, 900, "Cannot create outbound", msg);
680 681 682 683 684

    nua_register_usage_update_params(du,
				     NULL,
				     nh->nh_prefs,
				     nh->nh_dprefs);
685
  }
686

Pekka Pessi's avatar
Pekka Pessi committed
687 688 689 690 691 692 693 694
  if (nr->nr_ob) {
    outbound_t *ob = nr->nr_ob;
    sip_contact_t *m;

    if (!unreg && sip->sip_contact) {
      for (m = sip->sip_contact; m; m = m->m_next)
	if (!m->m_expires || strtoul(m->m_expires, NULL, 10) != 0)
	  break;
695

Pekka Pessi's avatar
Pekka Pessi committed
696 697 698 699 700 701
      if (m == NULL)
	unreg = 1;	/* All contacts have expires=0 */
    }

    if (outbound_set_contact(ob, sip->sip_contact, nr->nr_via, unreg) < 0)
      return nua_client_return(cr, 900, "Cannot set outbound contact", msg);
702 703
  }

Pekka Pessi's avatar
Pekka Pessi committed
704
  return 0;
705 706
}

Pekka Pessi's avatar
Pekka Pessi committed
707 708 709 710
static
int nua_register_client_request(nua_client_request_t *cr,
				msg_t *msg, sip_t *sip,
				tagi_t const *tags)
711
{
Pekka Pessi's avatar
Pekka Pessi committed
712
  nua_handle_t *nh = cr->cr_owner;
713
  nua_dialog_usage_t *du = cr->cr_usage;
Pekka Pessi's avatar
Pekka Pessi committed
714
  nua_registration_t *nr;
715 716
  sip_contact_t *m, *contacts = sip->sip_contact;
  char const *min_expires = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
717
  int unreg;
718
  tport_t *tport = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
719 720 721 722 723 724 725 726 727

  (void)nh;

  /* Explicit empty (NULL) contact - used for CPL store/remove? */
  if (!contacts && cr->cr_has_contact)
    return nua_base_client_request(cr, msg, sip, tags);

  if ((du && du->du_shutdown) ||
      (sip->sip_expires && sip->sip_expires->ex_delta == 0))
728
    nua_client_set_terminating(cr, 1);
Pekka Pessi's avatar
Pekka Pessi committed
729 730 731 732 733 734 735 736

  if (contacts) {
    if (!cr->cr_terminating) {
      for (m = contacts; m; m = m->m_next)
	if (!m->m_expires || strtoul(m->m_expires, NULL, 10) != 0)
	  break;
      /* All contacts have expires=0 */
      if (m == NULL)
737
	nua_client_set_terminating(cr, 1);
Pekka Pessi's avatar
Pekka Pessi committed
738 739
    }
  }
740

Pekka Pessi's avatar
Pekka Pessi committed
741
  unreg = cr->cr_terminating;
742

743
  nr = nua_dialog_usage_private(du);
744

745
  if (nr) {
Pekka Pessi's avatar
Pekka Pessi committed
746 747 748 749
    if (nr->nr_ob) {
      outbound_stop_keepalive(nr->nr_ob);
      outbound_start_registering(nr->nr_ob);
    }
750

Pekka Pessi's avatar
Pekka Pessi committed
751
    if (nr->nr_by_stack) {
752 753
      sip_contact_t *m = nr->nr_contact, *previous = NULL;

Pekka Pessi's avatar
Pekka Pessi committed
754
      outbound_get_contacts(nr->nr_ob, &m, &previous);
755 756

      sip_add_dup(msg, sip, (sip_header_t *)m);
757
      /* previous is an outdated contact generated by stack
758 759 760 761
       * and it is now unregistered */
      if (previous)
	sip_add_dup(msg, sip, (sip_header_t *)previous);
    }
762 763

    tport = nr->nr_tport;
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
  }

  for (m = sip->sip_contact; m; m = m->m_next) {
    if (m->m_url->url_type == url_any) {
      /* If there is a '*' in contact list, remove everything else */
      while (m != sip->sip_contact)
	sip_header_remove(msg, sip, (sip_header_t *)sip->sip_contact);
      while (m->m_next)
	sip_header_remove(msg, sip, (sip_header_t *)m->m_next);
      contacts = m;
      break;
    }

    if (!m->m_expires)
      continue;
    if (unreg) {
      /* Remove the expire parameters from contacts */
      msg_header_remove_param(m->m_common, "expires");
    }
783
    else if (nr && nr->nr_min_expires &&
784
	     strtoul(m->m_expires, 0, 10) < nr->nr_min_expires) {
785 786
      if (min_expires == NULL)
	min_expires = su_sprintf(msg_home(msg), "expires=%lu",
787 788
				 nr->nr_min_expires);
      msg_header_replace_param(msg_home(msg), m->m_common, min_expires);
Pekka Pessi's avatar
Pekka Pessi committed
789 790
    }
  }
791

Pekka Pessi's avatar
Pekka Pessi committed
792 793 794 795 796 797
  return nua_base_client_trequest(cr, msg, sip,
				  TAG_IF(unreg, SIPTAG_EXPIRES_STR("0")),
#if 0
				  TAG_IF(unreg, NTATAG_SIGCOMP_CLOSE(1)),
				  TAG_IF(!unreg, NTATAG_COMP("sigcomp")),
#endif
798
				  NTATAG_TPORT(tport),
Pekka Pessi's avatar
Pekka Pessi committed
799
				  TAG_NEXT(tags));
800
}
801

Pekka Pessi's avatar
Pekka Pessi committed
802 803 804
static int nua_register_client_check_restart(nua_client_request_t *cr,
					     int status, char const *phrase,
					     sip_t const *sip)
805
{
Pekka Pessi's avatar
Pekka Pessi committed
806 807 808 809 810 811 812
  nua_registration_t *nr = nua_dialog_usage_private(cr->cr_usage);
  unsigned short retry_count = cr->cr_retry_count;
  int restart = 0, retry;

  if (nr && nr->nr_ob) {
    msg_t *_reqmsg = nta_outgoing_getrequest(cr->cr_orq);
    sip_t *req = sip_object(_reqmsg); msg_destroy(_reqmsg);
813

Pekka Pessi's avatar
Pekka Pessi committed
814 815
    retry = outbound_register_response(nr->nr_ob, cr->cr_terminating,
				       req, sip);
816

Pekka Pessi's avatar
Pekka Pessi committed
817
    restart = retry >= ob_reregister_now;
818

Pekka Pessi's avatar
Pekka Pessi committed
819 820 821 822 823
    if (retry == ob_reregister)
      /* outbound restarts REGISTER later */;

    if (retry < 0)
      /* XXX - report an error? */;
824
  }
825

826 827 828 829 830
  if (nr && status == 423) {
    if (sip->sip_min_expires)
      nr->nr_min_expires = sip->sip_min_expires->me_delta;
  }

Pekka Pessi's avatar
Pekka Pessi committed
831 832 833
  /* Check for status-specific reasons to retry */
  if (nua_base_client_check_restart(cr, status, phrase, sip))
    return 1;
834

Pekka Pessi's avatar
Pekka Pessi committed
835 836
  /* Restart only if nua_base_client_check_restart() did not try to restart */
  if (restart && retry_count == cr->cr_retry_count)
837
    return nua_client_restart(cr, 100, "Outbound NAT Detected");
838

Pekka Pessi's avatar
Pekka Pessi committed
839
  return 0;
840 841
}

Pekka Pessi's avatar
Pekka Pessi committed
842 843 844
static int nua_register_client_response(nua_client_request_t *cr,
					int status, char const *phrase,
					sip_t const *sip)
845
{
Pekka Pessi's avatar
Pekka Pessi committed
846 847
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
848
  nua_registration_t *nr = nua_dialog_usage_private(du);
Pekka Pessi's avatar
Pekka Pessi committed
849
  int ready;
850

Pekka Pessi's avatar
Pekka Pessi committed
851
  ready = du && !cr->cr_terminated && status < 300;
852

Pekka Pessi's avatar
Pekka Pessi committed
853 854 855
  if (ready) {
    sip_time_t mindelta = 0;
    sip_time_t now = sip_now(), delta, reqdelta, mdelta;
856

Pekka Pessi's avatar
Pekka Pessi committed
857
    sip_contact_t const *m, *sent;
858

Pekka Pessi's avatar
Pekka Pessi committed
859 860 861
    msg_t *_reqmsg = nta_outgoing_getrequest(cr->cr_orq);
    sip_t *req = sip_object(_reqmsg);

862 863
    tport_t *tport;

Pekka Pessi's avatar
Pekka Pessi committed
864 865
    msg_destroy(_reqmsg);

Pekka Pessi's avatar
Pekka Pessi committed
866
    assert(nr); assert(sip); assert(req);
867

Pekka Pessi's avatar
Pekka Pessi committed
868 869 870 871 872 873 874 875
#if HAVE_SIGCOMP
    {
      struct sigcomp_compartment *cc;
      cc = nta_outgoing_compartment(cr->cr_orq);
      sigcomp_compartment_unref(nr->nr_compartment);
      nr->nr_compartment = cc;
    }
#endif
876

877
    /* XXX - if store/remove, remove
Pekka Pessi's avatar
Pekka Pessi committed
878 879 880 881
       Content-Disposition
       Content-Type
       body
    */
882

Pekka Pessi's avatar
Pekka Pessi committed
883 884
    /** Search for lowest delta of SIP contacts we tried to register */
    mindelta = SIP_TIME_MAX;
885

Pekka Pessi's avatar
Pekka Pessi committed
886
    reqdelta = req->sip_expires ? req->sip_expires->ex_delta : 0;
887

Pekka Pessi's avatar
Pekka Pessi committed
888
    for (m = sip->sip_contact; m; m = m->m_next) {
889
      if (m->m_url->url_type != url_sip &&
Pekka Pessi's avatar
Pekka Pessi committed
890 891
	  m->m_url->url_type != url_sips)
	continue;
892

Pekka Pessi's avatar
Pekka Pessi committed
893 894 895 896 897 898 899 900 901 902 903
      for (sent = req->sip_contact; sent; sent = sent->m_next) {
	if (url_cmp(m->m_url, sent->m_url))
	  continue;

	if (sent->m_expires)
	  mdelta = strtoul(sent->m_expires, NULL, 10);
	else
	  mdelta = reqdelta;

	if (mdelta == 0)
	  mdelta = 3600;
904

Pekka Pessi's avatar
Pekka Pessi committed
905 906 907 908 909 910 911
	delta = sip_contact_expires(m, sip->sip_expires, sip->sip_date,
				    mdelta, now);
	if (delta > 0 && delta < mindelta)
	  mindelta = delta;

	if (url_cmp_all(m->m_url, sent->m_url) == 0)
	  break;
912
      }
913
    }
914

Pekka Pessi's avatar
Pekka Pessi committed
915 916 917
    if (mindelta == SIP_TIME_MAX)
      mindelta = 3600;

918
    nua_dialog_usage_set_refresh(du, mindelta);
919

920
  /*  RFC 3608 Section 6.1 Procedures at the UA
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935

   The UA performs a registration as usual.  The REGISTER response may
   contain a Service-Route header field.  If so, the UA MAY store the
   value of the Service-Route header field in an association with the
   address-of-record for which the REGISTER transaction had registered a
   contact.  If the UA supports multiple addresses-of-record, it may be
   able to store multiple service routes, one per address-of-record.  If
   the UA refreshes the registration, the stored value of the Service-
   Route is updated according to the Service-Route header field of the
   latest 200 class response.  If there is no Service-Route header field
   in the response, the UA clears any service route for that address-
   of-record previously stored by the UA.  If the re-registration
   request is refused or if an existing registration expires and the UA
   chooses not to re-register, the UA SHOULD discard any stored service
   route for that address-of-record.
936

937
  */
938 939
    su_free(nh->nh_home, nr->nr_route);
    nr->nr_route = sip_route_dup(nh->nh_home, sip->sip_service_route);
940

Pekka Pessi's avatar
Pekka Pessi committed
941 942 943 944
    {
      /* RFC 3327 */
      /* Store last URI in Path header */
      sip_path_t *path = sip->sip_path;
945

Pekka Pessi's avatar
Pekka Pessi committed
946 947
      while (path && path->r_next)
	path = path->r_next;
948

Pekka Pessi's avatar
Pekka Pessi committed
949 950 951 952 953
      if (!nr->nr_path || !path ||
	  url_cmp_all(nr->nr_path->r_url, path->r_url)) {
	su_free(nh->nh_home, nr->nr_path);
	nr->nr_path = sip_path_dup(nh->nh_home, path);
      }
954 955
    }

956
    if (sip->sip_to->a_url->url_type == url_sips)
957
      nr->nr_secure = 1;
958

Pekka Pessi's avatar
Pekka Pessi committed
959
    if (nr->nr_ob) {
960
      outbound_gruuize(nr->nr_ob, sip);
Pekka Pessi's avatar
Pekka Pessi committed
961
      outbound_start_keepalive(nr->nr_ob, cr->cr_orq);
962
    }
Pekka Pessi's avatar
Pekka Pessi committed
963

964 965 966 967 968 969 970 971 972 973 974 975 976 977
    tport = nta_outgoing_transport (cr->cr_orq);

    /* cache persistant connection for registration */
    if (tport && tport != nr->nr_tport) {
      if (nr->nr_error_report_id) {
	if (tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0) < 0)
	  SU_DEBUG_1(("nua_register: tport_release() failed\n"));
	nr->nr_error_report_id = 0;
      }
      tport_unref(nr->nr_tport);
      nr->nr_tport = tport;

      if (tport_is_secondary(tport)) {
	tport_set_params(tport, TPTAG_SDWN_ERROR(1), TAG_END());
978
	nr->nr_error_report_id =
979 980 981 982 983
	  tport_pend(tport, NULL, nua_register_connection_closed, nr);
      }
    }
    else
      tport_unref(tport);    /* note: nta_outgoing_transport() makes a ref */
984

Pekka Pessi's avatar
Pekka Pessi committed
985 986 987
    nua_registration_set_ready(nr, 1);
  }
  else if (du) {
988
    nua_dialog_usage_reset_refresh(du);
Pekka Pessi's avatar
Pekka Pessi committed
989 990 991 992 993 994

    su_free(nh->nh_home, nr->nr_route);
    nr->nr_route = NULL;

    outbound_stop_keepalive(nr->nr_ob);

995
    /* release the persistant transport for registration */
996 997 998 999 1000 1001
    if (nr->nr_tport) {
      if (nr->nr_error_report_id) {
	if (tport_release(nr->nr_tport, nr->nr_error_report_id, NULL, NULL, nr, 0) < 0)
	  SU_DEBUG_1(("nua_register: tport_release() failed\n"));
	nr->nr_error_report_id = 0;
      }
1002

1003 1004
      tport_unref(nr->nr_tport), nr->nr_tport = NULL;
    }
Pekka Pessi's avatar
Pekka Pessi committed
1005
    nua_registration_set_ready(nr, 0);
1006 1007 1008
  }


Pekka Pessi's avatar
Pekka Pessi committed
1009