diff options
author | Sergey Kostanbaev <sergey.kostanbaev@gmail.com> | 2015-10-23 20:27:35 +0300 |
---|---|---|
committer | Ivan Kluchnikov <kluchnikovi@gmail.com> | 2017-02-07 17:29:15 +0300 |
commit | c9c4293847461391eed0e93325e8ccee82d78ca3 (patch) | |
tree | 38809c4aae1eaf78176ff858c5d4cb46d4e60113 | |
parent | 6b986c24228a4cc83b22e1d8aae22b94fe36e6f2 (diff) |
modify USSD structures to support external handling
-rw-r--r-- | include/osmocom/gsm/gsm0480.h | 23 | ||||
-rw-r--r-- | src/gsm/gsm0480.c | 127 |
2 files changed, 143 insertions, 7 deletions
diff --git a/include/osmocom/gsm/gsm0480.h b/include/osmocom/gsm/gsm0480.h index 6ca23e98..c4e5286b 100644 --- a/include/osmocom/gsm/gsm0480.h +++ b/include/osmocom/gsm/gsm0480.h @@ -5,7 +5,7 @@ #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_04_80.h> -#define MAX_LEN_USSD_STRING 31 +#define MAX_LEN_USSD_STRING 182 /* deprecated */ struct ussd_request { @@ -19,13 +19,24 @@ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *request) OSMO_DEPRECATED("Use gsm0480_decode_ss_request() instead"); struct ss_request { - uint8_t opcode; - uint8_t ss_code; + uint8_t transaction_id; /**< L3 transaction ID */ + uint8_t message_type; /**< Message type 2.2 */ + + uint8_t component_type; /**< Component type 3.6.2 */ + uint8_t invoke_id; /**< Invoke id 3.6.3 */ + + union { + uint8_t opcode; /**< Operational code 3.6.4 */ + uint8_t error_code; /**< Error code 3.6.6 */ + uint8_t problem_code; /**< Problem code 3.6.7 */ + }; + + uint8_t ussd_text_language; + uint8_t ussd_text_len; uint8_t ussd_text[MAX_LEN_USSD_STRING + 1]; - uint8_t transaction_id; - uint8_t invoke_id; -}; + uint8_t ss_code; /**< parameters of a Interrogate/Activate/DeactivateSS Request */ +}; int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *request); diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c index 3c23f6fc..c6c6f7ab 100644 --- a/src/gsm/gsm0480.c +++ b/src/gsm/gsm0480.c @@ -196,12 +196,18 @@ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req); static int parse_ss_info_elements(const uint8_t *ussd_ie, uint16_t len, struct ss_request *req); +static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len, + struct ss_request *req); static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, struct ss_request *req); static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, struct ss_request *req); +static int parse_ss_return_result(const uint8_t *rr_data, uint16_t length, + struct ss_request *req); static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, struct ss_request *req); +static int parse_process_uss_data(const uint8_t *uss_req_data, uint16_t length, + struct ss_request *req); static int parse_ss_for_bs_req(const uint8_t *ss_req_data, uint16_t length, struct ss_request *req); @@ -270,6 +276,7 @@ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request int rc = 1; uint8_t msg_type = hdr->msg_type & 0x3F; /* message-type - section 3.4 */ + req->message_type = msg_type; switch (msg_type) { case GSM0480_MTYPE_RELEASE_COMPLETE: LOGP(0, LOGL_DEBUG, "SS Release Complete\n"); @@ -277,9 +284,11 @@ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request req->ussd_text[0] = 0xFF; break; case GSM0480_MTYPE_REGISTER: - case GSM0480_MTYPE_FACILITY: rc &= parse_ss_info_elements(&hdr->data[0], len - sizeof(*hdr), req); break; + case GSM0480_MTYPE_FACILITY: + rc &= parse_ss_facility(&hdr->data[0], len - sizeof(*hdr), req); + break; default: LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n", hdr->msg_type); @@ -290,6 +299,18 @@ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request return rc; } +static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len, + struct ss_request *req) +{ + uint8_t facility_length; + + facility_length = ss_facility[0]; + if (len - 1 < facility_length) + return 0; + + return parse_facility_ie(ss_facility + 1, facility_length, req); +} + static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len, struct ss_request *req) { @@ -340,6 +361,8 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, return 0; } + req->component_type = component_type; + switch (component_type) { case GSM0480_CTYPE_INVOKE: rc &= parse_ss_invoke(facility_ie+2, @@ -347,10 +370,17 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, req); break; case GSM0480_CTYPE_RETURN_RESULT: + rc &= parse_ss_return_result(facility_ie+2, + component_length, + req); break; case GSM0480_CTYPE_RETURN_ERROR: + // TODO Error codes + LOGP(0, LOGL_DEBUG, "Ignored GSM0480_CTYPE_RETURN_ERROR"); break; case GSM0480_CTYPE_REJECT: + // TODO rejects + LOGP(0, LOGL_DEBUG, "Ignored GSM0480_CTYPE_REJECT"); break; default: LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility " @@ -364,6 +394,57 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, return rc; } +/* Parse an Return Result component - see table 3.4 */ +static int parse_ss_return_result(const uint8_t *rr_data, uint16_t length, + struct ss_request *req) +{ + int rc = 1; + uint8_t offset; + + if (length < 3) + return 0; + + /* mandatory part */ + if (rr_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag " + "0x%02x (expecting Invoke ID tag)\n", rr_data[0]); + } + + offset = rr_data[1] + 2; + req->invoke_id = rr_data[2]; + + if (offset < length) { + if (rr_data[offset] != GSM_0480_SEQUENCE_TAG) + return 0; + if (offset + 2 > length) + return 0; + offset += 2; + + uint8_t operation_code = rr_data[offset+2]; + req->opcode = operation_code; + switch (operation_code) { + case GSM0480_OP_CODE_USS_NOTIFY: + case GSM0480_OP_CODE_USS_REQUEST: + case GSM0480_OP_CODE_PROCESS_USS_REQ: + rc = parse_process_uss_req(rr_data + offset + 3, + length - offset - 3, + req); + break; + case GSM0480_OP_CODE_PROCESS_USS_DATA: + rc = parse_process_uss_data(rr_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; + } + } + return rc; +} + /* Parse an Invoke component - see table 3.3 */ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, struct ss_request *req) @@ -398,11 +479,18 @@ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, uint8_t operation_code = invoke_data[offset+2]; req->opcode = operation_code; switch (operation_code) { + case GSM0480_OP_CODE_USS_NOTIFY: + case GSM0480_OP_CODE_USS_REQUEST: case GSM0480_OP_CODE_PROCESS_USS_REQ: rc = parse_process_uss_req(invoke_data + offset + 3, length - offset - 3, req); break; + case GSM0480_OP_CODE_PROCESS_USS_DATA: + rc = parse_process_uss_data(invoke_data + offset + 3, + length - offset - 3, + req); + break; case GSM0480_OP_CODE_ACTIVATE_SS: case GSM0480_OP_CODE_DEACTIVATE_SS: case GSM0480_OP_CODE_INTERROGATE_SS: @@ -426,6 +514,30 @@ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, return rc; } +static int parse_process_uss_data(const uint8_t *uss_req_data, uint16_t length, + struct ss_request *req) +{ + uint8_t num_chars; + + /* we need at least that much */ + if (length < 3) + return 0; + + if (uss_req_data[0] != ASN1_IA5_STRING_TAG) + return 0; + + num_chars = uss_req_data[1]; + if (num_chars > length - 2) + return 0; + if (num_chars > MAX_LEN_USSD_STRING) + num_chars = MAX_LEN_USSD_STRING; + + req->ussd_text_language = 1; + req->ussd_text_len = num_chars; + memcpy(req->ussd_text, uss_req_data + 2, num_chars); + return 1; +} + /* Parse the parameters of a Process UnstructuredSS Request */ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, struct ss_request *req) @@ -452,6 +564,19 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, gsm_7bit_decode_n_ussd((char *)req->ussd_text, sizeof(req->ussd_text), &(uss_req_data[7]), num_chars); + + req->ussd_text_language = 1; + req->ussd_text_len = num_chars; + rc = 1; + } else if (uss_req_data[5] == ASN1_OCTET_STRING_TAG) { + num_chars = uss_req_data[6]; + /* Prevent a mobile-originated buffer-overrun! */ + if (num_chars > MAX_LEN_USSD_STRING) + num_chars = MAX_LEN_USSD_STRING; + + req->ussd_text_language = dcs; + req->ussd_text_len = num_chars; + memcpy(req->ussd_text, &(uss_req_data[7]), num_chars); rc = 1; } } |