/* ITU-T Q.77x TCAP / CSL - Component Sub-Layer */ /* (C) 2010 by Harald Welte * (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 . */ #include #include #include #include #include #include #include #include #include #include #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; int rc; tcm = talloc_zero(td, struct TCMessage); tcm->present = TCMessage_PR_unidirectional; if (app_ctx) { AUDT_apdu_t *audt; /* build AUDT apdu */ 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); ext = talloc_zero(dial, ExternalPDU_t); ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_UniDialoguePDU, dial); any = ANY_new_fromType(&asn_DEF_ExternalPDU, ext); tcm->choice.unidirectional.dialoguePortion = (OCTET_STRING_t *) any; } /* Request components to CHA */ /* Process components */ /* Assemble TSL user data */ memcpy(&tcm->choice.unidirectional.components, td->pend_comp, sizeof(*td->pend_comp)); talloc_free(td->pend_comp); td->pend_comp = talloc_zero(td, struct ComponentPortion); /* 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; dial = talloc_zero(tcm, 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; /* Build AARQ apdu */ fprintf(stdout, "\nTC-BEGIN.req Dialogue portion:\n"); xer_fprint(stdout, &asn_DEF_DialoguePDU, dial); ext = talloc_zero(dial, ExternalPDU_t); rc = ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_DialoguePDU, dial); if (rc < 0) fprintf(stderr, "Error encoding DialoguePDU portion\n"); any = ANY_new_fromType(&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 = td->pend_comp; td->pend_comp = talloc_zero(td, struct ComponentPortion); /* Assign local transaction ID */ trans_id = ntohl(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 int gen_ext_AARE(struct DialoguePDU *dial, ANY_t *any, ExternalPDU_t *ext, OBJECT_IDENTIFIER_t *app_ctx, struct user_information *user_info) { AARE_apdu_t *aare; int rc; memset(any, 0, sizeof(*any)); memset(ext, 0, sizeof(*ext)); memset(dial, 0, sizeof(*dial)); 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 */ rc = ANY_fromType((ANY_t *) &ext->dialog, &asn_DEF_DialoguePDU, dial); if (rc < 0) { fprintf(stderr, "Error generating ExternalPDU from DialoguePDU\n"); return rc; } /* 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 rc; } return 0; } /* 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; struct ComponentPortion cp; ExternalPDU_t ext; DialoguePDU_t dial; 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) { gen_ext_AARE(&dial, &any, &ext, app_ctx, user_info); fprintf(stdout, "\nTC-CONTINUE.req Dialogue portion:\n"); xer_fprint(stdout, &asn_DEF_DialoguePDU, &dial); 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 = td->pend_comp; td->pend_comp = talloc_zero(td, struct ComponentPortion); /* 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); 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; struct ComponentPortion cp; ExternalPDU_t ext; DialoguePDU_t dial; 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)) { gen_ext_AARE(&dial, &any, &ext, app_ctx, user_info); fprintf(stdout, "\nTC-END.req Dialogue portion:\n"); xer_fprint(stdout, &asn_DEF_DialoguePDU, &dial); tcm.choice.end.dialoguePortion = (OCTET_STRING_t *) &any; } /* Request component to CHA */ /* Process components */ /* Assemble TLS user data */ tcm.choice.end.components = td->pend_comp; talloc_zero(td, struct ComponentPortion); /* 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; } /* 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; }