Add special 7-bit encoding and decoding functions for USSD coding
Handling 7-bit coding is a little different for USSD, as TS 03.38
states:
To avoid the situation where the receiving entity confuses 7 binary
zero pad bits as the @ character, the carriage return or <CR>
character shall be used for padding in this situation [...].
If <CR> is intended to be the last character and the message
(including the wanted <CR>) ends on an octet boundary, then another
<CR> must be added together with a padding bit 0. The receiving entity
will perform the carriage return function twice, but this will not
result in misoperation as the definition of <CR> [...] is identical to
the definition of <CR><CR>.
The receiving entity shall remove the final <CR> character where the
message ends on an octet boundary with <CR> as the last character.
Jacob has verified the fix with fakeBTS and the wireshark dissector.
Fixes: OW#947
Reviewed-by: Jacob Erlbeck <jerlbeck@sysmocom.de>
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index b9b3ed9..cc693fe 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -105,7 +105,7 @@
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
ussd_len_ptr = msgb_put(msg, 1);
data = msgb_put(msg, 0);
- len = gsm_7bit_encode(data, text);
+ gsm_7bit_encode_ussd(data, text, &len);
msgb_put(msg, len);
ussd_len_ptr[0] = len;
/* USSD-String } */
@@ -172,7 +172,7 @@
msgb_put_u8(msg, 0x82);
tmp_len = msgb_put(msg, 1);
data = msgb_put(msg, 0);
- len = gsm_7bit_encode(data, text);
+ gsm_7bit_encode_ussd(data, text, &len);
tmp_len[0] = len;
msgb_put(msg, len);
@@ -401,8 +401,8 @@
/* Prevent a mobile-originated buffer-overrun! */
if (num_chars > MAX_LEN_USSD_STRING)
num_chars = MAX_LEN_USSD_STRING;
- gsm_7bit_decode(req->text,
- &(uss_req_data[7]), num_chars);
+ gsm_7bit_decode_ussd(req->text,
+ &(uss_req_data[7]), num_chars);
rc = 1;
}
}
@@ -423,7 +423,7 @@
/* First put the payload text into the message */
ptr8 = msgb_put(msg, 0);
- response_len = gsm_7bit_encode(ptr8, text);
+ gsm_7bit_encode_ussd(ptr8, text, &response_len);
msgb_put(msg, response_len);
/* Then wrap it as an Octet String */
diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c
index 54b965e..3dd1537 100644
--- a/src/gsm/gsm_utils.c
+++ b/src/gsm/gsm_utils.c
@@ -172,6 +172,19 @@
return gsm_7bit_decode_hdr(text, user_data, septet_l, 0);
}
+int gsm_7bit_decode_ussd(char *text, const uint8_t *user_data, uint8_t length)
+{
+ int i;
+
+ gsm_7bit_decode_hdr(text, user_data, length, 0);
+ i = strlen(text);
+ /* remove last <CR>, if it fits up to the end of last octet */
+ if (i && (user_data[gsm_get_octet_len(length) - 1] >> 1) == '\r')
+ text[--i] = '\0';
+
+ return i;
+}
+
/* GSM 03.38 6.2.1 Prepare character packing */
int gsm_septet_encode(uint8_t *result, const char *data)
{
@@ -254,6 +267,24 @@
return gsm_7bit_encode_oct(result, data, &out);
}
+int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets)
+{
+ int y;
+
+ y = gsm_7bit_encode_oct(result, data, octets);
+ /* if last octet contains only one bit, add <CR> */
+ if (((y * 7) & 7) == 1)
+ result[(*octets) - 1] |= ('\r' << 1);
+ /* if last character is <CR> and completely fills last octet, add
+ * another <CR>. */
+ if (y && ((y * 7) & 7) == 0 && (result[(*octets) - 1] >> 1) == '\r') {
+ result[(*octets)++] = '\r';
+ y++;
+ }
+
+ return y;
+}
+
int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets)
{
int y = 0;
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 8a020db..1b985e1 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -132,8 +132,10 @@
gsm48_rr_att_tlvdef;
gsm_7bit_decode;
+gsm_7bit_decode_ussd;
gsm_7bit_decode_hdr;
gsm_7bit_encode;
+gsm_7bit_encode_ussd;
gsm_7bit_encode_oct;
gsm_arfcn2band;