sip_dialog.c revision 111456cc2c513639ef04ad370756b1c65b99f7d3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <strings.h>
#include <sip.h>
#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_hash.h"
#include "sip_dialog.h"
#include "sip_parse_generic.h"
#define SIP_DLG_XCHG_FROM 0
#define SIP_DLG_XCHG_TO 1
/*
* Dialog state change callback function
*/
/*
*/
/*
* Complete dialog hash table
*/
/*
* Partial dialog hash table
*/
/*
* Route set structure
*/
typedef struct sip_dlg_route_set_s {
char *sip_dlg_route;
struct sip_dlg_route_set_s *sip_dlg_route_next;
boolean_t, int);
void sip_dialog_delete(_sip_dialog_t *);
void sip_dialog_init();
boolean_t sip_dialog_match(void *, void *);
boolean_t sip_dialog_free(void *, void *, int *);
char *sip_dialog_req_uri(sip_dialog_t);
static void sip_release_dialog_res(_sip_dialog_t *);
void sip_dlg_self_destruct(void *);
int);
static void sip_dialog_free_rset(sip_dlg_route_set_t *);
/*
* Timer object for partial dialogs
*/
typedef struct sip_dialog_timer_obj_s {
/*
* To avoid duplication all over the place
*/
static void
{
}
}
}
/*
* Get the route information from the 'value' and add it to the route
* set.
*/
static sip_dlg_route_set_t *
{
int vlen = 0;
char *crlf;
const sip_param_t *uri_param;
int error;
return (NULL);
/*
* check for CRLF
*/
}
return (NULL);
}
/*
* loose routing
*/
/*
* Check if the 'lr' param is present for this route.
*/
if (error != 0) {
return (NULL);
}
strlen("lr"));
}
return (rset);
}
/*
* Depending on the route-set, determine the request URI.
*/
char *
{
char *uri;
NULL);
return (NULL);
} else {
}
return (NULL);
return (uri);
}
/*
* Free the route set.
*/
void
{
}
}
/*
* Recompute route-set
*/
static int
{
int ret;
}
}
}
return (ret);
}
/*
* If the route set is empty, the UAC MUST place the remote target URI
* into the Request-URI. The UAC MUST NOT add a Route header field to
* the request.
*
* If the route set is not empty, and the first URI in the route set
* contains the lr parameter (see Section 19.1.1), the UAC MUST place
* the remote target URI into the Request-URI and MUST include a Route
* header field containing the route set values in order, including all
* parameters.
*
* If the route set is not empty, and its first URI does not contain the
* lr parameter, the UAC MUST place the first URI from the route set
* into the Request-URI, stripping any parameters that are not allowed
* in a Request-URI. The UAC MUST add a Route header field containing
* the remainder of the route set values in order, including all
* parameters. The UAC MUST then place the remote target URI into the
* Route header field as the last value.
*/
int
{
char *rset;
char *rp;
char *rsp;
int count;
int rspl;
int rpl;
return (ENOMEM);
if (!route->sip_dlg_route_lr) {
return (ENOMEM);
}
SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
sizeof (char);
return (ENOMEM);
}
}
/*
* rcnt - 1 is for the number of COMMAs
*/
return (ENOMEM);
}
return (ENOMEM);
}
if (first) {
} else {
}
} else {
}
}
'\0';
if (first) {
uri, SIP_RAQUOT);
} else {
}
}
return (0);
}
/*
* UAC Behavior
* The route set MUST be set to the list of URIs in the Record-Route
* header field from the response, taken in reverse order and preserving
* all URI parameters.
*
* UAS behavior
* The route set MUST be set to the list of URIs in the Record-Route
* header field from the request, taken in order and preserving all URI
* parameters.
*/
static int
{
int error;
int rset_cnt = 0;
int rset_len = 0;
char *crlf;
continue;
}
goto r_error;
/*
* Add one for COMMA
*/
rset_cnt++;
/*
* Check for CRLF
*/
}
} else if (what == SIP_UAS_DIALOG) {
} else if (what == SIP_UAC_DIALOG) {
} else {
assert(0);
}
}
}
if (rset_cnt == 0)
return (0);
rset_len) != 0) {
goto r_error;
}
return (0);
return (ENOMEM);
}
/*
* UAS behavior:
* The remote sequence number MUST be set to the value of the sequence
* number in the CSeq header field of the request. The local sequence
* number MUST be empty. The call identifier component of the dialog ID
* MUST be set to the value of the Call-ID in the request. The local
* tag component of the dialog ID MUST be set to the tag in the To field
* in the response to the request (which always includes a tag), and the
* remote tag component of the dialog ID MUST be set to the tag from the
* From field in the request. A UAS MUST be prepared to receive a
* request without a tag in the From field, in which case the tag is
* considered to have a value of null.
* The remote URI MUST be set to the URI in the From field, and the
* local URI MUST be set to the URI in the To field.
* The remote target MUST be set to the URI from the Contact header field
* of the request.
*
* UAC behavior:
* The local sequence number MUST be set to the value of the sequence
* number in the CSeq header field of the request. The remote sequence
* number MUST be empty (it is established when the remote UA sends a
* request within the dialog). The call identifier component of the
* dialog ID MUST be set to the value of the Call-ID in the request.
* The local tag component of the dialog ID MUST be set to the tag in
* the From field in the request, and the remote tag component of the
* dialog ID MUST be set to the tag in the To field of the response. A
* UAC MUST be prepared to receive a response without a tag in the To
* field, in which case the tag is considered to have a value of null.
* The remote URI MUST be set to the URI in the To field, and the local
* URI MUST be set to the URI in the From field.
* The remote target MUST be set to the URI from the Contact header field
* of the response.
*/
/*
* This is the routine that seeds a dialog.
*/
{
int cseq;
int timer1 = sip_timer_T1;
int error;
return (NULL);
/*
* Only INVITE and SUBSCRIBE supported
*/
return (NULL);
/*
* A request outside of a dialog MUST NOT contain a To tag
*/
return (NULL);
if (dlg_type == SIP_UAS_DIALOG) {
} else {
}
return (NULL);
}
/*
* Sanity check since we just store the headers in the dialog
*/
return (NULL);
}
return (NULL);
}
return (NULL);
}
/*
* We will take the TO header with the tag when we complete this
* dialog
*/
if (dlg_type == SIP_UAS_DIALOG) {
/*
* We take the remote target from the incoming request on the
* UAS. For the UAC, we will take it from the response.
*/
NULL) {
goto dia_err;
}
} else {
NULL) {
goto dia_err;
}
/*
* We take the local contact from the originating request on
* UAC. For the UAS, we will take it from the response.
*/
NULL) {
goto dia_err;
} else {
}
}
goto dia_err;
goto dia_err;
}
}
/*
* Get the route set from the request, if present
*/
if (dlg_type == SIP_UAS_DIALOG &&
goto dia_err;
}
if (dlg_type == SIP_UAC_DIALOG)
else
/*
* Set the partial dialog timer with the INVITE timeout val
*/
if (sip_conn_timer1 != NULL)
/*
* Since at the client we never pass the partial dialog, we need not
* invoke the callback when the partial dialog self-destructs.
*/
if (dlg_type == SIP_UAS_DIALOG)
goto dia_err;
if (dlg_type == SIP_UAC_DIALOG) {
/*
* Add it to the partial hash table
*/
goto dia_err;
}
}
return ((sip_dialog_t)dialog);
return (NULL);
}
/*
* When creating a dialog from a NOTIFY request, we need to get the FROM
* header for the dialog from the TO header of the NOTIFY.
*/
{
int len;
int cnt;
const struct sip_header *hdr;
int hdrsize;
int error;
return (NULL);
return (NULL);
if (what == SIP_DLG_XCHG_FROM) {
} else {
sizeof (char) + SIP_SPACE_LEN;
}
return (NULL);
if (what == SIP_DLG_XCHG_FROM) {
} else {
}
/*
* FROM and TO have common parsing functions
*/
return (newhdr);
}
/*
* This is the response that completes the dialog that was created
* in sip_seed_dialog().
*/
{
int resp_code;
int error = 0;
int prev_state;
else
return (NULL);
}
dialog->sip_dlg_local_cseq) ||
return (NULL);
}
return (NULL);
alloc_thdr = B_TRUE;
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
strlen("pending")) != 0) ||
strlen("active")) != 0))) {
return (NULL);
}
} else {
alloc_thdr = B_TRUE;
} else {
SIP_CONTACT, NULL);
}
}
return (NULL);
}
}
if (alloc_thdr)
return (NULL);
}
int error;
return (NULL);
}
&error);
if (error == 0) {
}
return (NULL);
}
"id", &error);
if (error != 0 ||
event->sip_str_len != 0)) {
return (NULL);
}
return (NULL);
0) {
return (NULL);
}
}
if ((dialog->sip_dlg_remote_target =
return (NULL);
}
} else {
}
} else {
if ((dialog->sip_dlg_remote_target =
(void) pthread_mutex_unlock(
&dialog->sip_dlg_mutex);
if (alloc_thdr)
goto terminate_new_dlg;
}
dialog->sip_dlg_type) != 0) {
(void) pthread_mutex_unlock(
&dialog->sip_dlg_mutex);
if (alloc_thdr)
goto terminate_new_dlg;
}
}
if (SIP_PROVISIONAL_RESP(resp_code)) {
} else if (SIP_OK_RESP(resp_code)) {
/*
* Per 12.1 the UAS must include the contact header
* for a dialog establishing response, so if we
* don't find one, we terminate it.
*/
(void) pthread_mutex_unlock(
&dialog->sip_dlg_mutex);
if (sip_ulp_dlg_del_cb != NULL) {
}
if (alloc_thdr)
goto terminate_new_dlg;
}
} else {
if (sip_ulp_dlg_del_cb != NULL) {
NULL);
}
if (alloc_thdr)
goto terminate_new_dlg;
}
} else {
if ((dialog->sip_dlg_remote_uri_tag =
(void) pthread_mutex_unlock(
&dialog->sip_dlg_mutex);
goto terminate_new_dlg;
}
}
}
/*
* We take the local contact for UAS Dialog from the response (either
* NOTIFY for SUBSCRIBE request or from final response 2xx to INVITE
* request)
*/
== SIP_DLG_CONFIRMED)) {
NULL);
}
if (alloc_thdr)
goto terminate_new_dlg;
}
}
/*
* Cancel the partial dialog timer
*/
&error);
} else {
&error);
}
/*
* Get an ID for this dialog
*/
} else {
}
/*
* Add it to the hash table
*/
/*
* So that sip_dialog_delete() does not try to remove
* this from the hash table.
*/
}
} else {
}
}
return (NULL);
}
if (sip_dlg_ulp_state_cb != NULL) {
}
return ((sip_dialog_t)dialog);
}
/*
* Check if this dialog is a match.
*/
{
return (B_FALSE);
}
sizeof (dialog->sip_dlg_id)) == 0) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Don't delete, just take it out of the hash
*/
{
*found = 0;
== 0) {
*found = 1;
return (B_TRUE);
}
return (B_FALSE);
}
/*
* Free resources associated with the dialog, the object will be removed
* from the hash list by sip_hash_delete.
*/
{
*found = 0;
== 0) {
*found = 1;
if (dialog->sip_dlg_ref_cnt != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
return (B_FALSE);
}
/*
* The UAS will receive the request from the transaction layer. If the
* request has a tag in the To header field, the UAS core computes the
* dialog identifier corresponding to the request and compares it with
* existing dialogs. If there is a match, this is a mid-dialog request.
*/
{
int error;
if (error != 0)
return (NULL);
if (is_request) {
if (error == 0)
} else {
if (error == 0)
}
if (error != 0)
return (NULL);
return (NULL);
}
}
return ((sip_dialog_t)dialog);
}
/*
* We keep this partial dialog for the duration of the INVITE
* transaction timeout duration, i.e. Timer B.
*/
void
sip_dlg_self_destruct(void *args)
{
int index;
} else {
}
}
/*
* Terminate a dialog
*/
void
{
int prev_state;
if (sip_dlg_ulp_state_cb != NULL) {
}
}
/*
* Delete a dialog
*/
void
{
int index;
/*
* partial dialog, not in the hash table
*/
/*
* Cancel the partial dialog timer
*/
return;
}
}
/*
* Get the remote target from the CONTACT header from the 200 OK response
*/
static boolean_t
{
return (B_TRUE);
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
*/
/* ARGSUSED */
int
{
int error;
if (error != 0)
return (EINVAL);
if (request) {
if (error != 0)
return (EINVAL);
if (error != 0)
return (EINVAL);
method) {
return (EINVAL);
}
/*
* Requests that do not change in any way the state
* of a dialog may be received within a dialog.
* They are processed as if they had been received
* outside the dialog.
* For dialogs that have been established with an
* INVITE, the only target refresh request defined is
* re-INVITE.
*/
return (EPROTO);
}
/*
* Target-Refresh request
*/
NULL);
}
}
}
} else {
int resp_code;
int error;
if (error == 0) {
&error);
}
if (error != 0)
return (error);
return (0);
}
/*
* Let the user delete the dialog if it is not a 1XX/2XX resp
* for an early INVITE dialog.
*/
if (SIP_OK_RESP(resp_code)) {
(void) pthread_mutex_unlock(
&_dialog->sip_dlg_mutex);
if (sip_ulp_dlg_del_cb != NULL) {
}
return (0);
}
(void) pthread_mutex_unlock(
&_dialog->sip_dlg_mutex);
(void) sip_dlg_recompute_rset(_dialog,
if (sip_dlg_ulp_state_cb != NULL) {
}
return (0);
} else if (_dialog->sip_dlg_new_local_contact
!= NULL) {
!= NULL);
NULL;
}
}
}
}
return (0);
}
/*
* Copy partial dialog to create a complete dialog
*/
{
return (NULL);
return (NULL);
}
}
return (NULL);
}
return (NULL);
}
}
if ((new_dlg->sip_dlg_local_uri_tag =
return (NULL);
}
return (NULL);
}
}
return (new_dlg);
}
/*
* Update the dialog using the response
*/
{
int resp_code = 0;
int prev_state;
int error;
if (error != 0)
return (dialog);
if (isreq) {
return (dialog);
}
} else {
if (error != 0) {
return (dialog);
}
if (error != 0) {
return (dialog);
}
}
/*
* Let the user delete the dialog if it is not a 1XX/2XX resp
* for an early dialog.
*/
if (SIP_OK_RESP(resp_code)) {
/*
* If we recieved provisional response before we would
* not have captured local contact. So store it now.
*/
(void) pthread_mutex_lock(&sip_msg->
SIP_CONTACT, NULL);
(void) pthread_mutex_unlock(&sip_msg->
= sip_dup_header(chdr);
NULL;
}
}
if (sip_dlg_ulp_state_cb != NULL) {
}
} else {
}
return (dialog);
}
if (_dialog->sip_dlg_on_fork) {
(void) pthread_mutex_unlock(
&_dialog->sip_dlg_mutex);
return (dialog);
}
/*
* has incremented the ref on the partial
* dialog, we release it here and incr the
* ref on the new dialog which will be
* released by the caller.
*/
(void) pthread_mutex_unlock(
&_dialog->sip_dlg_mutex);
(void) pthread_mutex_lock(
&_dialog->sip_dlg_mutex);
} else {
int index;
/*
* take it out of the list so that further
* responses will not result in a dialog.
* We will have an extra refcount when we
* come back from sip_complete_dialog(), i.e.
* one when the partial dialog was created -
* in sip_seed_dialog(), one held by the caller
* and one that will be added by
* sip_complete_dialog(). We need to release
* the one added by the sip_seed_dialog(),
* since the one in sip_complete_dialog()
* is for the same purpose.
*/
if (SIP_IS_TIMER_RUNNING(
_dialog->sip_dlg_timer)) {
}
(void) pthread_mutex_unlock(
&_dialog->sip_dlg_mutex);
(void *)_dialog->sip_dlg_id,
(void) pthread_mutex_lock(
&_dialog->sip_dlg_mutex);
}
} else {
}
NULL) {
return (NULL);
}
if (decr_ref)
} else {
}
return (dialog);
}
/*
* Initialize the hash table
*/
void
{
int cnt;
(void) pthread_mutex_init(
(void) pthread_mutex_init(
}
if (ulp_dlg_del != NULL)
if (ulp_state_cb != NULL)
}
/*
* Copy the new contact header of re-INVITE
*/
void
{
return;
!= SIP_DLG_CONFIRMED) {
return;
}
}
}