@@ -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);
@@ -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;
}
}
@@ -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;
@@ -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);
From: Sergey Kostanbaev <Sergey.Kostanbaev@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(-)