From 0b28bf0aab0b4f01d3ceb91fe241bafd9cff900d Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 28 May 2010 20:26:23 +0000 Subject: [PATCH] ETSI Malicious Call ID support. Add the ability to report malicious callers. Relevant specification: EN 300 180 Review: https://reviewboard.asterisk.org/r/575/ git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1757 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 58 ++++++++++++++++ pri_facility.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ pri_internal.h | 1 + rose.c | 12 ++++ rose.h | 3 + rosetest.c | 7 ++ 6 files changed, 264 insertions(+) diff --git a/libpri.h b/libpri.h index e5d3ac9..4573daa 100644 --- a/libpri.h +++ b/libpri.h @@ -545,6 +545,8 @@ struct pri_rerouting_data { #define PRI_SUBCMD_AOC_E 20 /*!< Advice Of Charge End information */ //#define PRI_SUBCMD_AOC_CHARGING_REQ 21 /*!< Advice Of Charge Request information */ //#define PRI_SUBCMD_AOC_CHARGING_REQ_RSP 22 /*!< Advice Of Charge Request Response information */ +#define PRI_SUBCMD_MCID_REQ 23 /*!< Malicious Call ID Request */ +#define PRI_SUBCMD_MCID_RSP 24 /*!< Malicious Call ID Request response */ #if defined(STATUS_REQUEST_PLACE_HOLDER) struct pri_subcmd_status_request { @@ -875,6 +877,38 @@ struct pri_subcmd_aoc_e { struct pri_aoc_e_charging_association associated; }; +struct pri_subcmd_mcid_req { + /*! + * \brief Information libpri knows about the malicious caller. + * \note For the convenience of the upper layer. This information + * may be incomplete if the upper layer redacted some caller + * information because it was restricted. + */ + struct pri_party_id originator; + /*! \brief Information libpri knows about the callee. */ + struct pri_party_id answerer; +}; + +struct pri_subcmd_mcid_rsp { + /*! + * \brief MCID request response status. + * \details + * success(0), + * timeout(1), + * error(2), + * reject(3) + */ + int status; + /*! + * \brief Failure code that can be converted to a string to further + * explain the non-timeout failure. + * \note Valid when status is error or reject. + * \note Use pri_facility_error2str() to convert the error_code. + * \note Use pri_facility_reject2str() to convert the reject_code. + */ + int fail_code; +}; + struct pri_subcommand { /*! PRI_SUBCMD_xxx defined values */ int cmd; @@ -903,6 +937,8 @@ struct pri_subcommand { struct pri_subcmd_aoc_s aoc_s; struct pri_subcmd_aoc_d aoc_d; struct pri_subcmd_aoc_e aoc_e; + struct pri_subcmd_mcid_req mcid_req; + struct pri_subcmd_mcid_rsp mcid_rsp; } u; }; @@ -1734,6 +1770,28 @@ int pri_status_req(struct pri *ctrl, int request_id, const struct pri_sr *req); void pri_status_req_rsp(struct pri *ctrl, int invoke_id, int status); #endif /* defined(STATUS_REQUEST_PLACE_HOLDER) */ +/*! + * \brief Set the Malicious Call ID feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable MCID feature. + * + * \return Nothing + */ +void pri_mcid_enable(struct pri *ctrl, int enable); + +/*! + * \brief Send the MCID request message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. You should wait for a PRI_SUBCMD_MCID_RSP + * to continue clearing the call if it was in progress. + * \retval -1 on error. + */ +int pri_mcid_req_send(struct pri *ctrl, q931_call *call); + /*! * \brief Set the call completion feature enable flag. * diff --git a/pri_facility.c b/pri_facility.c index 418a269..5194ae1 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -3541,6 +3541,151 @@ int pri_transfer_rsp(struct pri *ctrl, q931_call *call, int invoke_id, int is_su } } +/*! + * \internal + * \brief MCIDRequest response callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \return TRUE if no more responses are expected. + */ +static int mcid_req_response(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const struct apdu_msg_data *msg) +{ + struct pri_subcommand *subcmd; + int status; + int fail_code; + + switch (reason) { + case APDU_CALLBACK_REASON_TIMEOUT: + status = 1;/* timeout */ + fail_code = 0; + break; + case APDU_CALLBACK_REASON_MSG_RESULT: + status = 0;/* success */ + fail_code = 0; + break; + case APDU_CALLBACK_REASON_MSG_ERROR: + status = 2;/* error */ + fail_code = msg->response.error->code; + break; + case APDU_CALLBACK_REASON_MSG_REJECT: + status = 3;/* reject */ + fail_code = 0; + fail_code = msg->response.reject->code; + break; + default: + return 1; + } + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + /* Indicate that our MCID request has completed. */ + subcmd->cmd = PRI_SUBCMD_MCID_RSP; + subcmd->u.mcid_rsp.status = status; + subcmd->u.mcid_rsp.fail_code = fail_code; + } else { + /* Oh, well. */ + } + return 1; +} + +/*! + * \internal + * \brief Encode a MCIDRequest message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param call Call leg from which to encode message. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_mcid_req(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call) +{ + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = get_invokeid(ctrl); + msg.operation = ROSE_ETSI_MCIDRequest; + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue a MCID request message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode message. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_mcid_req_encode(struct pri *ctrl, q931_call *call) +{ + unsigned char buffer[256]; + unsigned char *end; + struct apdu_callback_data response; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_mcid_req(ctrl, buffer, buffer + sizeof(buffer), call); + break; + default: + return -1; + } + if (!end) { + return -1; + } + + memset(&response, 0, sizeof(response)); + response.invoke_id = ctrl->last_invoke; + response.timeout_time = ctrl->timers[PRI_TIMER_T_RESPONSE]; + response.callback = mcid_req_response; + + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, &response); +} + +int pri_mcid_req_send(struct pri *ctrl, q931_call *call) +{ + if (!ctrl || !call) { + return -1; + } + if (call->cc.originated) { + /* We can only send MCID if we answered the call. */ + return -1; + } + + if (rose_mcid_req_encode(ctrl, call) || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for MCID request message.\n"); + return -1; + } + + return 0; +} + +void pri_mcid_enable(struct pri *ctrl, int enable) +{ + if (ctrl) { + ctrl = PRI_MASTER(ctrl); + ctrl->mcid_support = enable ? 1 : 0; + } +} + /*! * \brief Handle the ROSE reject message. * @@ -4375,6 +4520,44 @@ void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie call->cc.record = cc_record; pri_cc_event(ctrl, call, cc_record, CC_EVENT_AVAILABLE); break; + case ROSE_ETSI_MCIDRequest: + if (q931_is_dummy_call(call)) { + /* Don't even dignify this with a response. */ + break; + } + if (!PRI_MASTER(ctrl)->mcid_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + if (!call->cc.originated) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotIncomingCall); + break; + } + switch (call->ourcallstate) { + case Q931_CALL_STATE_ACTIVE: + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_DISCONNECT_REQUEST:/* XXX We are really in the wrong state for this mode. */ + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotAvailable); + break; + } + + subcmd->cmd = PRI_SUBCMD_MCID_REQ; + q931_party_id_copy_to_pri(&subcmd->u.mcid_req.originator, &call->local_id); + q931_party_id_copy_to_pri(&subcmd->u.mcid_req.answerer, &call->remote_id); + + send_facility_result_ok(ctrl, call, invoke->invoke_id); + break; + default: + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_InvalidCallState); + break; + } + break; case ROSE_QSIG_CallingName: /* CallingName is put in remote_id.name */ rose_copy_name_to_q931(ctrl, &call->remote_id.name, diff --git a/pri_internal.h b/pri_internal.h index 8f410f0..9a26c6b 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -109,6 +109,7 @@ struct pri { unsigned int transfer_support:1;/* TRUE if the upper layer supports ECT */ unsigned int aoc_support:1;/* TRUE if can send AOC events to the upper layer. */ unsigned int manual_connect_ack:1;/* TRUE if the CONNECT_ACKNOWLEDGE is sent with API call */ + unsigned int mcid_support:1;/* TRUE if the upper layer supports MCID */ /* MDL variables */ int mdl_error; diff --git a/rose.c b/rose.c index 04f2ad0..65341cb 100644 --- a/rose.c +++ b/rose.c @@ -531,6 +531,16 @@ static const struct rose_convert_msg rose_etsi_msgs[] = { rose_enc_etsi_CCNR_T_Request_ARG, rose_enc_etsi_CCNR_T_Request_RES, rose_dec_etsi_CCNR_T_Request_ARG, rose_dec_etsi_CCNR_T_Request_RES }, + + /* + * localValue's from MCID-Operations + * {ccitt identified-organization etsi(0) 130 operations-and-errors(1)} + */ + { + ROSE_ETSI_MCIDRequest, NULL, 3, + NULL, NULL, + NULL, NULL + }, /* *INDENT-ON* */ }; @@ -1391,6 +1401,8 @@ const char *rose_operation2str(enum rose_operation operation) { ROSE_ETSI_CCNR_T_Request, "ROSE_ETSI_CCNR_T_Request" }, + { ROSE_ETSI_MCIDRequest, "ROSE_ETSI_MCIDRequest" }, + { ROSE_QSIG_CallingName, "ROSE_QSIG_CallingName" }, { ROSE_QSIG_CalledName, "ROSE_QSIG_CalledName" }, { ROSE_QSIG_ConnectedName, "ROSE_QSIG_ConnectedName" }, diff --git a/rose.h b/rose.h index f9b4607..4d5132f 100644 --- a/rose.h +++ b/rose.h @@ -134,6 +134,9 @@ enum rose_operation { /* ETSI CCNR-private-networks-Operations-and-Errors */ ROSE_ETSI_CCNR_T_Request, /*!< Invoke/Result */ + /* ETSI MCID-Operations */ + ROSE_ETSI_MCIDRequest, /*!< Invoke/Result */ + /* Q.SIG Name-Operations */ ROSE_QSIG_CallingName, /*!< Invoke only */ ROSE_QSIG_CalledName, /*!< Invoke only */ diff --git a/rosetest.c b/rosetest.c index b8f8acc..49fd02b 100644 --- a/rosetest.c +++ b/rosetest.c @@ -1389,6 +1389,13 @@ static const struct rose_message rose_etsi_msgs[] = { .component.result.invoke_id = 53, .component.result.args.etsi.CCNR_T_Request.retention_supported = 1, }, + + /* MCID */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_MCIDRequest, + .component.invoke.invoke_id = 54, + }, /* *INDENT-ON* */ };