From patchwork Fri Apr 22 12:41:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sergey Kostanbaev X-Patchwork-Id: 613583 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (lists.osmocom.org [IPv6:2a01:4f8:191:444b::2:7]) by ozlabs.org (Postfix) with ESMTP id 3qrwGm1qfcz9t3h for ; Fri, 22 Apr 2016 22:41:36 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=zH2a3h+Y; dkim-atps=neutral Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id C32F313151; Fri, 22 Apr 2016 12:41:29 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail-wm0-x234.google.com (mail-wm0-x234.google.com [IPv6:2a00:1450:400c:c09::234]) by lists.osmocom.org (Postfix) with ESMTP id 0FB3F13116 for ; Fri, 22 Apr 2016 12:41:26 +0000 (UTC) Received: by mail-wm0-x234.google.com with SMTP id n3so25099930wmn.0 for ; Fri, 22 Apr 2016 05:41:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HeYdRVaHtNFGXOU+b2EyFa5BDSQqcBtyMfS03+Wnsag=; b=zH2a3h+YTaUovYDWeduUfDn429DQQFp57fShwisAjrwSEd+LXAsa62tfs33b91VTcZ P+ZMgBv+qqhCTYTy7c24Vg1DgvrCarcAKW/c0woPSKanB1/Jg8hwSy1wuCJgVPmf939t Sy1PWAPBi8hKM/xYAPsFI16z4rw2fgz6dffAMqRQ2wNXpUjNNs5KWT+53poqNa19gh6v d+i0vRTUjfCJd6NQuhsm/BX8kV7r9eeudftKZBI264UbbLmVF2v8xJAtiSoPCRV0B0tP 31/ou0bGuGdq33DeUYlHChZZzJ6c4QnYs00NZHt/aoQR4XLlS8RAQfjjd4q/+ZQ934o0 TU3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=HeYdRVaHtNFGXOU+b2EyFa5BDSQqcBtyMfS03+Wnsag=; b=gFRuWjbbopAB7vZuSezevQWyFPAzYeyrVEcPCzXSgrkLUON1avpGhLOMjlojsLFXFi Q0FuLXb+z53yeaUvuShcTFOXrGP7lmrD3RKeJmTA58I5WI78Q3kvlSDEgDo0EI9R4uei 3QzTNUNKjSPSWBl8VVB7ctvfhAO6wCyn8fUpXjGmx8GASi7SDK1G+huCQKYxGgsim45x 3XHoqeR9iGKDO5qKyO6sOSS5CudUdV8QB/DODXA36sCSOTntcsZRwK/yOyOY1eXT6i20 bD+a4RriwcW7Mj2to4izeAq+jk1ei+s7n0s0CpwD6It+kmVzlzfzPpW+iQGFnq3ouqiS 5B0w== X-Gm-Message-State: AOPr4FXO14tUNwFvv98CoGLnHD/Guzz+99hWJ6FAB0jRqwjvlucgSzJXqm/lMApDP9ElQg== X-Received: by 10.28.59.7 with SMTP id i7mr3715315wma.66.1461328886391; Fri, 22 Apr 2016 05:41:26 -0700 (PDT) Received: from serg-X55SR.in-berlin.de ([185.11.247.108]) by smtp.gmail.com with ESMTPSA id r8sm3220204wme.14.2016.04.22.05.41.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 22 Apr 2016 05:41:25 -0700 (PDT) From: Sergey Kostanbaev To: openbsc@lists.osmocom.org Subject: [PATCH 3/4] gsm0480: Separate ss_request struct to header and payload Date: Fri, 22 Apr 2016 14:41:14 +0200 Message-Id: <1461328875-8253-3-git-send-email-sergey.kostanbaev@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1461328875-8253-1-git-send-email-sergey.kostanbaev@gmail.com> References: <1461328875-8253-1-git-send-email-sergey.kostanbaev@gmail.com> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: "Development of OpenBSC, OsmoBSC, OsmoNITB, OsmoCSCN" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" From: Sergey Kostanbaev 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 #include -#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);