libosmo-tcap/src/csl_dha.c

690 lines
19 KiB
C

/* ITU-T Q.77x TCAP / CSL - Component Sub-Layer */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <netinet/in.h>
#include <osmocore/talloc.h>
#include <osmocore/msgb.h>
#include <asn1c/OBJECT_IDENTIFIER.h>
#include <osmocom/tcap/TCMessage.h>
#include <osmocom/tcap/ExternalPDU.h>
#include <osmocom/tcap/DialoguePDU.h>
#include <osmocom/tcap/UniDialoguePDU.h>
#include <osmocom/tcap/DialoguePortion.h>
#include "tcap.h"
static uint8_t _dial_version1 = 0x80;
static BIT_STRING_t dial_version1 = {
.buf = &_dial_version1,
.size = 1,
.bits_unused = 7,
};
/* Extract the Application Context Name and User Info from a DialoguePDU */
static int extract_appctx_uinfo(OBJECT_IDENTIFIER_t **app_ctx_name, struct user_information **user_info,
struct DialoguePDU *dial_pdu)
{
switch (dial_pdu->present) {
case DialoguePDU_PR_dialogueRequest:
*app_ctx_name = &dial_pdu->choice.dialogueRequest.application_context_name;
*user_info = dial_pdu->choice.dialogueRequest.user_information;
break;
case DialoguePDU_PR_dialogueResponse:
*app_ctx_name = &dial_pdu->choice.dialogueResponse.application_context_name;
*user_info = dial_pdu->choice.dialogueResponse.user_information;
break;
case DialoguePDU_PR_dialogueAbort:
*app_ctx_name = NULL;
*user_info = dial_pdu->choice.dialogueAbort.user_information;
break;
default:
*app_ctx_name = NULL;
*user_info = NULL;
return -EINVAL;
}
return 0;
}
/* TC-UNI.req from TCU */
int tcap_csl_tc_uni_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
{
struct TCMessage *tcm;
ExternalPDU_t *ext;
UniDialoguePDU_t *dial;
ANY_t *any;
ComponentPortion_t *pend_comp;
int rc;
tcm = talloc_zero(td, struct TCMessage);
tcm->present = TCMessage_PR_unidirectional;
if (app_ctx) {
AUDT_apdu_t *audt;
/* build AUDT apdu */
any = talloc_zero(tcm, ANY_t);
ext = talloc_zero(any, ExternalPDU_t);
dial = talloc_zero(td, UniDialoguePDU_t);
dial->present = UniDialoguePDU_PR_unidialoguePDU;
audt = &dial->choice.unidialoguePDU;
if (user_info)
audt->user_information = user_info;
memcpy(&audt->application_context_name, app_ctx, sizeof(audt->application_context_name));
audt->protocol_version = &dial_version1;
fprintf(stdout, "\nTC-UNI.req Dialogue portion:\n");
xer_fprint(stdout, &asn_DEF_UniDialoguePDU, dial);
ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_UniDialoguePDU, dial);
ANY_fromType(any, &asn_DEF_ExternalPDU, ext);
tcm->choice.unidirectional.dialoguePortion = (OCTET_STRING_t *) any;
}
/* Request components to CHA */
pend_comp = tcap_cha_req_components(td);
/* Process components */
memcpy(&tcm->choice.unidirectional.components, pend_comp, sizeof(*pend_comp));
talloc_free(pend_comp);
/* Assemble TSL user data */
/* TR-UNI-REQ to TSL */
rc = tcap_tco_tr_uni_req(&td->trans, tcm);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
asn_DEF_TCMessage.free_struct(&asn_DEF_TCMessage, tcm, 0);
return rc;
}
/* TC-BEGIN.req from TCU */
int tcap_csl_tc_begin_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
{
struct TCMessage *tcm;
ExternalPDU_t *ext;
DialoguePDU_t *dial;
struct tcap_invocation *ti;
uint32_t trans_id;
int rc;
tcm = talloc_zero(td, struct TCMessage);
tcm->present = TCMessage_PR_begin;
if (app_ctx) {
AARQ_apdu_t *aarq;
ANY_t *any;
/* Build AARQ apdu */
any = talloc_zero(tcm, ANY_t);
ext = talloc_zero(any, ExternalPDU_t);
dial = talloc_zero(ext, DialoguePDU_t);
dial->present = DialoguePDU_PR_dialogueRequest;
aarq = &dial->choice.dialogueRequest;
if (user_info)
aarq->user_information = user_info;
/* Set Application context mode */
td->app_ctx_mode = 1;
memcpy(&aarq->application_context_name, app_ctx, sizeof(aarq->application_context_name));
/* Set protocol_version = 1 */
aarq->protocol_version = &dial_version1;
fprintf(stdout, "\nTC-BEGIN.req Dialogue portion:\n");
xer_fprint(stdout, &asn_DEF_DialoguePDU, dial);
ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_DialoguePDU, dial);
ANY_fromType(any, &asn_DEF_ExternalPDU, ext);
tcm->choice.begin.dialoguePortion = (OCTET_STRING_t *) any;
}
/* Request components to CHA */
/* Process components */
/* Assemble TSL user data */
tcm->choice.begin.components = tcap_cha_req_components(td);
/* Assign local transaction ID */
trans_id = htonl(td->trans.tid_local);
OCTET_STRING_fromBuf(&tcm->choice.begin.otid,
(const char *) &trans_id, sizeof(trans_id));
/* TR-BEGIN-REQ to TSL */
rc = tcap_tco_tr_begin_req(&td->trans, tcm);
tcap_trans_set_state(&td->trans, TCAP_TS_INIT_SENT);
asn_DEF_TCMessage.free_struct(&asn_DEF_TCMessage, tcm, 0);
return rc;
}
/* Exteract and BER-decode the DialoguePDU contained in the ExternalPDU */
static struct DialoguePDU *unwrap_ext_dialg_pdu(DialoguePortion_t *dialg_por)
{
struct DialoguePDU *dial_pdu = NULL;
struct ExternalPDU *ext_pdu = NULL;
asn_dec_rval_t adr;
/* extract dialogue portion */
adr = ber_decode(NULL, &asn_DEF_ExternalPDU, (void **) &ext_pdu, dialg_por->buf,
dialg_por->size);
if (adr.code != RC_OK) {
fprintf(stderr, "Error parsing ExternalPDU in Dialogue Portion\n");
return NULL;
}
adr = ber_decode(NULL, &asn_DEF_DialoguePDU, (void **) &dial_pdu,
ext_pdu->dialog.buf, ext_pdu->dialog.size);
if (adr.code != RC_OK) {
fprintf(stderr, "Error parsing DialoguePDU in ExternalPDU\n");
return NULL;
}
/* Release the External part */
asn_DEF_ExternalPDU.free_struct(&asn_DEF_ExternalPDU, ext_pdu, 0);
return dial_pdu;
}
/* TR-UNI.ind from TSL */
int tcap_csl_tr_uni_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct Unidirectional *unimsg = &tcmsg->choice.unidirectional;
struct DialoguePDU *dial_pdu = NULL;
OBJECT_IDENTIFIER_t *app_ctx_name = NULL;
struct user_information *user_info = NULL;
int rc = 0;
if (unimsg->dialoguePortion) {
/* extract dialogue portion */
dial_pdu = unwrap_ext_dialg_pdu(unimsg->dialoguePortion);
/* Check for correctness */
if (!dial_pdu) {
/* FIXME */
}
/* Check version 1 */
extract_appctx_uinfo(&app_ctx_name, &user_info, dial_pdu);
}
/* Assign Dialogue ID */
td->dialogue_id = tcap_dialg_id_alloc();
/* TC-UNI.ind to TCU */
rc = tcap_tcu_uni_ind(td, app_ctx_name, user_info, 1);
/* Components to CHA */
rc = tcap_cha_proc_components(td, &unimsg->components);
/* Dialogue Terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
if (unimsg->dialoguePortion && dial_pdu) {
/* Release resources of the ber_decode */
asn_DEF_DialoguePDU.free_struct(&asn_DEF_DialoguePDU, dial_pdu, 0);
}
return rc;
}
/* TR-BEGIN.ind from TSL(TSM) -> CSL(DHA) */
int tcap_csl_tr_begin_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct Begin *bgmsg = &tcmsg->choice.begin;
struct DialoguePDU *dial_pdu = NULL;
OBJECT_IDENTIFIER_t *app_ctx_name = NULL;
struct user_information *user_info = NULL;
int rc = 0;
if (bgmsg->dialoguePortion) {
struct AARQ_apdu *aarq;
/* extract dialogue portion */
dial_pdu = unwrap_ext_dialg_pdu(bgmsg->dialoguePortion);
if (!dial_pdu) {
fprintf(stderr, "TC-BEGIN.ind Error parsing Dialogue portion\n");
/* Build ABORT apdu */
/* Discard components */
/* TR-U-ABPRT.req to TSL */
rc = tcap_tco_tr_u_abort_req(tt, tcmsg);
/* Dalogue terminated to CHA */
tcap_cha_dialg_term(td);
return rc;
}
fprintf(stdout, "\nTC-BEGIN.ind Dialogue portion:\n");
xer_fprint(stdout, &asn_DEF_DialoguePDU, dial_pdu);
aarq = &dial_pdu->choice.dialogueRequest;
if (aarq->protocol_version && 0/* Check version 1 */) {
/* Build AARE apdu */
/* Discard components */
/* TR-P-ABPRT.req to TSL */
/* Dalogue terminated to CHA */
tcap_cha_dialg_term(td);
}
/* Set application context mode */
td->app_ctx_mode = 1;
extract_appctx_uinfo(&app_ctx_name, &user_info, dial_pdu);
}
/* Assign Dialogue ID */
td->dialogue_id = tcap_dialg_id_alloc();
tcap_trans_set_state(tt, TCAP_TS_INIT_RECV);
/* TC-BEGIN.ind to TCU */
rc = tcap_tcu_begin_ind(td, app_ctx_name, user_info, bgmsg->components ? 1 : 0);
if (bgmsg->components) {
/* Components to CHA */
rc = tcap_cha_proc_components(td, bgmsg->components);
}
if (bgmsg->dialoguePortion && dial_pdu) {
/* Release resources of the ber_decode */
asn_DEF_DialoguePDU.free_struct(&asn_DEF_DialoguePDU, dial_pdu, 0);
}
return rc;
}
static ANY_t *gen_ext_AARE(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx,
struct user_information *user_info)
{
ANY_t *any;
ExternalPDU_t *ext;
DialoguePDU_t *dial;
AARE_apdu_t *aare;
int rc;
any = talloc_zero(td, ANY_t);
ext = talloc_zero(any, ExternalPDU_t);
dial = talloc_zero(ext, DialoguePDU_t);
dial->present = DialoguePDU_PR_dialogueResponse;
aare = &dial->choice.dialogueResponse;
/* Set protocol version 1 */
aare->protocol_version = &dial_version1;
/* Build AARE-apdu (accepted) */
memcpy(&aare->application_context_name, app_ctx, sizeof(aare->application_context_name));
asn_long2INTEGER(&aare->result, Associate_result_accepted);
aare->user_information = user_info;
aare->result_source_diagnostic.present = Associate_source_diagnostic_PR_dialogue_service_user;
asn_long2INTEGER(&aare->result_source_diagnostic.choice.dialogue_service_user,
dialogue_service_user_no_reason_given);
/* Link Dialogue into External PDU */
printf("calling ANY_fromType(%p, %p, %p)\n", &ext->dialog, &asn_DEF_DialoguePDU, dial);
rc = ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_DialoguePDU, dial);
if (rc < 0) {
fprintf(stderr, "Error generating ExternalPDU from DialoguePDU\n");
return NULL;
}
/* Link External PDU into Dialogue Portion */
rc = ANY_fromType(any, &asn_DEF_ExternalPDU, ext);
if (rc < 0) {
fprintf(stderr, "Error generating ANY_t from ExternalPDU\n");
return NULL;
}
xer_fprint(stdout, &asn_DEF_DialoguePDU, dial);
return any;
}
/* TC-CONTINUE.req from TCU */
int tcap_csl_tc_cont_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
{
struct TCMessage tcm;
ANY_t *any;
uint32_t trans_id;
int rc = 0;
memset(&tcm, 0, sizeof(tcm));
tcm.present = TCMessage_PR_continue;
switch (td->trans.state) {
case TCAP_TS_INIT_RECV:
if (app_ctx) {
fprintf(stdout, "\nTC-CONTINUE.req Dialogue portion:\n");
any = gen_ext_AARE(td, app_ctx, user_info);
tcm.choice.Continue.dialoguePortion = (OCTET_STRING_t *) any;
}
break;
case TCAP_TS_ACTIVE:
break;
default:
fprintf(stderr, "TC-CONTNUE.req in invalid state %s\n",
tcap_trans_state_name(td->trans.state));
return -EINVAL;
}
/* Request components to CHA */
/* Process components */
/* Assemble TSL user data */
tcm.choice.Continue.components = tcap_cha_req_components(td);
/* Assign local transaction ID */
trans_id = htonl(td->trans.tid_local);
OCTET_STRING_fromBuf(&tcm.choice.Continue.otid,
(const char *) &trans_id, sizeof(trans_id));
/* Assign remote transaction ID */
trans_id = htonl(td->trans.tid_remote);
OCTET_STRING_fromBuf(&tcm.choice.Continue.dtid,
(const char *) &trans_id, sizeof(trans_id));
/* TR-CONTINUE to TSL */
rc = tcap_tco_tr_continue_req(&td->trans, &tcm);
tcap_trans_set_state(&td->trans, TCAP_TS_ACTIVE);
asn_DEF_ANY.free_struct(&asn_DEF_ANY, any, 0);
return rc;
}
/* TC-END.req from TCU */
int tcap_csl_tc_end_req(struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info, int prearranged)
{
struct TCMessage tcm;
ANY_t *any;
int rc = 0;
memset(&tcm, 0, sizeof(tcm));
tcm.present = TCMessage_PR_end;
switch (td->trans.state) {
case TCAP_TS_INIT_RECV:
case TCAP_TS_ACTIVE:
break;
case TCAP_TS_INIT_SENT:
/* TR-END.req to TSL */
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
return rc;
default:
fprintf(stderr, "TC-END.req in invalid state %s\n",
tcap_trans_state_name(td->trans.state));
return -EINVAL;
}
if (prearranged) {
/* TR-END.req to TSL */
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return rc;
}
if (td->trans.state == TCAP_TS_INIT_RECV && (app_ctx && user_info)) {
fprintf(stdout, "\nTC-END.req Dialogue portion:\n");
any = gen_ext_AARE(td, app_ctx, user_info);
tcm.choice.end.dialoguePortion = (OCTET_STRING_t *) any;
}
/* Request component to CHA */
/* Process components */
/* Assemble TLS user data */
tcm.choice.end.components = tcap_cha_req_components(td);
/* TR-END.req to TSL */
rc = tcap_tco_tr_end_req(&td->trans, &tcm);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
asn_DEF_ANY.free_struct(&asn_DEF_ANY, any, 0);
return rc;
}
/* TC-U-APORT.req from TCU */
int tcap_csl_tc_u_abort_req(struct tcap_dialogue *td, uint32_t *abrt_reason,
OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info)
{
struct tcap_transaction *tt = &td->trans;
void *app_ctx_mode;
switch (tt->state) {
case TCAP_TS_INIT_RECV:
case TCAP_TS_ACTIVE:
break;
case TCAP_TS_INIT_SENT:
app_ctx_mode = NULL;
break;
default:
fprintf(stderr, "TC-U-ABORT.req in invalid state %s\n",
tcap_trans_state_name(td->trans.state));
return -EINVAL;
}
if (app_ctx_mode) {
if (tt->state != TCAP_TS_ACTIVE && 0 /* Abort reason present && AC-name not supported || dialogure refused */) {
/* Set protocol version 1 */
/* Build AARE apdu (rejected) */
} else {
/* Build ABRT apdu (source = dialogue-service-user) */
}
}
/* FIXME: TR-U-ABORT.req to TSL */
//tcap_tco_tr_u_abort_req(tt, tcmsg);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return 0;
}
/* TR-END.ind from TSL */
int tcap_csl_tr_end_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct End *endmsg = &tcmsg->choice.end;
struct DialoguePDU *dial_pdu;
OBJECT_IDENTIFIER_t *app_ctx_name = NULL;
struct user_information *user_info = NULL;
int rc = 0;
switch (td->trans.state) {
case TCAP_TS_ACTIVE:
if (endmsg->dialoguePortion) {
/* FIXME: Discard components */
/* TC-P-ABORT.ind to TCU */
//rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
} else {
/* TC-END.ind to TCU */
rc = tcap_tcu_end_ind(td, NULL, NULL, endmsg->components ? 1 : 0);
if (endmsg->components) {
/* Components to CHA */
rc = tcap_cha_proc_components(td, endmsg->components);
}
}
break;
case TCAP_TS_INIT_SENT:
if (endmsg->dialoguePortion) {
if (td->app_ctx_mode) {
/* Extract dialogue portion */
dial_pdu = unwrap_ext_dialg_pdu(endmsg->dialoguePortion);
/* Is dialogue portion corect? */
if (0 /*!correct */) {
/* TC-P-ABORT.ind to TCU */
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
}
extract_appctx_uinfo(&app_ctx_name, &user_info, dial_pdu);
/* TC-END.ind to TCU */
rc = tcap_tcu_end_ind(td, app_ctx_name, user_info, endmsg->components ? 1 : 0);
if (endmsg->components) {
/* Components to CHA */
rc = tcap_cha_proc_components(td, endmsg->components);
}
}
} else {
if (td->app_ctx_mode) {
/* Discard components */
/* TC-P-ABORT.ind to TCU */
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
}
}
break;
default:
return -EINVAL;
}
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return 0;
}
/* TR-NOTICE.ind from TSL */
int tcap_csl_tr_notice_ind(struct tcap_transaction *tt)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
uint32_t cause = 0;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
case TCAP_TS_ACTIVE:
break;
default:
return -EINVAL;
}
/* TR-NOTICE.ind to TCU */
return tcap_tcu_notice_ind(td, cause);
}
/* TR-CONTINUE.ind from TSL */
int tcap_csl_tr_continue_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
struct Continue *ctmsg = &tcmsg->choice.Continue;
struct DialoguePDU *dial_pdu = NULL;
OBJECT_IDENTIFIER_t *app_ctx_name = NULL;
struct user_information *user_info = NULL;
int rc = 0;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
if (ctmsg->dialoguePortion) {
if (td->app_ctx_mode) {
/* Extract dialogue portion */
dial_pdu = unwrap_ext_dialg_pdu(ctmsg->dialoguePortion);
if (!dial_pdu /*!correct*/) {
err_discard:
/* FIXME Discard components */
/* TC-P-ABORT.ind to TCU */
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_info);
/* FIXME Build ABRT apdu */
/* TR-U-ABORT.req to TSL */
rc = tcap_tco_tr_u_abort_req(tt, tcmsg);
/* Dialogue terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return rc;
}
fprintf(stdout, "\nTC-CONTINUE.ind Dialogue portion:\n");
xer_fprint(stdout, &asn_DEF_DialoguePDU, dial_pdu);
extract_appctx_uinfo(&app_ctx_name, &user_info, dial_pdu);
} else
goto err_discard;
} else {
if (td->app_ctx_mode)
goto err_discard;
}
break;
case TCAP_TS_ACTIVE:
if (ctmsg->dialoguePortion) {
/* FIXME: Is abstract syntax = dialoguePDU-AS ?? */
if (!td->app_ctx_mode)
goto err_discard;
}
break;
default:
return -EINVAL;
}
tcap_trans_set_state(tt, TCAP_TS_ACTIVE);
/* TC-CONTINUE.ind to TCU */
rc = tcap_tcu_cont_ind(td, app_ctx_name, user_info, ctmsg->components ? 1 : 0);
if (rc < 0)
return rc;
if (ctmsg->components) {
/* Components to CHA */
rc = tcap_cha_proc_components(td, ctmsg->components);
if (rc < 0)
return rc;
}
return rc;
}
/* TR-U-ABORT.ind from TSL */
int tcap_csl_tr_u_abort_ind(struct tcap_transaction *tt, struct TCMessage *tcmsg, struct msgb *msg)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
switch (tt->state) {
case TCAP_TS_INIT_SENT:
break;
/* FIXME: TCAP_TS_ACTIVE */
default:
return -EINVAL;
}
/* FIXME */
}
/* TR-P-ABORT.ind from TSL */
int tcap_csl_tr_p_abort_ind(struct tcap_transaction *tt)
{
struct tcap_dialogue *td = dialg_by_trans(tt);
OBJECT_IDENTIFIER_t *app_ctx_name = NULL;
struct user_information *user_data = NULL;
int rc;
switch (tt->state) {
case TCAP_TS_INIT_SENT:
case TCAP_TS_ACTIVE:
break;
default:
return -EINVAL;
}
/* TC-P-ABORT.ind to TCU */
rc = tcap_tcu_abort_ind(td, app_ctx_name, user_data);
/* Dialog terminated to CHA */
tcap_cha_dialg_term(td);
/* Free Dialogue ID */
tcap_dialg_free(td);
return rc;
}