/* TC-User API / Interface between TCAP protocol manager and User Application */ /* (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 "tcap.h" #include "tcap_user.h" void *tcap_ind_ctx; static const struct value_string tcap_prim_names[] = { /* dialogue handling */ { TCAP_PR_TC_UNI, "TC-UNI" }, { TCAP_PR_TC_BEGIN, "TC-BEGIN" }, { TCAP_PR_TC_CONTINUE, "TC-CONTINUE" }, { TCAP_PR_TC_END, "TC-END" }, { TCAP_PR_TC_U_ABORT, "TC-U-ABORT" }, { TCAP_PR_TC_NOTICE, "TC-NOTICE" }, /* component handling */ { TCAP_PR_TC_INVOKE, "TC-INVOKE" }, { TCAP_PR_TC_RESULT_L, "TC-RESULT-L" }, { TCAP_PR_TC_RESULT_NL, "TC-RESULT-NL" }, { TCAP_PR_TC_U_ERROR, "TC-U-ERROR" }, { TCAP_PR_TC_U_REJECT, "TC-U-REJECT" }, { TCAP_PR_TC_CANCEL, "TC-CANCEL" }, { TCAP_PR_TC_L_REJECT, "TC-L-REJECT" }, { TCAP_PR_TC_R_REJECT, "TC-R-REJECT" }, { TCAP_PR_TC_P_ABORT, "TC-P-ABORT" }, { 0, NULL } }; LIB_EXPORTED const char *tcap_prim_name(enum tcap_primitive prim) { return get_value_string(tcap_prim_names, prim); } /***********************************************************************/ /* Dialogue Primitives */ /***********************************************************************/ static struct tcap_dialg_ind *tcap_dialg_ind_alloc(void) { struct tcap_dialg_ind *tcdi = talloc_zero(tcap_ind_ctx, struct tcap_dialg_ind); return tcdi; } /* fill the application context and user information part of 'tcap_dialg_ind' */ static int fill_tcap_dialg_ind(struct tcap_dialg_ind *tcdi, OBJECT_IDENTIFIER_t *app_ctx_name, struct user_information *user_info) { int rc; if (app_ctx_name) { /* Parse the Application Context from an OID into our array of ARCs */ rc = OBJECT_IDENTIFIER_get_arcs(app_ctx_name, &tcdi->app_ctx_name.arc, sizeof(tcdi->app_ctx_name.arc[0]), ARRAY_SIZE(tcdi->app_ctx_name.arc)); if (rc < 0) return rc; tcdi->app_ctx_name.num_arcs = rc; tcdi->app_ctx_present = 1; } /* copy the user_info */ if (user_info) { EXTERNAL_t *ext; if (user_info->list.count != 1) return -EIO; ext = user_info->list.array[0]; if (ext->size > sizeof(tcdi->user_info.data)) return -EIO; memcpy(tcdi->user_info.data, ext->buf, ext->size); tcdi->user_info.data_len = ext->size; } return 0; } /* Take any TC-Dialogue.ind from the stack and pass it to the user */ static int _tcap_tcu_dialg_ind(enum tcap_primitive prim, struct tcap_dialogue *td, OBJECT_IDENTIFIER_t *app_ctx_name, struct user_information *user_info, int comp_present) { struct tcap_dialg_ind *tcdi = tcap_dialg_ind_alloc(); int rc; if (!tcdi) return -ENOMEM; tcdi->dialg_id = td->dialogue_id; rc = fill_tcap_dialg_ind(tcdi, app_ctx_name, user_info); if (rc < 0) { /* FIXME: reject the dialogue */ fprintf(stderr, "Error filling the Dialogue Indication\n"); return rc; } if (comp_present) tcdi->components_present = 1; return tcap_user_ind_dialg(prim, tcdi); } /* TC-BEGIN.ind from DHA */ int tcap_tcu_begin_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present) { return _tcap_tcu_dialg_ind(TCAP_PR_TC_BEGIN, td, app_ctx_name, user_info, comp_present); } /* TC-UNI.ind from DHA */ int tcap_tcu_uni_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present) { return _tcap_tcu_dialg_ind(TCAP_PR_TC_UNI, td, app_ctx_name, user_info, comp_present); } /* TC-CONT.ind from DHA */ int tcap_tcu_cont_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present) { return _tcap_tcu_dialg_ind(TCAP_PR_TC_CONTINUE, td, app_ctx_name, user_info, comp_present); } /* TC-END.ind from DHA */ int tcap_tcu_end_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info, int comp_present) { return _tcap_tcu_dialg_ind(TCAP_PR_TC_END, td, app_ctx_name, user_info, comp_present); } /* TC-ABORT.ind from DHA */ int tcap_tcu_abort_ind(struct tcap_dialogue *td, void *app_ctx_name, void *user_info) { } /* TC-NOTICE.ind from DHA */ int tcap_tcu_notice_ind(struct tcap_dialogue *td, uint32_t cause) { } /* DIALOGUE primitive received from TC-User */ LIB_EXPORTED int tcap_user_req_dialg(enum tcap_primitive prim, struct tcap_dialg_ind *tcdi) { /* Allocate some structures on the stack */ OBJECT_IDENTIFIER_t *app_ctx; EXTERNAL_t *ext; struct user_information *user_info; struct tcap_dialogue *td; int rc = 0; fprintf(stdout, "<- USER_REQ_DIALG(%s)\n", tcap_prim_name(prim)); /* Resolve (or allocate) the dialogue/transaction state */ td = tcap_dialg_by_dialg_id(tcdi->dialg_id); if (!td) { switch (prim) { case TCAP_PR_TC_BEGIN: td = tcap_dialg_alloc(tcdi->dialg_id); if (!td) return -EINVAL; break; default: return -EINVAL; } } /* Parse the tcap_dialg_ind into something that the asn1c code understands */ if (tcdi->app_ctx_present) { app_ctx = talloc_zero(td, OBJECT_IDENTIFIER_t); OBJECT_IDENTIFIER_set_arcs(app_ctx, &tcdi->app_ctx_name.arc, sizeof(tcdi->app_ctx_name.arc[0]), tcdi->app_ctx_name.num_arcs); } /* Parse the tcap_dialg_ind into something that the asn1c code understands */ if (tcdi->user_info_present) { ext = talloc_zero(td, EXTERNAL_t); ext->buf = tcdi->user_info.data; ext->size = tcdi->user_info.data_len; user_info = talloc_zero(ext, struct user_information); ASN_SEQUENCE_ADD(&user_info->list, ext); } switch (prim) { case TCAP_PR_TC_UNI: rc = tcap_csl_tc_uni_req(td, app_ctx, user_info); break; case TCAP_PR_TC_BEGIN: td->transp_ent = tcdi->transp_ent; rc = tcap_csl_tc_begin_req(td, app_ctx, user_info); break; case TCAP_PR_TC_CONTINUE: rc = tcap_csl_tc_cont_req(td, app_ctx, user_info); break; case TCAP_PR_TC_END: rc = tcap_csl_tc_end_req(td, app_ctx, user_info, tcdi->prearranged_end); break; case TCAP_PR_TC_U_ABORT: rc = tcap_csl_tc_u_abort_req(td, &tcdi->reason, app_ctx, user_info); break; default: fprintf(stderr, "unsupported component primitive %s\n", tcap_prim_name(prim)); return -EINVAL; } return rc; } /***********************************************************************/ /* Component Primitives */ /***********************************************************************/ static struct tcap_component_ind *tcap_comp_ind_alloc(void) { struct tcap_component_ind *tcci = talloc_zero(tcap_ind_ctx, struct tcap_component_ind); return tcci; } static int _tcu_comp_ind(enum tcap_primitive prim, struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last) { struct tcap_component_ind *tcci = tcap_comp_ind_alloc(); int rc; tcci->dialg_id = ti->dialogue->dialogue_id; tcci->invoke_id = ti->invoke_id; if (ti->linked_id) { tcci->_linked_id = ti->_linked_id; tcci->linked_id = &tcci->_linked_id; } if (oper && oper->present != OPERATION_PR_NOTHING) { switch (oper->present) { case OPERATION_PR_localValue: rc = asn_INTEGER2long(&oper->choice.localValue, &tcci->operation.local); if (rc < 0) goto out_free; break; case OPERATION_PR_globalValue: rc = OBJECT_IDENTIFIER_get_arcs(&oper->choice.globalValue, &tcci->operation.global.arc, sizeof(tcci->operation.global.arc[0]), ARRAY_SIZE(tcci->operation.global.arc)); if (rc < 0) goto out_free; tcci->operation.global.num_arcs = rc; break; default: break; } } if (param) { if (param->size > sizeof(tcci->parameter.data)) goto out_free; memcpy(tcci->parameter.data, param->buf, param->size); tcci->parameter.data_len = param->size; } tcci->last_component = last; return tcap_user_ind_comp(prim, tcci); out_free: talloc_free(tcci); return rc; } /* Table 10 / Q.771 : TC-INVOKE.ind */ int tcap_tcu_tc_invoke_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last) { return _tcu_comp_ind(TCAP_PR_TC_INVOKE, ti, oper, param, last); } /* TC-L-REJECT.ind */ int tcap_tcu_tc_l_rej_ind(struct tcap_invocation *ti, int8_t *invoke_id, uint32_t problem) { /* FIXME */ } /* TC-R-REJECT.ind */ int tcap_tcu_tc_r_rej_ind(struct tcap_invocation *ti, int8_t *invoke_id, uint32_t problem, int last_component) { /* FIXME */ } /* TC-RESULT-L.ind from ISM */ int tcap_tcu_result_l_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last) { return _tcu_comp_ind(TCAP_PR_TC_RESULT_L, ti, oper, param, last); } int tcap_tcu_result_nl_ind(struct tcap_invocation *ti, struct OPERATION *oper, Parameter_t *param, int last) { return _tcu_comp_ind(TCAP_PR_TC_RESULT_NL, ti, oper, param, last); } /* Allocate and Fill a OPERATION_t from information inside a tcap_component_ind */ static OPERATION_t *generate_op(struct tcap_dialogue *td, struct tcap_component_ind *tcci) { OPERATION_t *op; op = talloc_zero(td, OPERATION_t); if (!op) return NULL; if (tcci->operation.is_global) { op->present = OPERATION_PR_globalValue; OBJECT_IDENTIFIER_set_arcs(&op->choice.globalValue, tcci->operation.global.arc, sizeof(tcci->operation.global.arc[0]), tcci->operation.global.num_arcs); } else { op->present = OPERATION_PR_localValue; asn_long2INTEGER(&op->choice.localValue, tcci->operation.local); } return op; } /* Allocate and Fill an ErrorCode_t from information inside a tcap_component_ind */ static ErrorCode_t *generate_errcode(struct tcap_dialogue *td, struct tcap_component_ind *tcci) { ErrorCode_t *err; err = talloc_zero(td, ErrorCode_t); if (!err) return NULL; if (tcci->error.is_private) { err->present = ErrorCode_PR_privateer; asn_long2INTEGER(&err->choice.privateer, tcci->error.err); } else { err->present = ErrorCode_PR_nationaler; err->choice.nationaler = tcci->error.err; } return err; } LIB_EXPORTED int tcap_user_req_comp(enum tcap_primitive prim, struct tcap_component_ind *tcci) { struct tcap_dialogue *td; OPERATION_t *op = NULL; ErrorCode_t *err = NULL; int rc = 0; fprintf(stdout, "<- USER_REQ_COMP(%s)\n", tcap_prim_name(prim)); /* Resolve (or allocate) the dialogue/transaction state */ td = tcap_dialg_by_dialg_id(tcci->dialg_id); if (!td) { switch (prim) { case TCAP_PR_TC_INVOKE: td = tcap_dialg_alloc(tcci->dialg_id); td->trans.tid_local = tcap_trans_id_alloc(); break; default: return -EINVAL; } } /* Actually dispatch the primitive */ switch (prim) { case TCAP_PR_TC_INVOKE: op = generate_op(td, tcci); rc = tcap_cha_tc_invoke_req(td, tcci->op_class, tcci->invoke_id, tcci->linked_id, op, tcci->parameter.data, tcci->parameter.data_len, tcci->timeout_secs); break; case TCAP_PR_TC_RESULT_L: op = generate_op(td, tcci); rc = tcap_cha_tc_result_req(td, tcci->invoke_id, 1, op, tcci->parameter.data, tcci->parameter.data_len); break; case TCAP_PR_TC_RESULT_NL: op = generate_op(td, tcci); rc = tcap_cha_tc_result_req(td, tcci->invoke_id, 0, op, tcci->parameter.data, tcci->parameter.data_len); break; case TCAP_PR_TC_U_ERROR: rc = tcap_cha_tc_u_error_req(td, tcci->invoke_id, generate_errcode(td, tcci), tcci->parameter.data, tcci->parameter.data_len); break; case TCAP_PR_TC_U_REJECT: rc = tcap_cha_tc_u_rej_req(td, &tcci->invoke_id, tcci->reject.problem_type, tcci->reject.problem_code); break; case TCAP_PR_TC_CANCEL: rc = tcap_cha_tc_cancel_req(td, tcci->invoke_id); break; case TCAP_PR_TC_TIMER_RESET: default: fprintf(stderr, "unsupported dialogue primitive %s\n", tcap_prim_name(prim)); return -EINVAL; } talloc_free(op); talloc_free(err); return rc; }