diff options
author | Michal Mazur <michamazur@google.com> | 2024-09-19 19:29:40 +0000 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2024-09-25 12:08:40 +0000 |
commit | a030eaef7639e491f085cdd0ee52a217b8b7bea9 (patch) | |
tree | 97a94697cbfdcca96fabc97264d93b08b3190a10 /src | |
parent | 257839c66ee6982eee9108b6661a5816b85ad885 (diff) |
sim: add common helpers to parse operator name and mnc length
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-base-sim.c | 31 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 50 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 9 | ||||
-rw-r--r-- | src/mm-sim-mbim.c | 43 | ||||
-rw-r--r-- | src/mm-sim-qmi.c | 45 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 86 |
6 files changed, 163 insertions, 101 deletions
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c index 37d8b3fc..e3f3cd77 100644 --- a/src/mm-base-sim.c +++ b/src/mm-base-sim.c @@ -2050,7 +2050,6 @@ parse_mnc_length (const gchar *response, (sw1 == 0x92) || (sw1 == 0x9f)) { gsize buflen = 0; - guint32 mnc_len; g_autofree guint8 *bin = NULL; /* Convert hex string to binary */ @@ -2059,20 +2058,8 @@ parse_mnc_length (const gchar *response, g_prefix_error (error, "SIM returned malformed response '%s': ", hex); return 0; } - if (buflen < 4) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "SIM returned malformed response '%s': too short", hex); - return 0; - } - /* MNC length is byte 4 of this SIM file */ - mnc_len = bin[3]; - if (mnc_len == 2 || mnc_len == 3) - return mnc_len; - - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len); - return 0; + return mm_sim_validate_mnc_length (bin, buflen, error); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, @@ -2157,7 +2144,6 @@ parse_spn (const gchar *response, (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { - g_autoptr(GByteArray) bin_array = NULL; g_autofree guint8 *bin = NULL; gsize binlen = 0; @@ -2168,20 +2154,7 @@ parse_spn (const gchar *response, return NULL; } - /* Remove the FF filler at the end */ - while (binlen > 1 && bin[binlen - 1] == 0xff) - binlen--; - if (binlen <= 1) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "SIM returned empty response '%s'", hex); - return NULL; - } - /* Setup as bytearray. - * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ - bin_array = g_byte_array_sized_new (binlen - 1); - g_byte_array_append (bin_array, bin + 1, binlen - 1); - - return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); + return mm_sim_convert_spn_to_utf8 (bin, binlen, error); } g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 99d91ed9..2058fe5c 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -5329,6 +5329,56 @@ mm_sim_parse_cpol_test_response (const gchar *response, return TRUE; } +gchar * +mm_sim_convert_spn_to_utf8 (const guint8 *bin, + gsize binlen, + GError **error) +{ + g_autoptr(GByteArray) bin_array = NULL; + + /* Remove the FF filler at the end */ + while (binlen > 1 && bin[binlen - 1] == 0xff) + binlen--; + if (binlen <= 1) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "SIM returned empty response"); + return NULL; + } + + /* Setup as bytearray. + * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ + bin_array = g_byte_array_sized_new (binlen - 1); + g_byte_array_append (bin_array, bin + 1, binlen - 1); + + return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); +} + +guint +mm_sim_validate_mnc_length (const guint8 *bin, + gsize binlen, + GError **error) +{ + guint mnc_len; + + if (binlen < 4) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "SIM returned too short response of length %lu (should be 4)", + binlen); + return 0; + } + + /* MNC length is byte 4 of this SIM file */ + mnc_len = bin[3]; + if (mnc_len != 2 && mnc_len != 3) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "SIM returned invalid MNC length %u (should be either 2 or 3)", + mnc_len); + return 0; + } + + return mnc_len; +} + #define EID_BYTE_LENGTH 16 gchar * diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index ddcff724..874a87bd 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -571,6 +571,15 @@ gboolean mm_sim_parse_cpol_test_response (const gchar *response, guint *out_max_index, GError **error); +/* Parse operator name and mnc length */ +gchar *mm_sim_convert_spn_to_utf8 (const guint8 *bin, + gsize len, + GError **error); + +guint mm_sim_validate_mnc_length (const guint8 *bin, + gsize len, + GError **error); + /*****************************************************************************/ /* Useful when clamp-ing an unsigned integer with implicit low limit set to 0, diff --git a/src/mm-sim-mbim.c b/src/mm-sim-mbim.c index 6655ff13..525b3296 100644 --- a/src/mm-sim-mbim.c +++ b/src/mm-sim-mbim.c @@ -1067,20 +1067,15 @@ common_read_binary_operator_id_ready (MMSimMbim *self, GByteArray *value; value = common_read_binary_finish (self, res, &error); - if (!value) { - mm_obj_dbg (self, "failed reading operator ID using MBIM: %s", error->message); - } else if (value->len != 4) { - mm_obj_dbg (self, "failed reading operator ID using MBIM: unexpected field size"); - } else { - guint mnc_len = value->data[3]; - - if (mnc_len == 2 || mnc_len == 3) { + if (value) { + guint mnc_len = mm_sim_validate_mnc_length (value->data, value->len, &error);; + if (mnc_len) { g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_len), g_free); g_object_unref (task); return; } - mm_obj_dbg (self, "failed reading operator ID using MBIM: unexpected MNC length: %u", mnc_len); } + mm_obj_dbg (self, "failed reading operator ID using MBIM: %s", error->message); /* Fallback to parent implementation if possible */ MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_identifier (MM_BASE_SIM (self), @@ -1145,31 +1140,15 @@ common_read_binary_operator_name_ready (MMSimMbim *self, GByteArray *value; value = common_read_binary_finish (self, res, &error); - if (!value) { - mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message); - } else { - gsize len = value->len; - - while (len > 1 && value->data[len - 1] == 0xff) - len--; - if (len <= 1) { - mm_obj_dbg (self, "failed reading operator name using MBIM: value is empty"); - } else { - g_autoptr(GByteArray) array = NULL; - gchar *name; - - /* Remove the first metadata byte and convert remainder to UTF8 string */ - array = g_byte_array_sized_new (len - 1); - g_byte_array_append (array, value->data + 1, len - 1); - name = mm_modem_charset_bytearray_to_utf8 (array, MM_MODEM_CHARSET_GSM, FALSE, &error); - if (name) { - g_task_return_pointer (task, name, g_free); - g_object_unref (task); - return; - } - mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message); + if (value) { + gchar *name = mm_sim_convert_spn_to_utf8 (value->data, value->len, &error); + if (name) { + g_task_return_pointer (task, name, g_free); + g_object_unref (task); + return; } } + mm_obj_dbg (self, "failed reading operator name using MBIM: %s", error->message); /* Fallback to parent implementation if possible */ MM_BASE_SIM_CLASS (mm_sim_mbim_parent_class)->load_operator_name (MM_BASE_SIM (self), diff --git a/src/mm-sim-qmi.c b/src/mm-sim-qmi.c index 1d24c23b..e408ab7e 100644 --- a/src/mm-sim-qmi.c +++ b/src/mm-sim-qmi.c @@ -679,20 +679,11 @@ uim_read_efad_ready (QmiClientUim *client, return; } - if (read_result->len < 4) { - g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "Unexpected response length reading EFad: %u", read_result->len); - g_object_unref (task); - return; - } - - /* MNC length is byte 4 of this SIM file */ - mnc_length = read_result->data[3]; - if (mnc_length == 2 || mnc_length == 3) { - g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free); + mnc_length = mm_sim_validate_mnc_length ((const guint8 *) read_result->data, read_result->len, &error); + if (!mnc_length) { + g_task_return_error (task, error); } else { - g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, - "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_length); + g_task_return_pointer (task, g_strndup (self->priv->imsi, 3 + mnc_length), g_free); } g_object_unref (task); } @@ -738,31 +729,6 @@ load_operator_name_finish (MMBaseSim *self, return g_task_propagate_pointer (G_TASK (res), error); } -static gchar * -parse_spn (const guint8 *bin, - gsize len, - GError **error) -{ - g_autoptr(GByteArray) bin_array = NULL; - gsize binlen; - - /* Remove the FF filler at the end */ - binlen = len; - while (binlen > 1 && bin[binlen - 1] == 0xff) - binlen--; - if (binlen <= 1) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "SIM returned empty spn"); - return NULL; - } - - /* Setup as bytearray. - * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */ - bin_array = g_byte_array_sized_new (binlen - 1); - g_byte_array_append (bin_array, bin + 1, binlen - 1); - - return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error); -} - static void uim_read_efspn_ready (QmiClientUim *client, GAsyncResult *res, @@ -779,8 +745,7 @@ uim_read_efspn_ready (QmiClientUim *client, return; } - spn = parse_spn ((const guint8 *) read_result->data, read_result->len, &error); - + spn = mm_sim_convert_spn_to_utf8 ((const guint8 *) read_result->data, read_result->len, &error); if (!spn) { g_task_return_error (task, error); } else { diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 176d0189..538aedfe 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -4689,6 +4689,89 @@ test_mm_split_string_groups (void) /*****************************************************************************/ +typedef struct { + const guint8 bin_array[10]; + gsize bin_len; + const gchar *expected_name; + gboolean expected_error; +} TestSpnData; + +static const TestSpnData test_spn_data[] = { + { { 0, 'T', 'e', 's', 't', 0xff, 0xff, 0xff, 0xff, 0xff }, 10, "Test", FALSE }, + { { 'T', 'e', 's', 't', 0xff, 0xff }, 6, "est", FALSE }, + { { 0, 0, '$', 'T', 'e', 's', 't' }, 7, "@¤Test", FALSE }, + { { 0 }, 0, "", TRUE }, + { { 0, 0xff }, 2, "", TRUE }, + { { 0xff, 0xff }, 2, "", TRUE }, +}; + +static void +test_spn_to_utf8 (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (test_spn_data); i++) { + gchar *result = NULL; + GError *error = NULL; + + result = mm_sim_convert_spn_to_utf8 (test_spn_data[i].bin_array, + test_spn_data[i].bin_len, + &error); + + if (test_spn_data[i].expected_error) { + g_assert (!result); + g_assert (error); + g_error_free (error); + } else { + g_assert (result); + g_assert_no_error (error); + g_assert_cmpstr (result, ==, test_spn_data[i].expected_name); + g_free (result); + } + } +} + +typedef struct { + const guint8 bin_array[4]; + gsize bin_len; + guint expected_length; + gboolean expected_error; +} TestMncData; + +static const TestMncData test_mnc_data[] = { + { { 0 }, 0, 0, TRUE }, + { { 0, 0, 0, 2 }, 3, 0, TRUE }, + { { 0, 0, 0, 2 }, 5, 2, FALSE }, + { { 0, 0, 0, 2 }, 4, 2, FALSE }, + { { 0, 0, 0, 3 }, 4, 3, FALSE }, + { { 0, 0, 0, 4 }, 4, 0, TRUE }, +}; + +static void +test_mnc_length (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (test_mnc_data); i++) { + guint result; + GError *error = NULL; + + result = mm_sim_validate_mnc_length (test_mnc_data[i].bin_array, + test_mnc_data[i].bin_len, + &error); + + if (test_mnc_data[i].expected_error) { + g_assert (error); + g_error_free (error); + } else { + g_assert_no_error (error); + } + g_assert_cmpuint (result, ==, test_mnc_data[i].expected_length); + } +} + +/*****************************************************************************/ + #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL) int main (int argc, char **argv) @@ -4929,6 +5012,9 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_mm_split_string_groups, NULL)); + g_test_suite_add (suite, TESTCASE (test_spn_to_utf8, NULL)); + g_test_suite_add (suite, TESTCASE (test_mnc_length, NULL)); + result = g_test_run (); reg_test_data_free (reg_data); |