X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fservice.c;h=ae1f6fd834ab690dd260e5644b48f293e12475bb;hb=72397eb95321ba03392052db5d877ddef79f6d0d;hp=aeaf17081fb16ca9a95726e690c14319d5886db2;hpb=b44e71853d9de3927b1e77e71fca3ffe0da82e49;p=platform%2Fupstream%2Fconnman.git diff --git a/src/service.c b/src/service.c index aeaf170..ae1f6fd 100644 --- a/src/service.c +++ b/src/service.c @@ -58,7 +58,8 @@ struct connman_service { enum connman_service_type type; enum connman_service_mode mode; enum connman_service_security security; - enum connman_service_state state; + enum connman_service_state state_ipv4; + enum connman_service_state state_ipv6; enum connman_service_error error; connman_uint8_t strength; connman_bool_t favorite; @@ -72,11 +73,6 @@ struct connman_service { char *name; char *passphrase; char *profile; - char *apn; - char *username; - char *password; - char *mcc; - char *mnc; connman_bool_t roaming; connman_bool_t login_required; struct connman_ipconfig *ipconfig_ipv4; @@ -84,9 +80,10 @@ struct connman_service { struct connman_network *network; struct connman_provider *provider; char **nameservers; - char *nameserver; + char **nameservers_config; char **domains; char *domainname; + char **timeservers; /* 802.1x settings from the config files */ char *eap; char *identity; @@ -102,9 +99,11 @@ struct connman_service { struct connman_stats stats_roaming; GHashTable *counter_table; enum connman_service_proxy_method proxy; + enum connman_service_proxy_method proxy_config; char **proxies; char **excludes; char *pac; + connman_bool_t wps; }; static void append_path(gpointer value, gpointer user_data) @@ -259,6 +258,12 @@ static const char *error2string(enum connman_service_error error) return "dhcp-failed"; case CONNMAN_SERVICE_ERROR_CONNECT_FAILED: return "connect-failed"; + case CONNMAN_SERVICE_ERROR_LOGIN_FAILED: + return "login-failed"; + case CONNMAN_SERVICE_ERROR_AUTH_FAILED: + return "auth-failed"; + case CONNMAN_SERVICE_ERROR_INVALID_KEY: + return "invalid-key"; } return NULL; @@ -270,6 +275,8 @@ static enum connman_service_error string2error(const char *error) return CONNMAN_SERVICE_ERROR_DHCP_FAILED; else if (g_strcmp0(error, "pin-missing") == 0) return CONNMAN_SERVICE_ERROR_PIN_MISSING; + else if (g_strcmp0(error, "invalid-key") == 0) + return CONNMAN_SERVICE_ERROR_INVALID_KEY; return CONNMAN_SERVICE_ERROR_UNKNOWN; } @@ -302,12 +309,126 @@ static enum connman_service_proxy_method string2proxymethod(const char *method) return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN; } -static connman_bool_t is_connecting(struct connman_service *service) +static enum connman_service_state combine_state( + enum connman_service_state state_a, + enum connman_service_state state_b) +{ + enum connman_service_state result; + + if (state_a == state_b) { + result = state_a; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_UNKNOWN) { + result = state_b; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_UNKNOWN) { + result = state_a; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_IDLE) { + result = state_b; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_IDLE) { + result = state_a; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_ASSOCIATION) { + if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION || + state_b == CONNMAN_SERVICE_STATE_ONLINE || + state_b == CONNMAN_SERVICE_STATE_READY) + result = state_b; + else + result = state_a; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_ASSOCIATION) { + if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION || + state_a == CONNMAN_SERVICE_STATE_ONLINE || + state_a == CONNMAN_SERVICE_STATE_READY) + result = state_a; + else + result = state_b; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION) { + if (state_b == CONNMAN_SERVICE_STATE_ONLINE || + state_b == CONNMAN_SERVICE_STATE_READY) + result = state_b; + else + result = state_a; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION) { + if (state_a == CONNMAN_SERVICE_STATE_ONLINE || + state_a == CONNMAN_SERVICE_STATE_READY) + result = state_a; + else + result = state_b; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_READY) { + if (state_b == CONNMAN_SERVICE_STATE_ONLINE) + result = state_b; + else + result = state_a; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_READY) { + if (state_a == CONNMAN_SERVICE_STATE_ONLINE) + result = state_a; + else + result = state_b; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_ONLINE) { + result = state_a; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_ONLINE) { + result = state_b; + goto done; + } + + if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT) { + result = state_b; + goto done; + } + + if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT) { + result = state_a; + goto done; + } + + result = CONNMAN_SERVICE_STATE_FAILURE; + +done: + return result; +} + +static connman_bool_t is_connecting_state(struct connman_service *service, + enum connman_service_state state) { - switch (service->state) { + switch (state) { case CONNMAN_SERVICE_STATE_UNKNOWN: case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_FAILURE: + if (service->network != NULL) + return connman_network_get_connecting(service->network); case CONNMAN_SERVICE_STATE_DISCONNECT: case CONNMAN_SERVICE_STATE_READY: case CONNMAN_SERVICE_STATE_ONLINE: @@ -320,9 +441,10 @@ static connman_bool_t is_connecting(struct connman_service *service) return FALSE; } -static connman_bool_t is_connected(const struct connman_service *service) +static connman_bool_t is_connected_state(const struct connman_service *service, + enum connman_service_state state) { - switch (service->state) { + switch (state) { case CONNMAN_SERVICE_STATE_UNKNOWN: case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_ASSOCIATION: @@ -338,6 +460,24 @@ static connman_bool_t is_connected(const struct connman_service *service) return FALSE; } +static connman_bool_t is_connecting(struct connman_service *service) +{ + enum connman_service_state state; + + state = combine_state(service->state_ipv4, service->state_ipv6); + + return is_connecting_state(service, state); +} + +static connman_bool_t is_connected(struct connman_service *service) +{ + enum connman_service_state state; + + state = combine_state(service->state_ipv4, service->state_ipv6); + + return is_connected_state(service, state); +} + static void update_nameservers(struct connman_service *service) { const char *ifname; @@ -352,7 +492,7 @@ static void update_nameservers(struct connman_service *service) if (ifname == NULL) return; - switch (service->state) { + switch (combine_state(service->state_ipv4, service->state_ipv6)) { case CONNMAN_SERVICE_STATE_UNKNOWN: case CONNMAN_SERVICE_STATE_IDLE: case CONNMAN_SERVICE_STATE_ASSOCIATION: @@ -369,14 +509,21 @@ static void update_nameservers(struct connman_service *service) connman_resolver_remove_all(ifname); - if (service->nameservers != NULL) { + if (service->nameservers_config != NULL) { int i; - for (i = 0; service->nameservers[i]; i++) + for (i = 0; service->nameservers_config[i] != NULL; i++) { + connman_resolver_append(ifname, NULL, + service->nameservers_config[i]); + } + } else if (service->nameservers != NULL) { + int i; + + for (i = 0; service->nameservers[i] != NULL; i++) { connman_resolver_append(ifname, NULL, service->nameservers[i]); - } else if (service->nameserver != NULL) - connman_resolver_append(ifname, NULL, service->nameserver); + } + } if (service->domains != NULL) { int i; @@ -390,47 +537,136 @@ static void update_nameservers(struct connman_service *service) connman_resolver_flush(); } -void __connman_service_append_nameserver(struct connman_service *service, +int __connman_service_nameserver_append(struct connman_service *service, const char *nameserver) { - DBG("service %p nameserver %s", service, nameserver); + int len; + + DBG("service %p nameserver %s", service, nameserver); if (nameserver == NULL) - return; + return -EINVAL; + + if (service->nameservers != NULL) { + int i; + + for (i = 0; service->nameservers[i] != NULL; i++) + if (g_strcmp0(service->nameservers[i], nameserver) == 0) + return -EEXIST; - g_free(service->nameserver); - service->nameserver = g_strdup(nameserver); + len = g_strv_length(service->nameservers); + service->nameservers = g_try_renew(char *, service->nameservers, + len + 2); + } else { + len = 0; + service->nameservers = g_try_new0(char *, len + 2); + } + + if (service->nameservers == NULL) + return -ENOMEM; + + service->nameservers[len] = g_strdup(nameserver); + service->nameservers[len + 1] = NULL; update_nameservers(service); + + return 0; } -void __connman_service_remove_nameserver(struct connman_service *service, +int __connman_service_nameserver_remove(struct connman_service *service, const char *nameserver) { + char **servers; + int len, i, j; + DBG("service %p nameserver %s", service, nameserver); if (nameserver == NULL) - return; + return -EINVAL; + + if (service->nameservers == NULL) + return 0; + + len = g_strv_length(service->nameservers); + if (len == 1) { + if (g_strcmp0(service->nameservers[0], nameserver) != 0) + return 0; + + g_strfreev(service->nameservers); + service->nameservers = NULL; + + return 0; + } + + servers = g_try_new0(char *, len - 1); + if (servers == NULL) + return -ENOMEM; + + for (i = 0, j = 0; i < len; i++) { + if (g_strcmp0(service->nameservers[i], nameserver) != 0) { + servers[j] = g_strdup(service->nameservers[i]); + j++; + } + } + servers[len - 2] = NULL; + + g_strfreev(service->nameservers); + service->nameservers = servers; + + update_nameservers(service); + + return 0; +} - g_free(service->nameserver); - service->nameserver = NULL; +void __connman_service_nameserver_clear(struct connman_service *service) +{ + g_strfreev(service->nameservers); + service->nameservers = NULL; update_nameservers(service); } +static void nameserver_add_routes(int index, char **nameservers, + const char *gw) +{ + int i; + + for (i = 0; nameservers[i] != NULL; i++) { + if (connman_inet_compare_subnet(index, nameservers[i])) + continue; + + connman_inet_add_host_route(index, nameservers[i], gw); + } +} + +static void nameserver_del_routes(int index, char **nameservers) +{ + int i; + + for (i = 0; nameservers[i] != NULL; i++) + connman_inet_del_host_route(index, nameservers[i]); +} + void __connman_service_nameserver_add_routes(struct connman_service *service, const char *gw) { - int index; + int index = -1; if (service == NULL) return; - index = connman_network_get_index(service->network); - - if (service->nameservers != NULL) { - int i; + if (service->network != NULL) + index = connman_network_get_index(service->network); + else if (service->provider != NULL) + index = connman_provider_get_index(service->provider); + if (service->nameservers_config != NULL) { + /* + * Configured nameserver takes preference over the + * discoverd nameserver gathered from DHCP, VPN, etc. + */ + nameserver_add_routes(index, service->nameservers_config, gw); + } else if (service->nameservers != NULL) { /* * We add nameservers host routes for nameservers that * are not on our subnet. For those who are, the subnet @@ -438,40 +674,26 @@ void __connman_service_nameserver_add_routes(struct connman_service *service, * tries to reach them. The subnet route is installed * when setting the interface IP address. */ - for (i = 0; service->nameservers[i]; i++) { - if (connman_inet_compare_subnet(index, - service->nameservers[i])) - continue; - - connman_inet_add_host_route(index, - service->nameservers[i], gw); - } - } else if (service->nameserver != NULL) { - if (connman_inet_compare_subnet(index, service->nameserver)) - return; - - connman_inet_add_host_route(index, service->nameserver, gw); + nameserver_add_routes(index, service->nameservers, gw); } } void __connman_service_nameserver_del_routes(struct connman_service *service) { - int index; + int index = -1; if (service == NULL) return; - index = connman_network_get_index(service->network); - - if (service->nameservers != NULL) { - int i; + if (service->network != NULL) + index = connman_network_get_index(service->network); + else if (service->provider != NULL) + index = connman_provider_get_index(service->provider); - for (i = 0; service->nameservers[i]; i++) - connman_inet_del_host_route(index, - service->nameservers[i]); - } else if (service->nameserver != NULL) { - connman_inet_del_host_route(index, service->nameserver); - } + if (service->nameservers_config != NULL) + nameserver_del_routes(index, service->nameservers_config); + else if (service->nameservers != NULL) + nameserver_del_routes(index, service->nameservers); } static struct connman_stats *stats_get(struct connman_service *service) @@ -615,7 +837,8 @@ static void state_changed(struct connman_service *service) { const char *str; - str = state2string(service->state); + str = state2string(combine_state(service->state_ipv4, + service->state_ipv6)); if (str == NULL) return; @@ -726,30 +949,20 @@ static void login_changed(struct connman_service *service) DBUS_TYPE_BOOLEAN, &required); } -static void apn_changed(struct connman_service *service) +static void append_security(DBusMessageIter *iter, void *user_data) { - dbus_bool_t required; - - switch (service->type) { - case CONNMAN_SERVICE_TYPE_UNKNOWN: - case CONNMAN_SERVICE_TYPE_SYSTEM: - case CONNMAN_SERVICE_TYPE_ETHERNET: - case CONNMAN_SERVICE_TYPE_WIMAX: - case CONNMAN_SERVICE_TYPE_BLUETOOTH: - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_GADGET: - return; - case CONNMAN_SERVICE_TYPE_CELLULAR: - break; - } + struct connman_service *service = user_data; + const char *str; - required = (service->apn == NULL) ? TRUE : FALSE; + str = security2string(service->security); + if (str != NULL) + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &str); - connman_dbus_property_changed_basic(service->path, - CONNMAN_SERVICE_INTERFACE, "SetupRequired", - DBUS_TYPE_BOOLEAN, &required); + str = "wps"; + if (service->wps == TRUE) + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &str); } static void append_ethernet(DBusMessageIter *iter, void *user_data) @@ -768,7 +981,10 @@ static void append_ipv4(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (is_connected(service) == FALSE) + DBG("ipv4 %p state %s", service->ipconfig_ipv4, + state2string(service->state_ipv4)); + + if (is_connected_state(service, service->state_ipv4) == FALSE) return; if (service->ipconfig_ipv4 != NULL) @@ -779,11 +995,15 @@ static void append_ipv6(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (is_connected(service) == FALSE) + DBG("ipv6 %p state %s", service->ipconfig_ipv6, + state2string(service->state_ipv6)); + + if (is_connected_state(service, service->state_ipv6) == FALSE) return; if (service->ipconfig_ipv6 != NULL) - __connman_ipconfig_append_ipv6(service->ipconfig_ipv6, iter); + __connman_ipconfig_append_ipv6(service->ipconfig_ipv6, iter, + service->ipconfig_ipv4); } static void append_ipv4config(DBusMessageIter *iter, void *user_data) @@ -804,6 +1024,19 @@ static void append_ipv6config(DBusMessageIter *iter, void *user_data) iter); } +static void append_nameserver(DBusMessageIter *iter, char ***nameservers) +{ + char **servers; + int i; + + servers = *nameservers; + + for (i = 0; servers[i] != NULL; i++) { + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &servers[i]); + } +} + static void append_dns(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; @@ -811,21 +1044,13 @@ static void append_dns(DBusMessageIter *iter, void *user_data) if (is_connected(service) == FALSE) return; - if (service->nameservers != NULL) { - int i; - - for (i = 0; service->nameservers[i]; i++) - dbus_message_iter_append_basic(iter, - DBUS_TYPE_STRING, &service->nameservers[i]); - + if (service->nameservers_config != NULL) { + append_nameserver(iter, &service->nameservers_config); return; - } - - if (service->nameserver == NULL) + } else if (service->nameservers != NULL) { + append_nameserver(iter, &service->nameservers); return; - - dbus_message_iter_append_basic(iter, - DBUS_TYPE_STRING, &service->nameserver); + } } static void append_dnsconfig(DBusMessageIter *iter, void *user_data) @@ -833,19 +1058,22 @@ static void append_dnsconfig(DBusMessageIter *iter, void *user_data) struct connman_service *service = user_data; int i; - if (service->nameservers == NULL) + if (service->nameservers_config == NULL) return; - for (i = 0; service->nameservers[i]; i++) + for (i = 0; service->nameservers_config[i]; i++) { dbus_message_iter_append_basic(iter, - DBUS_TYPE_STRING, &service->nameservers[i]); + DBUS_TYPE_STRING, + &service->nameservers_config[i]); + } } static void append_domain(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - if (is_connected(service) == FALSE && is_connecting(service) == FALSE) + if (is_connected(service) == FALSE && + is_connecting(service) == FALSE) return; if (service->domainname == NULL) @@ -897,6 +1125,7 @@ static void append_excludes(DBusMessageIter *iter, void *user_data) static void append_proxy(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; + enum connman_service_proxy_method proxy; const char *pac = NULL; const char *method = proxymethod2string( CONNMAN_SERVICE_PROXY_METHOD_DIRECT); @@ -906,10 +1135,11 @@ static void append_proxy(DBusMessageIter *iter, void *user_data) if (is_connected(service) == FALSE) return; - switch (service->proxy) { + proxy = connman_service_get_proxy_method(service); + + switch (proxy) { case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN: - service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT; - /* fall through */ + return; case CONNMAN_SERVICE_PROXY_METHOD_DIRECT: goto done; case CONNMAN_SERVICE_PROXY_METHOD_MANUAL: @@ -941,7 +1171,7 @@ static void append_proxy(DBusMessageIter *iter, void *user_data) break; } - method = proxymethod2string(service->proxy); + method = proxymethod2string(proxy); done: connman_dbus_dict_append_basic(iter, "Method", @@ -953,7 +1183,12 @@ static void append_proxyconfig(DBusMessageIter *iter, void *user_data) struct connman_service *service = user_data; const char *method; - switch (service->proxy) { + if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) + return; + + switch (service->proxy_config) { + case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN: + return; case CONNMAN_SERVICE_PROXY_METHOD_DIRECT: break; case CONNMAN_SERVICE_PROXY_METHOD_MANUAL: @@ -972,12 +1207,9 @@ static void append_proxyconfig(DBusMessageIter *iter, void *user_data) connman_dbus_dict_append_basic(iter, "URL", DBUS_TYPE_STRING, &service->pac); break; - case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN: - service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO; - break; } - method = proxymethod2string(service->proxy); + method = proxymethod2string(service->proxy_config); connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &method); @@ -1333,12 +1565,11 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_basic(dict, "Mode", DBUS_TYPE_STRING, &str); - str = security2string(service->security); - if (str != NULL) - connman_dbus_dict_append_basic(dict, "Security", - DBUS_TYPE_STRING, &str); + connman_dbus_dict_append_array(dict, "Security", + DBUS_TYPE_STRING, append_security, service); - str = state2string(service->state); + str = state2string(combine_state(service->state_ipv4, + service->state_ipv6)); if (str != NULL) connman_dbus_dict_append_basic(dict, "State", DBUS_TYPE_STRING, &str); @@ -1383,31 +1614,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_basic(dict, "Roaming", DBUS_TYPE_BOOLEAN, &service->roaming); - if (service->mcc != NULL && service->mnc != NULL) { - connman_dbus_dict_append_basic(dict, "MCC", - DBUS_TYPE_STRING, &service->mcc); - connman_dbus_dict_append_basic(dict, "MNC", - DBUS_TYPE_STRING, &service->mnc); - } - - if (service->apn != NULL) { - connman_dbus_dict_append_basic(dict, "APN", - DBUS_TYPE_STRING, &service->apn); - - if (service->username != NULL) - connman_dbus_dict_append_basic(dict, - "Username", DBUS_TYPE_STRING, - &service->username); - - if (service->password != NULL) - connman_dbus_dict_append_basic(dict, - "Password", DBUS_TYPE_STRING, - &service->password); - - required = FALSE; - } else - required = TRUE; - + required = FALSE; connman_dbus_dict_append_basic(dict, "SetupRequired", DBUS_TYPE_BOOLEAN, &required); connman_dbus_dict_append_dict(dict, "Ethernet", @@ -1437,6 +1644,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_basic(dict, "PassphraseRequired", DBUS_TYPE_BOOLEAN, &required); + /* fall through */ case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIMAX: @@ -1508,10 +1716,12 @@ int __connman_service_get_index(struct connman_service *service) if (service == NULL) return -1; - if (service->network == NULL) - return -1; + if (service->network != NULL) + return connman_network_get_index(service->network); + else if (service->provider != NULL) + return connman_provider_get_index(service->provider); - return connman_network_get_index(service->network); + return -1; } void __connman_service_set_domainname(struct connman_service *service, @@ -1537,12 +1747,31 @@ const char *connman_service_get_domainname(struct connman_service *service) return service->domainname; } -const char *connman_service_get_nameserver(struct connman_service *service) +char **connman_service_get_nameservers(struct connman_service *service) { if (service == NULL) return NULL; - return service->nameserver; + if (service->nameservers_config != NULL) + return service->nameservers_config; + else if (service->nameservers != NULL) + return service->nameservers; + + return NULL; +} + +void connman_service_set_proxy_method(struct connman_service *service, + enum connman_service_proxy_method method) +{ + if (service == NULL) + return; + + service->proxy = method; + + proxy_changed(service); + + if (method != CONNMAN_SERVICE_PROXY_METHOD_AUTO) + __connman_notifier_proxy_changed(service); } enum connman_service_proxy_method connman_service_get_proxy_method( @@ -1551,6 +1780,14 @@ enum connman_service_proxy_method connman_service_get_proxy_method( if (service == NULL) return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN; + if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) { + if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_AUTO && + service->pac == NULL) + return service->proxy; + + return service->proxy_config; + } + return service->proxy; } @@ -1578,6 +1815,8 @@ void __connman_service_set_proxy_autoconfig(struct connman_service *service, if (service == NULL) return; + service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO; + if (service->ipconfig_ipv4) { if (__connman_ipconfig_set_proxy_autoconfig( service->ipconfig_ipv4, url) < 0) @@ -1590,6 +1829,8 @@ void __connman_service_set_proxy_autoconfig(struct connman_service *service, return; proxy_changed(service); + + __connman_notifier_proxy_changed(service); } const char *connman_service_get_proxy_autoconfig(struct connman_service *service) @@ -1606,6 +1847,124 @@ const char *connman_service_get_proxy_autoconfig(struct connman_service *service return NULL; } +static void update_timeservers(struct connman_service *service) +{ + int i; + + if (service->timeservers == NULL) + return; + + switch (combine_state(service->state_ipv4, service->state_ipv6)) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + return; + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + for (i = 0; service->timeservers[i] != NULL; i++) + connman_timeserver_remove(service->timeservers[i]); + return; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + break; + } + + for (i = 0; service->timeservers[i] != NULL; i++) + connman_timeserver_append(service->timeservers[i]); +} + +int __connman_service_timeserver_append(struct connman_service *service, + const char *timeserver) +{ + int len; + + DBG("service %p timeserver %s", service, timeserver); + + if (timeserver == NULL) + return -EINVAL; + + if (service->timeservers != NULL) { + int i; + + for (i = 0; service->timeservers[i] != NULL; i++) + if (g_strcmp0(service->timeservers[i], timeserver) == 0) + return -EEXIST; + + len = g_strv_length(service->timeservers); + service->timeservers = g_try_renew(char *, service->timeservers, + len + 2); + } else { + len = 0; + service->timeservers = g_try_new0(char *, len + 2); + } + + if (service->timeservers == NULL) + return -ENOMEM; + + service->timeservers[len] = g_strdup(timeserver); + service->timeservers[len + 1] = NULL; + + update_timeservers(service); + + return 0; +} + +int __connman_service_timeserver_remove(struct connman_service *service, + const char *timeserver) +{ + char **servers; + int len, i, j; + + DBG("service %p timeserver %s", service, timeserver); + + if (timeserver == NULL) + return -EINVAL; + + if (service->timeservers == NULL) + return 0; + + len = g_strv_length(service->timeservers); + if (len == 1) { + if (g_strcmp0(service->timeservers[0], timeserver) != 0) + return 0; + + g_strfreev(service->timeservers); + service->timeservers = NULL; + + return 0; + } + + servers = g_try_new0(char *, len - 1); + if (servers == NULL) + return -ENOMEM; + + for (i = 0, j = 0; i < len; i++) { + if (g_strcmp0(service->timeservers[i], timeserver) != 0) { + servers[j] = g_strdup(service->timeservers[i]); + j++; + } + } + servers[len - 2] = NULL; + + g_strfreev(service->timeservers); + service->timeservers = servers; + + update_timeservers(service); + + return 0; +} + +void __connman_service_set_pac(struct connman_service *service, + const char *pac) +{ + if (pac == NULL) + return; + + g_free(service->pac); + service->pac = g_strdup(pac); +} + void __connman_service_set_passphrase(struct connman_service *service, const char* passphrase) { @@ -1806,7 +2165,7 @@ static int update_proxy_configuration(struct connman_service *service, if (excludes_str != NULL) g_string_free(excludes_str, TRUE); - service->proxy = method; + service->proxy_config = method; return 0; @@ -1820,6 +2179,49 @@ error: return -EINVAL; } +static int set_ipconfig(struct connman_service *service, + struct connman_ipconfig *ipconfig, + DBusMessageIter *array, + enum connman_service_state state, + enum connman_service_state *new_state) +{ + enum connman_ipconfig_method old_method; + enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + enum connman_ipconfig_type type; + int err; + + old_method = __connman_ipconfig_get_method(ipconfig); + + if (is_connecting_state(service, state) || + is_connected_state(service, state)) + __connman_network_clear_ipconfig(service->network, ipconfig); + + err = __connman_ipconfig_set_config(ipconfig, array); + method = __connman_ipconfig_get_method(ipconfig); + type = __connman_ipconfig_get_config_type(ipconfig); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { + if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF && + method == CONNMAN_IPCONFIG_METHOD_DHCP) { + *new_state = service->state_ipv4 = + CONNMAN_SERVICE_STATE_CONFIGURATION; + __connman_ipconfig_enable(ipconfig); + } + + } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) { + if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF && + method == CONNMAN_IPCONFIG_METHOD_AUTO) { + *new_state = service->state_ipv6; + __connman_ipconfig_enable(ipconfig); + } + } + + DBG("err %d ipconfig %p type %d method %d state %s", err, ipconfig, + type, method, state2string(*new_state)); + + return err; +} + static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -1837,87 +2239,29 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); - type = dbus_message_iter_get_arg_type(&value); - - if (g_str_has_prefix(name, "AutoConnect") == TRUE) { - connman_bool_t autoconnect; - - if (type != DBUS_TYPE_BOOLEAN) - return __connman_error_invalid_arguments(msg); - - if (service->favorite == FALSE) - return __connman_error_invalid_service(msg); - - dbus_message_iter_get_basic(&value, &autoconnect); - - if (service->autoconnect == autoconnect) - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - - service->autoconnect = autoconnect; - - autoconnect_changed(service); - - __connman_storage_save_service(service); - } else if (g_str_equal(name, "Passphrase") == TRUE) { - const char *passphrase; - - if (type != DBUS_TYPE_STRING) - return __connman_error_invalid_arguments(msg); - - if (service->immutable == TRUE) - return __connman_error_not_supported(msg); - - dbus_message_iter_get_basic(&value, &passphrase); - - __connman_service_set_passphrase(service, passphrase); - } else if (g_str_equal(name, "APN") == TRUE) { - const char *apn; - - if (type != DBUS_TYPE_STRING) - return __connman_error_invalid_arguments(msg); - - if (service->immutable == TRUE) - return __connman_error_not_supported(msg); - - if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR) - return __connman_error_invalid_service(msg); - - dbus_message_iter_get_basic(&value, &apn); - - g_free(service->apn); - service->apn = g_strdup(apn); - - apn_changed(service); - - if (service->network != NULL) - connman_network_set_string(service->network, - "Cellular.APN", service->apn); - - __connman_storage_save_service(service); - } else if (g_str_equal(name, "Username") == TRUE) { - const char *username; + type = dbus_message_iter_get_arg_type(&value); - if (type != DBUS_TYPE_STRING) - return __connman_error_invalid_arguments(msg); + if (g_str_equal(name, "AutoConnect") == TRUE) { + connman_bool_t autoconnect; - if (service->immutable == TRUE) - return __connman_error_not_supported(msg); + if (type != DBUS_TYPE_BOOLEAN) + return __connman_error_invalid_arguments(msg); - if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR) + if (service->favorite == FALSE) return __connman_error_invalid_service(msg); - dbus_message_iter_get_basic(&value, &username); + dbus_message_iter_get_basic(&value, &autoconnect); - g_free(service->username); - service->username = g_strdup(username); + if (service->autoconnect == autoconnect) + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - if (service->network != NULL) - connman_network_set_string(service->network, - "Cellular.Username", service->username); + service->autoconnect = autoconnect; + + autoconnect_changed(service); __connman_storage_save_service(service); - } else if (g_str_equal(name, "Password") == TRUE) { - const char *password; + } else if (g_str_equal(name, "Passphrase") == TRUE) { + const char *passphrase; if (type != DBUS_TYPE_STRING) return __connman_error_invalid_arguments(msg); @@ -1925,19 +2269,9 @@ static DBusMessage *set_property(DBusConnection *conn, if (service->immutable == TRUE) return __connman_error_not_supported(msg); - if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR) - return __connman_error_invalid_service(msg); - - dbus_message_iter_get_basic(&value, &password); - - g_free(service->password); - service->password = g_strdup(password); - - if (service->network != NULL) - connman_network_set_string(service->network, - "Cellular.Password", service->password); + dbus_message_iter_get_basic(&value, &passphrase); - __connman_storage_save_service(service); + __connman_service_set_passphrase(service, passphrase); } else if (g_str_equal(name, "Nameservers.Configuration") == TRUE) { DBusMessageIter entry; GString *str; @@ -1952,7 +2286,7 @@ static DBusMessage *set_property(DBusConnection *conn, return __connman_error_invalid_arguments(msg); index = connman_network_get_index(service->network); - gw = __connman_ipconfig_get_gateway(index); + gw = __connman_ipconfig_get_gateway_from_index(index); if (gw && strlen(gw)) __connman_service_nameserver_del_routes(service); @@ -1969,12 +2303,14 @@ static DBusMessage *set_property(DBusConnection *conn, g_string_append(str, val); } - g_strfreev(service->nameservers); + g_strfreev(service->nameservers_config); - if (str->len > 0) - service->nameservers = g_strsplit_set(str->str, " ", 0); - else - service->nameservers = NULL; + if (str->len > 0) { + service->nameservers_config = + g_strsplit_set(str->str, " ", 0); + } else { + service->nameservers_config = NULL; + } g_string_free(str, TRUE); @@ -2038,7 +2374,9 @@ static DBusMessage *set_property(DBusConnection *conn, } else if (g_str_equal(name, "IPv4.Configuration") == TRUE || g_str_equal(name, "IPv6.Configuration")) { - enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; + struct connman_ipconfig *ipv4 = NULL, *ipv6 = NULL; + enum connman_service_state state = + CONNMAN_SERVICE_STATE_UNKNOWN; int err = 0; DBG("%s", name); @@ -2047,44 +2385,33 @@ static DBusMessage *set_property(DBusConnection *conn, service->ipconfig_ipv6 == NULL) return __connman_error_invalid_property(msg); - if (is_connecting(service) || - is_connected(service)) { - __connman_network_clear_ipconfig(service->network, - service->ipconfig_ipv4); - __connman_network_clear_ipconfig(service->network, - service->ipconfig_ipv6); - } - if (g_str_equal(name, "IPv4.Configuration") == TRUE) { - type = CONNMAN_IPCONFIG_TYPE_IPV4; - err = __connman_ipconfig_set_config( - service->ipconfig_ipv4, type, &value); + ipv4 = service->ipconfig_ipv4; + err = set_ipconfig(service, ipv4, &value, + service->state_ipv4, &state); + } else if (g_str_equal(name, "IPv6.Configuration") == TRUE) { - type = CONNMAN_IPCONFIG_TYPE_IPV6; - err = __connman_ipconfig_set_config( - service->ipconfig_ipv6, type, &value); + ipv6 = service->ipconfig_ipv6; + err = set_ipconfig(service, ipv6, &value, + service->state_ipv6, &state); } if (err < 0) { - if (is_connected(service) || - is_connecting(service)) - __connman_network_set_ipconfig( - service->network, - service->ipconfig_ipv4, - service->ipconfig_ipv6); + if (is_connected_state(service, state) || + is_connecting_state(service, state)) + __connman_network_set_ipconfig(service->network, + ipv4, ipv6); return __connman_error_failed(msg, -err); } - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + if (ipv4) ipv4_configuration_changed(service); - else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + else if (ipv6) ipv6_configuration_changed(service); - if (is_connecting(service) || - is_connected(service)) + if (is_connecting(service) || is_connected(service)) __connman_network_set_ipconfig(service->network, - service->ipconfig_ipv4, - service->ipconfig_ipv6); + ipv4, ipv6); __connman_storage_save_service(service); } else @@ -2095,7 +2422,7 @@ static DBusMessage *set_property(DBusConnection *conn, static void set_idle(struct connman_service *service) { - service->state = CONNMAN_SERVICE_STATE_IDLE; + service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE; service->error = CONNMAN_SERVICE_ERROR_UNKNOWN; state_changed(service); } @@ -2143,7 +2470,8 @@ static connman_bool_t is_ignore(struct connman_service *service) if (service->ignore == TRUE) return TRUE; - if (service->state == CONNMAN_SERVICE_STATE_FAILURE) + if (combine_state(service->state_ipv4, service->state_ipv6) == + CONNMAN_SERVICE_STATE_FAILURE) return TRUE; return FALSE; @@ -2174,7 +2502,9 @@ void __connman_service_auto_connect(void) return; if (is_ignore(service) == FALSE && - service->state == CONNMAN_SERVICE_STATE_IDLE) + combine_state(service->state_ipv4, + service->state_ipv6) == + CONNMAN_SERVICE_STATE_IDLE) break; service = NULL; @@ -2208,9 +2538,21 @@ static void reply_pending(struct connman_service *service, int error) error); if (reply != NULL) g_dbus_send_message(connection, reply); - } else - g_dbus_send_reply(connection, service->pending, + } else { + const char *sender; + + sender = dbus_message_get_interface(service->pending); + + DBG("sender %s", sender); + + if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0) + g_dbus_send_reply(connection, service->pending, + DBUS_TYPE_OBJECT_PATH, &service->path, DBUS_TYPE_INVALID); + else + g_dbus_send_reply(connection, service->pending, + DBUS_TYPE_INVALID); + } dbus_message_unref(service->pending); service->pending = NULL; @@ -2229,13 +2571,8 @@ static gboolean connect_timeout(gpointer user_data) if (service->network != NULL) __connman_network_disconnect(service->network); - if (service->ipconfig_ipv4) - if (!__connman_ipconfig_disable(service->ipconfig_ipv4)) - service->ipconfig_ipv4 = NULL; - - if (service->ipconfig_ipv6) - if (!__connman_ipconfig_disable(service->ipconfig_ipv6)) - service->ipconfig_ipv6 = NULL; + __connman_ipconfig_disable(service->ipconfig_ipv4); + __connman_ipconfig_disable(service->ipconfig_ipv6); __connman_stats_service_unregister(service); @@ -2252,7 +2589,11 @@ static gboolean connect_timeout(gpointer user_data) autoconnect = TRUE; __connman_service_indicate_state(service, - CONNMAN_SERVICE_STATE_FAILURE); + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV6); if (autoconnect == TRUE && service->userconnect == FALSE) __connman_service_auto_connect(); @@ -2294,9 +2635,12 @@ static void request_input_cb (struct connman_service *service, { DBG ("RequestInput return, %p", service); - if (passphrase == NULL) + if (passphrase == NULL && service->wps == FALSE) return; - __connman_service_set_passphrase(service, passphrase); + + if (passphrase != NULL) + __connman_service_set_passphrase(service, passphrase); + __connman_service_connect(service); } @@ -2378,8 +2722,7 @@ static DBusMessage *connect_service(DBusConnection *conn, while (g_sequence_iter_is_end(iter) == FALSE) { struct connman_service *temp = g_sequence_get(iter); - if (service->type == temp->type && - is_connecting(temp) == TRUE) + if (service->type == temp->type && is_connecting(temp) == TRUE) return __connman_error_in_progress(msg); iter = g_sequence_iter_next(iter); @@ -2402,6 +2745,9 @@ static DBusMessage *connect_service(DBusConnection *conn, return NULL; } + if (service->pending == NULL) + return NULL; + if (err != -EINPROGRESS) { dbus_message_unref(service->pending); service->pending = NULL; @@ -2454,7 +2800,8 @@ static DBusMessage *remove_service(DBusConnection *conn, return __connman_error_not_supported(msg); if (service->favorite == FALSE && - service->state != CONNMAN_SERVICE_STATE_FAILURE) + combine_state(service->state_ipv4, service->state_ipv6) != + CONNMAN_SERVICE_STATE_FAILURE) return __connman_error_not_supported(msg); if (service->network != NULL) { @@ -2463,24 +2810,11 @@ static DBusMessage *remove_service(DBusConnection *conn, __connman_network_disconnect(service->network); } - g_hash_table_destroy(service->counter_table); - g_free(service->passphrase); service->passphrase = NULL; passphrase_changed(service); - g_free(service->apn); - service->apn = NULL; - - g_free(service->username); - service->username = NULL; - - g_free(service->password); - service->password = NULL; - - apn_changed(service); - set_idle(service); __connman_service_set_favorite(service, FALSE); @@ -2512,7 +2846,8 @@ static DBusMessage *move_service(DBusConnection *conn, DBG("target %s", target->identifier); - if (target->state != service->state) + if (target->state_ipv4 != service->state_ipv4 && + target->state_ipv6 != service->state_ipv6) return __connman_error_invalid_service(msg); g_get_current_time(&service->modified); @@ -2593,6 +2928,8 @@ static void service_free(gpointer user_data) g_free(path); } + g_hash_table_destroy(service->counter_table); + if (service->network != NULL) connman_network_unref(service->network); @@ -2613,18 +2950,13 @@ static void service_free(gpointer user_data) connman_location_unref(service->location); g_strfreev(service->nameservers); + g_strfreev(service->nameservers_config); g_strfreev(service->domains); g_strfreev(service->proxies); g_strfreev(service->excludes); - g_free(service->nameserver); g_free(service->domainname); g_free(service->pac); - g_free(service->mcc); - g_free(service->mnc); - g_free(service->apn); - g_free(service->username); - g_free(service->password); g_free(service->profile); g_free(service->name); g_free(service->passphrase); @@ -2692,7 +3024,9 @@ static void service_initialize(struct connman_service *service) service->type = CONNMAN_SERVICE_TYPE_UNKNOWN; service->mode = CONNMAN_SERVICE_MODE_UNKNOWN; service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN; - service->state = CONNMAN_SERVICE_STATE_UNKNOWN; + + service->state_ipv4 = CONNMAN_SERVICE_STATE_UNKNOWN; + service->state_ipv6 = CONNMAN_SERVICE_STATE_UNKNOWN; service->favorite = FALSE; service->immutable = FALSE; @@ -2707,6 +3041,8 @@ static void service_initialize(struct connman_service *service) stats_init(service); service->provider = NULL; + + service->wps = FALSE; } /** @@ -2792,12 +3128,21 @@ static gint service_compare(gconstpointer a, gconstpointer b, { struct connman_service *service_a = (void *) a; struct connman_service *service_b = (void *) b; + enum connman_service_state state_a, state_b; + + state_a = combine_state(service_a->state_ipv4, service_a->state_ipv6); + state_b = combine_state(service_b->state_ipv4, service_b->state_ipv6); - if (service_a->state != service_b->state) { + if (state_a != state_b) { if (is_connected(service_a) == TRUE) return -1; if (is_connected(service_b) == TRUE) return 1; + + if (is_connecting(service_a) == TRUE) + return -1; + if (is_connecting(service_b) == TRUE) + return 1; } if (service_a->order > service_b->order) @@ -2914,6 +3259,18 @@ __connman_service_get_ip6config(struct connman_service *service) return service->ipconfig_ipv6; } +struct connman_ipconfig * +__connman_service_get_ipconfig(struct connman_service *service, int family) +{ + if (family == AF_INET) + return __connman_service_get_ip4config(service); + else if (family == AF_INET6) + return __connman_service_get_ip6config(service); + else + return NULL; + +} + enum connman_service_security __connman_service_get_security(struct connman_service *service) { if (service == NULL) @@ -2922,6 +3279,14 @@ enum connman_service_security __connman_service_get_security(struct connman_serv return service->security; } +connman_bool_t __connman_service_wps_enabled(struct connman_service *service) +{ + if (service == NULL) + return FALSE; + + return service->wps; +} + /** * __connman_service_set_favorite: * @service: service structure @@ -3016,29 +3381,49 @@ static void report_error_cb(struct connman_service *service, } int __connman_service_indicate_state(struct connman_service *service, - enum connman_service_state state) + enum connman_service_state new_state, + enum connman_ipconfig_type type) { + enum connman_service_state service_state, + state = CONNMAN_SERVICE_STATE_IDLE; GSequenceIter *iter; - DBG("service %p state %d", service, state); - if (service == NULL) return -EINVAL; - if (service->state == state) - return -EALREADY; + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { + if (service->state_ipv4 == new_state) + return -EALREADY; + state = combine_state(new_state, service->state_ipv6); + } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) { + if (service->state_ipv6 == new_state) + return -EALREADY; + state = combine_state(new_state, service->state_ipv4); + } else + return -EINVAL; + + service_state = combine_state(service->state_ipv4, + service->state_ipv6); - if (service->state == CONNMAN_SERVICE_STATE_FAILURE && + DBG("service %p state %s/%s => %s new %s/%d => %s", + service, + state2string(service->state_ipv4), + state2string(service->state_ipv6), + state2string(service_state), + state2string(new_state), + type, + state2string(state)); + + if (service_state == CONNMAN_SERVICE_STATE_FAILURE && state == CONNMAN_SERVICE_STATE_IDLE) return -EINVAL; - if (service->state == CONNMAN_SERVICE_STATE_IDLE && + if (service_state == CONNMAN_SERVICE_STATE_IDLE && state == CONNMAN_SERVICE_STATE_DISCONNECT) return -EINVAL; if (state == CONNMAN_SERVICE_STATE_IDLE && - service->state != CONNMAN_SERVICE_STATE_DISCONNECT) { - service->state = CONNMAN_SERVICE_STATE_DISCONNECT; + service_state != CONNMAN_SERVICE_STATE_DISCONNECT) { state_changed(service); reply_pending(service, ECONNABORTED); @@ -3046,7 +3431,7 @@ int __connman_service_indicate_state(struct connman_service *service, __connman_service_disconnect(service); } - if (state == CONNMAN_SERVICE_STATE_CONFIGURATION) { + if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) { if (__connman_stats_service_register(service) == 0) { __connman_stats_get(service, FALSE, &service->stats.data); @@ -3054,13 +3439,22 @@ int __connman_service_indicate_state(struct connman_service *service, &service->stats_roaming.data); } - if (service->ipconfig_ipv4) + if (type == CONNMAN_IPCONFIG_TYPE_IPV4 && + new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) __connman_ipconfig_enable(service->ipconfig_ipv4); - if (service->ipconfig_ipv6) + else if (type == CONNMAN_IPCONFIG_TYPE_IPV6 && + new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) __connman_ipconfig_enable(service->ipconfig_ipv6); } - service->state = state; + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + service->state_ipv4 = new_state; + else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + service->state_ipv6 = new_state; + + if (state == service_state) + return -EALREADY; + state_changed(service); if (state == CONNMAN_SERVICE_STATE_ONLINE) { @@ -3081,6 +3475,9 @@ int __connman_service_indicate_state(struct connman_service *service, } if (state == CONNMAN_SERVICE_STATE_READY) { + enum connman_service_proxy_method proxy_config; + enum connman_ipconfig_method method; + set_reconnect_state(service, TRUE); __connman_service_set_favorite(service, TRUE); @@ -3096,11 +3493,48 @@ int __connman_service_indicate_state(struct connman_service *service, dns_changed(service); domain_changed(service); - __connman_wpad_start(service); + proxy_config = service->proxy_config; + + /* + * We start WPAD if we haven't got a PAC URL from DHCP and + * if our proxy manual configuration is either empty or set + * to AUTO with an empty URL. + */ + if (service->proxy == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN && + (proxy_config == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN || + (proxy_config == + CONNMAN_SERVICE_PROXY_METHOD_AUTO && + service->pac == NULL))) + if (__connman_wpad_start(service) < 0) { + service->proxy = + CONNMAN_SERVICE_PROXY_METHOD_DIRECT; + + __connman_notifier_proxy_changed(service); + } __connman_notifier_connect(service->type); + if (service->type == CONNMAN_SERVICE_TYPE_WIFI && + connman_network_get_bool(service->network, + "WiFi.UseWPS") == TRUE) { + const char *pass; + + pass = connman_network_get_string(service->network, + "WiFi.Passphrase"); + + __connman_service_set_passphrase(service, pass); + + connman_network_set_bool(service->network, + "WiFi.UseWPS", FALSE); + } + default_changed(); + + method = __connman_ipconfig_get_method(service->ipconfig_ipv6); + if (method == CONNMAN_IPCONFIG_METHOD_OFF) + __connman_ipconfig_disable_ipv6( + service->ipconfig_ipv6); + } else if (state == CONNMAN_SERVICE_STATE_DISCONNECT) { __connman_location_finish(service); @@ -3131,10 +3565,12 @@ int __connman_service_indicate_state(struct connman_service *service, __connman_profile_changed(FALSE); - if (service->state == CONNMAN_SERVICE_STATE_ONLINE) + service_state = combine_state(service->state_ipv4, + service->state_ipv6); + if (service_state == CONNMAN_SERVICE_STATE_ONLINE) default_changed(); - if (service->state == CONNMAN_SERVICE_STATE_DISCONNECT) { + if (service_state == CONNMAN_SERVICE_STATE_DISCONNECT) { struct connman_service *def_service = get_default(); if (__connman_notifier_count_connected() == 0 && @@ -3143,8 +3579,8 @@ int __connman_service_indicate_state(struct connman_service *service, __connman_provider_disconnect(def_service->provider); } - if (service->state == CONNMAN_SERVICE_STATE_IDLE || - service->state == CONNMAN_SERVICE_STATE_FAILURE) + if (service_state == CONNMAN_SERVICE_STATE_IDLE || + service_state == CONNMAN_SERVICE_STATE_FAILURE) __connman_element_request_scan(CONNMAN_ELEMENT_TYPE_UNKNOWN); return 0; @@ -3160,8 +3596,49 @@ int __connman_service_indicate_error(struct connman_service *service, service->error = error; + if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY) + __connman_service_set_passphrase(service, NULL); + + return __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV4); +} + +int __connman_service_clear_error(struct connman_service *service) +{ + enum connman_service_state state; + + DBG("service %p", service); + + if (service == NULL) + return -EINVAL; + + state = combine_state(service->state_ipv4, service->state_ipv6); + + if (state != CONNMAN_SERVICE_STATE_FAILURE) + return -EINVAL; + + service->state_ipv4 = service->state_ipv6 = + CONNMAN_SERVICE_STATE_UNKNOWN; + service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;; + + if (service->favorite == TRUE) + set_reconnect_state(service, TRUE); + + __connman_service_indicate_state(service, CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV6); + + /* + * Toggling the IPv6 state to IDLE could trigger the auto connect + * machinery and consequently the IPv4 state. + */ + if (service->state_ipv4 != CONNMAN_SERVICE_STATE_UNKNOWN && + service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE) + return 0; + return __connman_service_indicate_state(service, - CONNMAN_SERVICE_STATE_FAILURE); + CONNMAN_SERVICE_STATE_IDLE, + CONNMAN_IPCONFIG_TYPE_IPV4); } int __connman_service_indicate_default(struct connman_service *service) @@ -3204,22 +3681,15 @@ static connman_bool_t prepare_network(struct connman_service *service) &ssid_len) == NULL) return FALSE; - connman_network_set_string(service->network, + if (service->passphrase != NULL) + connman_network_set_string(service->network, "WiFi.Passphrase", service->passphrase); break; case CONNMAN_NETWORK_TYPE_ETHERNET: case CONNMAN_NETWORK_TYPE_WIMAX: case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN: case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN: - break; case CONNMAN_NETWORK_TYPE_CELLULAR: - connman_network_set_string(service->network, - "Cellular.APN", service->apn); - - connman_network_set_string(service->network, - "Cellular.Username", service->username); - connman_network_set_string(service->network, - "Cellular.Password", service->password); break; } @@ -3260,18 +3730,10 @@ static void prepare_8021x(struct connman_service *service) service->phase2); } -int __connman_service_connect(struct connman_service *service) +static int service_connect(struct connman_service *service) { int err; - DBG("service %p", service); - - if (is_connected(service) == TRUE) - return -EISCONN; - - if (is_connecting(service) == TRUE) - return -EALREADY; - switch (service->type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: @@ -3281,11 +3743,8 @@ int __connman_service_connect(struct connman_service *service) case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: - case CONNMAN_SERVICE_TYPE_VPN: - break; case CONNMAN_SERVICE_TYPE_CELLULAR: - if (service->apn == NULL) - return -EINVAL; + case CONNMAN_SERVICE_TYPE_VPN: break; case CONNMAN_SERVICE_TYPE_WIFI: switch (service->security) { @@ -3296,8 +3755,16 @@ int __connman_service_connect(struct connman_service *service) case CONNMAN_SERVICE_SECURITY_PSK: case CONNMAN_SERVICE_SECURITY_WPA: case CONNMAN_SERVICE_SECURITY_RSN: - if (service->passphrase == NULL) - return -ENOKEY; + if (service->passphrase == NULL) { + if (service->network == NULL) + return -EOPNOTSUPP; + + if (service->wps == FALSE || + connman_network_get_bool( + service->network, + "WiFi.UseWPS") == FALSE) + return -ENOKEY; + } break; case CONNMAN_SERVICE_SECURITY_8021X: break; @@ -3343,32 +3810,65 @@ int __connman_service_connect(struct connman_service *service) if (err < 0) { if (err != -EINPROGRESS) { - if (service->ipconfig_ipv4) - if (!__connman_ipconfig_disable( - service->ipconfig_ipv4)) - service->ipconfig_ipv4 = NULL; - - if (service->ipconfig_ipv6) - if (!__connman_ipconfig_disable( - service->ipconfig_ipv6)) - service->ipconfig_ipv6 = NULL; - + __connman_ipconfig_disable(service->ipconfig_ipv4); + __connman_ipconfig_disable(service->ipconfig_ipv6); __connman_stats_service_unregister(service); - if (service->userconnect == TRUE) - return __connman_agent_report_error(service, - error2string(service->error), - report_error_cb, NULL); - else - return err; } + } + + return err; +} + + +int __connman_service_connect(struct connman_service *service) +{ + enum connman_service_state state; + int err; + + state = combine_state(service->state_ipv4, service->state_ipv6); + + DBG("service %p state %s", service, state2string(state)); + + if (is_connected(service) == TRUE) + return -EISCONN; + + if (is_connecting(service) == TRUE) + return -EALREADY; + switch (service->type) { + case CONNMAN_SERVICE_TYPE_UNKNOWN: + case CONNMAN_SERVICE_TYPE_SYSTEM: + case CONNMAN_SERVICE_TYPE_GPS: + case CONNMAN_SERVICE_TYPE_GADGET: + return -EINVAL; + default: + err = service_connect(service); + } + + if (err >= 0) + return 0; + + if (err == -EINPROGRESS) { service->timeout = g_timeout_add_seconds(CONNECT_TIMEOUT, connect_timeout, service); return -EINPROGRESS; } - return 0; + if (err == -ENOKEY) + return -ENOKEY; + + if (service->userconnect == TRUE) + reply_pending(service, err); + + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_FAILURE, + CONNMAN_IPCONFIG_TYPE_IPV6); + + return err; } int __connman_service_disconnect(struct connman_service *service) @@ -3385,6 +3885,11 @@ int __connman_service_disconnect(struct connman_service *service) else return -EOPNOTSUPP; + if (err < 0 && err != -EINPROGRESS) + return err; + + __connman_6to4_remove(service->ipconfig_ipv4); + if (service->ipconfig_ipv4) __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv4, NULL); @@ -3392,25 +3897,15 @@ int __connman_service_disconnect(struct connman_service *service) __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv6, NULL); - __connman_ipconfig_clear_address(service->ipconfig_ipv4); - __connman_ipconfig_clear_address(service->ipconfig_ipv6); + __connman_ipconfig_address_remove(service->ipconfig_ipv4); + __connman_ipconfig_address_remove(service->ipconfig_ipv6); - if (!__connman_ipconfig_disable(service->ipconfig_ipv4)) - service->ipconfig_ipv4 = NULL; - - if (!__connman_ipconfig_disable(service->ipconfig_ipv6)) - service->ipconfig_ipv6 = NULL; + __connman_ipconfig_disable(service->ipconfig_ipv4); + __connman_ipconfig_disable(service->ipconfig_ipv6); __connman_stats_service_unregister(service); - if (err < 0) { - if (err != -EINPROGRESS) - return err; - - return -EINPROGRESS; - } - - return 0; + return err; } /** @@ -3623,6 +4118,16 @@ done: goto failed; } + if (is_connected(service) == TRUE) { + err = -EISCONN; + goto failed; + } + + if (is_connecting(service) == TRUE) { + err = -EALREADY; + goto failed; + } + set_reconnect_state(service, FALSE); __connman_device_disconnect(device); @@ -3638,9 +4143,7 @@ done: if (err < 0 && err != -EINPROGRESS) goto failed; - g_dbus_send_reply(connection, msg, - DBUS_TYPE_OBJECT_PATH, &service->path, - DBUS_TYPE_INVALID); + service->pending = dbus_message_ref(msg); return 0; @@ -3766,18 +4269,52 @@ static void service_lower_down(struct connman_ipconfig *ipconfig) static void service_ip_bound(struct connman_ipconfig *ipconfig) { struct connman_service *service = connman_ipconfig_get_data(ipconfig); + enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; connman_info("%s ip bound", connman_ipconfig_get_ifname(ipconfig)); + type = __connman_ipconfig_get_config_type(ipconfig); + method = __connman_ipconfig_get_method(ipconfig); + + DBG("service %p ipconfig %p type %d method %d", service, ipconfig, + type, method); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV6 && + method == CONNMAN_IPCONFIG_METHOD_AUTO) + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_READY, + CONNMAN_IPCONFIG_TYPE_IPV6); + settings_changed(service); } static void service_ip_release(struct connman_ipconfig *ipconfig) { struct connman_service *service = connman_ipconfig_get_data(ipconfig); + enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; + enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; connman_info("%s ip release", connman_ipconfig_get_ifname(ipconfig)); + type = __connman_ipconfig_get_config_type(ipconfig); + method = __connman_ipconfig_get_method(ipconfig); + + DBG("service %p ipconfig %p type %d method %d", service, ipconfig, + type, method); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV6 && + method == CONNMAN_IPCONFIG_METHOD_OFF) + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_DISCONNECT, + CONNMAN_IPCONFIG_TYPE_IPV6); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV4 && + method == CONNMAN_IPCONFIG_METHOD_OFF) + __connman_service_indicate_state(service, + CONNMAN_SERVICE_STATE_DISCONNECT, + CONNMAN_IPCONFIG_TYPE_IPV4); + settings_changed(service); } @@ -3818,9 +4355,6 @@ static void setup_ip6config(struct connman_service *service, int index) if (service->ipconfig_ipv6 == NULL) return; - connman_ipconfig_set_method(service->ipconfig_ipv6, - CONNMAN_IPCONFIG_METHOD_OFF); - connman_ipconfig_set_data(service->ipconfig_ipv6, service); connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops); @@ -4086,20 +4620,13 @@ static void update_from_network(struct connman_service *service, str = connman_network_get_string(network, "WiFi.Security"); service->security = convert_wifi_security(str); - str = connman_network_get_string(network, "Cellular.MCC"); - g_free(service->mcc); - service->mcc = g_strdup(str); - - str = connman_network_get_string(network, "Cellular.MNC"); - g_free(service->mnc); - service->mnc = g_strdup(str); - if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) { connman_uint8_t value = connman_network_get_uint8(network, "Cellular.Mode"); service->mode = convert_cellular_mode(value); - } + } else if (service->type == CONNMAN_SERVICE_TYPE_WIFI) + service->wps = connman_network_get_bool(network, "WiFi.WPS"); if (service->strength > strength && service->network != NULL) { connman_network_unref(service->network); @@ -4125,6 +4652,7 @@ static void update_from_network(struct connman_service *service, struct connman_service * __connman_service_create_from_network(struct connman_network *network) { struct connman_service *service; + struct connman_device *device; const char *ident, *group; char *name; int index; @@ -4175,18 +4703,25 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne break; } - service->state = CONNMAN_SERVICE_STATE_IDLE; + service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE; update_from_network(service, network); index = connman_network_get_index(network); - setup_ip4config(service, index); - setup_ip6config(service, index); + + if (service->ipconfig_ipv4 == NULL) + setup_ip4config(service, index); + + if (service->ipconfig_ipv6 == NULL) + setup_ip6config(service, index); service_register(service); - if (service->favorite == TRUE) - __connman_service_auto_connect(); + if (service->favorite == TRUE) { + device = connman_network_get_device(service->network); + if (device && __connman_device_scanning(device) == FALSE) + __connman_service_auto_connect(); + } return service; } @@ -4219,6 +4754,9 @@ void __connman_service_update_from_network(struct connman_network *network) DBUS_TYPE_STRING, &service->name); } + if (service->type == CONNMAN_SERVICE_TYPE_WIFI) + service->wps = connman_network_get_bool(network, "WiFi.WPS"); + strength = connman_network_get_uint8(service->network, "Strength"); if (strength == service->strength) goto roaming; @@ -4306,7 +4844,7 @@ __connman_service_create_from_provider(struct connman_provider *provider) service->provider = connman_provider_ref(provider); service->autoconnect = FALSE; - service->state = CONNMAN_SERVICE_STATE_IDLE; + service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE; str = connman_provider_get_string(provider, "Name"); if (str != NULL) { @@ -4321,7 +4859,8 @@ __connman_service_create_from_provider(struct connman_provider *provider) service->strength = 0; - service->ipconfig_ipv4 = connman_ipconfig_create(index, + if (service->ipconfig_ipv4 == NULL) + service->ipconfig_ipv4 = connman_ipconfig_create(index, CONNMAN_IPCONFIG_TYPE_IPV4); if (service->ipconfig_ipv4 == NULL) return service; @@ -4331,7 +4870,8 @@ __connman_service_create_from_provider(struct connman_provider *provider) connman_ipconfig_set_data(service->ipconfig_ipv4, service); connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops); - service->ipconfig_ipv6 = connman_ipconfig_create(index, + if (service->ipconfig_ipv6 == NULL) + service->ipconfig_ipv6 = connman_ipconfig_create(index, CONNMAN_IPCONFIG_TYPE_IPV6); if (service->ipconfig_ipv6 == NULL) return service; @@ -4445,15 +4985,6 @@ static int service_load(struct connman_service *service) case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: - service->apn = g_key_file_get_string(keyfile, - service->identifier, "APN", NULL); - - service->username = g_key_file_get_string(keyfile, - service->identifier, "Username", NULL); - - service->password = g_key_file_get_string(keyfile, - service->identifier, "Password", NULL); - service->favorite = g_key_file_get_boolean(keyfile, service->identifier, "Favorite", NULL); @@ -4467,7 +4998,8 @@ static int service_load(struct connman_service *service) service->identifier, "Failure", NULL); if (str != NULL) { if (service->favorite == FALSE) - service->state = CONNMAN_SERVICE_STATE_FAILURE; + service->state_ipv4 = service->state_ipv6 = + CONNMAN_SERVICE_STATE_FAILURE; service->error = string2error(str); } break; @@ -4495,11 +5027,11 @@ static int service_load(struct connman_service *service) __connman_ipconfig_load(service->ipconfig_ipv6, keyfile, service->identifier, "IPv6."); - service->nameservers = g_key_file_get_string_list(keyfile, + service->nameservers_config = g_key_file_get_string_list(keyfile, service->identifier, "Nameservers", &length, NULL); - if (service->nameservers != NULL && length == 0) { - g_strfreev(service->nameservers); - service->nameservers = NULL; + if (service->nameservers_config != NULL && length == 0) { + g_strfreev(service->nameservers_config); + service->nameservers_config = NULL; } service->domains = g_key_file_get_string_list(keyfile, @@ -4511,7 +5043,10 @@ static int service_load(struct connman_service *service) str = g_key_file_get_string(keyfile, service->identifier, "Proxy.Method", NULL); - service->proxy = string2proxymethod(str); + if (str != NULL) + service->proxy_config = string2proxymethod(str); + + g_free(str); service->proxies = g_key_file_get_string_list(keyfile, service->identifier, "Proxy.Servers", &length, NULL); @@ -4619,18 +5154,6 @@ update: case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: case CONNMAN_SERVICE_TYPE_CELLULAR: - if (service->apn != NULL) - g_key_file_set_string(keyfile, service->identifier, - "APN", service->apn); - - if (service->username != NULL) - g_key_file_set_string(keyfile, service->identifier, - "Username", service->username); - - if (service->password != NULL) - g_key_file_set_string(keyfile, service->identifier, - "Password", service->password); - g_key_file_set_boolean(keyfile, service->identifier, "Favorite", service->favorite); @@ -4638,7 +5161,8 @@ update: g_key_file_set_boolean(keyfile, service->identifier, "AutoConnect", service->autoconnect); - if (service->state == CONNMAN_SERVICE_STATE_FAILURE) { + if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE || + service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) { const char *failure = error2string(service->error); if (failure != NULL) g_key_file_set_string(keyfile, @@ -4673,12 +5197,12 @@ update: __connman_ipconfig_save(service->ipconfig_ipv6, keyfile, service->identifier, "IPv6."); - if (service->nameservers != NULL) { - guint len = g_strv_length(service->nameservers); + if (service->nameservers_config != NULL) { + guint len = g_strv_length(service->nameservers_config); g_key_file_set_string_list(keyfile, service->identifier, "Nameservers", - (const gchar **) service->nameservers, len); + (const gchar **) service->nameservers_config, len); } else g_key_file_remove_key(keyfile, service->identifier, "Nameservers", NULL); @@ -4693,7 +5217,7 @@ update: g_key_file_remove_key(keyfile, service->identifier, "Domains", NULL); - cst_str = proxymethod2string(service->proxy); + cst_str = proxymethod2string(service->proxy_config); if (cst_str != NULL) g_key_file_set_string(keyfile, service->identifier, "Proxy.Method", cst_str);