diff options
-rw-r--r-- | include/osmocore/gsm0480.h | 11 | ||||
-rw-r--r-- | src/gsm0480.c | 188 |
2 files changed, 199 insertions, 0 deletions
diff --git a/include/osmocore/gsm0480.h b/include/osmocore/gsm0480.h index 52592f77..ae92c1f9 100644 --- a/include/osmocore/gsm0480.h +++ b/include/osmocore/gsm0480.h @@ -3,6 +3,17 @@ #include "msgb.h" +#define MAX_LEN_USSD_STRING 31 + +struct ussd_request { + char text[MAX_LEN_USSD_STRING + 1]; + uint8_t transaction_id; + uint8_t invoke_id; +}; + +int gsm0480_decode_ussd_request(const struct msgb *msg, + struct ussd_request *request); + struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text); struct msgb *gsm0480_create_notifySS(const char *text); diff --git a/src/gsm0480.c b/src/gsm0480.c index ffcbc449..7d607a95 100644 --- a/src/gsm0480.c +++ b/src/gsm0480.c @@ -25,6 +25,9 @@ #include <osmocore/gsm0480.h> #include <osmocore/gsm_utils.h> +#include <osmocore/logging.h> + +#include <osmocore/protocol/gsm_04_08.h> #include <osmocore/protocol/gsm_04_80.h> #include <string.h> @@ -188,3 +191,188 @@ struct msgb *gsm0480_create_notifySS(const char *text) return msg; } +/* Forward declarations */ +static int parse_ussd(uint8_t *ussd, struct ussd_request *req); +static int parse_ussd_info_elements(uint8_t *ussd_ie, + struct ussd_request *req); +static int parse_facility_ie(uint8_t *facility_ie, uint8_t length, + struct ussd_request *req); +static int parse_ss_invoke(uint8_t *invoke_data, uint8_t length, + struct ussd_request *req); +static int parse_process_uss_req(uint8_t *uss_req_data, uint8_t length, + struct ussd_request *req); + +/* Decode a mobile-originated USSD-request message */ +int gsm0480_decode_ussd_request(const struct msgb *msg, struct ussd_request *req) +{ + int rc = 0; + uint8_t *parse_ptr = msgb_l3(msg); + + if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) { + req->transaction_id = *parse_ptr & 0x70; + rc = parse_ussd(parse_ptr+1, req); + } + + if (!rc) + LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n"); + + return rc; +} + +static int parse_ussd(uint8_t *ussd, struct ussd_request *req) +{ + int rc = 1; + uint8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */ + + switch (msg_type) { + case GSM0480_MTYPE_RELEASE_COMPLETE: + LOGP(0, LOGL_DEBUG, "USS Release Complete\n"); + /* could also parse out the optional Cause/Facility data */ + req->text[0] = 0xFF; + break; + case GSM0480_MTYPE_REGISTER: + case GSM0480_MTYPE_FACILITY: + rc &= parse_ussd_info_elements(ussd+1, req); + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n", + ussd[0]); + rc = 0; + break; + } + + return rc; +} + +static int parse_ussd_info_elements(uint8_t *ussd_ie, struct ussd_request *req) +{ + int rc = -1; + /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ + uint8_t iei = ussd_ie[0]; + uint8_t iei_length = ussd_ie[1]; + + switch (iei) { + case GSM48_IE_CAUSE: + break; + case GSM0480_IE_FACILITY: + rc = parse_facility_ie(ussd_ie+2, iei_length, req); + break; + case GSM0480_IE_SS_VERSION: + break; + default: + LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n", + iei); + rc = 0; + break; + } + + return rc; +} + +static int parse_facility_ie(uint8_t *facility_ie, uint8_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset = 0; + + do { + /* Component Type tag - table 3.7 */ + uint8_t component_type = facility_ie[offset]; + uint8_t component_length = facility_ie[offset+1]; + + switch (component_type) { + case GSM0480_CTYPE_INVOKE: + rc &= parse_ss_invoke(facility_ie+2, + component_length, + req); + break; + case GSM0480_CTYPE_RETURN_RESULT: + break; + case GSM0480_CTYPE_RETURN_ERROR: + break; + case GSM0480_CTYPE_REJECT: + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility " + "Component Type 0x%02x\n", component_type); + rc = 0; + break; + } + offset += (component_length+2); + } while (offset < length); + + return rc; +} + +/* Parse an Invoke component - see table 3.3 */ +static int parse_ss_invoke(uint8_t *invoke_data, uint8_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset; + + /* mandatory part */ + if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag " + "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]); + } + + offset = invoke_data[1] + 2; + req->invoke_id = invoke_data[2]; + + /* optional part */ + if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID) + offset += invoke_data[offset+1] + 2; /* skip over it */ + + /* mandatory part */ + if (invoke_data[offset] == GSM0480_OPERATION_CODE) { + uint8_t operation_code = invoke_data[offset+2]; + switch (operation_code) { + case GSM0480_OP_CODE_PROCESS_USS_REQ: + rc = parse_process_uss_req(invoke_data + offset + 3, + length - offset - 3, + req); + break; + default: + LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x " + "is not yet handled\n", operation_code); + rc = 0; + break; + } + } else { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x " + "(expecting Operation Code tag)\n", + invoke_data[0]); + rc = 0; + } + + return rc; +} + +/* Parse the parameters of a Process UnstructuredSS Request */ +static int parse_process_uss_req(uint8_t *uss_req_data, uint8_t length, + struct ussd_request *req) +{ + int rc = 0; + int num_chars; + uint8_t dcs; + + if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) { + if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) { + dcs = uss_req_data[4]; + if ((dcs == 0x0F) && + (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) { + num_chars = (uss_req_data[6] * 8) / 7; + /* Prevent a mobile-originated buffer-overrun! */ + if (num_chars > MAX_LEN_USSD_STRING) + num_chars = MAX_LEN_USSD_STRING; + gsm_7bit_decode(req->text, + &(uss_req_data[7]), num_chars); + /* append null-terminator */ + req->text[num_chars+1] = 0; + rc = 1; + } + } + } + return rc; +} |