static void detailed_connect_context_complete_and_free_successful (DetailedConnectContext *ctx) { MMBearerIpConfig *config; config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); g_simple_async_result_set_op_res_gpointer (ctx->result, config, (GDestroyNotify)g_object_unref); detailed_connect_context_complete_and_free (ctx); }
static void connect_context_step (ConnectContext *ctx) { /* If cancelled, complete */ if (g_cancellable_is_cancelled (ctx->cancellable)) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Connection setup operation has been cancelled"); connect_context_complete_and_free (ctx); return; } switch (ctx->step) { case CONNECT_STEP_FIRST: g_assert (ctx->ipv4 || ctx->ipv6); /* Fall down */ ctx->step++; case CONNECT_STEP_OPEN_QMI_PORT: if (!mm_qmi_port_is_open (ctx->qmi)) { mm_qmi_port_open (ctx->qmi, TRUE, ctx->cancellable, (GAsyncReadyCallback)qmi_port_open_ready, ctx); return; } /* If already open, just fall down */ ctx->step++; case CONNECT_STEP_IPV4: /* If no IPv4 setup needed, jump to IPv6 */ if (!ctx->ipv4) { ctx->step = CONNECT_STEP_IPV6; connect_context_step (ctx); return; } /* Start IPv4 setup */ mm_dbg ("Running IPv4 connection setup"); ctx->running_ipv4 = TRUE; ctx->running_ipv6 = FALSE; /* Just fall down */ ctx->step++; case CONNECT_STEP_WDS_CLIENT_IPV4: { QmiClient *client; client = mm_qmi_port_get_client (ctx->qmi, QMI_SERVICE_WDS, MM_QMI_PORT_FLAG_WDS_IPV4); if (!client) { mm_dbg ("Allocating IPv4-specific WDS client"); mm_qmi_port_allocate_client (ctx->qmi, QMI_SERVICE_WDS, MM_QMI_PORT_FLAG_WDS_IPV4, ctx->cancellable, (GAsyncReadyCallback)qmi_port_allocate_client_ready, ctx); return; } ctx->client_ipv4 = QMI_CLIENT_WDS (client); /* Just fall down */ ctx->step++; } case CONNECT_STEP_IP_FAMILY_IPV4: /* If client is new enough, select IP family */ if (!ctx->no_ip_family_preference && qmi_client_check_version (QMI_CLIENT (ctx->client_ipv4), 1, 9)) { QmiMessageWdsSetIpFamilyInput *input; mm_dbg ("Setting default IP family to: IPv4"); input = qmi_message_wds_set_ip_family_input_new (); qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV4, NULL); qmi_client_wds_set_ip_family (ctx->client_ipv4, input, 10, ctx->cancellable, (GAsyncReadyCallback)set_ip_family_ready, ctx); qmi_message_wds_set_ip_family_input_unref (input); return; } ctx->default_ip_family_set = FALSE; /* Just fall down */ ctx->step++; case CONNECT_STEP_START_NETWORK_IPV4: { QmiMessageWdsStartNetworkInput *input; mm_dbg ("Starting IPv4 connection..."); input = build_start_network_input (ctx); qmi_client_wds_start_network (ctx->client_ipv4, input, 45, ctx->cancellable, (GAsyncReadyCallback)start_network_ready, ctx); qmi_message_wds_start_network_input_unref (input); return; } case CONNECT_STEP_IPV6: /* If no IPv6 setup needed, jump to last */ if (!ctx->ipv6) { ctx->step = CONNECT_STEP_GET_CURRENT_SETTINGS; connect_context_step (ctx); return; } /* Start IPv6 setup */ mm_dbg ("Running IPv6 connection setup"); ctx->running_ipv4 = FALSE; ctx->running_ipv6 = TRUE; /* Just fall down */ ctx->step++; case CONNECT_STEP_WDS_CLIENT_IPV6: { QmiClient *client; client = mm_qmi_port_get_client (ctx->qmi, QMI_SERVICE_WDS, MM_QMI_PORT_FLAG_WDS_IPV6); if (!client) { mm_dbg ("Allocating IPv6-specific WDS client"); mm_qmi_port_allocate_client (ctx->qmi, QMI_SERVICE_WDS, MM_QMI_PORT_FLAG_WDS_IPV6, ctx->cancellable, (GAsyncReadyCallback)qmi_port_allocate_client_ready, ctx); return; } ctx->client_ipv6 = QMI_CLIENT_WDS (client); /* Just fall down */ ctx->step++; } case CONNECT_STEP_IP_FAMILY_IPV6: g_assert (ctx->no_ip_family_preference == FALSE); /* If client is new enough, select IP family */ if (qmi_client_check_version (QMI_CLIENT (ctx->client_ipv6), 1, 9)) { QmiMessageWdsSetIpFamilyInput *input; mm_dbg ("Setting default IP family to: IPv6"); input = qmi_message_wds_set_ip_family_input_new (); qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL); qmi_client_wds_set_ip_family (ctx->client_ipv6, input, 10, ctx->cancellable, (GAsyncReadyCallback)set_ip_family_ready, ctx); qmi_message_wds_set_ip_family_input_unref (input); return; } ctx->default_ip_family_set = FALSE; /* Just fall down */ ctx->step++; case CONNECT_STEP_START_NETWORK_IPV6: { QmiMessageWdsStartNetworkInput *input; mm_dbg ("Starting IPv6 connection..."); input = build_start_network_input (ctx); qmi_client_wds_start_network (ctx->client_ipv6, input, 45, ctx->cancellable, (GAsyncReadyCallback)start_network_ready, ctx); qmi_message_wds_start_network_input_unref (input); return; } case CONNECT_STEP_GET_CURRENT_SETTINGS: { QmiMessageWdsGetCurrentSettingsInput *input; QmiClientWds *client; if (ctx->running_ipv4) client = ctx->client_ipv4; else if (ctx->running_ipv6) client = ctx->client_ipv6; else g_assert_not_reached (); mm_dbg ("Getting IP configuration..."); input = build_get_current_settings_input (ctx); qmi_client_wds_get_current_settings (client, input, 45, ctx->cancellable, (GAsyncReadyCallback)get_current_settings_ready, ctx); qmi_message_wds_get_current_settings_input_unref (input); return; } case CONNECT_STEP_LAST: /* If one of IPv4 or IPv6 succeeds, we're connected */ if (ctx->packet_data_handle_ipv4 || ctx->packet_data_handle_ipv6) { MMBearerIpConfig *config; ConnectResult *result; /* Port is connected; update the state */ mm_port_set_connected (MM_PORT (ctx->data), TRUE); /* Keep connection related data */ g_assert (ctx->self->priv->data == NULL); ctx->self->priv->data = g_object_ref (ctx->data); g_assert (ctx->self->priv->packet_data_handle_ipv4 == 0); g_assert (ctx->self->priv->client_ipv4 == NULL); if (ctx->packet_data_handle_ipv4) { ctx->self->priv->packet_data_handle_ipv4 = ctx->packet_data_handle_ipv4; ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4); } g_assert (ctx->self->priv->packet_data_handle_ipv6 == 0); g_assert (ctx->self->priv->client_ipv6 == NULL); if (ctx->packet_data_handle_ipv6) { ctx->self->priv->packet_data_handle_ipv6 = ctx->packet_data_handle_ipv6; ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6); } /* Build IP config; always DHCP based */ config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP); /* Build result */ result = g_slice_new0 (ConnectResult); result->data = g_object_ref (ctx->data); if (ctx->packet_data_handle_ipv4) result->ipv4_config = g_object_ref (config); if (ctx->packet_data_handle_ipv6) result->ipv6_config = g_object_ref (config); g_object_unref (config); /* Set operation result */ g_simple_async_result_set_op_res_gpointer (ctx->result, result, (GDestroyNotify)connect_result_free); } else { GError *error; /* No connection, set error. If both set, IPv4 error preferred */ if (ctx->error_ipv4) { error = ctx->error_ipv4; ctx->error_ipv4 = NULL; } else { error = ctx->error_ipv6; ctx->error_ipv6 = NULL; } g_simple_async_result_take_error (ctx->result, error); } connect_context_complete_and_free (ctx); return; } }
static void ip_config_ready (MMBaseModem *modem, GAsyncResult *res, GetIpConfig3gppContext *ctx) { MMBearerIpConfig *ip_config = NULL; const gchar *response; GError *error = NULL; gchar **items; gchar *dns[3] = { 0 }; guint i; guint dns_i; guint32 tmp; response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error); if (error) { g_simple_async_result_take_error (ctx->result, error); get_ip_config_context_complete_and_free (ctx); return; } /* TODO: use a regex to parse this */ /* Check result */ if (!g_str_has_prefix (response, OWANDATA_TAG)) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: invalid response '%s'", response); get_ip_config_context_complete_and_free (ctx); return; } response = mm_strip_tag (response, OWANDATA_TAG); items = g_strsplit (response, ", ", 0); for (i = 0, dns_i = 0; items[i]; i++) { if (i == 0) { /* CID */ glong num; errno = 0; num = strtol (items[i], NULL, 10); if (errno != 0 || num < 0 || (gint) num != ctx->cid) { error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown CID in OWANDATA response (" "got %d, expected %d)", (guint) num, ctx->cid); break; } } else if (i == 1) { /* IP address */ if (!inet_pton (AF_INET, items[i], &tmp)) break; ip_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC); mm_bearer_ip_config_set_address (ip_config, items[i]); } else if (i == 3 || i == 4) { /* DNS entries */ if (!inet_pton (AF_INET, items[i], &tmp)) { g_clear_object (&ip_config); break; } dns[dns_i++] = items[i]; } } if (!ip_config) { if (error) g_simple_async_result_take_error (ctx->result, error); else g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't get IP config: couldn't parse response '%s'", response); } else { /* If we got DNS entries, set them in the IP config */ if (dns[0]) mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns); g_simple_async_result_set_op_res_gpointer (ctx->result, ip_config, (GDestroyNotify)g_object_unref); } get_ip_config_context_complete_and_free (ctx); g_strfreev (items); }
static void connect_3gpp_context_step (Connect3gppContext *ctx) { /* Check for cancellation */ if (g_cancellable_is_cancelled (ctx->cancellable)) { /* Clear context */ ctx->self->priv->connect_pending = NULL; /* If we already sent the connetion command, send the disconnection one */ if (ctx->step > CONNECT_3GPP_CONTEXT_STEP_NDISDUP) mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISDUP=1,0", 3, FALSE, FALSE, NULL, NULL, /* Do not care the AT response */ NULL); g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Huawei connection operation has been cancelled"); connect_3gpp_context_complete_and_free (ctx); return; } /* Network-initiated disconnect should not be outstanding at this point, * because it interferes with the connect attempt. */ g_assert (ctx->self->priv->network_disconnect_pending_id == 0); switch (ctx->step) { case CONNECT_3GPP_CONTEXT_STEP_FIRST: { MMBearerIpFamily ip_family; ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY) { gchar *ip_family_str; ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self)); ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family); mm_dbg ("No specific IP family requested, defaulting to %s", ip_family_str); g_free (ip_family_str); } if (ip_family != MM_BEARER_IP_FAMILY_IPV4) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Only IPv4 is supported by this modem"); connect_3gpp_context_complete_and_free (ctx); return; } /* Store the context */ ctx->self->priv->connect_pending = ctx; ctx->step++; /* Fall down to the next step */ } case CONNECT_3GPP_CONTEXT_STEP_NDISDUP: { const gchar *apn; const gchar *user; const gchar *passwd; MMBearerAllowedAuth auth; gint encoded_auth = MM_BEARER_HUAWEI_AUTH_UNKNOWN; gchar *command; apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); encoded_auth = huawei_parse_auth_type (auth); /* Default to no authentication if not specified */ if ((encoded_auth = huawei_parse_auth_type (auth)) == MM_BEARER_HUAWEI_AUTH_UNKNOWN) encoded_auth = MM_BEARER_HUAWEI_AUTH_NONE; if (!user && !passwd) command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\"", apn == NULL ? "" : apn); else if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE) command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\"", apn == NULL ? "" : apn, user == NULL ? "" : user, passwd == NULL ? "" : passwd); else command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\",%d", apn == NULL ? "" : apn, user == NULL ? "" : user, passwd == NULL ? "" : passwd, encoded_auth); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisdup_ready, g_object_ref (ctx->self)); g_free (command); return; } case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* Wait for dial up timeout, retries for 60 times * (1s between the retries, so it means 1 minute). * If too many retries, failed */ if (ctx->check_count > 60) { /* Clear context */ ctx->self->priv->connect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); connect_3gpp_context_complete_and_free (ctx); return; } /* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */ if (ctx->failed_ndisstatqry_count > 10) { /* Clear context */ ctx->self->priv->connect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Connection attempt not supported."); connect_3gpp_context_complete_and_free (ctx); return; } /* Check if connected */ ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisstatqry_check_ready, g_object_ref (ctx->self)); return; case CONNECT_3GPP_CONTEXT_STEP_LAST: /* Clear context */ ctx->self->priv->connect_pending = NULL; /* Setup result */ { MMBearerIpConfig *ipv4_config; ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP); g_simple_async_result_set_op_res_gpointer ( ctx->result, mm_bearer_connect_result_new (ctx->data, ipv4_config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (ipv4_config); } connect_3gpp_context_complete_and_free (ctx); return; } }
gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response, MMBearerIpConfig **out_ip4_config, MMBearerIpConfig **out_ip6_config, GError **error) { MMBearerIpConfig **ip_config = NULL; gboolean got_address = FALSE, got_gw = FALSE, got_dns = FALSE; GRegex *r; GMatchInfo *match_info = NULL; GError *match_error = NULL; gchar *dns[3] = { 0 }; guint dns_idx = 0; int family = AF_INET; MMBearerIpMethod method = MM_BEARER_IP_METHOD_STATIC; g_return_val_if_fail (out_ip4_config, FALSE); g_return_val_if_fail (out_ip6_config, FALSE); if (!response || !g_str_has_prefix (response, E2IPCFG_TAG)) { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing " E2IPCFG_TAG " prefix"); return FALSE; } response = mm_strip_tag (response, "*E2IPCFG: "); if (strchr (response, ':')) { family = AF_INET6; ip_config = out_ip6_config; method = MM_BEARER_IP_METHOD_DHCP; } else if (strchr (response, '.')) { family = AF_INET; ip_config = out_ip4_config; method = MM_BEARER_IP_METHOD_STATIC; } else { g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Failed to detect " E2IPCFG_TAG " address family"); return FALSE; } /* *E2IPCFG: (1,<IP>)(2,<gateway>)(3,<DNS>)(3,<DNS>) * * *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198") * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054") * *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009") */ r = g_regex_new ("\\((\\d),\"([0-9a-fA-F.:]+)\"\\)", 0, 0, NULL); g_assert (r != NULL); if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) { if (match_error) { g_propagate_error (error, match_error); g_prefix_error (error, "Could not parse " E2IPCFG_TAG " results: "); } else { g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match " E2IPCFG_TAG " reply"); } goto done; } *ip_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (*ip_config, method); while (g_match_info_matches (match_info)) { char *id = g_match_info_fetch (match_info, 1); char *str = g_match_info_fetch (match_info, 2); switch (atoi (id)) { case 1: if (validate_address (family, str)) { mm_bearer_ip_config_set_address (*ip_config, str); mm_bearer_ip_config_set_prefix (*ip_config, (family == AF_INET6) ? 64 : 28); got_address = TRUE; } break; case 2: if ((family == AF_INET) && validate_address (family, str)) { mm_bearer_ip_config_set_gateway (*ip_config, str); got_gw = TRUE; } break; case 3: if (validate_address (family, str)) { dns[dns_idx++] = g_strdup (str); got_dns = TRUE; } break; default: break; } g_free (id); g_free (str); g_match_info_next (match_info, NULL); } if (got_dns) { mm_bearer_ip_config_set_dns (*ip_config, (const gchar **) dns); g_free (dns[0]); g_free (dns[1]); } if (!got_address || (family == AF_INET && !got_gw)) { g_object_unref (*ip_config); *ip_config = NULL; g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Got incomplete IP configuration from " E2IPCFG_TAG); } done: if (match_info) g_match_info_free (match_info); g_regex_unref (r); return !!*ip_config; }