This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.
Sergey Kostanbaev sergey.kostanbaev at gmail.comFrom: Sergey Kostanbaev <Sergey.Kostanbaev at gmail.com> Add ss_header structure to store L3 specific data and modify ss_request to store GSM0480 MAP payload. Do not decode strings inside ss_request, leave it for upper level to support other languages. Parse Return Result accordingly. --- include/osmocom/gsm/gsm0480.h | 34 ++++++-- src/gsm/gsm0480.c | 186 ++++++++++++++++++++++++++++++++++-------- src/gsm/libosmogsm.map | 1 + tests/ussd/ussd_test.c | 23 +++++- 4 files changed, 198 insertions(+), 46 deletions(-) diff --git a/include/osmocom/gsm/gsm0480.h b/include/osmocom/gsm/gsm0480.h index deac322..7cacc84 100644 --- a/include/osmocom/gsm/gsm0480.h +++ b/include/osmocom/gsm/gsm0480.h @@ -5,7 +5,8 @@ #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 +#define MAX_ASN1_LEN_USSD_STRING 160 /* deprecated */ struct ussd_request { @@ -18,16 +19,35 @@ struct ussd_request { 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_header { + uint8_t transaction_id; /**< L3 transaction ID */ + uint8_t message_type; /**< Message type 2.2 */ + + uint8_t component_offset; /**< Component offset in L3 */ + uint8_t component_length; /**< Component length in L3 */ +}; + struct ss_request { - uint8_t opcode; - uint8_t ss_code; - uint8_t ussd_text[MAX_LEN_USSD_STRING + 1]; - uint8_t transaction_id; - uint8_t invoke_id; + 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_len; + uint8_t ussd_text_language; + uint8_t ussd_text[MAX_ASN1_LEN_USSD_STRING + 1]; + + uint8_t ss_code; /**< parameters of a Interrogate/Activate/DeactivateSS Request */ }; +int gsm0480_parse_ss_facility(const uint8_t* data, uint8_t len, struct ss_request *out); + int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, - struct ss_request *request); + struct ss_header *request); struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text); struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text); diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c index 9fc77a0..e073602 100644 --- a/src/gsm/gsm0480.c +++ b/src/gsm/gsm0480.c @@ -193,25 +193,44 @@ struct msgb *gsm0480_create_notifySS(const char *text) /* Forward declarations */ static int parse_ss(const struct gsm48_hdr *hdr, - uint16_t len, struct ss_request *req); + uint16_t len, struct ss_header *req); static int parse_ss_info_elements(const uint8_t *ussd_ie, uint16_t len, - struct ss_request *req); + struct ss_header *req); static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len, - struct ss_request *req); + struct ss_header *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_ss_for_bs_req(const uint8_t *ss_req_data, uint16_t length, struct ss_request *req); +static const uint8_t *parse_asn1_small_len(const uint8_t *codedlen, uint16_t available, + uint8_t *out_len) +{ + uint8_t lenb = codedlen[0]; + if (lenb < 0x80 && available > 0) { + *out_len = lenb; + return &codedlen[1]; + } else if (lenb == 0x81 && available > 1) { + *out_len = codedlen[1]; + return &codedlen[2]; + } + + return NULL; +} + /* Decode a mobile-originated USSD-request message */ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req) { + struct ss_header sshdr; struct ss_request ss; int rc = 0; @@ -225,16 +244,31 @@ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, if (gsm48_hdr_pdisc(hdr) == GSM48_PDISC_NC_SS) { req->transaction_id = hdr->proto_discr & 0x70; - ss.transaction_id = req->transaction_id; - rc = parse_ss(hdr, len, &ss); + sshdr.transaction_id = req->transaction_id; + rc = parse_ss(hdr, len, &sshdr); + if (rc) { + /* this if block was inside parse_ss() */ + if (sshdr.message_type == GSM0480_MTYPE_RELEASE_COMPLETE) { + /* could also parse out the optional Cause/Facility data */ + ss.ussd_text[0] = 0xFF; + } + rc = gsm0480_parse_ss_facility(hdr->data + sshdr.component_offset, + sshdr.component_length, + &ss); + } /* convert from ss_request to legacy ussd_request */ - req->transaction_id = ss.transaction_id; + req->transaction_id = sshdr.transaction_id; req->invoke_id = ss.invoke_id; - if (ss.ussd_text[0] == 0xFF) + if (ss.ussd_text_language != 0x0f || + ss.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ) { + req->text[0] = '\0'; - else { - memcpy(req->text, ss.ussd_text, sizeof(req->text)); + } else { + gsm_7bit_decode_n_ussd(req->text, + sizeof(req->text), + ss.ussd_text, ss.ussd_text_len); + req->text[sizeof(req->text)-1] = '\0'; } } @@ -247,7 +281,7 @@ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, /* Decode a mobile-originated SS request message */ int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, - struct ss_request *req) + struct ss_header *req) { int rc = 0; if (gsm48_hdr_pdisc(hdr) == GSM48_PDISC_NC_SS) { @@ -261,16 +295,17 @@ int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, return rc; } -static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req) +static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_header *req) { int rc = 1; uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */ + req->message_type = msg_type; + req->component_offset = 0; + switch (msg_type) { case GSM0480_MTYPE_RELEASE_COMPLETE: LOGP(0, LOGL_DEBUG, "SS Release Complete\n"); - /* could also parse out the optional Cause/Facility data */ - req->ussd_text[0] = 0xFF; break; case GSM0480_MTYPE_REGISTER: rc &= parse_ss_info_elements(&hdr->data[0], len - sizeof(*hdr), req); @@ -289,7 +324,7 @@ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request } static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len, - struct ss_request *req) + struct ss_header *req) { uint8_t facility_length; @@ -297,13 +332,15 @@ static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len, if (len - 1 < facility_length) return 0; - return parse_facility_ie(ss_facility + 1, facility_length, req); + req->component_offset += 1; + req->component_length = facility_length; + return 1; } static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len, - struct ss_request *req) + struct ss_header *req) { - int rc = -1; + int rc = 1; /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ uint8_t iei; uint8_t iei_length; @@ -319,7 +356,8 @@ static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len, case GSM48_IE_CAUSE: break; case GSM0480_IE_FACILITY: - rc = parse_facility_ie(ss_ie + 2, iei_length, req); + req->component_offset += 2; + req->component_length = iei_length; break; case GSM0480_IE_SS_VERSION: break; @@ -333,6 +371,11 @@ static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len, return rc; } +int gsm0480_parse_ss_facility(const uint8_t* data, uint8_t len, struct ss_request *out) +{ + return parse_facility_ie(data, len, out); +} + static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, struct ss_request *req) { @@ -342,25 +385,38 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, while (offset + 2 <= length) { /* Component Type tag - table 3.7 */ uint8_t component_type = facility_ie[offset]; - uint8_t component_length = facility_ie[offset+1]; + uint8_t component_length = 0; + const uint8_t *nxt = parse_asn1_small_len(facility_ie + offset + 1, + length - offset - 1, + &component_length); /* size check */ - if (offset + 2 + component_length > length) { + offset = nxt - facility_ie; + if (nxt == NULL || offset + component_length > length) { LOGP(0, LOGL_ERROR, "Component does not fit.\n"); return 0; } + req->component_type = component_type; + switch (component_type) { case GSM0480_CTYPE_INVOKE: - rc &= parse_ss_invoke(facility_ie+2, + rc &= parse_ss_invoke(nxt, component_length, req); break; case GSM0480_CTYPE_RETURN_RESULT: + rc &= parse_ss_return_result(nxt, + 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 " @@ -368,12 +424,58 @@ static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, rc = 0; break; } - offset += (component_length+2); + offset += component_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; + 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) @@ -408,6 +510,8 @@ 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, @@ -441,27 +545,37 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, struct ss_request *req) { int rc = 0; - int num_chars; - uint8_t dcs; - - + uint8_t dcs, seq_block_len, num_chars; + const uint8_t *next_ptr; /* we need at least that much */ if (length < 8) return 0; 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; + next_ptr = parse_asn1_small_len(&uss_req_data[1], length - 1, &seq_block_len); + if (next_ptr == NULL || seq_block_len < 6) + return 0; + + if (next_ptr[0] == ASN1_OCTET_STRING_TAG) { + if (next_ptr[1] != 1) + return 0; + + dcs = next_ptr[2]; + + if (next_ptr[3] == ASN1_OCTET_STRING_TAG) { + next_ptr = parse_asn1_small_len(&next_ptr[4], + seq_block_len - 4, &num_chars); + if (next_ptr == NULL) + return 0; + /* Prevent a mobile-originated buffer-overrun! */ - if (num_chars > MAX_LEN_USSD_STRING) - num_chars = MAX_LEN_USSD_STRING; - gsm_7bit_decode_n_ussd((char *)req->ussd_text, - sizeof(req->ussd_text), - &(uss_req_data[7]), num_chars); + if (num_chars > MAX_ASN1_LEN_USSD_STRING) + num_chars = MAX_ASN1_LEN_USSD_STRING; + + req->ussd_text_language = dcs; + req->ussd_text_len = num_chars; + memcpy(req->ussd_text, next_ptr, num_chars); rc = 1; } } diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 6886a6c..04cc6c4 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -50,6 +50,7 @@ gsm0480_create_unstructuredSS_Notify; gsm0480_create_ussd_resp; gsm0480_decode_ussd_request; gsm0480_decode_ss_request; +gsm0480_parse_ss_facility; gsm0480_wrap_facility; gsm0480_wrap_invoke; diff --git a/tests/ussd/ussd_test.c b/tests/ussd/ussd_test.c index f798e37..ac35baa 100644 --- a/tests/ussd/ussd_test.c +++ b/tests/ussd/ussd_test.c @@ -39,12 +39,18 @@ static int parse_ussd(const uint8_t *_data, int len) uint8_t *data; int rc; struct ss_request req; + struct ss_header reqhdr; struct gsm48_hdr *hdr; data = malloc(len); memcpy(data, _data, len); hdr = (struct gsm48_hdr *) &data[0]; - rc = gsm0480_decode_ss_request(hdr, len, &req); + rc = gsm0480_decode_ss_request(hdr, len, &reqhdr); + if (rc) { + rc = gsm0480_parse_ss_facility(hdr->data + reqhdr.component_offset, + reqhdr.component_length, + &req); + } free(data); return rc; @@ -55,13 +61,20 @@ static int parse_mangle_ussd(const uint8_t *_data, int len) uint8_t *data; int rc; struct ss_request req; + struct ss_header reqhdr; struct gsm48_hdr *hdr; data = malloc(len); memcpy(data, _data, len); hdr = (struct gsm48_hdr *) &data[0]; hdr->data[1] = len - sizeof(*hdr) - 2; - rc = gsm0480_decode_ss_request(hdr, len, &req); + rc = gsm0480_decode_ss_request(hdr, len, &reqhdr); + if (rc) { + rc = gsm0480_parse_ss_facility(hdr->data + reqhdr.component_offset, + reqhdr.component_length, + &req); + } + free(data); return rc; @@ -114,13 +127,17 @@ static void test_7bit_ussd(const char *text, const char *encoded_hex, const char int main(int argc, char **argv) { struct ss_request req; + struct ss_header reqhdr; const int size = sizeof(ussd_request); int i; struct msgb *msg; osmo_init_logging(&info); - gsm0480_decode_ss_request((struct gsm48_hdr *) ussd_request, size, &req); + gsm0480_decode_ss_request((struct gsm48_hdr *) ussd_request, size, &reqhdr); + gsm0480_parse_ss_facility(ussd_request + reqhdr.component_offset, + reqhdr.component_length, + &req); printf("Tested if it still works. Text was: %s\n", req.ussd_text); -- 1.9.1