mirror of https://gerrit.osmocom.org/libosmocore
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>
This commit is contained in:
parent
6bfa7445fc
commit
9597555a36
|
@ -57,8 +57,10 @@ const char *gsm_band_name(enum gsm_band band);
|
||||||
enum gsm_band gsm_band_parse(const char *mhz);
|
enum gsm_band gsm_band_parse(const char *mhz);
|
||||||
|
|
||||||
int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
|
int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length);
|
||||||
|
int gsm_7bit_decode_ussd(char *decoded, const uint8_t *user_data, uint8_t length);
|
||||||
int gsm_7bit_decode_hdr(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
|
int gsm_7bit_decode_hdr(char *decoded, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
|
||||||
int gsm_7bit_encode(uint8_t *result, const char *data);
|
int gsm_7bit_encode(uint8_t *result, const char *data);
|
||||||
|
int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets_written);
|
||||||
int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets_written);
|
int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets_written);
|
||||||
|
|
||||||
/* the three functions below are helper functions and here for the unit test */
|
/* the three functions below are helper functions and here for the unit test */
|
||||||
|
|
|
@ -105,7 +105,7 @@ struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *
|
||||||
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
|
msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
|
||||||
ussd_len_ptr = msgb_put(msg, 1);
|
ussd_len_ptr = msgb_put(msg, 1);
|
||||||
data = msgb_put(msg, 0);
|
data = msgb_put(msg, 0);
|
||||||
len = gsm_7bit_encode(data, text);
|
gsm_7bit_encode_ussd(data, text, &len);
|
||||||
msgb_put(msg, len);
|
msgb_put(msg, len);
|
||||||
ussd_len_ptr[0] = len;
|
ussd_len_ptr[0] = len;
|
||||||
/* USSD-String } */
|
/* USSD-String } */
|
||||||
|
@ -172,7 +172,7 @@ struct msgb *gsm0480_create_notifySS(const char *text)
|
||||||
msgb_put_u8(msg, 0x82);
|
msgb_put_u8(msg, 0x82);
|
||||||
tmp_len = msgb_put(msg, 1);
|
tmp_len = msgb_put(msg, 1);
|
||||||
data = msgb_put(msg, 0);
|
data = msgb_put(msg, 0);
|
||||||
len = gsm_7bit_encode(data, text);
|
gsm_7bit_encode_ussd(data, text, &len);
|
||||||
tmp_len[0] = len;
|
tmp_len[0] = len;
|
||||||
msgb_put(msg, len);
|
msgb_put(msg, len);
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
|
||||||
/* Prevent a mobile-originated buffer-overrun! */
|
/* Prevent a mobile-originated buffer-overrun! */
|
||||||
if (num_chars > MAX_LEN_USSD_STRING)
|
if (num_chars > MAX_LEN_USSD_STRING)
|
||||||
num_chars = MAX_LEN_USSD_STRING;
|
num_chars = MAX_LEN_USSD_STRING;
|
||||||
gsm_7bit_decode(req->text,
|
gsm_7bit_decode_ussd(req->text,
|
||||||
&(uss_req_data[7]), num_chars);
|
&(uss_req_data[7]), num_chars);
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
|
@ -423,7 +423,7 @@ struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const
|
||||||
|
|
||||||
/* First put the payload text into the message */
|
/* First put the payload text into the message */
|
||||||
ptr8 = msgb_put(msg, 0);
|
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);
|
msgb_put(msg, response_len);
|
||||||
|
|
||||||
/* Then wrap it as an Octet String */
|
/* Then wrap it as an Octet String */
|
||||||
|
|
|
@ -172,6 +172,19 @@ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l)
|
||||||
return gsm_7bit_decode_hdr(text, user_data, septet_l, 0);
|
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 */
|
/* GSM 03.38 6.2.1 Prepare character packing */
|
||||||
int gsm_septet_encode(uint8_t *result, const char *data)
|
int gsm_septet_encode(uint8_t *result, const char *data)
|
||||||
{
|
{
|
||||||
|
@ -254,6 +267,24 @@ int gsm_7bit_encode(uint8_t *result, const char *data)
|
||||||
return gsm_7bit_encode_oct(result, data, &out);
|
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 gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets)
|
||||||
{
|
{
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
|
@ -132,8 +132,10 @@ gsm48_parse_ra;
|
||||||
gsm48_rr_att_tlvdef;
|
gsm48_rr_att_tlvdef;
|
||||||
|
|
||||||
gsm_7bit_decode;
|
gsm_7bit_decode;
|
||||||
|
gsm_7bit_decode_ussd;
|
||||||
gsm_7bit_decode_hdr;
|
gsm_7bit_decode_hdr;
|
||||||
gsm_7bit_encode;
|
gsm_7bit_encode;
|
||||||
|
gsm_7bit_encode_ussd;
|
||||||
gsm_7bit_encode_oct;
|
gsm_7bit_encode_oct;
|
||||||
|
|
||||||
gsm_arfcn2band;
|
gsm_arfcn2band;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
#include <osmocom/gsm/gsm0480.h>
|
#include <osmocom/gsm/gsm0480.h>
|
||||||
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -68,6 +69,20 @@ static int parse_mangle_ussd(const uint8_t *_data, int len)
|
||||||
|
|
||||||
struct log_info info = {};
|
struct log_info info = {};
|
||||||
|
|
||||||
|
void gsm_7bit_ussd(char *text)
|
||||||
|
{
|
||||||
|
uint8_t coded[256];
|
||||||
|
char decoded[256];
|
||||||
|
int y;
|
||||||
|
|
||||||
|
printf("original = %s\n", osmo_hexdump((uint8_t *)text, strlen(text)));
|
||||||
|
gsm_7bit_encode_ussd(coded, text, &y);
|
||||||
|
printf("encoded = %s\n", osmo_hexdump(coded, y));
|
||||||
|
gsm_7bit_decode_ussd(decoded, coded, y * 8 / 7);
|
||||||
|
y = strlen(decoded);
|
||||||
|
printf("decoded = %s\n\n", osmo_hexdump((uint8_t *)decoded, y));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct ussd_request req;
|
struct ussd_request req;
|
||||||
|
@ -93,5 +108,12 @@ int main(int argc, char **argv)
|
||||||
printf("Result for %d is %d\n", rc, i);
|
printf("Result for %d is %d\n", rc, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("<CR> case test for 7 bit encode\n");
|
||||||
|
gsm_7bit_ussd("01234567");
|
||||||
|
gsm_7bit_ussd("0123456");
|
||||||
|
gsm_7bit_ussd("01234567\r");
|
||||||
|
gsm_7bit_ussd("0123456\r");
|
||||||
|
gsm_7bit_ussd("012345\r");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,3 +51,24 @@ Result for 0 is 8
|
||||||
Result for 0 is 7
|
Result for 0 is 7
|
||||||
Result for 0 is 6
|
Result for 0 is 6
|
||||||
Result for 1 is 5
|
Result for 1 is 5
|
||||||
|
<CR> case test for 7 bit encode
|
||||||
|
original = 30 31 32 33 34 35 36 37
|
||||||
|
encoded = b0 98 6c 46 ab d9 6e
|
||||||
|
decoded = 30 31 32 33 34 35 36 37
|
||||||
|
|
||||||
|
original = 30 31 32 33 34 35 36
|
||||||
|
encoded = b0 98 6c 46 ab d9 1a
|
||||||
|
decoded = 30 31 32 33 34 35 36
|
||||||
|
|
||||||
|
original = 30 31 32 33 34 35 36 37 0d
|
||||||
|
encoded = b0 98 6c 46 ab d9 6e 0d
|
||||||
|
decoded = 30 31 32 33 34 35 36 37 0d
|
||||||
|
|
||||||
|
original = 30 31 32 33 34 35 36 0d
|
||||||
|
encoded = b0 98 6c 46 ab d9 1a 0d
|
||||||
|
decoded = 30 31 32 33 34 35 36 0d 0d
|
||||||
|
|
||||||
|
original = 30 31 32 33 34 35 0d
|
||||||
|
encoded = b0 98 6c 46 ab 35 1a
|
||||||
|
decoded = 30 31 32 33 34 35 0d
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue