aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Kostanbaev <sergey.kostanbaev@gmail.com>2015-10-23 20:27:35 +0300
committerIvan Kluchnikov <kluchnikovi@gmail.com>2017-02-07 17:29:15 +0300
commitc9c4293847461391eed0e93325e8ccee82d78ca3 (patch)
tree38809c4aae1eaf78176ff858c5d4cb46d4e60113
parent6b986c24228a4cc83b22e1d8aae22b94fe36e6f2 (diff)
modify USSD structures to support external handling
-rw-r--r--include/osmocom/gsm/gsm0480.h23
-rw-r--r--src/gsm/gsm0480.c127
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;
}
}