[PATCH 3/4] gsm0480: Separate ss_request struct to header and payload

Sergey Kostanbaev sergey.kostanbaev at gmail.com
Fri Apr 22 12:41:14 UTC 2016


From: 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



More information about the OpenBSC mailing list