diff mbox series

isimodem: parse extra details from REG_STATUS_IND

Message ID 20231118122948.418378-1-absicsz@gmail.com (mailing list archive)
State Superseded
Headers show
Series isimodem: parse extra details from REG_STATUS_IND | expand

Commit Message

Sicelo Nov. 18, 2023, 12:29 p.m. UTC
Extract additional operator information available in REG_STATUS_IND messages.
These include mnc, mnc, and different variations of operator name.

Additionally, in case NET_OPER_NAME_READ_RESP is an error result, which happens
with some operators, report the extracted information to core instead of
bailing out.
---
 drivers/isimodem/debug.c                |  1 +
 drivers/isimodem/network-registration.c | 73 ++++++++++++++++++++++++-
 drivers/isimodem/network.h              |  1 +
 3 files changed, 74 insertions(+), 1 deletion(-)

Comments

Denis Kenzior Nov. 20, 2023, 3:07 p.m. UTC | #1
Hi Sicelo,

On 11/18/23 06:29, Sicelo A. Mhlongo wrote:
> Extract additional operator information available in REG_STATUS_IND messages.
> These include mnc, mnc, and different variations of operator name.
> 
> Additionally, in case NET_OPER_NAME_READ_RESP is an error result, which happens
> with some operators, report the extracted information to core instead of
> bailing out.
> ---
>   drivers/isimodem/debug.c                |  1 +
>   drivers/isimodem/network-registration.c | 73 ++++++++++++++++++++++++-
>   drivers/isimodem/network.h              |  1 +
>   3 files changed, 74 insertions(+), 1 deletion(-)
> 

I do get a compiler warning here:

drivers/isimodem/network-registration.c:537:3: error: misleading indentation; 
statement is not part of the previous 'if' [-Werror,-Wmisleading-indentation]
                 strncpy(op.name, nd->nitz_name, OFONO_MAX_OPERATOR_NAME_LENGTH);
                 ^
drivers/isimodem/network-registration.c:532:2: note: previous statement is here
         if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) &&
         ^

A few nits:

> @@ -193,6 +195,12 @@ static void reg_status_ind_cb(const GIsiMessage *msg, void *data)
>   	struct netreg_data *nd = ofono_netreg_get_data(netreg);
>   	GIsiSubBlockIter iter;
>   
> +	uint8_t len = 0;
> +	uint8_t type = 0;
> +	char *abbrev = NULL;
> +	char *an = NULL;
> +	char *fn = NULL;
> +

You set these to NULL here, but ...

>   	if (netreg == NULL || nd == NULL)
>   		return;
>   
> @@ -209,16 +217,69 @@ static void reg_status_ind_cb(const GIsiMessage *msg, void *data)
>   
>   			if (!parse_common_info(&iter, &nd->reg))
>   				return;
> +
> +			/* Parse abbreviated operator name */
> +			if (!g_isi_sb_iter_get_byte(&iter, &len, 7))
> +				return;
> +
> +			len *= 2;
> +
> +			if (!g_isi_sb_iter_get_alpha_tag(&iter, &abbrev, len, 8))
> +				return;

Is the intent really to ignore the whole message if this attribute can't be parsed?

>   			break;
>   
>   		case NET_GSM_REG_INFO:
>   
>   			if (!parse_gsm_info(&iter, &nd->gsm))
>   				return;
> +
> +			/* Parse mcc and mnc */
> +			if (!g_isi_sb_iter_get_oper_code(&iter, nd->mcc,
> +								nd->mnc, 8))
> +				return;
> +			break;
> +
> +		case NET_REG_STATUS_IND_OPER_NAME:
> +
> +			if (!g_isi_sb_iter_get_byte(&iter, &type, 2))
> +				return;
> +
> +			if (!g_isi_sb_iter_get_byte(&iter, &len, 5))
> +				return;
> +
> +			/* Name is UCS-2 encoded */
> +			len *= 2;
> +
> +			switch (type) {
> +			case 2:
> +				/*
> +				 * Alternate full name, which is similar to
> +				 * long name, but may have less whitespace
> +				 */
> +				g_isi_sb_iter_get_alpha_tag(&iter, &an, len, 6);
> +				break;
> +			case 3:
> +				/* Full name */
> +				g_isi_sb_iter_get_alpha_tag(&iter, &fn, len, 6);
> +				break;

But you don't check the error returns of these two calls

> +			}
>   			break;
>   		}
>   	}
>   
> +	if (fn[0] != '\0')
> +		strncpy(nd->nitz_name, fn, OFONO_MAX_OPERATOR_NAME_LENGTH);
> +	else if (an[0] != '\0')
> +		strncpy(nd->nitz_name, an, OFONO_MAX_OPERATOR_NAME_LENGTH);

So how do you know fn/an aren't NULL?

> +	else if (abbrev[0] != '\0')
> +		strncpy(nd->nitz_name, abbrev, OFONO_MAX_OPERATOR_NAME_LENGTH);

Use g_strlcpy instead of strncpy since it takes care of setting the terminating 
NULL.

> +
> +	nd->nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
> +
> +	g_free(an);
> +	g_free(fn);
> +	g_free(abbrev);
> +
>   	ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg),
>   					nd->gsm.lac, nd->gsm.ci,
>   					isi_to_at_tech(&nd->rat, &nd->gsm));
> @@ -458,6 +519,8 @@ static void name_get_resp_cb(const GIsiMessage *msg, void *data)
>   {
>   	struct isi_cb_data *cbd = data;
>   	ofono_netreg_operator_cb_t cb = cbd->cb;
> +	struct ofono_netreg *netreg = cbd->user;
> +	struct netreg_data *nd = ofono_netreg_get_data(netreg);
>   	struct ofono_network_operator op;
>   
>   	GIsiSubBlockIter iter;
> @@ -468,7 +531,14 @@ static void name_get_resp_cb(const GIsiMessage *msg, void *data)
>   
>   	if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) &&
>   			!check_response_status(msg, NET_OPER_NAME_READ_RESP))
> -		goto error;
> +		if (nd->nitz_name[0] == '\0')
> +			goto error;
> +
> +		strncpy(op.name, nd->nitz_name, OFONO_MAX_OPERATOR_NAME_LENGTH);
> +		op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
> +		strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
> +		strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);

ditto here, g_strlcpy.

> +		goto success;
>   
>   	for (g_isi_sb_iter_init(&iter, msg, 6);
>   			g_isi_sb_iter_is_valid(&iter);
> @@ -500,6 +570,7 @@ static void name_get_resp_cb(const GIsiMessage *msg, void *data)
>   		}
>   	}
>   
> +success:
>   	CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
>   	return;
>   

Regards,
-Denis
Sicelo Nov. 22, 2023, 5:53 p.m. UTC | #2
Hello Denis

On Mon, Nov 20, 2023 at 09:07:26AM -0600, Denis Kenzior wrote:
> I do get a compiler warning here:
> 
> drivers/isimodem/network-registration.c:537:3: error: misleading
> indentation; statement is not part of the previous 'if'
> [-Werror,-Wmisleading-indentation]
>                 strncpy(op.name, nd->nitz_name, OFONO_MAX_OPERATOR_NAME_LENGTH);
>                 ^
> drivers/isimodem/network-registration.c:532:2: note: previous statement is here
>         if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) &&
>         ^

Interestingly this one was not caught on my compilation runs. I will fix
in v2.

> > +				g_isi_sb_iter_get_alpha_tag(&iter, &an, len, 6);
> > +				break;
> > +			case 3:
> > +				/* Full name */
> > +				g_isi_sb_iter_get_alpha_tag(&iter, &fn, len, 6);
> > +				break;
> 
> But you don't check the error returns of these two calls

I left out the checking here because in both cases, we are breaking at
the next statement, regardless of the success or failure of the calls. I
will see if I can implement checking.

> > +	if (fn[0] != '\0')
> > +		strncpy(nd->nitz_name, fn, OFONO_MAX_OPERATOR_NAME_LENGTH);
> > +	else if (an[0] != '\0')
> > +		strncpy(nd->nitz_name, an, OFONO_MAX_OPERATOR_NAME_LENGTH);
> 
> So how do you know fn/an aren't NULL?

Please excuse my confusion here - I thought the `if` conditions do check
whether an or fn are NULL. I will appreciate assistance or suggestion
for an alternative way to ensure this, since I definitely do not want to
copy over garbage into nd->nitz_name at this point in the code.

> Use g_strlcpy instead of strncpy since it takes care of setting the
> terminating NULL.

I will do this in v2. While going through some older commits, I also
found use of l_strlcpy. Should I prefer g_strlcpy in this instance?

Thank you very much for the review, and looking forward to hearing from
you soon.

Sincerely
Sicelo
Denis Kenzior Nov. 23, 2023, 3:33 p.m. UTC | #3
Hi Sicelo,

> 
>>> +				g_isi_sb_iter_get_alpha_tag(&iter, &an, len, 6);
>>> +				break;
>>> +			case 3:
>>> +				/* Full name */
>>> +				g_isi_sb_iter_get_alpha_tag(&iter, &fn, len, 6);
>>> +				break;
>>
>> But you don't check the error returns of these two calls
> 
> I left out the checking here because in both cases, we are breaking at
> the next statement, regardless of the success or failure of the calls. I
> will see if I can implement checking.

You break out of the outer switch, but you still don't know whether obtaining 
the alpha tag succeeded or not.  So fn/an (or both) can still be NULL.

> 
>>> +	if (fn[0] != '\0')
>>> +		strncpy(nd->nitz_name, fn, OFONO_MAX_OPERATOR_NAME_LENGTH);
>>> +	else if (an[0] != '\0')
>>> +		strncpy(nd->nitz_name, an, OFONO_MAX_OPERATOR_NAME_LENGTH);
>>
>> So how do you know fn/an aren't NULL?
> 
> Please excuse my confusion here - I thought the `if` conditions do check
> whether an or fn are NULL. I will appreciate assistance or suggestion

No, you're checking that fn/an are not equal to an empty string, not that fn/an 
are NULL.

You want something like:
if (fn && fn[0] != '\0')
	...

> for an alternative way to ensure this, since I definitely do not want to
> copy over garbage into nd->nitz_name at this point in the code.
> 

Same thing for abbrev.  You're checking whether or not abbrev is an empty string 
"", but the whole TLV might be absent and g_isi_sb_iter_get_alpha_tag would 
never be executed.

>> Use g_strlcpy instead of strncpy since it takes care of setting the
>> terminating NULL.
> 
> I will do this in v2. While going through some older commits, I also
> found use of l_strlcpy. Should I prefer g_strlcpy in this instance?

They're equivalent, you can use either.  g_strlcpy is the GLib version. 
l_strlcpy is the ell version.  oFono will slowly be migrating away from GLib to 
use ell.

Since you're already using l_utf8_from_ucs2be inside gisi/*, you might as well 
use the ell version.

Regards,
-Denis
diff mbox series

Patch

diff --git a/drivers/isimodem/debug.c b/drivers/isimodem/debug.c
index 18055791..8edcd972 100644
--- a/drivers/isimodem/debug.c
+++ b/drivers/isimodem/debug.c
@@ -1190,6 +1190,7 @@  const char *net_subblock_name(enum net_subblock value)
 		_(NET_REGISTRATION_CONF1_INFO);
 		_(NET_ROAMING_CONF1_INFO);
 		_(NET_AVAIL_NETWORK_INFO_COMMON);
+		_(NET_REG_STATUS_IND_OPER_NAME);
 		_(NET_OPER_NAME_INFO);
 	}
 	return "NET_<UNKNOWN>";
diff --git a/drivers/isimodem/network-registration.c b/drivers/isimodem/network-registration.c
index 8f70b3ee..bdfb8fd0 100644
--- a/drivers/isimodem/network-registration.c
+++ b/drivers/isimodem/network-registration.c
@@ -80,6 +80,8 @@  struct netreg_data {
 	struct rat_info rat;
 	GIsiVersion version;
 	char nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
+	char mcc[OFONO_MAX_MCC_LENGTH + 1];
+	char mnc[OFONO_MAX_MNC_LENGTH + 1];
 };
 
 static inline guint8 *mccmnc_to_bcd(const char *mcc, const char *mnc,
@@ -193,6 +195,12 @@  static void reg_status_ind_cb(const GIsiMessage *msg, void *data)
 	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 	GIsiSubBlockIter iter;
 
+	uint8_t len = 0;
+	uint8_t type = 0;
+	char *abbrev = NULL;
+	char *an = NULL;
+	char *fn = NULL;
+
 	if (netreg == NULL || nd == NULL)
 		return;
 
@@ -209,16 +217,69 @@  static void reg_status_ind_cb(const GIsiMessage *msg, void *data)
 
 			if (!parse_common_info(&iter, &nd->reg))
 				return;
+
+			/* Parse abbreviated operator name */
+			if (!g_isi_sb_iter_get_byte(&iter, &len, 7))
+				return;
+
+			len *= 2;
+
+			if (!g_isi_sb_iter_get_alpha_tag(&iter, &abbrev, len, 8))
+				return;
 			break;
 
 		case NET_GSM_REG_INFO:
 
 			if (!parse_gsm_info(&iter, &nd->gsm))
 				return;
+
+			/* Parse mcc and mnc */
+			if (!g_isi_sb_iter_get_oper_code(&iter, nd->mcc,
+								nd->mnc, 8))
+				return;
+			break;
+
+		case NET_REG_STATUS_IND_OPER_NAME:
+
+			if (!g_isi_sb_iter_get_byte(&iter, &type, 2))
+				return;
+
+			if (!g_isi_sb_iter_get_byte(&iter, &len, 5))
+				return;
+
+			/* Name is UCS-2 encoded */
+			len *= 2;
+
+			switch (type) {
+			case 2:
+				/*
+				 * Alternate full name, which is similar to
+				 * long name, but may have less whitespace
+				 */
+				g_isi_sb_iter_get_alpha_tag(&iter, &an, len, 6);
+				break;
+			case 3:
+				/* Full name */
+				g_isi_sb_iter_get_alpha_tag(&iter, &fn, len, 6);
+				break;
+			}
 			break;
 		}
 	}
 
+	if (fn[0] != '\0')
+		strncpy(nd->nitz_name, fn, OFONO_MAX_OPERATOR_NAME_LENGTH);
+	else if (an[0] != '\0')
+		strncpy(nd->nitz_name, an, OFONO_MAX_OPERATOR_NAME_LENGTH);
+	else if (abbrev[0] != '\0')
+		strncpy(nd->nitz_name, abbrev, OFONO_MAX_OPERATOR_NAME_LENGTH);
+
+	nd->nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+
+	g_free(an);
+	g_free(fn);
+	g_free(abbrev);
+
 	ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg),
 					nd->gsm.lac, nd->gsm.ci,
 					isi_to_at_tech(&nd->rat, &nd->gsm));
@@ -458,6 +519,8 @@  static void name_get_resp_cb(const GIsiMessage *msg, void *data)
 {
 	struct isi_cb_data *cbd = data;
 	ofono_netreg_operator_cb_t cb = cbd->cb;
+	struct ofono_netreg *netreg = cbd->user;
+	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 	struct ofono_network_operator op;
 
 	GIsiSubBlockIter iter;
@@ -468,7 +531,14 @@  static void name_get_resp_cb(const GIsiMessage *msg, void *data)
 
 	if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) &&
 			!check_response_status(msg, NET_OPER_NAME_READ_RESP))
-		goto error;
+		if (nd->nitz_name[0] == '\0')
+			goto error;
+
+		strncpy(op.name, nd->nitz_name, OFONO_MAX_OPERATOR_NAME_LENGTH);
+		op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
+		strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH);
+		strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH);
+		goto success;
 
 	for (g_isi_sb_iter_init(&iter, msg, 6);
 			g_isi_sb_iter_is_valid(&iter);
@@ -500,6 +570,7 @@  static void name_get_resp_cb(const GIsiMessage *msg, void *data)
 		}
 	}
 
+success:
 	CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
 	return;
 
diff --git a/drivers/isimodem/network.h b/drivers/isimodem/network.h
index 7449a1d0..3282b383 100644
--- a/drivers/isimodem/network.h
+++ b/drivers/isimodem/network.h
@@ -90,6 +90,7 @@  enum net_subblock {
 	NET_REGISTRATION_CONF1_INFO =			0x59,
 	NET_ROAMING_CONF1_INFO =			0x5A,
 	NET_AVAIL_NETWORK_INFO_COMMON =			0xE1,
+	NET_REG_STATUS_IND_OPER_NAME =                  0xE3,
 	NET_OPER_NAME_INFO =				0xE7,
 };