modem-helpers: new +CGCONTRDP=N response parser
This commit is contained in:
@@ -1392,6 +1392,204 @@ mm_3gpp_parse_crsm_response (const gchar *reply,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* CGCONTRDP=N response parser */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
split_local_address_and_subnet (const gchar *str,
|
||||||
|
gchar **local_address,
|
||||||
|
gchar **subnet)
|
||||||
|
{
|
||||||
|
const gchar *separator;
|
||||||
|
guint count = 0;
|
||||||
|
|
||||||
|
/* E.g. split: "2.43.2.44.255.255.255.255"
|
||||||
|
* into:
|
||||||
|
* local address: "2.43.2.44",
|
||||||
|
* subnet: "255.255.255.255"
|
||||||
|
*/
|
||||||
|
g_assert (str);
|
||||||
|
g_assert (local_address);
|
||||||
|
g_assert (subnet);
|
||||||
|
|
||||||
|
separator = str;
|
||||||
|
while (1) {
|
||||||
|
separator = strchr (separator, '.');
|
||||||
|
if (separator) {
|
||||||
|
count++;
|
||||||
|
if (count == 4) {
|
||||||
|
if (local_address)
|
||||||
|
*local_address = g_strndup (str, (separator - str));
|
||||||
|
if (subnet)
|
||||||
|
*subnet = g_strdup (++separator);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
separator++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not even the full IP? report error parsing */
|
||||||
|
if (count < 3)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (count == 3) {
|
||||||
|
if (local_address)
|
||||||
|
*local_address = g_strdup (str);
|
||||||
|
if (subnet)
|
||||||
|
*subnet = NULL;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_3gpp_parse_cgcontrdp_response (const gchar *response,
|
||||||
|
guint *out_cid,
|
||||||
|
guint *out_bearer_id,
|
||||||
|
gchar **out_apn,
|
||||||
|
gchar **out_local_address,
|
||||||
|
gchar **out_subnet,
|
||||||
|
gchar **out_gateway_address,
|
||||||
|
gchar **out_dns_primary_address,
|
||||||
|
gchar **out_dns_secondary_address,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GRegex *r;
|
||||||
|
GMatchInfo *match_info;
|
||||||
|
GError *inner_error = NULL;
|
||||||
|
guint cid = 0;
|
||||||
|
guint bearer_id = 0;
|
||||||
|
gchar *apn = NULL;
|
||||||
|
gchar *local_address_and_subnet = NULL;
|
||||||
|
gchar *local_address = NULL;
|
||||||
|
gchar *subnet = NULL;
|
||||||
|
gchar *gateway_address = NULL;
|
||||||
|
gchar *dns_primary_address = NULL;
|
||||||
|
gchar *dns_secondary_address = NULL;
|
||||||
|
guint field_format_extra_index = 0;
|
||||||
|
|
||||||
|
/* Response may be e.g.:
|
||||||
|
* +CGCONTRDP: 4,5,"ibox.tim.it.mnc001.mcc222.gprs","2.197.17.49.255.255.255.255","2.197.17.49","10.207.43.46","10.206.56.132","0.0.0.0","0.0.0.0",0
|
||||||
|
*
|
||||||
|
* We assume only ONE line is returned; because we request +CGCONTRDP with
|
||||||
|
* a specific N CID. Also, we don't parse all fields, we stop after
|
||||||
|
* secondary DNS.
|
||||||
|
*
|
||||||
|
* Only the 3 first parameters (cid, bearer id, apn) are mandatory in the
|
||||||
|
* response, all the others are optional, but, we'll anyway assume that APN
|
||||||
|
* may be empty.
|
||||||
|
*
|
||||||
|
* The format of the response changed in TS 27.007 v9.4.0, we try to detect
|
||||||
|
* both formats ('a' if >= v9.4.0, 'b' if < v9.4.0) with a single regex here.
|
||||||
|
*/
|
||||||
|
r = g_regex_new ("\\+CGCONTRDP: "
|
||||||
|
"(\\d+),(\\d+),([^,]*)" /* cid, bearer id, apn */
|
||||||
|
"(?:,([^,]*))?" /* (a)ip+mask or (b)ip */
|
||||||
|
"(?:,([^,]*))?" /* (a)gateway or (b)mask */
|
||||||
|
"(?:,([^,]*))?" /* (a)dns1 or (b)gateway */
|
||||||
|
"(?:,([^,]*))?" /* (a)dns2 or (b)dns1 */
|
||||||
|
"(?:,([^,]*))?" /* (a)p-cscf primary or (b)dns2 */
|
||||||
|
"(?:,(.*))?" /* others, ignored */
|
||||||
|
"(?:\\r\\n)?",
|
||||||
|
0, 0, NULL);
|
||||||
|
g_assert (r != NULL);
|
||||||
|
|
||||||
|
g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
|
||||||
|
if (inner_error)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!g_match_info_matches (match_info)) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match +CGCONTRDP response");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_bearer_id && !mm_get_uint_from_match_info (match_info, 2, &bearer_id)) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing bearer id");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remaining strings are optional or empty allowed */
|
||||||
|
|
||||||
|
if (out_apn)
|
||||||
|
apn = mm_get_string_unquoted_from_match_info (match_info, 3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The +CGCONTRDP=[cid] response format before version TS 27.007 v9.4.0 had
|
||||||
|
* the subnet in its own comma-separated field. Try to detect that.
|
||||||
|
*/
|
||||||
|
local_address_and_subnet = mm_get_string_unquoted_from_match_info (match_info, 4);
|
||||||
|
if (local_address_and_subnet && !split_local_address_and_subnet (local_address_and_subnet, &local_address, &subnet)) {
|
||||||
|
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing local address and subnet");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* If we don't have a subnet in field 4, we're using the old format with subnet in an extra field */
|
||||||
|
if (!subnet) {
|
||||||
|
if (out_subnet)
|
||||||
|
subnet = mm_get_string_unquoted_from_match_info (match_info, 5);
|
||||||
|
field_format_extra_index = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_gateway_address)
|
||||||
|
gateway_address = mm_get_string_unquoted_from_match_info (match_info, 5 + field_format_extra_index);
|
||||||
|
|
||||||
|
if (out_dns_primary_address)
|
||||||
|
dns_primary_address = mm_get_string_unquoted_from_match_info (match_info, 6 + field_format_extra_index);
|
||||||
|
|
||||||
|
if (out_dns_secondary_address)
|
||||||
|
dns_secondary_address = mm_get_string_unquoted_from_match_info (match_info, 7 + field_format_extra_index);
|
||||||
|
|
||||||
|
out:
|
||||||
|
|
||||||
|
if (match_info)
|
||||||
|
g_match_info_free (match_info);
|
||||||
|
g_regex_unref (r);
|
||||||
|
|
||||||
|
g_free (local_address_and_subnet);
|
||||||
|
|
||||||
|
if (inner_error) {
|
||||||
|
g_free (apn);
|
||||||
|
g_free (local_address);
|
||||||
|
g_free (subnet);
|
||||||
|
g_free (gateway_address);
|
||||||
|
g_free (dns_primary_address);
|
||||||
|
g_free (dns_secondary_address);
|
||||||
|
g_propagate_error (error, inner_error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_cid)
|
||||||
|
*out_cid = cid;
|
||||||
|
if (out_bearer_id)
|
||||||
|
*out_bearer_id = bearer_id;
|
||||||
|
if (out_apn)
|
||||||
|
*out_apn = apn;
|
||||||
|
|
||||||
|
/* Local address and subnet may always be retrieved, even if not requested
|
||||||
|
* by the caller, as we need them to know which +CGCONTRDP=[cid] response is
|
||||||
|
* being parsed. So make sure we free them if not needed. */
|
||||||
|
if (out_local_address)
|
||||||
|
*out_local_address = local_address;
|
||||||
|
else
|
||||||
|
g_free (local_address);
|
||||||
|
if (out_subnet)
|
||||||
|
*out_subnet = subnet;
|
||||||
|
else
|
||||||
|
g_free (subnet);
|
||||||
|
|
||||||
|
if (out_gateway_address)
|
||||||
|
*out_gateway_address = gateway_address;
|
||||||
|
if (out_dns_primary_address)
|
||||||
|
*out_dns_primary_address = dns_primary_address;
|
||||||
|
if (out_dns_secondary_address)
|
||||||
|
*out_dns_secondary_address = dns_secondary_address;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
static MMSmsStorage
|
static MMSmsStorage
|
||||||
|
@@ -221,6 +221,18 @@ gboolean mm_3gpp_parse_crsm_response (const gchar *reply,
|
|||||||
gchar **hex,
|
gchar **hex,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
/* AT+CGCONTRDP=N response parser */
|
||||||
|
gboolean mm_3gpp_parse_cgcontrdp_response (const gchar *response,
|
||||||
|
guint *out_cid,
|
||||||
|
guint *out_bearer_id,
|
||||||
|
gchar **out_apn,
|
||||||
|
gchar **out_local_address,
|
||||||
|
gchar **out_subnet,
|
||||||
|
gchar **out_gateway_address,
|
||||||
|
gchar **out_dns_primary_address,
|
||||||
|
gchar **out_dns_secondary_address,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
/* Additional 3GPP-specific helpers */
|
/* Additional 3GPP-specific helpers */
|
||||||
|
|
||||||
MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str);
|
MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str);
|
||||||
|
@@ -2732,6 +2732,168 @@ test_crsm_response (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Test CGCONTRDP=N responses */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const gchar *str;
|
||||||
|
guint cid;
|
||||||
|
guint bearer_id;
|
||||||
|
const gchar *apn;
|
||||||
|
const gchar *local_address;
|
||||||
|
const gchar *subnet;
|
||||||
|
const gchar *gateway_address;
|
||||||
|
const gchar *dns_primary_address;
|
||||||
|
const gchar *dns_secondary_address;
|
||||||
|
} CgcontrdpResponseTest;
|
||||||
|
|
||||||
|
static const CgcontrdpResponseTest cgcontrdp_response_tests[] = {
|
||||||
|
/* Post TS 27.007 v9.4.0 format */
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\",0",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
.dns_primary_address = "10.207.43.46",
|
||||||
|
.dns_secondary_address = "10.206.56.132",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
.dns_primary_address = "10.207.43.46",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
},
|
||||||
|
/* Pre TS 27.007 v9.4.0 format */
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
.dns_primary_address = "10.207.43.46",
|
||||||
|
.dns_secondary_address = "10.206.56.132",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
.dns_primary_address = "10.207.43.46",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
.gateway_address = "2.197.17.49",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
.subnet = "255.255.255.255",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
.local_address = "2.197.17.49",
|
||||||
|
},
|
||||||
|
/* Common */
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
.apn = "ibox.tim.it.mnc001.mcc222.gprs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.str = "+CGCONTRDP: 4,5,\"\"",
|
||||||
|
.cid = 4,
|
||||||
|
.bearer_id = 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_cgcontrdp_response (void)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (cgcontrdp_response_tests); i++) {
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean success;
|
||||||
|
guint cid = G_MAXUINT;
|
||||||
|
guint bearer_id = G_MAXUINT;
|
||||||
|
gchar *apn = NULL;
|
||||||
|
gchar *local_address = NULL;
|
||||||
|
gchar *subnet = NULL;
|
||||||
|
gchar *gateway_address = NULL;
|
||||||
|
gchar *dns_primary_address = NULL;
|
||||||
|
gchar *dns_secondary_address = NULL;
|
||||||
|
|
||||||
|
success = mm_3gpp_parse_cgcontrdp_response (cgcontrdp_response_tests[i].str,
|
||||||
|
&cid,
|
||||||
|
&bearer_id,
|
||||||
|
&apn,
|
||||||
|
&local_address,
|
||||||
|
&subnet,
|
||||||
|
&gateway_address,
|
||||||
|
&dns_primary_address,
|
||||||
|
&dns_secondary_address,
|
||||||
|
&error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert (success);
|
||||||
|
g_assert_cmpuint (cgcontrdp_response_tests[i].cid, ==, cid);
|
||||||
|
g_assert_cmpuint (cgcontrdp_response_tests[i].bearer_id, ==, bearer_id);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].apn, ==, apn);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].local_address, ==, local_address);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].subnet, ==, subnet);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].gateway_address, ==, gateway_address);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].dns_primary_address, ==, dns_primary_address);
|
||||||
|
g_assert_cmpstr (cgcontrdp_response_tests[i].dns_secondary_address, ==, dns_secondary_address);
|
||||||
|
|
||||||
|
g_free (apn);
|
||||||
|
g_free (local_address);
|
||||||
|
g_free (subnet);
|
||||||
|
g_free (gateway_address);
|
||||||
|
g_free (dns_primary_address);
|
||||||
|
g_free (dns_secondary_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2910,6 +3072,8 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
g_test_suite_add (suite, TESTCASE (test_crsm_response, NULL));
|
g_test_suite_add (suite, TESTCASE (test_crsm_response, NULL));
|
||||||
|
|
||||||
|
g_test_suite_add (suite, TESTCASE (test_cgcontrdp_response, NULL));
|
||||||
|
|
||||||
result = g_test_run ();
|
result = g_test_run ();
|
||||||
|
|
||||||
reg_test_data_free (reg_data);
|
reg_test_data_free (reg_data);
|
||||||
|
Reference in New Issue
Block a user