/* ITU-T Q.77x TCAP / CCO - Component CoOrdinator * part of CHA (ComponentHAndling), part of 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 "tcap.h" #if 0 int tcap_tc_invoke_req(uint32_t dialogue_id, uint8_t op_class, int8_t inv_id, int8_t *linked_id, struct OPERATION *op, uint8_t *param, uint32_t param_len, uint32_t timeout) { struct tcap_dialogue *td; /* resolve or allocate the dialogue for this ID */ td = tcap_dialg_by_dialg_id(dialogue_id); if (!td) td = tcap_dialg_alloc(dialogue_id); if (!td) return -ENOMEM; return tcap_cha_tc_invoke_req(td, op_class, inv_id, linked_id, op, param, param_len, timeout); } #endif /* Gernerate Parameter_t; copy data from original buffer */ static Parameter_t *gen_param(const void *ctx, uint8_t *param, uint32_t param_len) { Parameter_t *p = talloc_zero(ctx, Parameter_t); uint8_t *_param; if (!p) return NULL; /* make our local copy of the parameter data */ _param = talloc_size(p, param_len); if (!_param) { talloc_free(p); return NULL; } memcpy(_param, param, param_len); /* Note: Parameter_t == ANY_t == OCTET_STRING_t */ /* initialize Parameter structure and link it with invocation */ ANY_fromBuf((OCTET_STRING_t *)p, _param, param_len); return p; }; /* TC-INVOKE.req (TCU -> CHA) */ /* * linked_id not referenced after call, caller needs to free it * op not referenced after call, caller needs to free it * param not referenced after call, caller needs to free it */ int tcap_cha_tc_invoke_req(struct tcap_dialogue *td, uint8_t op_class, int8_t inv_id, int8_t *linked_id, struct OPERATION *op, uint8_t *param, uint32_t param_len, uint32_t timeout) { /* allocate new Invocation State Machine, link it with dialogue */ struct tcap_invocation *ti = tcap_ism_alloc(td, inv_id); struct Component *comp; struct Invoke *inv; if (!ti) return -ENOMEM; ti->inv_timeout = timeout; if (linked_id) { ti->_linked_id = *linked_id; ti->linked_id = &ti->_linked_id; } ti->op_class = op_class; /* Allocate a new component structure */ comp = talloc_zero(td, struct Component); if (!comp) return -ENOMEM; comp->present = Component_PR_invoke; /* Assemble INV component */ inv = &comp->choice.invoke; inv->invokeID = ti->invoke_id; if (ti->linked_id) { InvokeIdType_t *lid = talloc_zero(inv, InvokeIdType_t); *lid = *ti->linked_id; inv->linkedID = lid; } memcpy(&inv->opCode, op, sizeof(inv->opCode)); if (param && param_len) inv->parameter = gen_param(comp, param, param_len); /* Make component available for this Dialogue ID */ tcap_dialg_comp_add(td, comp); return 0; } /* TC-U-CANCEL.req (TCU -> CHA) */ int tcap_cha_tc_cancel_req(struct tcap_dialogue *td, int8_t inv_id) { struct tcap_invocation *ti = tcap_ism_lookup(td, inv_id); if (!ti || ti->state != TCAP_IS_IDLE) return -EINVAL; /* FIXME: Iterate over Component list of this dialogue * and evict any INVOKE component */ tcap_ism_terminate(ti); return 0; } /* Dialogue Terminated (CHA <- DHA) */ int tcap_cha_dialg_term(struct tcap_dialogue *td) { struct tcap_invocation *ti, *ti2; /* FIXME: Discard components awaiting transmission */ /* Any ISM alive for this dialogue ID? */ llist_for_each_entry_safe(ti, ti2, &td->ism_list, list) { /* Terminate ISM CCO -> ISM */ tcap_ism_terminate(ti); } } /* TC-RESULT-NL / TC-RESULT-L req (TCL -> CHA) */ int tcap_cha_tc_result_req(struct tcap_dialogue *td, int8_t inv_id, int last, struct OPERATION *op, uint8_t *param, uint32_t param_len) { struct tcap_invocation *ti = tcap_ism_lookup(td, inv_id); struct Component *comp; struct ReturnResult *res; struct resultretres *retres; if (!ti) return -ENOENT; /* Allocate a new component structure */ comp = talloc_zero(td, struct Component); if (!comp) return -ENOMEM; /* Assemble requested component */ if (last) { comp->present = Component_PR_returnResultLast; res = &comp->choice.returnResultLast; } else { comp->present = Component_PR_returnResultNotLast; res = &comp->choice.returnResultNotLast; } res->invokeID = ti->invoke_id; retres = talloc_zero(comp, struct resultretres); res->resultretres = retres; memcpy(&retres->opCode, op, sizeof(retres->opCode)); if (param && param_len) retres->parameter = gen_param(comp, param, param_len); /* Mark component available for this dialogue ID */ tcap_dialg_comp_add(td, comp); return 0; } /* TC-U-ERROR request TCL -> CHA */ int tcap_cha_tc_u_error_req(struct tcap_dialogue *td, int8_t inv_id, ErrorCode_t *err, uint8_t *param, uint32_t param_len) { struct tcap_invocation *ti = tcap_ism_lookup(td, inv_id); struct Component *comp; if (!ti) return -ENOENT; comp = talloc_zero(td, struct Component); if (!comp) return -ENOMEM; /* Assemble requested component */ comp->present = Component_PR_returnError; if (err) memcpy(&comp->choice.returnError.errorCode, err, sizeof(*err)); if (param && param_len) comp->choice.returnError.parameter = gen_param(comp, param, param_len); /* Mark component available for this dialogue ID */ tcap_dialg_comp_add(td, comp); return 0; } /* TC-U-REJECT.req (TCU -> CHA) */ int tcap_cha_tc_u_rej_req(struct tcap_dialogue *td, int8_t *invoke_id, enum problem_PR problem, long problem_code) { struct tcap_invocation *ti; struct Component *comp = talloc_zero(td, struct Component); struct Reject *rej; INTEGER_t *err_code_int = NULL; if (!comp) return -ENOMEM; /* Assemble reject component */ comp->present = Component_PR_reject; rej = &comp->choice.reject; if (invoke_id) { rej->invokeIDRej.present = invokeIDRej_PR_derivable; rej->invokeIDRej.choice.derivable = *invoke_id; } else rej->invokeIDRej.present = invokeIDRej_PR_not_derivable; rej->problem.present = problem; switch (problem) { case problem_PR_generalProblem: err_code_int = &rej->problem.choice.generalProblem; break; case problem_PR_invokeProblem: err_code_int = &rej->problem.choice.invokeProblem; break; case problem_PR_returnResultProblem: err_code_int = &rej->problem.choice.returnResultProblem; break; case problem_PR_returnErrorProblem: err_code_int = &rej->problem.choice.returnErrorProblem; break; case problem_PR_NOTHING: default: break; } if (err_code_int) asn_long2INTEGER(err_code_int, problem_code); /* Problem type invoke? */ if (problem == problem_PR_returnResultProblem || problem == problem_PR_returnErrorProblem) { /* Terminate ISM (CCO -> ISM) */ tcap_ism_terminate(ti); } /* Mark component available for this dialogue ID */ tcap_dialg_comp_add(td, comp); return 0; } /* Components (CHA <- DHA) */ int tcap_cha_proc_components(struct tcap_dialogue *td, struct ComponentPortion *comp_por) { struct tcap_invocation *ti; int rc; unsigned int i; /* Iterate over list of components as part of comp_por */ for (i = 0; i < comp_por->list.count; i++) { /* Extract next component */ struct Component *comp = comp_por->list.array[i]; int is_last_component = i+1 == comp_por->list.count ? 1 : 0; /* Check syntax error and proceed with 5, 3 or 4 */ switch (comp->present) { case Component_PR_invoke: ti = tcap_ism_alloc(td, comp->choice.invoke.invokeID); if (comp->choice.invoke.linkedID) { /* Linked to ISM not in sent state? Proceed with 6 */ ti->_linked_id = *comp->choice.invoke.linkedID; ti->linked_id = &ti->_linked_id; } /* TC-INVOKE.ind (TCU <- CHA) */ rc = tcap_tcu_tc_invoke_ind(ti, &comp->choice.invoke.opCode, comp->choice.invoke.parameter, is_last_component); break; case Component_PR_returnResultNotLast: ti = tcap_ism_lookup(td, comp->choice.returnResultNotLast.invokeID); /* ISM active? */ /* RR-NL.recv CCO -> ISM */ rc = tcap_ism_rr_nl_recv(ti, &comp->choice.returnResultNotLast, is_last_component); break; case Component_PR_returnResultLast: ti = tcap_ism_lookup(td, comp->choice.returnResultLast.invokeID); /* ISM active? */ /* RR-L.recv CCO -> ISM */ rc = tcap_ism_rr_l_recv(ti, &comp->choice.returnResultLast, is_last_component); break; case Component_PR_returnError: ti = tcap_ism_lookup(td, comp->choice.returnError.invokeID); /* ISM active? */ /* RE.recv CCO -> ISM */ rc = tcap_ism_re_recv(ti, &comp->choice.returnError, is_last_component); break; case Component_PR_reject: /* TC-R-REJECT or TC-U-REJECT.ind (TCU <- CHA) */ rc = tcap_tcu_tc_r_rej_ind(ti, &ti->invoke_id, 0 /* FIXME problem */, is_last_component); break; case Component_PR_NOTHING: break; } } } /* Generate REJ component (CCO <- ISM) */ int tcap_cco_gen_rej(struct tcap_invocation *ti) { /* FIXME: Assemble REJ component */ /* TC-L-REJ.ind (TCU <- CHA) */ return tcap_tcu_tc_l_rej_ind(ti, &ti->invoke_id, 0 /* FIXME problem */); } /* Return list of pending components for given dialogue */ ComponentPortion_t *tcap_cha_req_components(struct tcap_dialogue *td) { ComponentPortion_t *cp = td->pend_comp; Component_t *comp; struct tcap_invocation *ti; unsigned int i; /* iterate over list of components, find INV components and call * tcap_ism_op_sent() */ for (i = 0; i < cp->list.count; i++) { comp = cp->list.array[i]; if (comp->present != Component_PR_invoke) continue; ti = tcap_ism_lookup(td, comp->choice.invoke.invokeID); if (!ti) continue; tcap_ism_op_sent(ti); } td->pend_comp = NULL; return cp; }