diff options
author | Niraj Kumar Goit <niraj.g@samsung.com> | 2022-02-17 21:43:13 +0530 |
---|---|---|
committer | Niraj Kumar Goit <niraj.g@samsung.com> | 2022-02-23 21:00:32 +0530 |
commit | bf0e619ef451bde3568c1af509ccb12cbda2ff93 (patch) | |
tree | edc77ad122572a91f8ec267629fb1c4ffffabb4b | |
parent | 2985b9822ac3f5acfef2933cbda98c1285e11af4 (diff) | |
parent | f89b473dfd8e916314b534b3397442f8c869c783 (diff) | |
download | connman-bf0e619ef451bde3568c1af509ccb12cbda2ff93.tar.gz connman-bf0e619ef451bde3568c1af509ccb12cbda2ff93.tar.bz2 connman-bf0e619ef451bde3568c1af509ccb12cbda2ff93.zip |
Merge tag 'upstream/1.40' into tizen.submit/tizen/20220224.061208
Change-Id: I4ed89827d776db6eeec11878bc1cd0cd6c5e1e80
Signed-off-by: Niraj Kumar Goit <niraj.g@samsung.com>
Signed-off-by: Anjali Nijhara <a.nijhara@samsung.com>
81 files changed, 3249 insertions, 1524 deletions
@@ -11,3 +11,6 @@ Bing Niu <bing.niu@intel.com> <bing.niu@intel.com> Naveen Singh <naveensingh0977@gmail.com> <naveensingh0977@gmail.com> Mylène Josserand <josserand.mylene@gmail.com> <josserand.mylene@gmail.com> Måns Rullgård <mans@mansr.com> <mans@mansr.com> +Emmanuel VAUTRIN <Emmanuel.VAUTRIN@cpexterne.org> <Emmanuel.VAUTRIN@cpexterne.org> +Simon Holesch <Simon.Holesch@bshg.com> <Simon.Holesch@bshg.com> +Nishant Chaprana <n.chaprana@samsung.com> <n.chaprana@samsung.com> @@ -159,3 +159,15 @@ Yasser <yasser.toor@gmail.com> Matt Vogt <matthew.vogt@jollamobile.com> David Llewellyn-Jones <david.llewellyn-jones@jolla.com> David Weidenkopf <David.Weidenkopf@Arthrex.com> +Maxime Roussin-Bélanger <maxime.roussinbelanger@gmail.com> +Holesch, Simon (GED-SDD1) <Simon.Holesch@bshg.com> +Christoph Steiger <c.steiger@lemonage.de> +Markus Held <mjh42@gmx.de> +Sergey Matyukevich <geomatsi@gmail.com> +Pieter Cardoen <P.Cardoen@TELEVIC.com> +Emmanuel Vautrin <emmanuel.vautrin@cpexterne.org> +Boleslaw Tokarski <boleslaw.tokarski@jolla.com> +Gabriel FORTE <gforte@wyplay.com> +Colin Wee <cwee@tesla.com> +Valery Kashcheev <v.kascheev@omp.ru> +Alyssa Ross <hi@alyssa.is> @@ -1,3 +1,14 @@ +ver 1.40: + Fix issue with handling WiFi disconnecting status. + Fix issue with handling WiFi auto-connect and iwd backend. + Fix issue with DNS Proxy stack-based buffer overflow attack. + +ver 1.39: + Fix issue with scanning state synchronization and iwd. + Fix issue with invalid key with 4-way handshake offloading. + Fix issue with DNS proxy length checks to prevent buffer overflow. + Fix issue with DHCP leaking stack data via uninitialized variable. + ver 1.38: Fix issue with online check on IP address update. Fix issue with OpenVPN and encrypted private keys. diff --git a/Makefile.am b/Makefile.am index 14d6ca17..e8662e48 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,7 +21,7 @@ endif nodist_include_HEADERS = include/version.h noinst_HEADERS = include/rtnl.h include/task.h \ - include/dbus.h include/option.h \ + include/dbus.h \ include/provider.h include/vpn-dbus.h \ include/utsname.h include/timeserver.h include/proxy.h \ include/technology.h include/setting.h \ @@ -220,7 +220,7 @@ vpn_connman_vpnd_SOURCES = $(gdhcp_sources) $(builtin_vpn_sources) \ src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \ src/inotify.c src/firewall-iptables.c src/ipv6pd.c src/peer.c \ src/peer_service.c src/machine.c src/util.c \ - vpn/vpn-agent.c vpn/vpn-agent.h \ + vpn/vpn-agent.c vpn/vpn-util.c vpn/vpn-agent.h \ vpn/vpn-config.c vpn/vpn-settings.c src/acd.c if TIZEN_EXT_WIFI_MESH diff --git a/Makefile.plugins b/Makefile.plugins index 004bbe9e..a0c17cfa 100755 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -104,7 +104,9 @@ builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h if OPENCONNECT_BUILTIN builtin_vpn_modules += openconnect builtin_vpn_sources += vpn/plugins/openconnect.c -builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\" +builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\" \ + @LIBOPENCONNECT_CFLAGS@ +builtin_vpn_libadd += @LIBOPENCONNECT_LIBS@ else vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS) @@ -112,8 +114,10 @@ vpn_plugins_openconnect_la_SOURCES = vpn/plugins/openconnect.c vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \ -DOPENCONNECT=\"@OPENCONNECT@\" \ -DVPN_STATEDIR=\""$(vpn_statedir)"\" \ - -DSCRIPTDIR=\""$(build_scriptdir)"\" + -DSCRIPTDIR=\""$(build_scriptdir)"\" \ + @LIBOPENCONNECT_CFLAGS@ vpn_plugins_openconnect_la_LDFLAGS = $(plugin_ldflags) +vpn_plugins_openconnect_la_LIBADD = @LIBOPENCONNECT_LIBS@ endif endif @@ -444,10 +444,12 @@ Information =========== Mailing list: - connman@connman.net + connman@lists.linux.dev -For additional information about the project visit ConnMan web site: - https://01.org/connman - http://www.connman.net +If you would like to subscribe to receive mail in your inbox, just +send a (empty) message from your email account to -You can report bugs at https://01.org/jira/browse/CM + connman+subscribe@lists.linux.dev + +Mailing list archive: + https://lore.kernel.org/connman diff --git a/configure.ac b/configure.ac index 791e2a14..220d62b8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(connman, 1.38) +AC_INIT(connman, 1.40) AC_CONFIG_MACRO_DIR([m4]) @@ -120,6 +120,8 @@ if (test "${enable_openconnect}" != "no"); then OPENCONNECT="${path_openconnect}" AC_SUBST(OPENCONNECT) fi + PKG_CHECK_MODULES(LIBOPENCONNECT, openconnect >= 8, [], + AC_MSG_ERROR(openconnect >= 8 is required)) fi AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no") AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin") diff --git a/doc/clock-api.txt b/doc/clock-api.txt index 6818f5a8..a7fdf555 100755 --- a/doc/clock-api.txt +++ b/doc/clock-api.txt @@ -85,3 +85,12 @@ Properties uint64 Time [readonly or readwrite] [experimental] This list of servers is used when TimeUpdates is set to auto. + + boolean TimeserverSynced [readonly] [experimental] + + This value indicates if the current system time + is synced via NTP servers. + + True when TimeUpdates is set to auto and Time value + results from the system time synchronization with a NTP + server. Otherwise False. diff --git a/doc/connman.conf.5.in b/doc/connman.conf.5.in index a90c2291..2e06b3ef 100644 --- a/doc/connman.conf.5.in +++ b/doc/connman.conf.5.in @@ -167,6 +167,27 @@ transitioned to ONLINE state. If this setting is false, the default service will remain in READY state. Default value is true. .TP +.BI OnlineCheckInitialInterval= secs, OnlineCheckMaxInterval= secs +Range of intervals between two online check requests. +When an online check request fails, another one is triggered after a +longer interval. The intervals follow the power of two series of numbers +between OnlineCheckInitialInterval and OnlineCheckMaxInterval. +Default range is [1, 12], corresponding to the following intervals, in +seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144. +.TP +.BI EnableOnlineToReadyTransition=true\ \fR|\fB\ false +WARNING: Experimental feature!!! +In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET +to detect the loss of end-to-end connectivity. +If this setting is false, when the default service transitions to ONLINE +state, the HTTP GET request is no more called until next cycle, initiated +by a transition of the default service to DISCONNECT state. +If this setting is true, the HTTP GET request keeps beeing called to guarantee +that end-to-end connectivity is still successful. If not, the default service +will transition to READY state, enabling another service to become the +default one, in replacement. +Default value is false. +.TP .BI AutoConnectRoamingServices=true\ \fR|\fB\ false Automatically connect roaming services. This is not recommended unless you know you won't have any billing problem. diff --git a/doc/vpn-connection-api.txt b/doc/vpn-connection-api.txt index ec557889..6e6293e4 100755 --- a/doc/vpn-connection-api.txt +++ b/doc/vpn-connection-api.txt @@ -130,7 +130,14 @@ Properties string State [readonly] configured externally via a configuration file. The only valid operation are Connect(), Disconnect() - and GetProperties() + and GetProperties() + + boolean SplitRouting + + This value reflects the split routing setting on + connmand side. By default, this value is omitted and + defaults to false. The value needs to be explicitly + set to true for VPN to be split routed. int Index [readonly] diff --git a/gdbus/watch.c b/gdbus/watch.c index 1ca3c4bd..8fa76cda 100755 --- a/gdbus/watch.c +++ b/gdbus/watch.c @@ -136,84 +136,55 @@ static struct filter_data *filter_data_find(DBusConnection *connection) return NULL; } -#if defined TIZEN_EXT -#define SENDER_PREFIX ",sender='%s'" -#define PATH_PREFIX ",path='%s'" -#define IFACE_PREFIX ",interface='%s'" -#define MEMBER_PREFIX ",member='%s'" -#define ARG0_PREFIX ",arg0='%s'" - -static gboolean check_rule_length(int remains, const char *prefix, const char *data) -{ - if (!prefix || !data) - return FALSE; - - return strlen(prefix) - 2 + strlen(data) < remains; -} - -static void format_rule(struct filter_data *data, char *rule, size_t size) +static char *format_rule(struct filter_data *data) { + char *rule, *tmp; const char *sender; - int offset; - offset = snprintf(rule, size, "type='signal'"); + rule = g_strdup("type='signal'"); sender = data->name ? : data->owner; - if (sender && - check_rule_length(size - offset, SENDER_PREFIX, sender)) - offset += snprintf(rule + offset, size - offset, - SENDER_PREFIX, sender); - if (data->path && - check_rule_length(size - offset, PATH_PREFIX, data->path)) - offset += snprintf(rule + offset, size - offset, - PATH_PREFIX, data->path); - if (data->interface && - check_rule_length(size - offset, IFACE_PREFIX, data->interface)) - offset += snprintf(rule + offset, size - offset, - IFACE_PREFIX, data->interface); - if (data->member && - check_rule_length(size - offset, MEMBER_PREFIX, data->member)) - offset += snprintf(rule + offset, size - offset, - MEMBER_PREFIX, data->member); - if (data->argument && - check_rule_length(size - offset, ARG0_PREFIX, data->argument)) - snprintf(rule + offset, size - offset, - ARG0_PREFIX, data->argument); -} -#else -static void format_rule(struct filter_data *data, char *rule, size_t size) -{ - const char *sender; - int offset; + if (sender) { + tmp = rule; + rule = g_strdup_printf("%s,sender='%s'", rule, sender); + g_free(tmp); + } - offset = snprintf(rule, size, "type='signal'"); - sender = data->name ? : data->owner; + if (data->path) { + tmp = rule; + rule = g_strdup_printf("%s,path='%s'", rule, data->path); + g_free(tmp); + } - if (sender) - offset += snprintf(rule + offset, size - offset, - ",sender='%s'", sender); - if (data->path) - offset += snprintf(rule + offset, size - offset, - ",path='%s'", data->path); - if (data->interface) - offset += snprintf(rule + offset, size - offset, - ",interface='%s'", data->interface); - if (data->member) - offset += snprintf(rule + offset, size - offset, - ",member='%s'", data->member); - if (data->argument) - snprintf(rule + offset, size - offset, - ",arg0='%s'", data->argument); + if (data->interface){ + tmp = rule; + rule = g_strdup_printf("%s,interface='%s'", rule, + data->interface); + g_free(tmp); + } + + if (data->member) { + tmp = rule; + rule = g_strdup_printf("%s,member='%s'", rule, data->member); + g_free(tmp); + } + + if (data->argument) { + tmp = rule; + rule = g_strdup_printf("%s,arg0='%s'", rule, data->argument); + g_free(tmp); + } + + return rule; } -#endif static gboolean add_match(struct filter_data *data, DBusHandleMessageFunction filter) { DBusError err; - char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + char *rule; - format_rule(data, rule, sizeof(rule)); + rule = format_rule(data); dbus_error_init(&err); dbus_bus_add_match(data->connection, rule, &err); @@ -221,21 +192,23 @@ static gboolean add_match(struct filter_data *data, error("Adding match rule \"%s\" failed: %s", rule, err.message); dbus_error_free(&err); + g_free(rule); return FALSE; } data->handle_func = filter; data->registered = TRUE; + g_free(rule); return TRUE; } static gboolean remove_match(struct filter_data *data) { DBusError err; - char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + char *rule; - format_rule(data, rule, sizeof(rule)); + rule = format_rule(data); dbus_error_init(&err); @@ -244,9 +217,11 @@ static gboolean remove_match(struct filter_data *data) error("Removing owner match rule for %s failed: %s", rule, err.message); dbus_error_free(&err); + g_free(rule); return FALSE; } + g_free(rule); return TRUE; } diff --git a/gdhcp/client.c b/gdhcp/client.c index bdaa8821..cc0379ec 100755 --- a/gdhcp/client.c +++ b/gdhcp/client.c @@ -2313,7 +2313,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition, { GDHCPClient *dhcp_client = user_data; struct sockaddr_in dst_addr = { 0 }; - struct dhcp_packet packet; + struct dhcp_packet packet = { 0 }; struct dhcpv6_packet *packet6 = NULL; uint8_t *message_type = NULL, *client_id = NULL, *option, *server_id = NULL; diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c index 9ecaf069..c76c142e 100755 --- a/gsupplicant/supplicant.c +++ b/gsupplicant/supplicant.c @@ -5444,7 +5444,7 @@ static void country_result(const char *error, regdom->callback(result, regdom->alpha2, (void *) regdom->user_data); - dbus_free(regdom); + g_free(regdom); } static void country_params(DBusMessageIter *iter, void *user_data) @@ -5708,7 +5708,7 @@ static void interface_create_result(const char *error, SUPPLICANT_DBG(""); if (error) { - g_message("error %s", error); + g_warning("error %s", error); err = -EIO; goto done; } diff --git a/include/inet.h b/include/inet.h index 09f84540..579f7f7e 100644 --- a/include/inet.h +++ b/include/inet.h @@ -59,7 +59,7 @@ bool connman_inet_compare_ipv6_subnet(int index, const char *host); int connman_inet_set_ipv6_address(int index, struct connman_ipaddress *ipaddress); int connman_inet_clear_ipv6_address(int index, - const char *address, int prefix_len); + struct connman_ipaddress *ipaddress); int connman_inet_add_ipv6_network_route(int index, const char *host, const char *gateway, unsigned char prefix_len); int connman_inet_add_ipv6_host_route(int index, const char *host, @@ -82,6 +82,14 @@ int connman_inet_ipv6_get_dest_addr(int index, char **dest); int connman_inet_check_ipaddress(const char *host); bool connman_inet_check_hostname(const char *ptr, size_t len); bool connman_inet_is_ipv6_supported(); +bool connman_inet_is_default_route(int family, const char *host, + const char *gateway, const char *netmask); + +int connman_inet_get_route_addresses(int index, char **network, char **netmask, + char **destination); +int connman_inet_ipv6_get_route_addresses(int index, char **network, + char **netmask, + char **destination); #if defined TIZEN_EXT_WIFI_MESH char *connman_inet_ifaddr(const char *name); diff --git a/include/ipaddress.h b/include/ipaddress.h index 3655ca86..652db0f0 100755 --- a/include/ipaddress.h +++ b/include/ipaddress.h @@ -22,6 +22,8 @@ #ifndef __CONNMAN_IPADDRESS_H #define __CONNMAN_IPADDRESS_H +#include <stdbool.h> + #ifdef __cplusplus extern "C" { #endif @@ -36,6 +38,8 @@ struct connman_ipaddress; unsigned char connman_ipaddress_calc_netmask_len(const char *netmask); struct connman_ipaddress *connman_ipaddress_alloc(int family); +void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress, + bool value); void connman_ipaddress_free(struct connman_ipaddress *ipaddress); int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress, const char *address, const char *netmask, diff --git a/include/network.h b/include/network.h index 9f5bb1fc..4d09dfff 100755 --- a/include/network.h +++ b/include/network.h @@ -267,6 +267,8 @@ uint16_t connman_network_get_frequency(struct connman_network *network); int connman_network_set_wifi_channel(struct connman_network *network, uint16_t channel); uint16_t connman_network_get_wifi_channel(struct connman_network *network); +int connman_network_set_autoconnect(struct connman_network *network, + bool autoconnect); int connman_network_set_string(struct connman_network *network, const char *key, const char *value); @@ -300,6 +302,8 @@ struct connman_network_driver { void (*remove) (struct connman_network *network); int (*connect) (struct connman_network *network); int (*disconnect) (struct connman_network *network); + int (*set_autoconnect) (struct connman_network *network, + bool autoconnect); #if defined TIZEN_EXT int (*merge) (struct connman_network *network); #endif diff --git a/include/option.h b/include/option.h deleted file mode 100755 index 5e97ed4c..00000000 --- a/include/option.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * Connection Manager - * - * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef __CONNMAN_OPTION_H -#define __CONNMAN_OPTION_H - -#ifdef __cplusplus -extern "C" { -#endif - -const char *connman_option_get_string(const char *key); - -#ifdef __cplusplus -} -#endif - -#endif /* __CONNMAN_OPTION_H */ diff --git a/include/provider.h b/include/provider.h index b5856653..1f120990 100755 --- a/include/provider.h +++ b/include/provider.h @@ -113,6 +113,10 @@ int connman_provider_set_nameservers(struct connman_provider *provider, char * const *nameservers); void connman_provider_set_autoconnect(struct connman_provider *provider, bool flag); +bool connman_provider_is_split_routing(struct connman_provider *provider); +int connman_provider_set_split_routing(struct connman_provider *provider, + bool split_routing); +int connman_provider_get_family(struct connman_provider *provider); const char *connman_provider_get_driver_name(struct connman_provider *provider); const char *connman_provider_get_save_group(struct connman_provider *provider); diff --git a/include/service.h b/include/service.h index acd8852c..041949f5 100755 --- a/include/service.h +++ b/include/service.h @@ -117,6 +117,7 @@ enum connman_service_connect_reason { CONNMAN_SERVICE_CONNECT_REASON_AUTO = 1, CONNMAN_SERVICE_CONNECT_REASON_USER = 2, CONNMAN_SERVICE_CONNECT_REASON_SESSION = 3, + CONNMAN_SERVICE_CONNECT_REASON_NATIVE = 4, }; struct connman_service; diff --git a/include/setting.h b/include/setting.h index 3625f3e3..f141bf68 100755 --- a/include/setting.h +++ b/include/setting.h @@ -30,9 +30,10 @@ extern "C" { bool connman_setting_get_bool(const char *key); #if defined TIZEN_EXT -unsigned int connman_setting_get_uint(const char *key); int connman_setting_get_int(const char *key); #endif +unsigned int connman_setting_get_uint(const char *key); +char *connman_setting_get_string(const char *key); char **connman_setting_get_string_list(const char *key); unsigned int *connman_setting_get_uint_list(const char *key); diff --git a/packaging/connman.spec b/packaging/connman.spec index ff92bd63..8fbb03d1 100644 --- a/packaging/connman.spec +++ b/packaging/connman.spec @@ -5,8 +5,8 @@ %bcond_without connman_vpnd Name: connman -Version: 1.38 -Release: 14 +Version: 1.40 +Release: 1 License: GPL-2.0+ Summary: Connection Manager Url: http://connman.net diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c index 704d2164..a8383e75 100755 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -738,8 +738,6 @@ static bool tethering_create(const char *path, const char *method; bool result; - DBG("path %s bridge %s", path, bridge); - if (!bridge) { g_free(tethering); return false; @@ -751,6 +749,8 @@ static bool tethering_create(const char *path, return false; } + DBG("path %s bridge %s", path, bridge); + tethering->technology = technology; tethering->bridge = g_strdup(bridge); tethering->enable = enabled; diff --git a/plugins/dundee.c b/plugins/dundee.c index b5420acf..57571ec3 100755 --- a/plugins/dundee.c +++ b/plugins/dundee.c @@ -488,7 +488,7 @@ static void extract_settings(DBusMessageIter *array, if (index < 0) goto out; - info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4); + info->address = connman_ipaddress_alloc(AF_INET); if (!info->address) goto out; diff --git a/plugins/ethernet.c b/plugins/ethernet.c index 4dda80c8..766f8e44 100644 --- a/plugins/ethernet.c +++ b/plugins/ethernet.c @@ -54,7 +54,6 @@ #endif #if defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET -#include <connman/option.h> #include <gsupplicant/gsupplicant.h> #endif /* defined TIZEN_EXT && defined TIZEN_EXT_EAP_ON_ETHERNET */ @@ -84,7 +83,7 @@ static int get_vlan_vid(const char *ifname) return -errno; vifr.cmd = GET_VLAN_VID_CMD; - stpncpy(vifr.device1, ifname, sizeof(vifr.device1)); + stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1); if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) vid = vifr.u.VID; @@ -110,14 +109,16 @@ static int get_dsa_port(const char *ifname) return -errno; memset(&ifr, 0, sizeof(ifr)); - stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); /* check if it is a vlan and get physical interface name*/ vifr.cmd = GET_VLAN_REALDEV_NAME_CMD; - stpncpy(vifr.device1, ifname, sizeof(vifr.device1)); + stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1); - if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) - stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name)); + if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) { + stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name) - 1); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; + } /* get driver info */ drvinfocmd.cmd = ETHTOOL_GDRVINFO; @@ -254,7 +255,6 @@ static void enable_eapol_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply; DBusError error; - DBusMessageIter args; DBG(""); diff --git a/plugins/iwd.c b/plugins/iwd.c index bf6a2c26..194a99dd 100644 --- a/plugins/iwd.c +++ b/plugins/iwd.c @@ -95,6 +95,8 @@ struct iwd_network { struct iwd_device *iwdd; struct connman_network *network; + /* service's autoconnect */ + bool autoconnect; }; struct iwd_known_network { @@ -106,6 +108,9 @@ struct iwd_known_network { char *last_connected_time; bool auto_connect; int auto_connect_id; + + /* service's autoconnect */ + bool autoconnect; }; struct iwd_station { @@ -244,7 +249,7 @@ static void cm_network_connect_cb(DBusMessage *message, void *user_data) if (!strcmp(dbus_error, "net.connman.iwd.Failed")) connman_network_set_error(iwdn->network, CONNMAN_NETWORK_ERROR_INVALID_KEY); - else + else if (!iwdn->autoconnect) connman_network_set_error(iwdn->network, CONNMAN_NETWORK_ERROR_CONNECT_FAIL); return; @@ -318,12 +323,157 @@ static int cm_network_disconnect(struct connman_network *network) return -EINPROGRESS; } +struct auto_connect_cb_data { + char *path; + bool auto_connect; +}; + +static void auto_connect_cb_free(struct auto_connect_cb_data *cbd) +{ + g_free(cbd->path); + g_free(cbd); +} + +static void auto_connect_cb(const DBusError *error, void *user_data) +{ + struct auto_connect_cb_data *cbd = user_data; + struct iwd_known_network *iwdkn; + + iwdkn = g_hash_table_lookup(known_networks, cbd->path); + if (!iwdkn) + goto out; + + if (dbus_error_is_set(error)) + connman_warn("WiFi known network %s property auto connect %s", + cbd->path, error->message); + + /* property is updated via watch known_network_property_change() */ +out: + auto_connect_cb_free(cbd); +} + +static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect) +{ + dbus_bool_t dbus_auto_connect = auto_connect; + struct auto_connect_cb_data *cbd; + + if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect) + return -EALREADY; + + cbd = g_new(struct auto_connect_cb_data, 1); + cbd->path = g_strdup(iwdkn->path); + cbd->auto_connect = auto_connect; + + if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect", + DBUS_TYPE_BOOLEAN, + &dbus_auto_connect, + auto_connect_cb, cbd, NULL)) { + auto_connect_cb_free(cbd); + return -EIO; + } + + return -EINPROGRESS; +} + +static gboolean disable_auto_connect_cb(gpointer data) +{ + char *path = data; + struct iwd_known_network *iwdkn; + + iwdkn = g_hash_table_lookup(known_networks, path); + if (!iwdkn) + return FALSE; + + if (set_auto_connect(iwdkn, false) != -EINPROGRESS) + connman_warn("Failed to disable auto connect"); + + iwdkn->auto_connect_id = 0; + return FALSE; +} + +static int disable_auto_connect(struct iwd_known_network *iwdkn) +{ + if (iwdkn->auto_connect_id) + return -EBUSY; + + iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + 0, + disable_auto_connect_cb, + g_strdup(iwdkn->path), + g_free); + return 0; +} + +static gboolean enable_auto_connect_cb(gpointer data) +{ + char *path = data; + struct iwd_known_network *iwdkn; + + iwdkn = g_hash_table_lookup(known_networks, path); + if (!iwdkn) + return FALSE; + + if (set_auto_connect(iwdkn, true) != -EINPROGRESS) + connman_warn("Failed to enable auto connect"); + + iwdkn->auto_connect_id = 0; + return FALSE; +} + +static int enable_auto_connect(struct iwd_known_network *iwdkn) +{ + if (iwdkn->auto_connect_id) + return -EBUSY; + + iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + 0, + enable_auto_connect_cb, + g_strdup(iwdkn->path), + g_free); + return 0; +} + +static int update_auto_connect(struct iwd_known_network *iwdkn) +{ + DBG("auto_connect %d autoconnect %d", iwdkn->auto_connect, iwdkn->autoconnect); + + if (iwdkn->auto_connect == iwdkn->autoconnect) + return -EALREADY; + + if (iwdkn->autoconnect) + return enable_auto_connect(iwdkn); + return disable_auto_connect(iwdkn); +} + +static int cm_network_set_autoconnect(struct connman_network *network, + bool autoconnect) +{ + struct iwd_network *iwdn = connman_network_get_data(network); + struct iwd_known_network *iwdkn; + + DBG("autoconnect %d", autoconnect); + + iwdn->autoconnect = autoconnect; + + if (!iwdn->known_network) + return -ENOENT; + + iwdkn = g_hash_table_lookup(known_networks, iwdn->known_network); + if (!iwdkn) + return -ENOENT; + + iwdkn->autoconnect = autoconnect; + + return update_auto_connect(iwdkn); +} + static struct connman_network_driver network_driver = { - .name = "iwd", - .type = CONNMAN_NETWORK_TYPE_WIFI, - .probe = cm_network_probe, - .connect = cm_network_connect, - .disconnect = cm_network_disconnect, + .name = "iwd", + .type = CONNMAN_NETWORK_TYPE_WIFI, + .probe = cm_network_probe, + .connect = cm_network_connect, + .disconnect = cm_network_disconnect, + .set_autoconnect = cm_network_set_autoconnect, }; static int cm_device_probe(struct connman_device *device) @@ -535,7 +685,7 @@ static void tech_enable_tethering_cb(const DBusError *error, void *user_data) { struct tech_cb_data *cbd = user_data; struct iwd_device *iwdd; - struct iwd_ap *iwdap; + struct iwd_ap *iwdap = NULL; DBG(""); @@ -616,7 +766,7 @@ static void tech_disable_tethering_cb(const DBusError *error, void *user_data) if (!g_dbus_proxy_method_call(iwdap->proxy, "Stop", NULL, tech_ap_stop_cb, cbd, NULL)) { - connman_warn("iwd ap %s could not start AccessPoint mode: %s", + connman_warn("iwd ap %s could not stop AccessPoint mode: %s", cbd->path, error->message); goto out; } @@ -923,6 +1073,7 @@ static void network_property_change(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { struct iwd_network *iwdn; + struct iwd_known_network *iwdkn; const char *path; path = g_dbus_proxy_get_path(proxy); @@ -942,6 +1093,17 @@ static void network_property_change(GDBusProxy *proxy, const char *name, update_network_connected(iwdn); else update_network_disconnected(iwdn); + } else if (!strcmp(name, "KnownNetwork")) { + g_free(iwdn->known_network); + iwdn->known_network = + g_strdup(proxy_get_string(proxy, "KnownNetwork")); + if (!iwdn->known_network) + return; + + iwdkn = g_hash_table_lookup(known_networks, + iwdn->known_network); + if (iwdkn) + update_auto_connect(iwdkn); } } @@ -972,12 +1134,15 @@ static void _update_signal_strength(const char *path, int16_t signal_strength) connman_network_set_strength(iwdn->network, calculate_strength(signal_strength)); + connman_network_set_available(iwdn->network, true); connman_network_update(iwdn->network); } static void ordered_networks_cb(DBusMessage *message, void *user_data) { DBusMessageIter array, entry; + struct iwd_device *iwdd; + char *path = user_data; DBG(""); @@ -1005,6 +1170,11 @@ static void ordered_networks_cb(DBusMessage *message, void *user_data) dbus_message_iter_next(&entry); } + + iwdd = g_hash_table_lookup(devices, path); + if (iwdd) + connman_device_set_scanning(iwdd->device, + CONNMAN_SERVICE_TYPE_WIFI, false); } static void update_signal_strength(struct iwd_station *iwds) @@ -1012,7 +1182,7 @@ static void update_signal_strength(struct iwd_station *iwds) if (!g_dbus_proxy_method_call(iwds->proxy, "GetOrderedNetworks", NULL, ordered_networks_cb, - NULL, NULL)) + g_strdup(iwds->path), g_free)) DBG("GetOrderedNetworks() failed"); } @@ -1020,6 +1190,7 @@ static void station_property_change(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { struct iwd_station *iwds; + struct iwd_device *iwdd; const char *path; path = g_dbus_proxy_get_path(proxy); @@ -1053,8 +1224,15 @@ static void station_property_change(GDBusProxy *proxy, const char *name, dbus_message_iter_get_basic(iter, &scanning); iwds->scanning = scanning; - if (!iwds->scanning) + if (iwds->scanning) { + iwdd = g_hash_table_lookup(devices, path); + if (iwdd) + connman_device_set_scanning(iwdd->device, + CONNMAN_SERVICE_TYPE_WIFI, true); + } else { update_signal_strength(iwds); + } + DBG("%s scanning %d", path, iwds->scanning); } @@ -1551,8 +1729,31 @@ static void known_network_property_change(GDBusProxy *proxy, const char *name, DBG("%p auto_connect %d", path, iwdkn->auto_connect); - if (iwdkn->auto_connect) - disable_auto_connect(iwdkn); + update_auto_connect(iwdkn); + } +} + +static void init_auto_connect(struct iwd_known_network *iwdkn) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, networks); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct iwd_network *iwdn = value; + struct iwd_known_network *kn; + + if (!iwdn->known_network) + continue; + + kn = g_hash_table_lookup(known_networks, iwdn->known_network); + if (iwdkn != kn) + continue; + + iwdkn->autoconnect = iwdn->autoconnect; + update_auto_connect(iwdkn); + return; } } @@ -1589,11 +1790,10 @@ static void create_know_network(GDBusProxy *proxy) iwdkn->name, iwdkn->type, iwdkn->hidden, iwdkn->last_connected_time, iwdkn->auto_connect); + init_auto_connect(iwdkn); + g_dbus_proxy_set_property_watch(iwdkn->proxy, known_network_property_change, NULL); - - if (iwdkn->auto_connect) - disable_auto_connect(iwdkn); } static void create_station(GDBusProxy *proxy) diff --git a/plugins/loopback.c b/plugins/loopback.c index 28a59c9c..3809d8f9 100755 --- a/plugins/loopback.c +++ b/plugins/loopback.c @@ -48,16 +48,6 @@ static in_addr_t loopback_netmask; static char system_hostname[HOST_NAME_MAX + 1]; -static void create_hostname(void) -{ - const char *name = "localhost"; - - if (sethostname(name, strlen(name)) < 0) - connman_error("Failed to set hostname to %s", name); - - strncpy(system_hostname, name, HOST_NAME_MAX); -} - #if defined TIZEN_EXT static void _create_hostname(void) { @@ -90,6 +80,17 @@ static void _create_hostname(void) g_free(dev_id); fclose(fp); } +#else +static void create_hostname(void) +{ + const char *name = "localhost"; + + if (sethostname(name, strlen(name)) < 0) + connman_error("Failed to set hostname to %s", name); + + strncpy(system_hostname, name, HOST_NAME_MAX); +} + #endif static int setup_hostname(void) @@ -102,6 +103,7 @@ static int setup_hostname(void) connman_error("Failed to get current hostname"); return -EIO; } + #if defined TIZEN_EXT if (strlen(system_hostname) > 0 && strcmp(system_hostname, "(none)") != 0 && diff --git a/plugins/neard.c b/plugins/neard.c index 69586df6..45effd44 100755 --- a/plugins/neard.c +++ b/plugins/neard.c @@ -499,7 +499,7 @@ static void register_agent_cb(DBusPendingCall *pending, void *user_data) DBusMessage *reply; if (!dbus_pending_call_get_completed(pending)) - return; + goto out; register_call = NULL; diff --git a/plugins/ofono.c b/plugins/ofono.c index 36873d5a..f0bd3c5d 100755 --- a/plugins/ofono.c +++ b/plugins/ofono.c @@ -910,7 +910,7 @@ static void extract_ipv4_settings(DBusMessageIter *array, if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED) goto out; - context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4); + context->ipv4_address = connman_ipaddress_alloc(AF_INET); if (!context->ipv4_address) { context->index = -1; goto out; @@ -998,7 +998,7 @@ static void extract_ipv6_settings(DBusMessageIter *array, context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO; context->ipv6_address = - connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6); + connman_ipaddress_alloc(AF_INET6); if (!context->ipv6_address) goto out; diff --git a/plugins/vpn.c b/plugins/vpn.c index 5668c004..d708d1ff 100755 --- a/plugins/vpn.c +++ b/plugins/vpn.c @@ -85,6 +85,7 @@ struct connection_data { char *domain; char **nameservers; bool immutable; + bool default_route_set; GHashTable *server_routes; GHashTable *user_routes; @@ -94,6 +95,7 @@ struct connection_data { GResolv *resolv; guint resolv_id; + guint remove_resolv_id; }; static int set_string(struct connman_provider *provider, @@ -151,6 +153,8 @@ static const char *get_string(struct connman_provider *provider, return data->host_ip[0]; } else if (g_str_equal(key, "VPN.Domain")) return data->domain; + else if (g_str_equal(key, "Transport")) + return data->service_ident; return g_hash_table_lookup(data->setting_strings, key); } @@ -171,10 +175,15 @@ static char *get_ident(const char *path) static void cancel_host_resolv(struct connection_data *data) { - if (data->resolv_id != 0) + + if (data->remove_resolv_id) + g_source_remove(data->remove_resolv_id); + + if (data->resolv && data->resolv_id) g_resolv_cancel_lookup(data->resolv, data->resolv_id); data->resolv_id = 0; + data->remove_resolv_id = 0; g_resolv_unref(data->resolv); data->resolv = NULL; @@ -206,7 +215,7 @@ static void resolv_result(GResolvResultStatus status, * We cannot unref the resolver here as resolv struct is manipulated * by gresolv.c after we return from this callback. */ - g_idle_add(remove_resolv, data); + data->remove_resolv_id = g_idle_add(remove_resolv, data); data->resolv_id = 0; } @@ -261,14 +270,12 @@ static bool provider_is_connected(struct connection_data *data) static void set_provider_state(struct connection_data *data) { enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN; + bool connected; int err = 0; DBG("provider %p new state %s", data->provider, data->state); - if (!provider_is_connected(data)) { - g_free(data->service_ident); - data->service_ident = NULL; - } + connected = provider_is_connected(data); if (g_str_equal(data->state, "ready")) { state = CONNMAN_PROVIDER_STATE_READY; @@ -288,7 +295,7 @@ static void set_provider_state(struct connection_data *data) } connman_provider_set_state(data->provider, state); - return; + goto free; set: if (data->cb_data) @@ -299,6 +306,12 @@ set: free_config_cb_data(data->cb_data); data->cb_data = NULL; + +free: + if (!connected) { + g_free(data->service_ident); + data->service_ident = NULL; + } } static int create_provider(struct connection_data *data, void *user_data) @@ -431,6 +444,7 @@ static int extract_ip(DBusMessageIter *array, int family, } connman_ipaddress_set_peer(data->ip, peer); + connman_ipaddress_set_p2p(data->ip, true); return 0; } @@ -490,8 +504,12 @@ static void connect_reply(DBusPendingCall *call, void *user_data) struct connection_data *data = user_data; struct config_create_data *cb_data = data->cb_data; - if (!dbus_pending_call_get_completed(call)) - return; + DBG(""); + + if (!dbus_pending_call_get_completed(call)) { + connman_warn("vpn connect reply pending call incomplete"); + goto out; + } if (call != data->call) { connman_error("invalid call %p to VPN connect_reply data %p " @@ -769,12 +787,16 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data) DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING; - if (!dbus_pending_call_get_completed(call)) - return; - DBG(""); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("get connections reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); @@ -814,6 +836,7 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data) done: dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -861,12 +884,16 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data) DBusMessage *reply; DBusError error; - if (!dbus_pending_call_get_completed(call)) - return; - DBG(""); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("remove connection reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); @@ -884,6 +911,7 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data) dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -1010,11 +1038,14 @@ static int disconnect_provider(struct connection_data *data) dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply, data, NULL); - g_free(data->service_ident); - data->service_ident = NULL; + data->default_route_set = false; connman_provider_set_state(data->provider, CONNMAN_PROVIDER_STATE_DISCONNECT); + + g_free(data->service_ident); + data->service_ident = NULL; + return -EINPROGRESS; } @@ -1055,12 +1086,16 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data) struct connection_data *data; struct config_create_data *cb_data = user_data; - if (!dbus_pending_call_get_completed(call)) - return; - DBG("user %p", cb_data); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("configuration create reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); @@ -1114,6 +1149,7 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data) done: dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -1461,6 +1497,17 @@ static void set_route(struct connection_data *data, struct vpn_route *route) return; } + DBG("set route provider %p %s/%s/%s", data->provider, + route->network, route->gateway, + route->netmask); + + /* Do not add default route for split routed VPNs.*/ + if (connman_provider_is_split_routing(data->provider) && + connman_inet_is_default_route(route->family, + route->network, route->gateway, + route->netmask)) + return; + if (route->family == AF_INET6) { unsigned char prefix_len = atoi(route->netmask); @@ -1473,6 +1520,126 @@ static void set_route(struct connection_data *data, struct vpn_route *route) route->gateway, route->netmask); } + + if (connman_inet_is_default_route(route->family, route->network, + route->gateway, route->netmask)) + data->default_route_set = true; +} + +static int save_route(GHashTable *routes, int family, const char *network, + const char *netmask, const char *gateway); + +static int add_network_route(struct connection_data *data) +{ + struct vpn_route rt = { 0, }; + int err; + + if (!data) + return -EINVAL; + + rt.family = connman_provider_get_family(data->provider); + switch (rt.family) { + case PF_INET: + err = connman_inet_get_route_addresses(data->index, + &rt.network, &rt.netmask, &rt.gateway); + break; + case PF_INET6: + err = connman_inet_ipv6_get_route_addresses(data->index, + &rt.network, &rt.netmask, &rt.gateway); + break; + default: + connman_error("invalid protocol family %d", rt.family); + return -EINVAL; + } + + DBG("network %s gateway %s netmask %s for provider %p", + rt.network, rt.gateway, rt.netmask, + data->provider); + + if (err) { + connman_error("cannot get network/gateway/netmask for %p", + data->provider); + goto out; + } + + err = save_route(data->server_routes, rt.family, rt.network, rt.netmask, + rt.gateway); + if (err) { + connman_warn("failed to add network route for provider" + "%p", data->provider); + goto out; + } + + set_route(data, &rt); + +out: + g_free(rt.network); + g_free(rt.netmask); + g_free(rt.gateway); + + return 0; +} + +static bool is_valid_route_table(struct connman_provider *provider, + GHashTable *table) +{ + GHashTableIter iter; + gpointer value, key; + struct vpn_route *route; + size_t table_size; + + if (!table) + return false; + + table_size = g_hash_table_size(table); + + /* Non-split routed may have only the default route */ + if (table_size > 0 && !connman_provider_is_split_routing(provider)) + return true; + + /* Split routed has more than the default route */ + if (table_size > 1) + return true; + + /* + * Only one route for split routed VPN, which should not be the + * default route. + */ + g_hash_table_iter_init(&iter, table); + if (!g_hash_table_iter_next(&iter, &key, &value)) /* First and only */ + return false; + + route = value; + if (!route) + return false; + + DBG("check route %d %s/%s/%s", route->family, route->network, + route->gateway, route->netmask); + + if (!connman_inet_is_default_route(route->family, route->network, + route->gateway, route->netmask)) + return true; + + return false; +} + +static bool check_routes(struct connman_provider *provider) +{ + struct connection_data *data;; + + DBG("provider %p", provider); + + data = connman_provider_get_data(provider); + if (!data) + return false; + + if (is_valid_route_table(provider, data->user_routes)) + return true; + + if (is_valid_route_table(provider, data->server_routes)) + return true; + + return false; } static int set_routes(struct connman_provider *provider, @@ -1504,28 +1671,30 @@ static int set_routes(struct connman_provider *provider, set_route(data, value); } - return 0; -} - -static bool check_routes(struct connman_provider *provider) -{ - struct connection_data *data; - - DBG("provider %p", provider); + /* If non-split routed VPN does not have a default route, add it */ + if (!connman_provider_is_split_routing(provider) && + !data->default_route_set) { + int family = connman_provider_get_family(provider); + const char *ipaddr_any = family == AF_INET6 ? + "::" : "0.0.0.0"; + struct vpn_route def_route = {family, (char*) ipaddr_any, + (char*) ipaddr_any, NULL}; - data = connman_provider_get_data(provider); - if (!data) - return false; - - if (data->user_routes && - g_hash_table_size(data->user_routes) > 0) - return true; + set_route(data, &def_route); + } - if (data->server_routes && - g_hash_table_size(data->server_routes) > 0) - return true; + /* Split routed VPN must have at least one route to the network */ + if (connman_provider_is_split_routing(provider) && + !check_routes(provider)) { + int err = add_network_route(data); + if (err) { + connman_warn("cannot add network route provider %p", + provider); + return err; + } + } - return false; + return 0; } static struct connman_provider_driver provider_driver = { @@ -1694,12 +1863,52 @@ static int save_route(GHashTable *routes, int family, const char *network, route->gateway = g_strdup(gateway); g_hash_table_replace(routes, key, route); - } else + } else { g_free(key); + return -EALREADY; + } return 0; } +static void change_provider_split_routing(struct connman_provider *provider, + bool split_routing) +{ + struct connection_data *data; + int err; + + if (!provider) + return; + + if (connman_provider_is_split_routing(provider) == split_routing) + return; + + data = connman_provider_get_data(provider); + if (split_routing && data && provider_is_connected(data) && + !check_routes(provider)) { + err = add_network_route(data); + if (err) { + connman_warn("cannot add network route provider %p", + provider); + return; + } + } + + err = connman_provider_set_split_routing(provider, split_routing); + switch (err) { + case 0: + /* fall through */ + case -EALREADY: + break; + case -EINVAL: + /* fall through */ + case -EOPNOTSUPP: + connman_warn("cannot change split routing %d", err); + default: + break; + } +} + static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts) { DBusMessageIter dict; @@ -1876,6 +2085,10 @@ static gboolean property_changed(DBusConnection *conn, g_free(data->domain); data->domain = g_strdup(str); connman_provider_set_domain(data->provider, data->domain); + } else if (g_str_equal(key, "SplitRouting")) { + dbus_bool_t split_routing; + dbus_message_iter_get_basic(&value, &split_routing); + change_provider_split_routing(data->provider, split_routing); } if (ip_set && err == 0) { @@ -1964,6 +2177,10 @@ static void vpn_disconnect_check_provider(struct connection_data *data) if (!vpn_is_valid_transport(service)) { connman_provider_disconnect(data->provider); } + + /* VPN moved to be split routed, default route is not set */ + if (connman_provider_is_split_routing(data->provider)) + data->default_route_set = false; } } diff --git a/plugins/wifi.c b/plugins/wifi.c index 26b988e0..bcb93445 100755 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -49,7 +49,6 @@ #include <connman/service.h> #include <connman/peer.h> #include <connman/log.h> -#include <connman/option.h> #include <connman/storage.h> #include <include/setting.h> #include <connman/provision.h> @@ -68,12 +67,14 @@ #define BGSCAN_DEFAULT "simple:30:-65:300" #define AUTOSCAN_EXPONENTIAL "exponential:3:300" #define AUTOSCAN_SINGLE "single:3" +#define SCAN_MAX_DURATION 10 #define P2P_FIND_TIMEOUT 30 #define P2P_CONNECTION_TIMEOUT 100 #define P2P_LISTEN_PERIOD 500 #define P2P_LISTEN_INTERVAL 2000 +#define ASSOC_STATUS_AUTH_TIMEOUT 16 #define ASSOC_STATUS_NO_CLIENT 17 #if defined TIZEN_EXT #define LOAD_SHAPING_MAX_RETRIES 7 @@ -210,6 +211,11 @@ struct wifi_data { #endif }; +struct disconnect_data { + struct wifi_data *wifi; + struct connman_network *network; +}; + #if defined TIZEN_EXT #include "connman.h" #include "dbus.h" @@ -223,7 +229,6 @@ static GHashTable *failed_bssids = NULL; static unsigned char buff_bssid[WIFI_BSSID_LEN_MAX] = { 0, }; #endif - static GList *iface_list = NULL; static GList *pending_wifi_device = NULL; @@ -1153,15 +1158,23 @@ static GSupplicantP2PServiceParams *fill_in_peer_service_params( if (version > 0) { params->version = version; - params->service = g_memdup(spec, spec_length); + if (spec_length > 0) { + params->service = g_malloc(spec_length); + memcpy(params->service, spec, spec_length); + } } else if (query_length > 0 && spec_length > 0) { - params->query = g_memdup(query, query_length); + params->query = g_malloc(query_length); + memcpy(params->query, query, query_length); params->query_length = query_length; - params->response = g_memdup(spec, spec_length); + params->response = g_malloc(spec_length); + memcpy(params->response, spec, spec_length); params->response_length = spec_length; } else { - params->wfd_ies = g_memdup(spec, spec_length); + if (spec_length > 0) { + params->wfd_ies = g_malloc(spec_length); + memcpy(params->wfd_ies, spec, spec_length); + } params->wfd_ies_length = spec_length; } @@ -1433,14 +1446,15 @@ static void wifi_newlink(unsigned flags, unsigned change, void *user_data) } if ((wifi->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) { - if (flags & IFF_LOWER_UP) { + if (flags & IFF_LOWER_UP) DBG("carrier on"); - - handle_tethering(wifi); - } else + else DBG("carrier off"); } + if (flags & IFF_LOWER_UP) + handle_tethering(wifi); + wifi->flags = flags; } @@ -1996,11 +2010,14 @@ static int get_hidden_connections_params(struct wifi_data *wifi, scan_params->num_ssids = i; scan_params->ssids = g_slist_reverse(scan_params->ssids); - scan_params->freqs = g_memdup(orig_params->freqs, - sizeof(uint16_t) * orig_params->num_freqs); - if (!scan_params->freqs) + if (orig_params->num_freqs <= 0) goto err; + scan_params->freqs = + g_malloc(sizeof(uint16_t) * orig_params->num_freqs); + memcpy(scan_params->freqs, orig_params->freqs, + sizeof(uint16_t) *orig_params->num_freqs); + scan_params->num_freqs = orig_params->num_freqs; } else @@ -2584,6 +2601,8 @@ static void interface_create_callback(int result, void *user_data) { struct wifi_data *wifi = user_data; + char *bgscan_range_max; + long value; DBG("result %d ifname %s, wifi %p", result, g_supplicant_interface_get_ifname(interface), @@ -2599,6 +2618,24 @@ static void interface_create_callback(int result, wifi->interface_ready = true; finalize_interface_creation(wifi); } + + /* + * Set the BSS expiration age to match the long scanning + * interval to avoid the loss of unconnected networks between + * two scans. + */ + bgscan_range_max = strrchr(BGSCAN_DEFAULT, ':'); + if (!bgscan_range_max || strlen(bgscan_range_max) < 1) + return; + + value = strtol(bgscan_range_max + 1, NULL, 10); + if (value <= 0 || errno == ERANGE) + return; + + if (g_supplicant_interface_set_bss_expiration_age(interface, + value + SCAN_MAX_DURATION) < 0) { + connman_warn("Failed to set bss expiration age"); + } } static int wifi_enable(struct connman_device *device) @@ -2606,7 +2643,7 @@ static int wifi_enable(struct connman_device *device) struct wifi_data *wifi = connman_device_get_data(device); int index; char *interface; - const char *driver = connman_option_get_string("wifi"); + const char *driver = connman_setting_get_string("wifi"); int ret; DBG("device %p %p", device, wifi); @@ -3937,11 +3974,13 @@ static int network_connect(struct connman_network *network) static void disconnect_callback(int result, GSupplicantInterface *interface, void *user_data) { + struct disconnect_data *dd = user_data; + struct connman_network *network = dd->network; #if defined TIZEN_EXT GList *list; struct wifi_data *wifi; - struct connman_network *network = user_data; + g_free(dd); DBG("network %p result %d", network, result); for (list = iface_list; list; list = list->next) { @@ -3959,11 +3998,13 @@ static void disconnect_callback(int result, GSupplicantInterface *interface, found: #else - struct wifi_data *wifi = user_data; + struct wifi_data *wifi = dd->wifi; + g_free(dd); #endif - DBG("result %d supplicant interface %p wifi %p", - result, interface, wifi); + DBG("result %d supplicant interface %p wifi %p networks: current %p " + "pending %p disconnected %p", result, interface, wifi, + wifi->network, wifi->pending_network, network); if (result == -ECONNABORTED) { DBG("wifi interface no longer available"); @@ -3975,12 +4016,21 @@ found: (wifi->network != wifi->pending_network || connman_network_get_bool(wifi->network, "WiFi.Roaming"))) #else - if (wifi->network && wifi->network != wifi->pending_network) + if (g_slist_find(wifi->networks, network)) #endif - connman_network_set_connected(wifi->network, false); - wifi->network = NULL; + connman_network_set_connected(network, false); wifi->disconnecting = false; + + if (network != wifi->network) { + if (network == wifi->pending_network) + wifi->pending_network = NULL; + DBG("current wifi network has changed since disconnection"); + return; + } + + wifi->network = NULL; + wifi->connected = false; if (wifi->pending_network) { @@ -3994,6 +4044,7 @@ found: static int network_disconnect(struct connman_network *network) { struct connman_device *device = connman_network_get_device(network); + struct disconnect_data *dd; struct wifi_data *wifi; int err; #if defined TIZEN_EXT @@ -4036,16 +4087,16 @@ static int network_disconnect(struct connman_network *network) wifi->disconnecting = true; -#if defined TIZEN_EXT - err = g_supplicant_interface_disconnect(wifi->interface, - disconnect_callback, network); -#else - err = g_supplicant_interface_disconnect(wifi->interface, - disconnect_callback, wifi); -#endif + dd = g_malloc0(sizeof(*dd)); + dd->wifi = wifi; + dd->network = network; - if (err < 0) + err = g_supplicant_interface_disconnect(wifi->interface, + disconnect_callback, dd); + if (err < 0) { wifi->disconnecting = false; + g_free(dd); + } return err; } @@ -4342,6 +4393,7 @@ static bool handle_wps_completion(GSupplicantInterface *interface, if (wps) { const unsigned char *ssid, *wps_ssid; unsigned int ssid_len, wps_ssid_len; + struct disconnect_data *dd; const char *wps_key; /* Checking if we got associated with requested @@ -4354,16 +4406,16 @@ static bool handle_wps_completion(GSupplicantInterface *interface, if (!wps_ssid || wps_ssid_len != ssid_len || memcmp(ssid, wps_ssid, ssid_len) != 0) { + dd = g_malloc0(sizeof(*dd)); + dd->wifi = wifi; + dd->network = network; + connman_network_set_associating(network, false); -#if defined TIZEN_EXT g_supplicant_interface_disconnect(wifi->interface, - disconnect_callback, wifi->network); - + disconnect_callback, dd); +#if defined TIZEN_EXT connman_network_set_bool(network, "WiFi.UseWPS", false); connman_network_set_string(network, "WiFi.PinWPS", NULL); -#else - g_supplicant_interface_disconnect(wifi->interface, - disconnect_callback, wifi); #endif return false; } @@ -4424,10 +4476,10 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface, struct connman_network *network, struct wifi_data *wifi) { -#if defined TIZEN_EXT - const char *security; struct connman_service *service; +#if defined TIZEN_EXT + const char *security; if (wifi->connected) return false; @@ -4444,9 +4496,9 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface, if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) return false; #else - struct connman_service *service; - - if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) + if ((wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) && + !((wifi->state == G_SUPPLICANT_STATE_ASSOCIATING) && + (wifi->assoc_code == ASSOC_STATUS_AUTH_TIMEOUT))) return false; if (wifi->connected) @@ -4711,9 +4763,10 @@ static void interface_state(GSupplicantInterface *interface) /* See table 8-36 Reason codes in IEEE Std 802.11 */ switch (wifi->disconnect_code) { +#if defined TIZEN_EXT case 1: /* Unspecified reason */ /* Let's assume it's because we got blocked */ - +#endif case 6: /* Class 2 frame received from nonauthenticated STA */ connman_network_set_error(network, CONNMAN_NETWORK_ERROR_BLOCKED); @@ -6212,7 +6265,7 @@ static void sta_remove_callback(int result, void *user_data) { struct wifi_tethering_info *info = user_data; - const char *driver = connman_option_get_string("wifi"); + const char *driver = connman_setting_get_string("wifi"); DBG("ifname %s result %d ", info->ifname, result); @@ -6410,7 +6463,7 @@ static void supp_ins_init(void) const char *string; GSupplicantINSPreferredFreq preferred_freq; - string = connman_option_get_string("INSPreferredFreqBSSID"); + string = connman_setting_get_string("INSPreferredFreqBSSID"); if (g_strcmp0(string, "5GHz") == 0) preferred_freq = G_SUPPLICANT_INS_PREFERRED_FREQ_5GHZ; else if (g_strcmp0(string, "2.4GHz") == 0) diff --git a/src/bridge.c b/src/bridge.c index cd2d9cee..df19a6af 100755 --- a/src/bridge.c +++ b/src/bridge.c @@ -122,7 +122,8 @@ int __connman_bridge_enable(const char *name, const char *ip_address, err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, - ip_address, NULL, prefix_len, broadcast); + ip_address, NULL, prefix_len, broadcast, + false); if (err < 0) return err; diff --git a/src/clock.c b/src/clock.c index 40729b2e..58a52c0e 100755 --- a/src/clock.c +++ b/src/clock.c @@ -176,6 +176,7 @@ static DBusMessage *get_properties(DBusConnection *conn, { DBusMessage *reply; DBusMessageIter array, dict; + dbus_bool_t is_synced; struct timeval tv; const char *str; #if defined TIZEN_EXT @@ -222,6 +223,10 @@ static DBusMessage *get_properties(DBusConnection *conn, connman_dbus_dict_append_array(&dict, "Timeservers", DBUS_TYPE_STRING, append_timeservers, NULL); + is_synced = __connman_timeserver_is_synced(); + connman_dbus_dict_append_basic(&dict, "TimeserverSynced", + DBUS_TYPE_BOOLEAN, &is_synced); + connman_dbus_dict_close(&array, &dict); return reply; @@ -275,6 +280,7 @@ static DBusMessage *set_property(DBusConnection *conn, if (settimeofday(&tv, NULL) < 0) return __connman_error_invalid_arguments(msg); + __connman_timeserver_set_synced(false); connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, CONNMAN_CLOCK_INTERFACE, "Time", DBUS_TYPE_UINT64, &newval); @@ -301,6 +307,13 @@ static DBusMessage *set_property(DBusConnection *conn, connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, CONNMAN_CLOCK_INTERFACE, "TimeUpdates", DBUS_TYPE_STRING, &strval); + + if (newval == TIME_UPDATES_AUTO) { + struct connman_service *service; + + service = connman_service_get_default(); + __connman_timeserver_conf_update(service); + } } else if (g_str_equal(name, "Timezone")) { const char *strval; @@ -380,6 +393,8 @@ static DBusMessage *set_property(DBusConnection *conn, connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH, CONNMAN_CLOCK_INTERFACE, "Timeservers", DBUS_TYPE_STRING, append_timeservers, NULL); + } else if (g_str_equal(name, "TimeserverSynced")) { + return __connman_error_permission_denied(msg); } else return __connman_error_invalid_property(msg); diff --git a/src/connection.c b/src/connection.c index 6036db31..33f61031 100755 --- a/src/connection.c +++ b/src/connection.c @@ -1136,6 +1136,29 @@ int __connman_connection_get_vpn_index(int phy_index) return -1; } +int __connman_connection_get_vpn_phy_index(int vpn_index) +{ + GHashTableIter iter; + gpointer value, key; + + g_hash_table_iter_init(&iter, gateway_hash); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct gateway_data *data = value; + + if (data->index != vpn_index) + continue; + + if (data->ipv4_gateway) + return data->ipv4_gateway->vpn_phy_index; + + if (data->ipv6_gateway) + return data->ipv6_gateway->vpn_phy_index; + } + + return -1; +} + int __connman_connection_init(void) { int err; diff --git a/src/connman.h b/src/connman.h index e92f2b1a..18c4fe0e 100755 --- a/src/connman.h +++ b/src/connman.h @@ -150,8 +150,6 @@ void __connman_log_enable(struct connman_debug_desc *start, #include <connman/backtrace.h> -#include <connman/option.h> - #include <connman/setting.h> #include <connman/plugin.h> @@ -171,7 +169,8 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family, const char *address, const char *peer, unsigned char prefixlen, - const char *broadcast); + const char *broadcast, + bool is_p2p); int __connman_inet_get_interface_address(int index, int family, void *address); int __connman_inet_get_interface_ll_address(int index, int family, void *address); int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address); @@ -321,6 +320,7 @@ struct connman_ipaddress { char *peer; char *broadcast; char *gateway; + bool is_p2p; /* P2P connection or VPN, broadcast is excluded. */ }; struct connman_ipconfig_ops { @@ -481,7 +481,11 @@ char **__connman_timeserver_system_get(); GSList *__connman_timeserver_add_list(GSList *server_list, const char *timeserver); GSList *__connman_timeserver_get_all(struct connman_service *service); -int __connman_timeserver_sync(struct connman_service *service); +void __connman_timeserver_sync(struct connman_service *service); +void __connman_timeserver_conf_update(struct connman_service *service); + +bool __connman_timeserver_is_synced(void); +void __connman_timeserver_set_synced(bool status); enum __connman_dhcpv6_status { CONNMAN_DHCPV6_STATUS_FAIL = 0, @@ -539,6 +543,7 @@ int __connman_connection_gateway_add(struct connman_service *service, void __connman_connection_gateway_remove(struct connman_service *service, enum connman_ipconfig_type type); int __connman_connection_get_vpn_index(int phy_index); +int __connman_connection_get_vpn_phy_index(int vpn_index); bool __connman_connection_update_gateway(void); @@ -671,6 +676,7 @@ const char *__connman_network_get_type(struct connman_network *network); const char *__connman_network_get_group(struct connman_network *network); const char *__connman_network_get_ident(struct connman_network *network); bool __connman_network_get_weakness(struct connman_network *network); +bool __connman_network_native_autoconnect(struct connman_network *network); int __connman_config_init(); void __connman_config_cleanup(void); @@ -718,6 +724,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data); bool __connman_provider_is_immutable(struct connman_provider *provider); int __connman_provider_create_and_connect(DBusMessage *msg); const char * __connman_provider_get_ident(struct connman_provider *provider); +const char * __connman_provider_get_transport_ident( + struct connman_provider *provider); int __connman_provider_indicate_state(struct connman_provider *provider, enum connman_provider_state state); int __connman_provider_indicate_error(struct connman_provider *provider, @@ -732,6 +740,8 @@ int __connman_provider_init(void); int __connman_service_init(void); void __connman_service_cleanup(void); +int __connman_service_move(struct connman_service *service, + struct connman_service *target, bool before); int __connman_service_load_modifiable(struct connman_service *service); void __connman_service_list_struct(DBusMessageIter *iter); @@ -801,8 +811,9 @@ int __connman_service_set_mdns(struct connman_service *service, void __connman_service_set_string(struct connman_service *service, const char *key, const char *value); -int __connman_service_online_check_failed(struct connman_service *service, - enum connman_ipconfig_type type); +void __connman_service_online_check(struct connman_service *service, + enum connman_ipconfig_type type, + bool success); int __connman_service_ipconfig_indicate_state(struct connman_service *service, enum connman_service_state new_state, enum connman_ipconfig_type type); @@ -822,7 +833,6 @@ int __connman_service_indicate_default(struct connman_service *service); int __connman_service_connect(struct connman_service *service, enum connman_service_connect_reason reason); int __connman_service_disconnect(struct connman_service *service); -int __connman_service_disconnect_all(void); void __connman_service_set_active_session(bool enable, GSList *list); void __connman_service_auto_connect(enum connman_service_connect_reason reason); @@ -895,6 +905,9 @@ int check_passphrase_ext(struct connman_network *network, bool __connman_service_is_hidden(struct connman_service *service); bool __connman_service_is_split_routing(struct connman_service *service); bool __connman_service_index_is_split_routing(int index); +void __connman_service_set_split_routing(struct connman_service *service, + bool split_routing); +void __connman_service_split_routing_changed(struct connman_service *service); int __connman_service_get_index(struct connman_service *service); void __connman_service_set_hidden(struct connman_service *service); void __connman_service_set_hostname(struct connman_service *service, @@ -246,9 +246,7 @@ dbus_bool_t connman_dbus_property_changed_basic(const char *path, dbus_message_iter_init_append(signal, &iter); connman_dbus_property_append_basic(&iter, key, type, val); - g_dbus_send_message(connection, signal); - - return TRUE; + return g_dbus_send_message(connection, signal); } dbus_bool_t connman_dbus_property_changed_dict(const char *path, @@ -268,9 +266,7 @@ dbus_bool_t connman_dbus_property_changed_dict(const char *path, dbus_message_iter_init_append(signal, &iter); connman_dbus_property_append_dict(&iter, key, function, user_data); - g_dbus_send_message(connection, signal); - - return TRUE; + return g_dbus_send_message(connection, signal); } dbus_bool_t connman_dbus_property_changed_array(const char *path, @@ -291,9 +287,7 @@ dbus_bool_t connman_dbus_property_changed_array(const char *path, connman_dbus_property_append_array(&iter, key, type, function, user_data); - g_dbus_send_message(connection, signal); - - return TRUE; + return g_dbus_send_message(connection, signal); } dbus_bool_t connman_dbus_setting_changed_basic(const char *owner, @@ -319,9 +313,7 @@ dbus_bool_t connman_dbus_setting_changed_basic(const char *owner, connman_dbus_dict_close(&array, &dict); - g_dbus_send_message(connection, msg); - - return TRUE; + return g_dbus_send_message(connection, msg); } dbus_bool_t connman_dbus_setting_changed_dict(const char *owner, @@ -348,9 +340,7 @@ dbus_bool_t connman_dbus_setting_changed_dict(const char *owner, connman_dbus_dict_close(&array, &dict); - g_dbus_send_message(connection, msg); - - return TRUE; + return g_dbus_send_message(connection, msg); } dbus_bool_t connman_dbus_setting_changed_array(const char *owner, @@ -377,9 +367,7 @@ dbus_bool_t connman_dbus_setting_changed_array(const char *owner, connman_dbus_dict_close(&array, &dict); - g_dbus_send_message(connection, msg); - - return TRUE; + return g_dbus_send_message(connection, msg); } dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg, diff --git a/src/device.c b/src/device.c index 0c3eea98..51169001 100755 --- a/src/device.c +++ b/src/device.c @@ -233,8 +233,6 @@ static gboolean device_pending_reset(gpointer user_data) DBG("device %p", device); #if defined TIZEN_EXT - DBusMessage *reply; - /* Power request timed out, send ETIMEDOUT. */ if (device->pending_reply_list) { g_list_foreach(device->pending_reply_list, __device_pending_reset, NULL); @@ -300,11 +298,11 @@ int __connman_device_enable(struct connman_device *device) } /* * if err == -EINPROGRESS, then the DBus call to the respective daemon - * was successful. We set a 10 sec timeout so if the daemon never + * was successful. We set a 4 sec timeout so if the daemon never * returns a reply, we would reset the pending request. */ if (err == -EINPROGRESS) - device->pending_timeout = g_timeout_add_seconds(10, + device->pending_timeout = g_timeout_add_seconds(4, device_pending_reset, device); done: return err; @@ -684,7 +684,7 @@ static int dhcp_initialize(struct connman_dhcp *dhcp) g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER); g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET); - vendor_class_id = connman_option_get_string("VendorClassID"); + vendor_class_id = connman_setting_get_string("VendorClassID"); if (vendor_class_id) g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID, vendor_class_id); diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 4c07c769..ba54b892 100755 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -1008,7 +1008,7 @@ static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp) ref_own_address(user_data); - if (inet_pton(AF_INET6, address, &addr) < 0) { + if (inet_pton(AF_INET6, address, &addr) != 1) { DBG("Invalid IPv6 address %s %d/%s", address, -errno, strerror(errno)); goto fail; diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c index 5fe306c3..912ab3fe 100644 --- a/src/dns-systemd-resolved.c +++ b/src/dns-systemd-resolved.c @@ -106,7 +106,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data) if (type == AF_INET) { result = inet_pton(type, server, ipv4_bytes); - if (!result) { + if (result != 1) { DBG("Failed to parse IPv4 address: %s", server); return; @@ -128,7 +128,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data) &byte_array); } else if (type == AF_INET6) { result = inet_pton(type, server, ipv6_bytes); - if (!result) { + if (result != 1) { DBG("Failed to parse IPv6 address: %s", server); return; } diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 7956e7fb..18dc6480 100755 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -1819,6 +1819,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, char **uncompressed_ptr) { char *uptr = *uncompressed_ptr; /* position in result buffer */ + char * const uncomp_end = uncompressed + uncomp_len - 1; debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr); @@ -1839,14 +1840,15 @@ static char *uncompress(int16_t field_count, char *start, char *end, * tmp buffer. */ - ulen = strlen(name); - strncpy(uptr, name, uncomp_len - (uptr - uncompressed)); + ulen = strlen(name) + 1; + if ((uptr + ulen) > uncomp_end) + goto out; + strncpy(uptr, name, ulen); debug("pos %d ulen %d left %d name %s", pos, ulen, - (int)(uncomp_len - (uptr - uncompressed)), uptr); + (int)(uncomp_end - (uptr + ulen)), uptr); uptr += ulen; - *uptr++ = '\0'; ptr += pos; @@ -1854,6 +1856,10 @@ static char *uncompress(int16_t field_count, char *start, char *end, * We copy also the fixed portion of the result (type, class, * ttl, address length and the address) */ + if ((uptr + NS_RRFIXEDSZ) > uncomp_end) { + debug("uncompressed data too large for buffer"); + goto out; + } memcpy(uptr, ptr, NS_RRFIXEDSZ); dns_type = uptr[0] << 8 | uptr[1]; @@ -1885,7 +1891,7 @@ static char *uncompress(int16_t field_count, char *start, char *end, } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) { dlen = uptr[-2] << 8 | uptr[-1]; - if (ptr + dlen > end) { + if ((ptr + dlen) > end || (uptr + dlen) > uncomp_end) { debug("data len %d too long", dlen); goto out; } @@ -1924,6 +1930,10 @@ static char *uncompress(int16_t field_count, char *start, char *end, * refresh interval, retry interval, expiration * limit and minimum ttl). They are 20 bytes long. */ + if ((uptr + 20) > uncomp_end || (ptr + 20) > end) { + debug("soa record too long"); + goto out; + } memcpy(uptr, ptr, 20); uptr += 20; ptr += 20; @@ -3124,6 +3134,7 @@ static void dnsproxy_default_changed(struct connman_service *service) bool server_enabled = false; GSList *list; int index; + int vpn_index; DBG("service %p", service); @@ -3140,6 +3151,13 @@ static void dnsproxy_default_changed(struct connman_service *service) if (index < 0) return; + /* + * In case non-split-routed VPN is set as split routed the DNS servers + * the VPN must be enabled as well, when the transport becomes the + * default service. + */ + vpn_index = __connman_connection_get_vpn_index(index); + for (list = server_list; list; list = list->next) { struct server_data *data = list->data; @@ -3147,6 +3165,9 @@ static void dnsproxy_default_changed(struct connman_service *service) DBG("Enabling DNS server %s", data->server); data->enabled = true; server_enabled = true; + } else if (data->index == vpn_index) { + DBG("Enabling DNS server of VPN %s", data->server); + data->enabled = true; } else { DBG("Disabling DNS server %s", data->server); data->enabled = false; @@ -79,7 +79,8 @@ int __connman_inet_modify_address(int cmd, int flags, const char *address, const char *peer, unsigned char prefixlen, - const char *broadcast) + const char *broadcast, + bool is_p2p) { uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + @@ -94,8 +95,9 @@ int __connman_inet_modify_address(int cmd, int flags, int sk, err; DBG("cmd %#x flags %#x index %d family %d address %s peer %s " - "prefixlen %hhu broadcast %s", cmd, flags, index, family, - address, peer, prefixlen, broadcast); + "prefixlen %hhu broadcast %s p2p %s", cmd, flags, index, + family, address, peer, prefixlen, broadcast, + is_p2p ? "true" : "false"); if (!address) return -EINVAL; @@ -119,17 +121,11 @@ int __connman_inet_modify_address(int cmd, int flags, ifaddrmsg->ifa_index = index; if (family == AF_INET) { - if (inet_pton(AF_INET, address, &ipv4_addr) < 1) + if (inet_pton(AF_INET, address, &ipv4_addr) != 1) return -1; - if (broadcast) - inet_pton(AF_INET, broadcast, &ipv4_bcast); - else - ipv4_bcast.s_addr = ipv4_addr.s_addr | - htonl(0xfffffffflu >> prefixlen); - if (peer) { - if (inet_pton(AF_INET, peer, &ipv4_dest) < 1) + if (inet_pton(AF_INET, peer, &ipv4_dest) != 1) return -1; err = __connman_inet_rtnl_addattr_l(header, @@ -149,16 +145,27 @@ int __connman_inet_modify_address(int cmd, int flags, if (err < 0) return err; - err = __connman_inet_rtnl_addattr_l(header, - sizeof(request), - IFA_BROADCAST, - &ipv4_bcast, - sizeof(ipv4_bcast)); - if (err < 0) - return err; + /* + * Broadcast address must not be added for P2P / VPN as + * getifaddrs() cannot interpret destination address. + */ + if (!is_p2p) { + if (broadcast) + inet_pton(AF_INET, broadcast, &ipv4_bcast); + else + ipv4_bcast.s_addr = ipv4_addr.s_addr | + htonl(0xfffffffflu >> prefixlen); + err = __connman_inet_rtnl_addattr_l(header, + sizeof(request), + IFA_BROADCAST, + &ipv4_bcast, + sizeof(ipv4_bcast)); + if (err < 0) + return err; + } } else if (family == AF_INET6) { - if (inet_pton(AF_INET6, address, &ipv6_addr) < 1) + if (inet_pton(AF_INET6, address, &ipv6_addr) != 1) return -1; err = __connman_inet_rtnl_addattr_l(header, @@ -261,13 +268,46 @@ char *connman_inet_ifname2addr(const char *name) } #endif +static bool is_addr_unspec(int family, struct sockaddr *addr) +{ + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + + switch (family) { + case AF_INET: + in4 = (struct sockaddr_in*) addr; + return in4->sin_addr.s_addr == INADDR_ANY; + case AF_INET6: + in6 = (struct sockaddr_in6*) addr; + return IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr); + default: + return false; + } +} + +static bool is_addr_ll(int family, struct sockaddr *addr) +{ + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + + switch (family) { + case AF_INET: + in4 = (struct sockaddr_in*) addr; + return (in4->sin_addr.s_addr & IN_CLASSB_NET) == + ((in_addr_t) htonl(0xa9fe0000)); + case AF_INET6: + in6 = (struct sockaddr_in6*) addr; + return IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr); + default: + return false; + } +} + bool __connman_inet_is_any_addr(const char *address, int family) { bool ret = false; struct addrinfo hints; struct addrinfo *result = NULL; - struct sockaddr_in6 *in6 = NULL; - struct sockaddr_in *in4 = NULL; if (!address || !*address) goto out; @@ -280,14 +320,7 @@ bool __connman_inet_is_any_addr(const char *address, int family) goto out; if (result) { - if (result->ai_family == AF_INET6) { - in6 = (struct sockaddr_in6*)result->ai_addr; - ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr); - } else if (result->ai_family == AF_INET) { - in4 = (struct sockaddr_in*)result->ai_addr; - ret = in4->sin_addr.s_addr == INADDR_ANY; - } - + ret = is_addr_unspec(result->ai_family, result->ai_addr); freeaddrinfo(result); } @@ -521,18 +554,20 @@ int connman_inet_set_ipv6_address(int index, int err; unsigned char prefix_len; const char *address; + bool is_p2p; if (!ipaddress->local) return 0; prefix_len = ipaddress->prefixlen; address = ipaddress->local; + is_p2p = ipaddress->is_p2p; DBG("index %d address %s prefix_len %d", index, address, prefix_len); err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6, - address, NULL, prefix_len, NULL); + address, NULL, prefix_len, NULL, is_p2p); if (err < 0) { connman_error("%s: %s", __func__, strerror(-err)); return err; @@ -546,6 +581,7 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) int err; unsigned char prefix_len; const char *address, *broadcast, *peer; + bool is_p2p; if (!ipaddress->local) return -1; @@ -554,12 +590,13 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) address = ipaddress->local; broadcast = ipaddress->broadcast; peer = ipaddress->peer; + is_p2p = ipaddress->is_p2p; DBG("index %d address %s prefix_len %d", index, address, prefix_len); err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, - address, peer, prefix_len, broadcast); + address, peer, prefix_len, broadcast, is_p2p); if (err < 0) { connman_error("%s: %s", __func__, strerror(-err)); return err; @@ -568,10 +605,17 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress) return 0; } -int connman_inet_clear_ipv6_address(int index, const char *address, - int prefix_len) +int connman_inet_clear_ipv6_address(int index, + struct connman_ipaddress *ipaddress) { int err; + int prefix_len; + const char *address; + bool is_p2p; + + address = ipaddress->local; + prefix_len = ipaddress->prefixlen; + is_p2p = ipaddress->is_p2p; DBG("index %d address %s prefix_len %d", index, address, prefix_len); @@ -579,7 +623,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address, return -EINVAL; err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6, - address, NULL, prefix_len, NULL); + address, NULL, prefix_len, NULL, is_p2p); if (err < 0) { connman_error("%s: %s", __func__, strerror(-err)); return err; @@ -593,11 +637,13 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress) int err; unsigned char prefix_len; const char *address, *broadcast, *peer; + bool is_p2p; prefix_len = ipaddress->prefixlen; address = ipaddress->local; broadcast = ipaddress->broadcast; peer = ipaddress->peer; + is_p2p = ipaddress->is_p2p; DBG("index %d address %s prefix_len %d peer %s broadcast %s", index, address, prefix_len, peer, broadcast); @@ -606,7 +652,7 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress) return -EINVAL; err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET, - address, peer, prefix_len, broadcast); + address, peer, prefix_len, broadcast, is_p2p); if (err < 0) { connman_error("%s: %s", __func__, strerror(-err)); return err; @@ -773,7 +819,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host, rt.rtmsg_dst_len = prefix_len; - if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) { + if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) { err = -errno; goto out; } @@ -823,7 +869,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host, rt.rtmsg_dst_len = prefix_len; - if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) { + if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) { err = -errno; goto out; } @@ -839,7 +885,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host, */ if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) && - inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0) + inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) == 1) rt.rtmsg_flags |= RTF_GATEWAY; rt.rtmsg_metric = 1; @@ -882,7 +928,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway) memset(&rt, 0, sizeof(rt)); - if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) { + if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) != 1) { err = -errno; goto out; } @@ -1178,54 +1224,161 @@ out: return err; } -bool connman_inet_compare_subnet(int index, const char *host) +#define ADDR_TYPE_MAX 4 + +struct interface_address { + int index; + int family; + bool allow_unspec; + /* Applies only to ADDR_TYPE_IPADDR in ipaddrs */ + bool require_ll; + /* Real types must be in_addr for AF_INET and in6_addr for AF_INET6 */ + void *ipaddrs[ADDR_TYPE_MAX]; +}; + +enum ipaddr_type { + ADDR_TYPE_IPADDR = 0, + ADDR_TYPE_NETMASK, + ADDR_TYPE_BRDADDR, + ADDR_TYPE_DSTADDR +}; + +static int get_interface_addresses(struct interface_address *if_addr) { - struct ifreq ifr; - struct in_addr _host_addr; - in_addr_t host_addr, netmask_addr, if_addr; - struct sockaddr_in *netmask, *addr; - int sk; + struct ifaddrs *ifaddr; + struct ifaddrs *ifa; + struct sockaddr *addrs[ADDR_TYPE_MAX] = { 0 }; + struct sockaddr_in *addr_in; + struct sockaddr_in6 *addr_in6; + char name[IF_NAMESIZE] = { 0 }; + size_t len; + int err = -ENOENT; + int i; - DBG("host %s", host); + if (!if_addr) + return -EINVAL; - if (!host) - return false; + if (!if_indextoname(if_addr->index, name)) + return -EINVAL; - if (inet_aton(host, &_host_addr) == 0) - return false; - host_addr = _host_addr.s_addr; + DBG("index %d interface %s", if_addr->index, name); - sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sk < 0) - return false; + if (getifaddrs(&ifaddr) < 0) { + connman_error("Cannot get addresses err %d/%s", errno, + strerror(errno)); + return -errno; + } - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - close(sk); - return false; + if (g_strcmp0(ifa->ifa_name, name) || + ifa->ifa_addr->sa_family != + if_addr->family) + continue; + + + if (if_addr->ipaddrs[ADDR_TYPE_IPADDR]) { + if (!if_addr->allow_unspec && is_addr_unspec( + if_addr->family, + ifa->ifa_addr)) + continue; + + if (if_addr->require_ll && !is_addr_ll(if_addr->family, + ifa->ifa_addr)) + continue; + + addrs[ADDR_TYPE_IPADDR] = ifa->ifa_addr; + } + + if (if_addr->ipaddrs[ADDR_TYPE_NETMASK]) { + if (!if_addr->allow_unspec && is_addr_unspec( + if_addr->family, + ifa->ifa_netmask)) + continue; + + addrs[ADDR_TYPE_NETMASK] = ifa->ifa_netmask; + } + + if (if_addr->ipaddrs[ADDR_TYPE_BRDADDR] && + (ifa->ifa_flags & IFF_BROADCAST)) { + if (!if_addr->allow_unspec && is_addr_unspec( + if_addr->family, + ifa->ifa_ifu.ifu_broadaddr)) + continue; + + addrs[ADDR_TYPE_BRDADDR] = ifa->ifa_ifu.ifu_broadaddr; + } + + if (if_addr->ipaddrs[ADDR_TYPE_DSTADDR] && + (ifa->ifa_flags & IFF_POINTOPOINT)) { + if (!if_addr->allow_unspec && is_addr_unspec( + if_addr->family, + ifa->ifa_ifu.ifu_dstaddr)) + continue; + + addrs[ADDR_TYPE_DSTADDR] = ifa->ifa_ifu.ifu_dstaddr; + } + + err = 0; + + break; } - if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) { - close(sk); - return false; + if (err) + goto out; + + for (i = 0; i < ADDR_TYPE_MAX; i++) { + if (!addrs[i]) + continue; + + switch (if_addr->family) { + case AF_INET: + len = sizeof(struct in_addr); + addr_in = (struct sockaddr_in*) addrs[i]; + memcpy(if_addr->ipaddrs[i], &addr_in->sin_addr, len); + break; + case AF_INET6: + len = sizeof(struct in6_addr); + addr_in6 = (struct sockaddr_in6*) addrs[i]; + memcpy(if_addr->ipaddrs[i], &addr_in6->sin6_addr, len); + break; + default: + err = -EINVAL; + break; + } } - netmask = (struct sockaddr_in *)&ifr.ifr_netmask; - netmask_addr = netmask->sin_addr.s_addr; +out: + freeifaddrs(ifaddr); + return err; +} - if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) { - close(sk); +bool connman_inet_compare_subnet(int index, const char *host) +{ + struct interface_address if_addr = { 0 }; + struct in_addr iaddr = { 0 }; + struct in_addr imask = { 0 }; + struct in_addr haddr = { 0 }; + + DBG("host %s", host); + + if (!host) return false; - } - close(sk); + if (inet_pton(AF_INET, host, &haddr) != 1) + return false; - addr = (struct sockaddr_in *)&ifr.ifr_addr; - if_addr = addr->sin_addr.s_addr; + if_addr.index = index; + if_addr.family = AF_INET; + if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr; + if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask; - return ((if_addr & netmask_addr) == (host_addr & netmask_addr)); + if (get_interface_addresses(&if_addr)) + return false; + + return (iaddr.s_addr & imask.s_addr) == (haddr.s_addr & imask.s_addr); } static bool mem_mask_equal(const void *a, const void *b, @@ -1246,47 +1399,23 @@ static bool mem_mask_equal(const void *a, const void *b, bool connman_inet_compare_ipv6_subnet(int index, const char *host) { - struct ifaddrs *ifaddr, *ifa; - bool rv = false; - char name[IF_NAMESIZE]; - struct in6_addr haddr; - - if (inet_pton(AF_INET6, host, &haddr) <= 0) - return false; + struct interface_address addr = { 0 }; + struct in6_addr iaddr = { 0 }; + struct in6_addr imask = { 0 }; + struct in6_addr haddr = { 0 }; - if (!if_indextoname(index, name)) + if (inet_pton(AF_INET6, host, &haddr) != 1) return false; - DBG("index %d interface %s", index, name); + addr.index = index; + addr.family = AF_INET6; + addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr; + addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask; - if (getifaddrs(&ifaddr) < 0) { - DBG("Cannot get addresses err %d/%s", errno, strerror(errno)); + if (get_interface_addresses(&addr)) return false; - } - - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { - struct sockaddr_in6 *iaddr; - struct sockaddr_in6 *imask; - - if (!ifa->ifa_addr) - continue; - - if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 || - ifa->ifa_addr->sa_family != AF_INET6) - continue; - - iaddr = (struct sockaddr_in6 *)ifa->ifa_addr; - imask = (struct sockaddr_in6 *)ifa->ifa_netmask; - - rv = mem_mask_equal(&iaddr->sin6_addr, &haddr, - &imask->sin6_addr, - sizeof(haddr)); - goto out; - } -out: - freeifaddrs(ifaddr); - return rv; + return mem_mask_equal(&iaddr, &haddr, &imask, sizeof(haddr)); } int connman_inet_remove_from_bridge(int index, const char *bridge) @@ -2295,98 +2424,156 @@ GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr, return prefixes; } -static int get_dest_addr(int family, int index, char *buf, int len) +int connman_inet_get_dest_addr(int index, char **dest) { - struct ifreq ifr; - void *addr; - int sk; + struct interface_address if_addr = { 0 }; + struct in_addr dstaddr = { 0 }; + char buf[INET_ADDRSTRLEN] = { 0 }; + int err; - sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sk < 0) - return -errno; + if (!dest) + return -EINVAL; - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = index; + if_addr.index = index; + if_addr.family = AF_INET; + if_addr.allow_unspec = true; + if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr; - if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { - DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno)); - close(sk); - return -errno; - } + err = get_interface_addresses(&if_addr); + if (err) + return err; - if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { - DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno)); - close(sk); - return -errno; - } + if (inet_ntop(AF_INET, &dstaddr, buf, INET_ADDRSTRLEN)) + *dest = g_strdup(buf); - if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) { - close(sk); - errno = EINVAL; - return -errno; - } + DBG("destination %s", *dest); - DBG("index %d %s", index, ifr.ifr_name); + return *dest && **dest ? 0 : -ENOENT; +} - if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) { - connman_error("Get destination address failed (%s)", - strerror(errno)); - close(sk); - return -errno; - } +int connman_inet_ipv6_get_dest_addr(int index, char **dest) +{ + struct interface_address if_addr = { 0 }; + struct in_addr dstaddr = { 0 }; + char buf[INET6_ADDRSTRLEN] = { 0 }; + int err; - close(sk); + if (!dest) + return -EINVAL; - switch (family) { - case AF_INET: - addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr; - break; - case AF_INET6: - addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr; - break; - default: - errno = EINVAL; - return -errno; - } + if_addr.index = index; + if_addr.family = AF_INET6; + if_addr.allow_unspec = true; + if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr; - if (!inet_ntop(family, addr, buf, len)) { - DBG("error %d/%s", errno, strerror(errno)); - return -errno; - } + err = get_interface_addresses(&if_addr); + if (err) + return err; - return 0; + if (inet_ntop(AF_INET6, &dstaddr, buf, INET6_ADDRSTRLEN)) + *dest = g_strdup(buf); + + DBG("destination %s", *dest); + + return *dest && **dest ? 0 : -ENOENT; } -int connman_inet_get_dest_addr(int index, char **dest) +/* destination is optional */ +int connman_inet_get_route_addresses(int index, char **network, char **netmask, + char **destination) { - char addr[INET_ADDRSTRLEN]; - int ret; + struct interface_address if_addr = { 0 }; + struct in_addr addr = { 0 }; + struct in_addr mask = { 0 }; + struct in_addr dest = { 0 }; + struct in_addr nw_addr = { 0 }; + char buf[INET_ADDRSTRLEN] = { 0 }; + int err; - ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN); - if (ret < 0) - return ret; + if (!network || !netmask) + return -EINVAL; - *dest = g_strdup(addr); + if_addr.index = index; + if_addr.family = AF_INET; + if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr; + if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask; + if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest; - DBG("destination %s", *dest); + err = get_interface_addresses(&if_addr); + if (err) + return err; - return 0; + nw_addr.s_addr = (addr.s_addr & mask.s_addr); + + if (inet_ntop(AF_INET, &nw_addr, buf, INET_ADDRSTRLEN)) + *network = g_strdup(buf); + + memset(&buf, 0, INET_ADDRSTRLEN); + + if (inet_ntop(AF_INET, &mask, buf, INET_ADDRSTRLEN)) + *netmask = g_strdup(buf); + + if (destination) { + memset(&buf, 0, INET_ADDRSTRLEN); + + if (inet_ntop(AF_INET, &dest, buf, INET_ADDRSTRLEN)) + *destination = g_strdup(buf); + } + + DBG("network %s netmask %s destination %s", *network, *netmask, + destination ? *destination : NULL); + + return *network && **network && *netmask && **netmask ? 0 : -ENOENT; } -int connman_inet_ipv6_get_dest_addr(int index, char **dest) +int connman_inet_ipv6_get_route_addresses(int index, char **network, + char **netmask, char **destination) { - char addr[INET6_ADDRSTRLEN]; - int ret; + struct interface_address if_addr = { 0 }; + struct in6_addr addr = { 0 }; + struct in6_addr mask = { 0 }; + struct in6_addr dest = { 0 }; + struct in6_addr nw_addr = { 0 }; + char buf[INET6_ADDRSTRLEN] = { 0 }; + int err; - ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN); - if (ret < 0) - return ret; + if (!network) + return -EINVAL; - *dest = g_strdup(addr); + if_addr.index = index; + if_addr.family = AF_INET6; + if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr; + if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask; + if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest; - DBG("destination %s", *dest); + err = get_interface_addresses(&if_addr); + if (err) + return err; - return 0; + ipv6_addr_set(&nw_addr, addr.s6_addr32[0] & mask.s6_addr32[0], + addr.s6_addr32[1] & mask.s6_addr32[1], + addr.s6_addr32[2] & mask.s6_addr32[2], + addr.s6_addr32[3] & mask.s6_addr32[3]); + + if (inet_ntop(AF_INET6, &nw_addr, buf, INET6_ADDRSTRLEN)) + *network = g_strdup(buf); + + memset(&buf, 0, INET6_ADDRSTRLEN); + + if (inet_ntop(AF_INET6, &mask, buf, INET6_ADDRSTRLEN)) + *netmask = g_strdup(buf); + + if (destination) { + memset(&buf, 0, INET6_ADDRSTRLEN); + + if (inet_ntop(AF_INET6, &dest, buf, INET6_ADDRSTRLEN)) + *destination = g_strdup(buf); + } + + DBG("network %s netmask %s destination %s", *network, *netmask, + destination ? *destination : NULL); + + return *network && **network && *netmask && **netmask ? 0 : -ENOENT; } int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth) @@ -2983,58 +3170,30 @@ bool connman_inet_is_ipv6_supported() return true; } -int __connman_inet_get_interface_address(int index, int family, void *address) +/* + * Omits checking of the gateway matching the actual gateway IP since both + * connmand and vpnd use inet.c, getting the route is via ipconfig and ipconfig + * is different for both. Gateway is left here for possible future use. + * + * Gateway can be NULL and connection.c then assigns 0.0.0.0 address or ::, + * depending on IP family. + */ +bool connman_inet_is_default_route(int family, const char *host, + const char *gateway, const char *netmask) { - struct ifaddrs *ifaddr, *ifa; - int err = -ENOENT; - char name[IF_NAMESIZE]; - - if (!if_indextoname(index, name)) - return -EINVAL; - - DBG("index %d interface %s", index, name); - - if (getifaddrs(&ifaddr) < 0) { - err = -errno; - DBG("Cannot get addresses err %d/%s", err, strerror(-err)); - return err; - } - - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; - - if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 && - ifa->ifa_addr->sa_family == family) { - if (family == AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in *) - ifa->ifa_addr; - if (in4->sin_addr.s_addr == INADDR_ANY) - continue; - memcpy(address, &in4->sin_addr, - sizeof(struct in_addr)); - } else if (family == AF_INET6) { - struct sockaddr_in6 *in6 = - (struct sockaddr_in6 *)ifa->ifa_addr; - if (memcmp(&in6->sin6_addr, &in6addr_any, - sizeof(struct in6_addr)) == 0) - continue; - memcpy(address, &in6->sin6_addr, - sizeof(struct in6_addr)); + return __connman_inet_is_any_addr(host, family) && + __connman_inet_is_any_addr(netmask, family); +} - } else { - err = -EINVAL; - goto out; - } +int __connman_inet_get_interface_address(int index, int family, void *address) +{ + struct interface_address if_addr = { 0 }; - err = 0; - break; - } - } + if_addr.index = index; + if_addr.family = family; + if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address; -out: - freeifaddrs(ifaddr); - return err; + return get_interface_addresses(&if_addr); } int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address) @@ -3168,7 +3327,7 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex, ret = inet_pton(family, dst ? dst : gateway, buf); g_free(dst); - if (ret <= 0) + if (ret != 1) return -EINVAL; memset(&rth, 0, sizeof(rth)); @@ -3243,61 +3402,14 @@ int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex, int __connman_inet_get_interface_ll_address(int index, int family, void *address) { - struct ifaddrs *ifaddr, *ifa; - int err = -ENOENT; - char name[IF_NAMESIZE]; - - if (!if_indextoname(index, name)) - return -EINVAL; - - DBG("index %d interface %s", index, name); + struct interface_address if_addr = { 0 }; - if (getifaddrs(&ifaddr) < 0) { - err = -errno; - DBG("Cannot get addresses err %d/%s", err, strerror(-err)); - return err; - } + if_addr.index = index; + if_addr.family = family; + if_addr.require_ll = true; + if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address; - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; - - if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 && - ifa->ifa_addr->sa_family == family) { - if (family == AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in *) - ifa->ifa_addr; - if (in4->sin_addr.s_addr == INADDR_ANY) - continue; - if ((in4->sin_addr.s_addr & IN_CLASSB_NET) != - ((in_addr_t) 0xa9fe0000)) - continue; - memcpy(address, &in4->sin_addr, - sizeof(struct in_addr)); - } else if (family == AF_INET6) { - struct sockaddr_in6 *in6 = - (struct sockaddr_in6 *)ifa->ifa_addr; - if (memcmp(&in6->sin6_addr, &in6addr_any, - sizeof(struct in6_addr)) == 0) - continue; - if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) - continue; - - memcpy(address, &in6->sin6_addr, - sizeof(struct in6_addr)); - } else { - err = -EINVAL; - goto out; - } - - err = 0; - break; - } - } - -out: - freeifaddrs(ifaddr); - return err; + return get_interface_addresses(&if_addr); } int __connman_inet_get_address_netmask(int ifindex, @@ -3451,7 +3563,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file, addrstr[len] = '\0'; err = inet_pton(AF_INET, addrstr, addr); - if (err <= 0) { + if (err != 1) { connman_error("%s: Cannot convert to numeric addr \"%s\"\n", __func__, addrstr); err = -1; diff --git a/src/ipaddress.c b/src/ipaddress.c index d63d95c3..201d8345 100755 --- a/src/ipaddress.c +++ b/src/ipaddress.c @@ -70,10 +70,19 @@ struct connman_ipaddress *connman_ipaddress_alloc(int family) ipaddress->peer = NULL; ipaddress->broadcast = NULL; ipaddress->gateway = NULL; + ipaddress->is_p2p = false; return ipaddress; } +void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress, bool value) +{ + if (!ipaddress) + return; + + ipaddress->is_p2p = value; +} + void connman_ipaddress_free(struct connman_ipaddress *ipaddress) { if (!ipaddress) @@ -95,7 +104,7 @@ static bool check_ipv6_address(const char *address) return false; err = inet_pton(AF_INET6, address, buf); - if (err > 0) + if (err == 1) return true; return false; @@ -223,6 +232,7 @@ connman_ipaddress_copy(struct connman_ipaddress *ipaddress) copy->peer = g_strdup(ipaddress->peer); copy->broadcast = g_strdup(ipaddress->broadcast); copy->gateway = g_strdup(ipaddress->gateway); + copy->is_p2p = ipaddress->is_p2p; return copy; } diff --git a/src/ipconfig.c b/src/ipconfig.c index 7d4be738..4a0e4ad0 100755 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -262,153 +262,165 @@ static const char *scope2str(unsigned char scope) return ""; } -static bool get_ipv6_state(gchar *ifname) +#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf" +#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf" + +static int read_conf_value(const char *prefix, const char *ifname, + const char *suffix, int *value) { - int disabled; gchar *path; FILE *f; - bool enabled = false; - - if (!ifname) - path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6"); - else - path = g_strdup_printf( - "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname); + int err; + path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL); if (!path) - return enabled; + return -ENOMEM; + errno = 0; f = fopen(path, "r"); + if (!f) { + err = -errno; + } else { + errno = 0; /* Avoid stale errno values with fscanf */ - g_free(path); + err = fscanf(f, "%d", value); + if (err <= 0 && errno) + err = -errno; - if (f) { - if (fscanf(f, "%d", &disabled) > 0) - enabled = !disabled; fclose(f); } - return enabled; + if (err <= 0) + connman_error("failed to read %s", path); + + g_free(path); + + return err; +} + +static int read_ipv4_conf_value(const char *ifname, const char *suffix, + int *value) +{ + return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value); } -static void set_ipv6_state(gchar *ifname, bool enable) +static int read_ipv6_conf_value(const char *ifname, const char *suffix, + int *value) { + return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value); +} + +static int write_conf_value(const char *prefix, const char *ifname, + const char *suffix, int value) { gchar *path; FILE *f; + int rval; - if (!ifname) - path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6"); - else - path = g_strdup_printf( - "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname); - + path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL); if (!path) - return; + return -ENOMEM; f = fopen(path, "r+"); + if (!f) { + rval = -errno; + } else { + rval = fprintf(f, "%d", value); + fclose(f); + } + + if (rval <= 0) + connman_error("failed to set %s value %d", path, value); g_free(path); - if (!f) - return; + return rval; +} - if (!enable) - fprintf(f, "1"); - else - fprintf(f, "0"); +static int write_ipv4_conf_value(const char *ifname, const char *suffix, + int value) +{ + return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value); +} - fclose(f); +static int write_ipv6_conf_value(const char *ifname, const char *suffix, + int value) +{ + return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value); } -static int get_ipv6_privacy(gchar *ifname) +static bool get_ipv6_state(gchar *ifname) { - gchar *path; - FILE *f; - int value; + int disabled; + bool enabled = false; - if (!ifname) - return 0; + if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0) + enabled = !disabled; - path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr", - ifname); + return enabled; +} - if (!path) - return 0; +static int set_ipv6_state(gchar *ifname, bool enable) +{ + int disabled = enable ? 0 : 1; - f = fopen(path, "r"); + DBG("%s %d", ifname, disabled); - g_free(path); + return write_ipv6_conf_value(ifname, "disable_ipv6", disabled); +} - if (!f) +static int get_ipv6_privacy(gchar *ifname) +{ + int value; + + if (!ifname) return 0; - if (fscanf(f, "%d", &value) <= 0) + if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0) value = 0; - fclose(f); - return value; } /* Enable the IPv6 privacy extension for stateless address autoconfiguration. * The privacy extension is described in RFC 3041 and RFC 4941 */ -static void set_ipv6_privacy(gchar *ifname, int value) +static int set_ipv6_privacy(gchar *ifname, int value) { - gchar *path; - FILE *f; - if (!ifname) - return; - - path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr", - ifname); - - if (!path) - return; + return -EINVAL; if (value < 0) value = 0; - f = fopen(path, "r+"); - - g_free(path); - - if (!f) - return; - - fprintf(f, "%d", value); - fclose(f); + return write_ipv6_conf_value(ifname, "use_tempaddr", value); } static int get_rp_filter(void) { - FILE *f; - int value = -EINVAL, tmp; + int value; - f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r"); - - if (f) { - if (fscanf(f, "%d", &tmp) == 1) - value = tmp; - fclose(f); - } + if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0) + value = -EINVAL; return value; } -static void set_rp_filter(int value) +static int set_rp_filter(int value) { - FILE *f; - - f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+"); - - if (!f) - return; - - fprintf(f, "%d", value); + /* 0 = no validation, 1 = strict mode, 2 = loose mode */ + switch (value) { + case -1: + value = 0; + /* fall through */ + case 0: + case 1: + case 2: + break; + default: + return -EINVAL; + } - fclose(f); + return write_ipv4_conf_value(NULL, "rp_filter", value); } int __connman_ipconfig_set_rp_filter() @@ -710,6 +722,25 @@ static inline gint check_duplicate_address(gconstpointer a, gconstpointer b) return g_strcmp0(addr1->local, addr2->local); } +static bool is_index_p2p_service(int index) +{ + struct connman_service *service; + enum connman_service_type type; + + service = __connman_service_lookup_from_index(index); + if (!service) + return false; + + type = connman_service_get_type(service); + switch (type) { + case CONNMAN_SERVICE_TYPE_P2P: + case CONNMAN_SERVICE_TYPE_VPN: + return true; + default: + return false; + } +} + int __connman_ipconfig_newaddr(int index, int family, const char *label, unsigned char prefixlen, const char *address) { @@ -732,6 +763,9 @@ int __connman_ipconfig_newaddr(int index, int family, const char *label, ipaddress->prefixlen = prefixlen; ipaddress->local = g_strdup(address); + if (is_index_p2p_service(index)) + connman_ipaddress_set_p2p(ipaddress, true); + if (g_slist_find_custom(ipdevice->address_list, ipaddress, check_duplicate_address)) { connman_ipaddress_free(ipaddress); @@ -1216,6 +1250,15 @@ void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig, ipconfig->address->prefixlen = prefixlen; } +static void ipconfig_set_p2p(int index, struct connman_ipconfig *ipconfig) +{ + if (!is_index_p2p_service(index)) + return; + + connman_ipaddress_set_p2p(ipconfig->address, true); + connman_ipaddress_set_p2p(ipconfig->system, true); +} + static struct connman_ipconfig *create_ipv6config(int index) { struct connman_ipconfig *ipv6config; @@ -1251,6 +1294,8 @@ static struct connman_ipconfig *create_ipv6config(int index) ipv6config->system = connman_ipaddress_alloc(AF_INET6); + ipconfig_set_p2p(index, ipv6config); + DBG("ipconfig %p index %d method %s", ipv6config, index, __connman_ipconfig_method2string(ipv6config->method)); @@ -1288,6 +1333,9 @@ struct connman_ipconfig *__connman_ipconfig_create(int index, } ipconfig->system = connman_ipaddress_alloc(AF_INET); + + ipconfig_set_p2p(index, ipconfig); + #if defined TIZEN_EXT if (!simplified_log) #endif @@ -1495,10 +1543,8 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig) err = connman_inet_clear_address(ipconfig->index, ipconfig->address); else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) - err = connman_inet_clear_ipv6_address( - ipconfig->index, - ipconfig->address->local, - ipconfig->address->prefixlen); + err = connman_inet_clear_ipv6_address(ipconfig->index, + ipconfig->address); else err = -EINVAL; @@ -1691,6 +1737,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig) connman_ipaddress_clear(ipdevice->config_ipv4->system); __connman_ipconfig_unref(ipdevice->config_ipv4); + + g_free(ipdevice->ipv4_gateway); + ipdevice->ipv4_gateway = NULL; } if (type == CONNMAN_IPCONFIG_TYPE_IPV6 && @@ -1701,6 +1750,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig) connman_ipaddress_clear(ipdevice->config_ipv6->system); __connman_ipconfig_unref(ipdevice->config_ipv6); + + g_free(ipdevice->ipv6_gateway); + ipdevice->ipv6_gateway = NULL; } if (type == CONNMAN_IPCONFIG_TYPE_IPV4) @@ -1765,6 +1817,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig) connman_ipaddress_clear(ipdevice->config_ipv4->system); __connman_ipconfig_unref(ipdevice->config_ipv4); ipdevice->config_ipv4 = NULL; + + g_free(ipdevice->ipv4_gateway); + ipdevice->ipv4_gateway = NULL; + return 0; } @@ -1781,6 +1837,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig) connman_ipaddress_clear(ipdevice->config_ipv6->system); __connman_ipconfig_unref(ipdevice->config_ipv6); ipdevice->config_ipv6 = NULL; + + g_free(ipdevice->ipv6_gateway); + ipdevice->ipv6_gateway = NULL; + return 0; } diff --git a/src/iptables.c b/src/iptables.c index 2ee9485a..90a296e2 100755 --- a/src/iptables.c +++ b/src/iptables.c @@ -2947,7 +2947,7 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip, if (!tokens) return -1; - if (!inet_pton(AF_INET, tokens[0], ip)) { + if (inet_pton(AF_INET, tokens[0], ip) != 1) { err = -1; goto out; } @@ -2988,7 +2988,7 @@ static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip, if (!tokens) return -1; - if (!inet_pton(AF_INET6, tokens[0], ip)) { + if (inet_pton(AF_INET6, tokens[0], ip) != 1) { err = -1; goto out; } @@ -3399,7 +3399,7 @@ static int parse_rule_spec(struct connman_iptables *table, break; if (invert) - ctx->ip->invflags |= IP6T_INV_DSTIP; + ctx->ipv6->invflags |= IP6T_INV_DSTIP; } break; @@ -46,6 +46,14 @@ #define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000) #define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000) +/* + * We set the integer to 1 sec so that we have a chance to get + * necessary IPv6 router advertisement messages that might have + * DNS data etc. + */ +#define DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL 1 +#define DEFAULT_ONLINE_CHECK_MAX_INTERVAL 12 + #if defined TIZEN_EXT #define DEFAULT_WIFI_INTERFACE "wlan0" #define CONTAINER_FILE "/run/systemd/container" @@ -95,6 +103,9 @@ static struct { bool enable_6to4; char *vendor_class_id; bool enable_online_check; + bool enable_online_to_ready_transition; + unsigned int online_check_initial_interval; + unsigned int online_check_max_interval; bool auto_connect_roaming_services; bool acd; bool use_gateways_as_timeservers; @@ -130,6 +141,9 @@ static struct { .enable_6to4 = false, .vendor_class_id = NULL, .enable_online_check = true, + .enable_online_to_ready_transition = false, + .online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL, + .online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL, .auto_connect_roaming_services = false, .acd = false, .use_gateways_as_timeservers = false, @@ -222,6 +236,9 @@ static struct { #define CONF_ENABLE_6TO4 "Enable6to4" #define CONF_VENDOR_CLASS_ID "VendorClassID" #define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck" +#define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition" +#define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval" +#define CONF_ONLINE_CHECK_MAX_INTERVAL "OnlineCheckMaxInterval" #define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices" #define CONF_ACD "AddressConflictDetection" #define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers" @@ -271,6 +288,7 @@ static const char *supported_options[] = { CONF_BG_SCAN, CONF_PREF_TIMESERVERS, CONF_AUTO_CONNECT_TECHS, + CONF_FAVORITE_TECHS, CONF_ALWAYS_CONNECTED_TECHS, CONF_PREFERRED_TECHS, CONF_FALLBACK_NAMESERVERS, @@ -285,6 +303,9 @@ static const char *supported_options[] = { CONF_ENABLE_6TO4, CONF_VENDOR_CLASS_ID, CONF_ENABLE_ONLINE_CHECK, + CONF_ENABLE_ONLINE_TO_READY_TRANSITION, + CONF_ONLINE_CHECK_INITIAL_INTERVAL, + CONF_ONLINE_CHECK_MAX_INTERVAL, CONF_AUTO_CONNECT_ROAMING_SERVICES, CONF_ACD, CONF_USE_GATEWAYS_AS_TIMESERVERS, @@ -745,9 +766,9 @@ static void parse_config(GKeyFile *config) char **interfaces; char **str_list; char **tethering; - char *vendor_class_id; + char *string; gsize len; - int timeout; + int integer; if (!config) { connman_settings.auto_connect = @@ -836,17 +857,17 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); - timeout = g_key_file_get_integer(config, "General", + integer = g_key_file_get_integer(config, "General", CONF_TIMEOUT_INPUTREQ, &error); - if (!error && timeout >= 0) - connman_settings.timeout_inputreq = timeout * 1000; + if (!error && integer >= 0) + connman_settings.timeout_inputreq = integer * 1000; g_clear_error(&error); - timeout = g_key_file_get_integer(config, "General", + integer = g_key_file_get_integer(config, "General", CONF_TIMEOUT_BROWSERLAUNCH, &error); - if (!error && timeout >= 0) - connman_settings.timeout_browserlaunch = timeout * 1000; + if (!error && integer >= 0) + connman_settings.timeout_browserlaunch = integer * 1000; g_clear_error(&error); @@ -907,10 +928,10 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); - vendor_class_id = __connman_config_get_string(config, "General", + string = __connman_config_get_string(config, "General", CONF_VENDOR_CLASS_ID, &error); if (!error) - connman_settings.vendor_class_id = vendor_class_id; + connman_settings.vendor_class_id = string; g_clear_error(&error); @@ -925,6 +946,40 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); boolean = __connman_config_get_bool(config, "General", + CONF_ENABLE_ONLINE_TO_READY_TRANSITION, &error); + if (!error) { + connman_settings.enable_online_to_ready_transition = boolean; + } + + g_clear_error(&error); + + integer = g_key_file_get_integer(config, "General", + CONF_ONLINE_CHECK_INITIAL_INTERVAL, &error); + if (!error && integer >= 0) + connman_settings.online_check_initial_interval = integer; + + g_clear_error(&error); + + integer = g_key_file_get_integer(config, "General", + CONF_ONLINE_CHECK_MAX_INTERVAL, &error); + if (!error && integer >= 0) + connman_settings.online_check_max_interval = integer; + + g_clear_error(&error); + + if (connman_settings.online_check_initial_interval < 1 || + connman_settings.online_check_initial_interval > + connman_settings.online_check_max_interval) { + connman_warn("Incorrect online check intervals [%u, %u]", + connman_settings.online_check_initial_interval, + connman_settings.online_check_max_interval); + connman_settings.online_check_initial_interval = + DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL; + connman_settings.online_check_max_interval = + DEFAULT_ONLINE_CHECK_MAX_INTERVAL; + } + + boolean = __connman_config_get_bool(config, "General", CONF_AUTO_CONNECT_ROAMING_SERVICES, &error); if (!error) connman_settings.auto_connect_roaming_services = boolean; @@ -1127,7 +1182,7 @@ static GOptionEntry options[] = { { NULL }, }; -const char *connman_option_get_string(const char *key) +char *connman_setting_get_string(const char *key) { if (g_str_equal(key, CONF_VENDOR_CLASS_ID)) return connman_settings.vendor_class_id; @@ -1178,6 +1233,9 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK)) return connman_settings.enable_online_check; + if (g_str_equal(key, CONF_ENABLE_ONLINE_TO_READY_TRANSITION)) + return connman_settings.enable_online_to_ready_transition; + if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES)) return connman_settings.auto_connect_roaming_services; @@ -1208,9 +1266,7 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_CONNMAN_WIFI_ROAM)) return connman_settings.wifi_roam; -#endif -#if defined TIZEN_EXT if (g_str_equal(key, CONF_INS_LAST_CONNECTED_BSSID)) return connman_ins_settings.ins_last_connected_bssid; @@ -1236,9 +1292,15 @@ bool connman_setting_get_bool(const char *key) return false; } -#if defined TIZEN_EXT unsigned int connman_setting_get_uint(const char *key) { + if (g_str_equal(key, CONF_ONLINE_CHECK_INITIAL_INTERVAL)) + return connman_settings.online_check_initial_interval; + + if (g_str_equal(key, CONF_ONLINE_CHECK_MAX_INTERVAL)) + return connman_settings.online_check_max_interval; + +#if defined TIZEN_EXT if (g_str_equal(key, CONF_INS_PREFERRED_FREQ_BSSID_SCORE)) return connman_ins_settings.ins_preferred_freq_bssid_score; @@ -1268,10 +1330,11 @@ unsigned int connman_setting_get_uint(const char *key) if (g_str_equal(key, CONF_INS_INTERNET_SCORE)) return connman_ins_settings.ins_internet_score; - +#endif return 0; } +#if defined TIZEN_EXT int connman_setting_get_int(const char *key) { if (g_str_equal(key, CONF_INS_SIGNAL_LEVEL3_5GHZ)) @@ -1544,6 +1607,7 @@ int main(int argc, char *argv[]) g_strfreev(connman_settings.fallback_nameservers); g_strfreev(connman_settings.blacklisted_interfaces); g_strfreev(connman_settings.tethering_technologies); + g_free(connman_settings.vendor_class_id); #if defined TIZEN_EXT g_free(connman_ins_settings.ins_preferred_freq_bssid); diff --git a/src/main.conf b/src/main.conf index f761da7c..e734ecfe 100755 --- a/src/main.conf +++ b/src/main.conf @@ -130,6 +130,27 @@ SingleConnectedTechnology = true # Default value is true. # EnableOnlineCheck = false +# Range of intervals between two online check requests. +# When an online check request fails, another one is triggered after a +# longer interval. The intervals follow the power of two series of numbers +# between OnlineCheckInitialInterval and OnlineCheckMaxInterval. +# Default range is [1, 12], corresponding to the following intervals, in +# seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144. +# OnlineCheckInitialInterval = 1 +# OnlineCheckMaxInterval = 12 + +# WARNING: Experimental feature!!! +# In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET +# to detect the loss of end-to-end connectivity. +# If this setting is false, when the default service transitions to ONLINE +# state, the HTTP GET request is no more called until next cycle, initiated +# by a transition of the default service to DISCONNECT state. +# If this setting is true, the HTTP GET request keeps beeing called to guarantee +# that end-to-end connectivity is still successful. If not, the default service +# will transition to READY state, enabling another service to become the +# default one, in replacement. +# EnableOnlineToReadyTransition = false + # List of technologies with AutoConnect = true which are always connected # regardless of PreferredTechnologies setting. Default value is empty and # will connect a technology only if it is at a higher preference than any diff --git a/src/manager.c b/src/manager.c index 4f8306a1..0000f784 100755 --- a/src/manager.c +++ b/src/manager.c @@ -221,7 +221,7 @@ static DBusMessage *get_interfaces(DBusConnection *conn, DBusMessage *msg, void { DBusMessage *reply; DBusMessageIter iter, array; - const char *default_interface = connman_option_get_string("DefaultWifiInterface"); + const char *default_interface = connman_setting_get_string("DefaultWifiInterface"); DBG("DefaultWifiInterface %s", default_interface); diff --git a/src/network.c b/src/network.c index 6849c1b7..70461370 100755 --- a/src/network.c +++ b/src/network.c @@ -2819,6 +2819,21 @@ int connman_network_set_wifi_channel(struct connman_network *network, return 0; } +int connman_network_set_autoconnect(struct connman_network *network, + bool autoconnect) +{ + if (!network->driver || !network->driver->set_autoconnect) + return 0; + return network->driver->set_autoconnect(network, autoconnect); +} + +bool __connman_network_native_autoconnect(struct connman_network *network) +{ + if (!network->driver || !network->driver->set_autoconnect) + return false; + return true; +} + uint16_t connman_network_get_wifi_channel(struct connman_network *network) { return network->wifi.channel; @@ -3142,6 +3157,7 @@ const void *connman_network_get_blob(struct connman_network *network, return network->wifi.transition_mode_ssid; #endif } + return NULL; } @@ -154,7 +154,7 @@ static int start_dhcp_server(struct connman_peer *peer) err = __connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, - gateway, NULL, prefixlen, broadcast); + gateway, NULL, prefixlen, broadcast, true); if (err < 0) goto error; @@ -983,7 +983,10 @@ void connman_peer_add_service(struct connman_peer *peer, service = g_malloc0(sizeof(struct _peer_service)); service->type = type; - service->data = g_memdup(data, data_length * sizeof(unsigned char)); + if (data_length > 0) { + service->data = g_malloc(data_length * sizeof(unsigned char)); + memcpy(service->data, data, data_length * sizeof(unsigned char)); + } service->length = data_length; peer->services = g_slist_prepend(peer->services, service); diff --git a/src/provider.c b/src/provider.c index c437c91b..195ae226 100755 --- a/src/provider.c +++ b/src/provider.c @@ -53,6 +53,7 @@ void __connman_provider_append_properties(struct connman_provider *provider, DBusMessageIter *iter) { const char *host, *domain, *type; + dbus_bool_t split_routing; if (!provider->driver || !provider->driver->get_property) return; @@ -72,6 +73,12 @@ void __connman_provider_append_properties(struct connman_provider *provider, if (type) connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &type); + + if (provider->vpn_service) { + split_routing = connman_provider_is_split_routing(provider); + connman_dbus_dict_append_basic(iter, "SplitRouting", + DBUS_TYPE_BOOLEAN, &split_routing); + } } struct connman_provider * @@ -439,6 +446,15 @@ const char *__connman_provider_get_ident(struct connman_provider *provider) return provider->identifier; } +const char * __connman_provider_get_transport_ident( + struct connman_provider *provider) +{ + if (provider && provider && provider->driver && provider->driver->get_property) + return provider->driver->get_property(provider, "Transport"); + + return NULL; +} + int connman_provider_set_string(struct connman_provider *provider, const char *key, const char *value) { @@ -607,6 +623,100 @@ void connman_provider_set_autoconnect(struct connman_provider *provider, __connman_service_save(provider->vpn_service); } +bool connman_provider_is_split_routing(struct connman_provider *provider) +{ + if (!provider || !provider->vpn_service) + return false; + + return __connman_service_is_split_routing(provider->vpn_service); +} + +int connman_provider_set_split_routing(struct connman_provider *provider, + bool split_routing) +{ + struct connman_service *service; + enum connman_ipconfig_type type; + int service_index; + int vpn_index; + bool service_split_routing; + int err = 0; + + DBG(""); + + if (!provider || !provider->vpn_service) + return -EINVAL; + + service_split_routing = __connman_service_is_split_routing( + provider->vpn_service); + + if (service_split_routing == split_routing) { + DBG("split_routing already set %s", + split_routing ? "true" : "false"); + return -EALREADY; + } + + switch (provider->family) { + case AF_INET: + type = CONNMAN_IPCONFIG_TYPE_IPV4; + break; + case AF_INET6: + type = CONNMAN_IPCONFIG_TYPE_IPV6; + break; + case AF_UNSPEC: + type = CONNMAN_IPCONFIG_TYPE_ALL; + break; + default: + type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; + } + + if (!__connman_service_is_connected_state(provider->vpn_service, + type)) { + DBG("%p VPN not connected", provider->vpn_service); + goto save; + } + + vpn_index = __connman_service_get_index(provider->vpn_service); + service_index = __connman_connection_get_vpn_phy_index(vpn_index); + service = __connman_service_lookup_from_index(service_index); + if (!service) + goto save; + + if (split_routing) + err = __connman_service_move(service, provider->vpn_service, + true); + else + err = __connman_service_move(provider->vpn_service, service, + true); + + if (err) { + connman_warn("cannot move service %p and VPN %p error %d", + service, provider->vpn_service, err); + + /* + * In case of error notify vpnd about the current split routing + * state. + */ + __connman_service_split_routing_changed(provider->vpn_service); + goto out; + } + +save: + __connman_service_set_split_routing(provider->vpn_service, + split_routing); + __connman_service_save(provider->vpn_service); + +out: + return err; +} + +int connman_provider_get_family(struct connman_provider *provider) +{ + if (!provider) + return AF_UNSPEC; + + return provider->family; +} + static void unregister_provider(gpointer data) { struct connman_provider *provider = data; @@ -217,6 +217,9 @@ static void read_uevent(struct interface_data *interface) } else if (strcmp(line + 8, "bond") == 0) { interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; + } else if (strcmp(line + 8, "dsa") == 0) { + interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; + interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; } else { interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; diff --git a/src/service.c b/src/service.c index 19056a81..411b617f 100755 --- a/src/service.c +++ b/src/service.c @@ -66,6 +66,9 @@ static unsigned int autoconnect_id = 0; static unsigned int vpn_autoconnect_id = 0; static struct connman_service *current_default = NULL; static bool services_dirty = false; +static bool enable_online_to_ready_transition = false; +static unsigned int online_check_initial_interval = 0; +static unsigned int online_check_max_interval = 0; #if defined TIZEN_EXT static bool auto_connect_mode = TRUE; @@ -187,8 +190,8 @@ struct connman_service { bool wps; bool wps_advertizing; guint online_timeout; - int online_check_interval_ipv4; - int online_check_interval_ipv6; + unsigned int online_check_interval_ipv4; + unsigned int online_check_interval_ipv6; bool do_split_routing; bool new_service; bool hidden_service; @@ -352,6 +355,8 @@ static const char *reason2string(enum connman_service_connect_reason reason) return "auto"; case CONNMAN_SERVICE_CONNECT_REASON_SESSION: return "session"; + case CONNMAN_SERVICE_CONNECT_REASON_NATIVE: + return "native"; } return "unknown"; @@ -584,7 +589,26 @@ static enum connman_dnsconfig_method __connman_dnsconfig_string2method( } #endif -static void set_split_routing(struct connman_service *service, bool value) +void __connman_service_split_routing_changed(struct connman_service *service) +{ + dbus_bool_t split_routing; + + if (!service->path) + return; + + if (!allow_property_changed(service)) + return; + + split_routing = service->do_split_routing; + if (!connman_dbus_property_changed_basic(service->path, + CONNMAN_SERVICE_INTERFACE, "SplitRouting", + DBUS_TYPE_BOOLEAN, &split_routing)) + connman_warn("cannot send SplitRouting property change on %s", + service->identifier); +} + +void __connman_service_set_split_routing(struct connman_service *service, + bool value) { if (service->type != CONNMAN_SERVICE_TYPE_VPN) return; @@ -595,6 +619,12 @@ static void set_split_routing(struct connman_service *service, bool value) service->order = 0; else service->order = 10; + + /* + * In order to make sure the value is propagated also when loading the + * VPN service signal the value regardless of the value change. + */ + __connman_service_split_routing_changed(service); } int __connman_service_load_modifiable(struct connman_service *service) @@ -620,9 +650,10 @@ int __connman_service_load_modifiable(struct connman_service *service) #endif break; case CONNMAN_SERVICE_TYPE_VPN: - set_split_routing(service, g_key_file_get_boolean(keyfile, - service->identifier, - "SplitRouting", NULL)); + __connman_service_set_split_routing(service, + g_key_file_get_boolean(keyfile, + service->identifier, + "SplitRouting", NULL)); /* fall through */ case CONNMAN_SERVICE_TYPE_WIFI: @@ -1063,9 +1094,10 @@ static int service_load(struct connman_service *service) #endif break; case CONNMAN_SERVICE_TYPE_VPN: - set_split_routing(service, g_key_file_get_boolean(keyfile, - service->identifier, - "SplitRouting", NULL)); + __connman_service_set_split_routing(service, + g_key_file_get_boolean(keyfile, + service->identifier, + "SplitRouting", NULL)); autoconnect = g_key_file_get_boolean(keyfile, service->identifier, "AutoConnect", &error); @@ -1200,8 +1232,10 @@ static int service_load(struct connman_service *service) str = g_key_file_get_string(keyfile, service->identifier, "Passphrase", NULL); if (str) { + char *dec = g_strcompress(str); + g_free(str); g_free(service->passphrase); - service->passphrase = str; + service->passphrase = dec; } if (service->ipconfig_ipv4) @@ -1565,9 +1599,12 @@ static int service_save(struct connman_service *service) g_free(str); } - if (service->passphrase && strlen(service->passphrase) > 0) + if (service->passphrase && strlen(service->passphrase) > 0) { + char *enc = g_strescape(service->passphrase, NULL); g_key_file_set_string(keyfile, service->identifier, - "Passphrase", service->passphrase); + "Passphrase", enc); + g_free(enc); + } if (service->ipconfig_ipv4) __connman_ipconfig_save(service->ipconfig_ipv4, keyfile, @@ -1653,15 +1690,14 @@ static int service_save(struct connman_service *service) "mDNS", TRUE); if (service->hidden_service) - g_key_file_set_boolean(keyfile, service->identifier, "Hidden", - TRUE); + g_key_file_set_boolean(keyfile, service->identifier, + "Hidden", TRUE); if (service->config_file && strlen(service->config_file) > 0) g_key_file_set_string(keyfile, service->identifier, "Config.file", service->config_file); - if (service->config_entry && - strlen(service->config_entry) > 0) + if (service->config_entry && strlen(service->config_entry) > 0) g_key_file_set_string(keyfile, service->identifier, "Config.ident", service->config_entry); @@ -2267,7 +2303,7 @@ static int nameserver_add_all(struct connman_service *service, __connman_resolver_append_fallback_nameservers(); #if defined TIZEN_EXT - const char *global_dns = connman_option_get_string("GlobalNameserver"); + const char *global_dns = connman_setting_get_string("GlobalNameserver"); if (global_dns) nameserver_add(service, type, global_dns); #endif @@ -2442,7 +2478,7 @@ static int nameserver_remove_all(struct connman_service *service, } #if defined TIZEN_EXT - const char *global_dns = connman_option_get_string("GlobalNameserver"); + const char *global_dns = connman_setting_get_string("GlobalNameserver"); if (global_dns) nameserver_remove(service, type, global_dns); #endif @@ -2765,6 +2801,12 @@ static void start_online_check(struct connman_service *service, "Default service remains in READY state."); return; } + enable_online_to_ready_transition = + connman_setting_get_bool("EnableOnlineToReadyTransition"); + online_check_initial_interval = + connman_setting_get_uint("OnlineCheckInitialInterval"); + online_check_max_interval = + connman_setting_get_uint("OnlineCheckMaxInterval"); if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) { cancel_online_check(service); @@ -3020,6 +3062,16 @@ static void default_changed(void) connman_setting_get_bool("AllowDomainnameUpdates")) __connman_utsname_set_domainname(service->domainname); + if (__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV4)) + __connman_service_wispr_start(service, + CONNMAN_IPCONFIG_TYPE_IPV4); + + if (__connman_service_is_connected_state(service, + CONNMAN_IPCONFIG_TYPE_IPV6)) + __connman_service_wispr_start(service, + CONNMAN_IPCONFIG_TYPE_IPV6); + /* * Connect VPN automatically when new default service * is set and connected, unless new default is VPN @@ -3261,6 +3313,8 @@ bool connman_service_set_autoconnect(struct connman_service *service, service->autoconnect = autoconnect; autoconnect_changed(service); + connman_network_set_autoconnect(service->network, autoconnect); + return true; } @@ -5714,15 +5768,28 @@ static void do_auto_connect(struct connman_service *service, return; /* + * Only user interaction should get VPN or WIFI connected in failure + * state. + */ + if (service->state == CONNMAN_SERVICE_STATE_FAILURE && + reason != CONNMAN_SERVICE_CONNECT_REASON_USER && + (service->type == CONNMAN_SERVICE_TYPE_VPN || + service->type == CONNMAN_SERVICE_TYPE_WIFI)) + return; + + /* + * Do not use the builtin auto connect, instead rely on the + * native auto connect feature of the service. + */ + if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) + return; + + /* * Run service auto connect for other than VPN services. Afterwards * start also VPN auto connect process. */ if (service->type != CONNMAN_SERVICE_TYPE_VPN) __connman_service_auto_connect(reason); - /* Only user interaction should get VPN connected in failure state. */ - else if (service->state == CONNMAN_SERVICE_STATE_FAILURE && - reason != CONNMAN_SERVICE_CONNECT_REASON_USER) - return; vpn_auto_connect(); } @@ -5801,14 +5868,6 @@ int __connman_service_reset_ipconfig(struct connman_service *service, return err; } -/* - * We set the timeout to 1 sec so that we have a chance to get - * necessary IPv6 router advertisement messages that might have - * DNS data etc. - */ -#define ONLINE_CHECK_INITIAL_INTERVAL 1 -#define ONLINE_CHECK_MAX_INTERVAL 12 - void __connman_service_wispr_start(struct connman_service *service, enum connman_ipconfig_type type) { @@ -5816,10 +5875,10 @@ void __connman_service_wispr_start(struct connman_service *service, if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service->online_check_interval_ipv4 = - ONLINE_CHECK_INITIAL_INTERVAL; + online_check_initial_interval; else service->online_check_interval_ipv6 = - ONLINE_CHECK_INITIAL_INTERVAL; + online_check_initial_interval; __connman_wispr_start(service, type); } @@ -6074,9 +6133,7 @@ static DBusMessage *set_property(DBusConnection *conn, service_save(service); timeservers_configuration_changed(service); - - if (service == connman_service_get_default()) - __connman_timeserver_sync(service); + __connman_timeserver_conf_update(service); } else if (g_str_equal(name, "Domains.Configuration")) { DBusMessageIter entry; @@ -6511,6 +6568,7 @@ static void set_always_connecting_technologies() always_connect[always_connected_techs[i]] = 1; } +#if !defined TIZEN_EXT static bool autoconnect_no_session_active(struct connman_service *service) { /* @@ -6523,6 +6581,7 @@ static bool autoconnect_no_session_active(struct connman_service *service) return false; } +#endif static bool autoconnect_already_connecting(struct connman_service *service, bool autoconnecting) @@ -6594,6 +6653,12 @@ static bool auto_connect_service(GList *services, continue; #endif + if (service->connect_reason == + CONNMAN_SERVICE_CONNECT_REASON_NATIVE) { + DBG("service %p uses native autonnect, skip", service); + continue; + } + if (service->pending || is_connecting(service->state) || is_connected(service->state)) { @@ -7023,7 +7088,7 @@ static DBusMessage *connect_service(DBusConnection *conn, break; #endif if (!is_connecting(temp->state) && !is_connected(temp->state)) - break; + continue; if (service == temp) continue; @@ -7392,27 +7457,22 @@ static void service_schedule_changed(void) services_notify->id = g_timeout_add(100, service_send_changed, NULL); } -static DBusMessage *move_service(DBusConnection *conn, - DBusMessage *msg, void *user_data, - bool before) +int __connman_service_move(struct connman_service *service, + struct connman_service *target, bool before) { - struct connman_service *service = user_data; - struct connman_service *target; - const char *path; enum connman_ipconfig_method target4, target6; enum connman_ipconfig_method service4, service6; DBG("service %p", service); - dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); + if (!service) + return -EINVAL; if (!service->favorite) - return __connman_error_not_supported(msg); + return -EOPNOTSUPP; - target = find_service(path); if (!target || !target->favorite || target == service) - return __connman_error_invalid_service(msg); + return -EINVAL; if (target->type == CONNMAN_SERVICE_TYPE_VPN) { /* @@ -7423,14 +7483,14 @@ static DBusMessage *move_service(DBusConnection *conn, connman_info("Cannot move service. " "No routes defined for provider %s", __connman_provider_get_ident(target->provider)); - return __connman_error_invalid_service(msg); + return -EINVAL; } - set_split_routing(target, true); + __connman_service_set_split_routing(target, true); } else - set_split_routing(target, false); + __connman_service_set_split_routing(target, false); - set_split_routing(service, false); + __connman_service_set_split_routing(service, false); target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4); target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6); @@ -7453,7 +7513,7 @@ static DBusMessage *move_service(DBusConnection *conn, if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) { if (!check_suitable_state(target->state_ipv6, service->state_ipv6)) - return __connman_error_invalid_service(msg); + return -EINVAL; } } @@ -7461,7 +7521,7 @@ static DBusMessage *move_service(DBusConnection *conn, if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) { if (!check_suitable_state(target->state_ipv4, service->state_ipv4)) - return __connman_error_invalid_service(msg); + return -EINVAL; } } @@ -7469,7 +7529,7 @@ static DBusMessage *move_service(DBusConnection *conn, if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) { if (!check_suitable_state(target->state_ipv6, service->state_ipv6)) - return __connman_error_invalid_service(msg); + return -EINVAL; } } @@ -7477,7 +7537,7 @@ static DBusMessage *move_service(DBusConnection *conn, if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) { if (!check_suitable_state(target->state_ipv4, service->state_ipv4)) - return __connman_error_invalid_service(msg); + return -EINVAL; } } @@ -7500,6 +7560,39 @@ static DBusMessage *move_service(DBusConnection *conn, service_schedule_changed(); + return 0; +} + +static DBusMessage *move_service(DBusConnection *conn, + DBusMessage *msg, void *user_data, + bool before) +{ + struct connman_service *service = user_data; + struct connman_service *target; + const char *path; + int err; + + DBG("service %p", service); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + target = find_service(path); + + err = __connman_service_move(service, target, before); + switch (err) { + case 0: + break; + case -EINVAL: + return __connman_error_invalid_service(msg); + case -EOPNOTSUPP: + return __connman_error_not_supported(msg); + default: + connman_warn("unsupported error code %d in move_service()", + err); + break; + } + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -7800,8 +7893,7 @@ static void service_initialize(struct connman_service *service) memset(service->last_connected_bssid, 0, WIFI_BSSID_LEN_MAX); service->is_internet_connection = false; service->assoc_reject_count = 0; -#endif -#if defined TIZEN_EXT + service->disconnection_requested = false; service->storage_reload = false; /* @@ -8081,6 +8173,40 @@ static int calculate_score(struct connman_service *service) } #endif /* defined TIZEN_EXT && defined TIZEN_EXT_INS */ +static gint service_compare(gconstpointer a, gconstpointer b); + +static gint service_compare_vpn(struct connman_service *a, + struct connman_service *b) +{ + struct connman_provider *provider; + struct connman_service *service; + struct connman_service *transport; + const char *ident; + bool reverse; + + if (a->provider) { + provider = a->provider; + service = b; + reverse = false; + } else if (b->provider) { + provider = b->provider; + service = a; + reverse = true; + } else { + return 0; + } + + ident = __connman_provider_get_transport_ident(provider); + transport = connman_service_lookup_from_identifier(ident); + if (!transport) + return 0; + + if (reverse) + return service_compare(service, transport); + + return service_compare(transport, service); +} + static gint service_compare(gconstpointer a, gconstpointer b) { struct connman_service *service_a = (void *) a; @@ -8105,7 +8231,7 @@ static gint service_compare(gconstpointer a, gconstpointer b) service_a->type == CONNMAN_SERVICE_TYPE_WIFI && service_b->type == CONNMAN_SERVICE_TYPE_WIFI) { const char *default_interface = - connman_option_get_string("DefaultWifiInterface"); + connman_setting_get_string("DefaultWifiInterface"); const char *ifname_a = connman_device_get_string( connman_network_get_device(service_a->network), "Interface"); const char *ifname_b = connman_device_get_string( @@ -8119,6 +8245,17 @@ static gint service_compare(gconstpointer a, gconstpointer b) #endif if (a_connected && b_connected) { + int rval; + + /* Compare the VPN transport and the service */ + if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN || + service_b->type == CONNMAN_SERVICE_TYPE_VPN) && + service_b->type != service_a->type) { + rval = service_compare_vpn(service_a, service_b); + if (rval) + return rval; + } + if (service_a->order > service_b->order) return -1; @@ -8619,6 +8756,7 @@ static void report_error_cb(void *user_context, bool retry, set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); #endif service_complete(service); + service_list_sort(); __connman_connection_update_gateway(); } } @@ -8690,14 +8828,18 @@ static void request_input_cb(struct connman_service *service, goto done; } - if (service->hidden && name_len > 0 && name_len <= 32) { - device = connman_network_get_device(service->network); - security = connman_network_get_string(service->network, - "WiFi.Security"); - err = __connman_device_request_hidden_scan(device, - name, name_len, - identity, passphrase, - security, user_data); + if (service->hidden) { + if (name_len > 0 && name_len <= 32) { + device = connman_network_get_device(service->network); + security = connman_network_get_string(service->network, + "WiFi.Security"); + err = __connman_device_request_hidden_scan(device, + name, name_len, + identity, passphrase, + security, user_data); + } else { + err = -EINVAL; + } if (err < 0) __connman_service_return_error(service, -err, user_data); @@ -9235,6 +9377,7 @@ static int service_indicate_state(struct connman_service *service) report_error_cb, get_dbus_sender(service), NULL); + goto notifier; } service_complete(service); break; @@ -9301,6 +9444,7 @@ static int service_indicate_state(struct connman_service *service) __connman_connection_update_gateway(); +notifier: if ((old_state == CONNMAN_SERVICE_STATE_ONLINE && new_state != CONNMAN_SERVICE_STATE_READY) || (old_state == CONNMAN_SERVICE_STATE_READY && @@ -9519,11 +9663,13 @@ static gboolean redo_wispr_ipv6(gpointer user_data) return FALSE; } -int __connman_service_online_check_failed(struct connman_service *service, - enum connman_ipconfig_type type) +void __connman_service_online_check(struct connman_service *service, + enum connman_ipconfig_type type, + bool success) { GSourceFunc redo_func; - int *interval; + unsigned int *interval; + enum connman_service_state current_state; if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { interval = &service->online_check_interval_ipv4; @@ -9533,6 +9679,22 @@ int __connman_service_online_check_failed(struct connman_service *service, redo_func = redo_wispr_ipv6; } + if(!enable_online_to_ready_transition) + goto redo_func; + + if (success) { + *interval = online_check_max_interval; + } else { + current_state = service->state; + downgrade_state(service); + if (current_state != service->state) + *interval = online_check_initial_interval; + if (service != connman_service_get_default()) { + return; + } + } + +redo_func: DBG("service %p type %s interval %d", service, __connman_ipconfig_type2string(type), *interval); @@ -9540,12 +9702,10 @@ int __connman_service_online_check_failed(struct connman_service *service, redo_func, connman_service_ref(service)); /* Increment the interval for the next time, set a maximum timeout of - * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds. + * online_check_max_interval seconds * online_check_max_interval seconds. */ - if (*interval < ONLINE_CHECK_MAX_INTERVAL) + if (*interval < online_check_max_interval) (*interval)++; - - return EAGAIN; } int __connman_service_ipconfig_indicate_state(struct connman_service *service, @@ -9806,8 +9966,8 @@ static void prepare_8021x(struct connman_service *service) service->phase1); #endif } -#if defined TIZEN_EXT +#if defined TIZEN_EXT static bool has_valid_configuration_object(struct connman_service *service) { return service->connector && service->c_sign_key && service->net_access_key; @@ -10058,6 +10218,12 @@ int __connman_service_connect(struct connman_service *service, __connman_service_clear_error(service); + if (service->network && service->autoconnect && + __connman_network_native_autoconnect(service->network)) { + DBG("service %p switch connecting reason to native", service); + reason = CONNMAN_SERVICE_CONNECT_REASON_NATIVE; + } + err = service_connect(service); DBG("service %p err %d", service, err); @@ -10339,6 +10505,9 @@ static int service_register(struct connman_service *service) service_methods, service_signals, NULL, service, NULL); + if (__connman_config_provision_service(service) < 0) + service_load(service); + service_list_sort(); __connman_connection_update_gateway(); @@ -10781,6 +10950,75 @@ static void update_from_network(struct connman_service *service, service_list_sort(); } +static void trigger_autoconnect(struct connman_service *service) +{ + struct connman_device *device; + bool native; + + if (!service->favorite) + return; + + native = __connman_network_native_autoconnect(service->network); + if (native && service->autoconnect) { + DBG("trigger native autoconnect"); + connman_network_set_autoconnect(service->network, true); + return; + } + + device = connman_network_get_device(service->network); + if (device && connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_UNKNOWN)) + return; + + switch (service->type) { + case CONNMAN_SERVICE_TYPE_UNKNOWN: + case CONNMAN_SERVICE_TYPE_SYSTEM: + case CONNMAN_SERVICE_TYPE_P2P: +#if defined TIZEN_EXT_WIFI_MESH + case CONNMAN_SERVICE_TYPE_MESH: +#endif + break; + + case CONNMAN_SERVICE_TYPE_GADGET: + case CONNMAN_SERVICE_TYPE_ETHERNET: + if (service->autoconnect) { + __connman_service_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); + break; + } + + /* fall through */ + case CONNMAN_SERVICE_TYPE_BLUETOOTH: + case CONNMAN_SERVICE_TYPE_GPS: + case CONNMAN_SERVICE_TYPE_VPN: + case CONNMAN_SERVICE_TYPE_WIFI: + case CONNMAN_SERVICE_TYPE_CELLULAR: + do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); + break; + } + +#if defined TIZEN_EXT + /* TIZEN synchronizes below information when the service creates */ + if (service->eap != NULL) + connman_network_set_string(service->network, "WiFi.EAP", + service->eap); + if (service->identity != NULL) + connman_network_set_string(service->network, "WiFi.Identity", + service->identity); + if (service->phase2 != NULL) + connman_network_set_string(service->network, "WiFi.Phase2", + service->phase2); + if (service->eap != NULL) + connman_network_set_string(service->network, "WiFi.Connector", + service->connector); + if (service->identity != NULL) + connman_network_set_string(service->network, "WiFi.CSignKey", + service->c_sign_key); + if (service->phase2 != NULL) + connman_network_set_string(service->network, "WiFi.NetAccessKey", + service->net_access_key); +#endif +} + /** * __connman_service_create_from_network: * @network: network structure @@ -10790,7 +11028,6 @@ 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; unsigned int *auto_connect_types, *favorite_types; @@ -10864,62 +11101,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne service_register(service); service_schedule_added(service); - if (service->favorite) { - device = connman_network_get_device(service->network); - if (device && !connman_device_get_scanning(device, - CONNMAN_SERVICE_TYPE_UNKNOWN)) { - - switch (service->type) { - case CONNMAN_SERVICE_TYPE_UNKNOWN: - case CONNMAN_SERVICE_TYPE_SYSTEM: - case CONNMAN_SERVICE_TYPE_P2P: -#if defined TIZEN_EXT_WIFI_MESH - case CONNMAN_SERVICE_TYPE_MESH: -#endif - break; - - case CONNMAN_SERVICE_TYPE_GADGET: - case CONNMAN_SERVICE_TYPE_ETHERNET: - if (service->autoconnect) { - __connman_service_connect(service, - CONNMAN_SERVICE_CONNECT_REASON_AUTO); - break; - } - - /* fall through */ - case CONNMAN_SERVICE_TYPE_BLUETOOTH: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_CELLULAR: - do_auto_connect(service, - CONNMAN_SERVICE_CONNECT_REASON_AUTO); - break; - } - } - -#if defined TIZEN_EXT - /* TIZEN synchronizes below information when the service creates */ - if (service->eap != NULL) - connman_network_set_string(service->network, "WiFi.EAP", - service->eap); - if (service->identity != NULL) - connman_network_set_string(service->network, "WiFi.Identity", - service->identity); - if (service->phase2 != NULL) - connman_network_set_string(service->network, "WiFi.Phase2", - service->phase2); - if (service->eap != NULL) - connman_network_set_string(service->network, "WiFi.Connector", - service->connector); - if (service->identity != NULL) - connman_network_set_string(service->network, "WiFi.CSignKey", - service->c_sign_key); - if (service->phase2 != NULL) - connman_network_set_string(service->network, "WiFi.NetAccessKey", - service->net_access_key); -#endif - } + trigger_autoconnect(service); __connman_notifier_service_add(service, service->name); @@ -11222,7 +11404,7 @@ static void ins_setting_init(void) ins_settings.last_user_selection_time = connman_setting_get_uint("INSLastUserSelectionTime"); ins_settings.last_connected = connman_setting_get_bool("INSLastConnected"); - string = connman_option_get_string("INSPreferredFreq"); + string = connman_setting_get_string("INSPreferredFreq"); if (g_strcmp0(string, "5GHz") == 0) ins_settings.preferred_freq = CONNMAN_INS_PREFERRED_FREQ_5GHZ; else if (g_strcmp0(string, "2.4GHz") == 0) diff --git a/src/session.c b/src/session.c index 6000b6d9..69adba9c 100644 --- a/src/session.c +++ b/src/session.c @@ -1815,7 +1815,7 @@ static void session_activate(struct connman_session *session) struct connman_service *service; struct connman_service_info *info; GSList *service_list = NULL; - enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED; + enum connman_service_state state = CONNMAN_SERVICE_STATE_DISCONNECT; g_hash_table_iter_init(&iter, service_hash); diff --git a/src/shared/util.c b/src/shared/util.c index 73c24aef..bda2d2b3 100755 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -28,6 +28,7 @@ #include <stdio.h> #include <ctype.h> #include <stdarg.h> +#include <string.h> #include "src/shared/util.h" diff --git a/src/technology.c b/src/technology.c index f16ee389..7480ce9e 100644 --- a/src/technology.c +++ b/src/technology.c @@ -216,10 +216,12 @@ static void technology_save(struct connman_technology *technology) "Tethering.Identifier", technology->tethering_ident); - if (technology->tethering_passphrase) + if (technology->tethering_passphrase) { + char *enc = g_strescape(technology->tethering_passphrase, NULL); g_key_file_set_string(keyfile, identifier, - "Tethering.Passphrase", - technology->tethering_passphrase); + "Tethering.Passphrase", enc); + g_free(enc); + } #ifdef TIZEN_EXT if (technology->type == CONNMAN_SERVICE_TYPE_WIFI) { @@ -446,6 +448,7 @@ static void technology_load(struct connman_technology *technology) gchar *identifier; GError *error = NULL; bool enable, need_saving = false; + char *enc; DBG("technology %p", technology); @@ -502,9 +505,10 @@ static void technology_load(struct connman_technology *technology) technology->tethering_ident = g_key_file_get_string(keyfile, identifier, "Tethering.Identifier", NULL); - technology->tethering_passphrase = g_key_file_get_string(keyfile, + enc = g_key_file_get_string(keyfile, identifier, "Tethering.Passphrase", NULL); - + if (enc) + technology->tethering_passphrase = g_strcompress(enc); #ifdef TIZEN_EXT if (technology->type == CONNMAN_SERVICE_TYPE_WIFI) { unsigned int val = 0; diff --git a/src/tethering.c b/src/tethering.c index e2687b6e..f930a26b 100755 --- a/src/tethering.c +++ b/src/tethering.c @@ -386,7 +386,8 @@ static void setup_tun_interface(unsigned int flags, unsigned change, if ((__connman_inet_modify_address(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET, - server_ip, peer_ip, prefixlen, NULL)) < 0) { + server_ip, peer_ip, prefixlen, NULL, true)) + < 0) { DBG("address setting failed"); return; } diff --git a/src/timeserver.c b/src/timeserver.c index a9a73a29..b2707fad 100755 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -29,6 +29,7 @@ #include <stdlib.h> #include <gweb/gresolv.h> #include <netdb.h> +#include <sys/time.h> #include "connman.h" @@ -40,6 +41,7 @@ static GSList *ts_list = NULL; static char *ts_current = NULL; static int ts_recheck_id = 0; static int ts_backoff_id = 0; +static bool ts_is_synced = false; static GResolv *resolv = NULL; static int resolv_id = 0; @@ -53,10 +55,26 @@ static void resolv_debug(const char *str, void *data) static void ntp_callback(bool success, void *user_data) { + dbus_uint64_t timestamp; + struct timeval tv; + DBG("success %d", success); - if (!success) + __connman_timeserver_set_synced(success); + if (!success) { sync_next(); + return; + } + + if (gettimeofday(&tv, NULL) < 0) { + connman_warn("Failed to get current time"); + } + + timestamp = tv.tv_sec; + connman_dbus_property_changed_basic( + CONNMAN_MANAGER_PATH, + CONNMAN_CLOCK_INTERFACE, "Time", + DBUS_TYPE_UINT64, ×tamp); } static void save_timeservers(char **servers) @@ -290,6 +308,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service) static gboolean ts_recheck(gpointer user_data) { + struct connman_service *service; GSList *ts; ts = __connman_timeserver_get_all(connman_service_get_default()); @@ -305,7 +324,8 @@ static gboolean ts_recheck(gpointer user_data) g_slist_free_full(ts, g_free); - __connman_timeserver_sync(NULL); + service = connman_service_get_default(); + __connman_timeserver_sync(service); return FALSE; } @@ -345,31 +365,16 @@ static void ts_recheck_enable(void) NULL); } -/* - * This function must be called every time the default service changes, the - * service timeserver(s) or gateway changes or the global timeserver(s) changes. - */ -int __connman_timeserver_sync(struct connman_service *default_service) +static void ts_reset(struct connman_service *service) { - struct connman_service *service; char **nameservers; int i; - if (default_service) - service = default_service; - else - service = connman_service_get_default(); - - if (!service) - return -EINVAL; + if (!resolv) + return; -#if !defined TIZEN_EXT - if (service == ts_service) - return -EALREADY; -#endif + __connman_timeserver_set_synced(false); - if (!resolv) - return 0; /* * Before we start creating the new timeserver list we must stop * any ongoing ntp query and server resolution. @@ -385,13 +390,12 @@ int __connman_timeserver_sync(struct connman_service *default_service) g_resolv_flush_nameservers(resolv); nameservers = connman_service_get_nameservers(service); - if (!nameservers) - return -EINVAL; - - for (i = 0; nameservers[i]; i++) - g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); + if (nameservers) { + for (i = 0; nameservers[i]; i++) + g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); - g_strfreev(nameservers); + g_strfreev(nameservers); + } g_slist_free_full(timeservers_list, g_free); @@ -401,15 +405,49 @@ int __connman_timeserver_sync(struct connman_service *default_service) if (!timeservers_list) { DBG("No timeservers set."); - return 0; + return; } ts_recheck_enable(); ts_service = service; timeserver_sync_start(); +} - return 0; +void __connman_timeserver_sync(struct connman_service *service) +{ + if (!service || ts_service == service) + return; + + ts_reset(service); +} + +void __connman_timeserver_conf_update(struct connman_service *service) +{ + if (!service || (ts_service && ts_service != service)) + return; + + ts_reset(service); +} + + +bool __connman_timeserver_is_synced(void) +{ + return ts_is_synced; +} + +void __connman_timeserver_set_synced(bool status) +{ + dbus_bool_t is_synced; + + if (ts_is_synced == status) + return; + + ts_is_synced = status; + is_synced = status; + connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH, + CONNMAN_CLOCK_INTERFACE, "TimeserverSynced", + DBUS_TYPE_BOOLEAN, &is_synced); } static int timeserver_start(struct connman_service *service) @@ -451,7 +489,9 @@ static int timeserver_start(struct connman_service *service) g_strfreev(nameservers); } - return __connman_timeserver_sync(service); + __connman_timeserver_sync(service); + + return 0; } static void timeserver_stop(void) @@ -478,9 +518,13 @@ static void timeserver_stop(void) int __connman_timeserver_system_set(char **servers) { + struct connman_service *service; + save_timeservers(servers); - __connman_timeserver_sync(NULL); + service = connman_service_get_default(); + if (service) + ts_reset(service); return 0; } diff --git a/src/wispr.c b/src/wispr.c index 3b203fba..fb101a1d 100755 --- a/src/wispr.c +++ b/src/wispr.c @@ -96,6 +96,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data); static GHashTable *wispr_portal_list = NULL; +static bool enable_online_to_ready_transition = false; + static void connman_wispr_message_init(struct connman_wispr_message *msg) { DBG(""); @@ -454,10 +456,14 @@ static void portal_manage_status(GWebResult *result, &str)) connman_info("Client-Timezone: %s", str); - free_connman_wispr_portal_context(wp_context); + if (!enable_online_to_ready_transition) + free_connman_wispr_portal_context(wp_context); __connman_service_ipconfig_indicate_state(service, CONNMAN_SERVICE_STATE_ONLINE, type); + + if (enable_online_to_ready_transition) + __connman_service_online_check(service, type, true); } static bool wispr_route_request(const char *address, int ai_family, @@ -761,7 +767,12 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data) wp_context->redirect_url, wp_context); break; + case 300: + case 301: case 302: + case 303: + case 307: + case 308: if (!g_web_supports_tls() || !g_web_result_get_header(result, "Location", &redirect)) { @@ -783,8 +794,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data) goto done; case 400: case 404: - if (__connman_service_online_check_failed(wp_context->service, - wp_context->type) == 0) { + __connman_service_online_check(wp_context->service, + wp_context->type, false); #if defined TIZEN_MAINTAIN_ONLINE if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) { if (retried == 0) { @@ -798,11 +809,6 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data) break; } #endif - wispr_portal_error(wp_context); - free_connman_wispr_portal_context(wp_context); - return false; - } - break; case 505: __connman_agent_request_browser(wp_context->service, @@ -1024,6 +1030,7 @@ int __connman_wispr_start(struct connman_service *service, void __connman_wispr_stop(struct connman_service *service) { + struct connman_wispr_portal *wispr_portal; int index; DBG("service %p", service); @@ -1035,7 +1042,22 @@ void __connman_wispr_stop(struct connman_service *service) if (index < 0) return; - g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index)); + wispr_portal = g_hash_table_lookup(wispr_portal_list, + GINT_TO_POINTER(index)); + if (!wispr_portal) + return; + + if (wispr_portal->ipv4_context) { + if (service == wispr_portal->ipv4_context->service) + g_hash_table_remove(wispr_portal_list, + GINT_TO_POINTER(index)); + } + + if (wispr_portal->ipv6_context) { + if (service == wispr_portal->ipv6_context->service) + g_hash_table_remove(wispr_portal_list, + GINT_TO_POINTER(index)); + } } int __connman_wispr_init(void) @@ -1046,6 +1068,9 @@ int __connman_wispr_init(void) g_direct_equal, NULL, free_connman_wispr_portal); + enable_online_to_ready_transition = + connman_setting_get_bool("EnableOnlineToReadyTransition"); + return 0; } diff --git a/test/monitor-connman b/test/monitor-connman index 8403f913..c6edd760 100755 --- a/test/monitor-connman +++ b/test/monitor-connman @@ -1,6 +1,6 @@ #!/usr/bin/python -import gobject +from gi.repository import GLib import dbus import dbus.mainloop.glib @@ -82,6 +82,6 @@ if __name__ == '__main__': bus.add_match_string("member=Update,interface=net.connman.Notification") bus.add_message_filter(message_filter) - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() mainloop.run() diff --git a/test/monitor-services b/test/monitor-services index d570e5f5..c520a8cd 100755 --- a/test/monitor-services +++ b/test/monitor-services @@ -1,6 +1,6 @@ #!/usr/bin/python -import gobject +from gi.repository import GLib import dbus import dbus.mainloop.glib @@ -102,5 +102,5 @@ if __name__ == '__main__': signal_name="PropertyChanged", path_keyword="path") - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() mainloop.run() diff --git a/test/p2p-on-supplicant b/test/p2p-on-supplicant index 339d5eba..22501fc3 100755 --- a/test/p2p-on-supplicant +++ b/test/p2p-on-supplicant @@ -3,10 +3,9 @@ from os import O_NONBLOCK from sys import stdin, stdout, exit, version_info, argv from fcntl import fcntl, F_GETFL, F_SETFL -import glib +from gi.repository import GLib import dbus import dbus.mainloop.glib -import gobject import argparse WPA_NAME='fi.w1.wpa_supplicant1' @@ -32,7 +31,7 @@ class InputLine: flags = fcntl(stdin.fileno(), F_GETFL) flags |= O_NONBLOCK fcntl(stdin.fileno(), F_SETFL, flags) - glib.io_add_watch(stdin, glib.IO_IN, self.input_cb) + GLib.io_add_watch(stdin, GLib.IO_IN, self.input_cb) self.prompt() @@ -42,7 +41,7 @@ class InputLine: stdout.flush() def input_cb(self, fd, event): - if event != glib.IO_IN: + if event != GLib.IO_IN: return self.line += fd.read(); @@ -610,10 +609,6 @@ def build_args(parser): return command def main(): - if version_info.major != 2: - print('You need to run this under Python 2.x') - exit(1) - parser = argparse.ArgumentParser(description='Connman P2P Test') command_list = build_args(parser) @@ -634,7 +629,7 @@ def main(): bus = dbus.SystemBus() - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() wpa_s = Wpa_s(bus, args.ifname, args.command + params) diff --git a/test/simple-agent b/test/simple-agent index 282785e2..04de3f60 100755 --- a/test/simple-agent +++ b/test/simple-agent @@ -1,6 +1,6 @@ #!/usr/bin/python -import gobject +from gi.repository import GLib import dbus import dbus.service @@ -351,7 +351,7 @@ if __name__ == '__main__': except: "Cannot register vpn agent" - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() mainloop.run() #manager.UnregisterAgent(path) diff --git a/test/test-counter b/test/test-counter index c09aabc1..4c551620 100755 --- a/test/test-counter +++ b/test/test-counter @@ -1,7 +1,8 @@ #!/usr/bin/python +from gi.repository import GLib + import sys -import gobject import dbus import dbus.service @@ -73,7 +74,7 @@ if __name__ == '__main__': manager.RegisterCounter(path, dbus.UInt32(10), dbus.UInt32(period)) - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() mainloop.run() #manager.UnregisterCounter(path) diff --git a/test/test-session b/test/test-session index e45d22b8..112074f1 100755 --- a/test/test-session +++ b/test/test-session @@ -1,15 +1,13 @@ #!/usr/bin/python +from gi.repository import GLib + import sys -import gobject -import string import dbus import dbus.service import dbus.mainloop.glib -import glib - import traceback def extract_list(list): @@ -293,11 +291,11 @@ def main(): app_path = sys.argv[2] bus = dbus.SessionBus() - app_name = "com.example.SessionApplication.%s" % (string.strip(app_path, "/")) + app_name = "com.example.SessionApplication.%s" % (str.strip(app_path, "/")) if sys.argv[1] == "run": name = dbus.service.BusName(app_name, bus) - mainloop = gobject.MainLoop() + mainloop = GLib.MainLoop() app = SessionApplication(bus, app_path, mainloop) @@ -44,74 +44,10 @@ #define CONFIGMAINFILE CONFIGDIR "/connman-vpn.conf" -#define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000 -#define DEFAULT_BROWSER_LAUNCH_TIMEOUT 300 * 1000 - static GMainLoop *main_loop = NULL; static unsigned int __terminated = 0; -static struct { - unsigned int timeout_inputreq; - unsigned int timeout_browserlaunch; -} connman_vpn_settings = { - .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT, - .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT, -}; - -static GKeyFile *load_config(const char *file) -{ - GError *err = NULL; - GKeyFile *keyfile; - - keyfile = g_key_file_new(); - - g_key_file_set_list_separator(keyfile, ','); - - if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { - if (err->code != G_FILE_ERROR_NOENT) { - connman_error("Parsing %s failed: %s", file, - err->message); - } - - g_error_free(err); - g_key_file_free(keyfile); - return NULL; - } - - return keyfile; -} - -static void parse_config(GKeyFile *config, const char *file) -{ - GError *error = NULL; - int timeout; - - if (!config) - return; - - DBG("parsing %s", file); - - timeout = g_key_file_get_integer(config, "General", - "InputRequestTimeout", &error); - if (!error && timeout >= 0) - connman_vpn_settings.timeout_inputreq = timeout * 1000; - - g_clear_error(&error); -} - -static int config_init(const char *file) -{ - GKeyFile *config; - - config = load_config(file); - parse_config(config, file); - if (config) - g_key_file_free(config); - - return 0; -} - static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { @@ -193,7 +129,6 @@ static gchar *option_plugin = NULL; static gchar *option_noplugin = NULL; static bool option_detach = true; static bool option_version = false; -static bool option_routes = false; static bool parse_debug(const char *key, const char *value, gpointer user_data, GError **error) @@ -220,19 +155,17 @@ static GOptionEntry options[] = { { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Don't fork daemon to background" }, - { "routes", 'r', 0, G_OPTION_ARG_NONE, &option_routes, - "Create/delete VPN routes" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; +#if defined TIZEN_EXT bool connman_setting_get_bool(const char *key) { return false; } -#if defined TIZEN_EXT unsigned int connman_setting_get_uint(const char *key) { return 0; @@ -242,7 +175,11 @@ int connman_setting_get_int(const char *key) { return 0; } -#endif + +char *connman_setting_get_string(const char *key) +{ + return NULL; +} char **connman_setting_get_string_list(const char *key) { @@ -254,6 +191,12 @@ unsigned int *connman_setting_get_uint_list(const char *key) return NULL; } +unsigned int connman_timeout_browser_launch(void) +{ + return 0; +} +#endif + /* * This function will be called from generic src/agent.c code so we have * to use connman_ prefix instead of vpn_ one. @@ -263,16 +206,6 @@ unsigned int connman_timeout_input_request(void) return __vpn_settings_get_timeout_inputreq(); } -unsigned int connman_timeout_browser_launch(void) -{ - return connman_vpn_settings.timeout_browserlaunch; -} - -const char *connman_option_get_string(const char *key) -{ - return NULL; -} - int main(int argc, char *argv[]) { GOptionContext *context; @@ -360,7 +293,7 @@ int main(int argc, char *argv[]) __connman_inotify_init(); __connman_agent_init(); - __vpn_provider_init(option_routes); + __vpn_provider_init(); __vpn_manager_init(); __vpn_ipconfig_init(); __vpn_rtnl_init(); diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c index 48894aa5..1e4fcd1f 100755 --- a/vpn/plugins/l2tp.c +++ b/vpn/plugins/l2tp.c @@ -246,6 +246,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); + vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_nameservers(provider, nameservers); diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c index d600e61e..fc6ceff0 100755 --- a/vpn/plugins/openconnect.c +++ b/vpn/plugins/openconnect.c @@ -42,6 +42,8 @@ #include <connman/setting.h> #include <connman/vpn-dbus.h> +#include <openconnect.h> + #include "../vpn-provider.h" #include "../vpn-agent.h" @@ -89,7 +91,6 @@ enum oc_connect_type { static const char *connect_types[] = {"cookie", "cookie_with_userpass", "userpass", "publickey", "pkcs", NULL}; -static const char *protocols[] = { "anyconnect", "nc", "gp", NULL}; struct oc_private_data { struct vpn_provider *provider; @@ -98,21 +99,46 @@ struct oc_private_data { char *dbus_sender; vpn_provider_connect_cb_t cb; void *user_data; + + GThread *cookie_thread; + struct openconnect_info *vpninfo; + int fd_cmd; + int err; + int fd_in; - int out_ch_id; int err_ch_id; - GIOChannel *out_ch; GIOChannel *err_ch; enum oc_connect_type connect_type; - bool interactive; + bool tried_passphrase; }; +typedef void (*request_input_reply_cb_t) (DBusMessage *reply, + void *user_data); + +static int run_connect(struct oc_private_data *data, const char *cookie); +static int request_input_credentials_full( + struct oc_private_data *data, + request_input_reply_cb_t cb, + void *user_data); + static bool is_valid_protocol(const char* protocol) { + int num_protocols; + int i; + struct oc_vpn_proto *protos; + if (!protocol || !*protocol) return false; - return g_strv_contains(protocols, protocol); + num_protocols = openconnect_get_supported_protocols(&protos); + + for (i = 0; i < num_protocols; i++) + if (!strcmp(protos[i].name, protocol)) + break; + + openconnect_free_supported_protocols(protos); + + return i < num_protocols; } static void oc_connect_done(struct oc_private_data *data, int err) @@ -139,11 +165,7 @@ static void close_io_channel(struct oc_private_data *data, GIOChannel *channel) if (!data || !channel) return; - if (data->out_ch == channel) { - id = data->out_ch_id; - data->out_ch = NULL; - data->out_ch_id = 0; - } else if (data->err_ch == channel) { + if (data->err_ch == channel) { id = data->err_ch_id; data->err_ch = NULL; data->err_ch_id = 0; @@ -167,6 +189,9 @@ static void free_private_data(struct oc_private_data *data) connman_info("provider %p", data->provider); + if (data->vpninfo) + openconnect_vpninfo_free(data->vpninfo); + if (vpn_provider_get_plugin_data(data->provider) == data) vpn_provider_set_plugin_data(data->provider, NULL); @@ -175,7 +200,6 @@ static void free_private_data(struct oc_private_data *data) if (data->fd_in > 0) close(data->fd_in); data->fd_in = -1; - close_io_channel(data, data->out_ch); close_io_channel(data, data->err_ch); g_free(data->dbus_sender); @@ -357,6 +381,8 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider) else connman_ipaddress_set_ipv6(ipaddress, addressv6, prefix_len, gateway); + + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_domain(provider, domain); @@ -371,7 +397,7 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider) return VPN_STATE_CONNECT; } -static ssize_t full_write(int fd, const void *buf, size_t len) +static ssize_t full_write(int fd, const char *buf, size_t len) { ssize_t byte_write; @@ -426,37 +452,6 @@ static void oc_died(struct connman_task *task, int exit_code, void *user_data) free_private_data(data); } -static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition, - gpointer user_data) -{ - struct oc_private_data *data; - char *str; - - data = user_data; - - if (data->out_ch != source) - return G_SOURCE_REMOVE; - - if ((condition & G_IO_IN) && - g_io_channel_read_line(source, &str, NULL, NULL, NULL) == - G_IO_STATUS_NORMAL) { - - g_strchomp(str); - - /* Only cookie is printed to stdout */ - vpn_provider_set_string_hide_value(data->provider, - "OpenConnect.Cookie", str); - - g_free(str); - } else if (condition & (G_IO_ERR | G_IO_HUP)) { - connman_info("Out channel termination"); - close_io_channel(data, source); - return G_SOURCE_REMOVE; - } - - return G_SOURCE_CONTINUE; -} - static bool strv_contains_prefix(const char *strv[], const char *str) { int i; @@ -472,56 +467,170 @@ static bool strv_contains_prefix(const char *strv[], const char *str) return false; } -static void clear_provider_credentials(struct vpn_provider *provider) +static void clear_provider_credentials(struct vpn_provider *provider, + bool clear_pkcs_pass) { - const char *keys[] = { "OpenConnect.Username", + const char *keys[] = { "OpenConnect.PKCSPassword", + "OpenConnect.Username", "OpenConnect.Password", - "OpenConnect.PKCSPassword", "OpenConnect.Cookie", NULL }; - int i; + size_t i; connman_info("provider %p", provider); - for (i = 0; keys[i]; i++) { + for (i = !clear_pkcs_pass; keys[i]; i++) { if (!vpn_provider_get_string_immutable(provider, keys[i])) vpn_provider_set_string_hide_value(provider, keys[i], "-"); } } -typedef void (* request_input_reply_cb_t) (DBusMessage *reply, - void *user_data); +static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data, + int level, const char *fmt, ...) +{ + va_list ap; + char *msg; -static int request_input_credentials(struct oc_private_data *data, - request_input_reply_cb_t cb); + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + + connman_debug("%s", msg); + g_free(msg); + + va_end(ap); +} + +/* + * There is no enum / defines for these in openconnect.h, but these values + * are based on the comment for openconnect_validate_peer_cert_vfn. + */ +enum oc_cert_status { + OC_CERT_ACCEPT = 0, + OC_CERT_REJECT = 1 +}; + +struct validate_cert_data { + GMutex mutex; + GCond cond; + const char *reason; + struct oc_private_data *data; + bool processed; + enum oc_cert_status status; +}; + +static gboolean validate_cert(void *user_data) +{ + struct validate_cert_data *cert_data = user_data; + struct oc_private_data *data; + const char *server_cert; + bool allow_self_signed; + + DBG(""); + + g_mutex_lock(&cert_data->mutex); + + data = cert_data->data; + server_cert = vpn_provider_get_string(data->provider, + "OpenConnect.ServerCert"); + allow_self_signed = vpn_provider_get_boolean(data->provider, + "OpenConnect.AllowSelfSignedCert", + false); + if (!allow_self_signed) { + cert_data->status = OC_CERT_REJECT; + } else if (server_cert) { + /* + * Check peer cert hash may return negative values on errors, + * but anything non-zero is acceptable. + */ + cert_data->status = openconnect_check_peer_cert_hash( + data->vpninfo, + server_cert); + } else { + /* + * We could verify this from the agent at this point, and + * release the thread upon reply. + */ + DBG("Server cert hash: %s", + openconnect_get_peer_cert_hash(data->vpninfo)); + vpn_provider_set_string(data->provider, + "OpenConnect.ServerCert", + openconnect_get_peer_cert_hash(data->vpninfo)); + cert_data->status = OC_CERT_ACCEPT; + } + + cert_data->processed = true; + g_cond_signal(&cert_data->cond); + g_mutex_unlock(&cert_data->mutex); + + return G_SOURCE_REMOVE; +} + +static int oc_validate_peer_cert(void *user_data, const char *reason) +{ + struct validate_cert_data data = { .reason = reason, + .data = user_data, + .processed = false }; + + g_cond_init(&data.cond); + g_mutex_init(&data.mutex); + + g_mutex_lock(&data.mutex); + + g_idle_add(validate_cert, &data); + + while (!data.processed) + g_cond_wait(&data.cond, &data.mutex); + + g_mutex_unlock(&data.mutex); + + g_mutex_clear(&data.mutex); + g_cond_clear(&data.cond); + + return data.status; +} + +struct process_form_data { + GMutex mutex; + GCond cond; + struct oc_auth_form *form; + struct oc_private_data *data; + bool processed; + int status; +}; static void request_input_pkcs_reply(DBusMessage *reply, void *user_data) { - struct oc_private_data *data = user_data; - const char *password = NULL; + struct process_form_data *form_data = user_data; + struct oc_private_data *data = form_data->data; + struct oc_form_opt *opt; const char *key; + const char *password = NULL; DBusMessageIter iter, dict; - int err; connman_info("provider %p", data->provider); - if (!reply) + if (!reply) { + data->err = ENOENT; goto err; + } - err = vpn_agent_check_and_process_reply_error(reply, data->provider, - data->task, data->cb, data->user_data); - if (err) { - /* Ensure cb is called only once */ + if ((data->err = vpn_agent_check_and_process_reply_error(reply, + data->provider, + data->task, + data->cb, + data->user_data))) { data->cb = NULL; data->user_data = NULL; goto err; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + data->err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -551,39 +660,44 @@ static void request_input_pkcs_reply(DBusMessage *reply, void *user_data) dbus_message_iter_next(&dict); } - if (data->connect_type != OC_CONNECT_PKCS || !password) + if (!password) goto err; - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take PKCS pass phrase on" - " stdin"); - goto err; + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; + + if (opt->type == OC_FORM_OPT_PASSWORD && + g_str_has_prefix(opt->name, + "openconnect_pkcs")) { + opt->_value = strdup(password); + form_data->status = OC_FORM_RESULT_OK; + data->tried_passphrase = true; + break; + } } - clear_provider_credentials(data->provider); + goto out; - return; err: - oc_connect_done(data, EACCES); + form_data->status = OC_FORM_RESULT_ERR; + +out: + form_data->processed = true; + g_cond_signal(&form_data->cond); + g_mutex_unlock(&form_data->mutex); } static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, - gpointer user_data) + gpointer user_data) { struct oc_private_data *data; const char *auth_failures[] = { - /* Login failed */ - "Got HTTP response: HTTP/1.1 401 Unauthorized", - "Failed to obtain WebVPN cookie", /* Cookie not valid */ "Got inappropriate HTTP CONNECT response: " "HTTP/1.1 401 Unauthorized", /* Invalid cookie */ "VPN service unavailable", - /* Problem with certificates */ - "SSL connection failure", - "Creating SSL connection failed", - "SSL connection cancelled", NULL }; const char *conn_failures[] = { @@ -591,21 +705,8 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, "Failed to open HTTPS connection to", NULL }; - /* Handle both PKCS#12 and PKCS#8 failures */ - const char *pkcs_failures[] = { - "Failed to decrypt PKCS#12 certificate file", - "Failed to decrypt PKCS#8 certificate file", - NULL - }; - /* Handle both PKCS#12 and PKCS#8 requests */ - const char *pkcs_requests[] = { - "Enter PKCS#12 pass phrase", - "Enter PKCS#8 pass phrase", - NULL - }; - const char *server_key_hash = " --servercert"; + const char *server_key_hash = " --servercert "; char *str; - bool close = false; int err = 0; data = user_data; @@ -618,51 +719,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, if ((condition & G_IO_IN)) { gsize len; - int pos; - - if (!data->interactive) { - if (g_io_channel_read_line(source, &str, &len, NULL, - NULL) != G_IO_STATUS_NORMAL) - err = EIO; - else - str[len - 1] = '\0'; - } else { - GIOStatus status; - str = g_try_new0(char, OC_MAX_READBUF_LEN); - if (!str) - return G_SOURCE_REMOVE; - for (pos = 0; pos < OC_MAX_READBUF_LEN - 1 ; ++pos) { - status = g_io_channel_read_chars(source, - str+pos, 1, &len, NULL); - - if (status == G_IO_STATUS_EOF) { - break; - } else if (status != G_IO_STATUS_NORMAL) { - err = EIO; - break; - } - - /* Ignore control chars and digits at start */ - if (!pos && (g_ascii_iscntrl(str[pos]) || - g_ascii_isdigit(str[pos]))) - --pos; - - /* Read zero length or no more to read */ - if (!len || g_io_channel_get_buffer_condition( - source) != G_IO_IN || - str[pos] == '\n') - break; - } - - /* - * When self signed certificates are allowed and server - * SHA1 fingerprint is printed to stderr there is a - * newline char at the end of SHA1 fingerprint. - */ - if (str[pos] == '\n') - str[pos] = '\0'; - } + if (g_io_channel_read_line(source, &str, &len, NULL, + NULL) != G_IO_STATUS_NORMAL) + err = EIO; + else + g_strchomp(str); connman_info("openconnect: %s", str); @@ -687,44 +749,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, vpn_provider_set_string(data->provider, "OpenConnect.ServerCert", - fingerprint); - - /* - * OpenConnect waits for "yes" or "no" as - * response to certificate acceptance request. - */ - if (write_data(data->fd_in, "yes") != 0) - connman_error("openconnect: cannot " - "write answer to certificate " - "accept request"); - + str + strlen(server_key_hash)); } else { connman_warn("Self signed certificate is not " - " allowed"); - - /* - * Close IO channel to avoid deadlock as an - * answer is expected for the certificate - * accept request. - */ - close = true; + "allowed"); err = ECONNREFUSED; } - } else if (strv_contains_prefix(pkcs_failures, str)) { - connman_warn("PKCS failure: %s", str); - close = true; - err = EACCES; - } else if (strv_contains_prefix(pkcs_requests, str)) { - connman_info("PKCS file pass phrase request: %s", str); - err = request_input_credentials(data, - request_input_pkcs_reply); - - if (err != -EINPROGRESS) { - err = EACCES; - close = true; - } else { - err = 0; - } } else if (strv_contains_prefix(auth_failures, str)) { connman_warn("authentication failed: %s", str); err = EACCES; @@ -736,13 +766,14 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, g_free(str); } else if (condition & (G_IO_ERR | G_IO_HUP)) { connman_info("Err channel termination"); - close = true; + close_io_channel(data, source); + return G_SOURCE_REMOVE; } if (err) { switch (err) { case EACCES: - clear_provider_credentials(data->provider); + clear_provider_credentials(data->provider, true); break; case ECONNREFUSED: /* @@ -756,168 +787,278 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition, oc_connect_done(data, err); } - if (close) { - close_io_channel(data, source); - return G_SOURCE_REMOVE; - } - return G_SOURCE_CONTINUE; } -static int run_connect(struct oc_private_data *data) +static gboolean process_auth_form(void *user_data) { - struct vpn_provider *provider; - struct connman_task *task; - const char *vpnhost; - const char *vpncookie = NULL; - const char *username; - const char *password = NULL; - const char *certificate = NULL; - const char *private_key; - const char *setting_str; - bool setting; - bool use_stdout = false; - int fd_out = -1; - int fd_err; - int err = 0; - - if (!data) - return -EINVAL; + struct process_form_data *form_data = user_data; + struct oc_private_data *data = form_data->data; + struct oc_form_opt *opt; + const char *password; - provider = data->provider; - task = data->task; + g_mutex_lock(&form_data->mutex); - connman_info("provider %p task %p", provider, task); + DBG(""); switch (data->connect_type) { - case OC_CONNECT_COOKIE: - vpncookie = vpn_provider_get_string(provider, - "OpenConnect.Cookie"); - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - err = -EACCES; - goto done; - } - - connman_task_add_argument(task, "--cookie-on-stdin", NULL); - break; + case OC_CONNECT_USERPASS: case OC_CONNECT_COOKIE_WITH_USERPASS: - vpncookie = vpn_provider_get_string(provider, - "OpenConnect.Cookie"); - /* No cookie set yet, username and password used first */ - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - username = vpn_provider_get_string(provider, - "OpenConnect.Username"); - password = vpn_provider_get_string(provider, - "OpenConnect.Password"); - if (!username || !password || - !g_strcmp0(username, "-") || - !g_strcmp0(password, "-")) { - err = -EACCES; - goto done; - } + break; + + case OC_CONNECT_PKCS: + password = vpn_provider_get_string(data->provider, + "OpenConnect.PKCSPassword"); - connman_task_add_argument(task, "--cookieonly", NULL); - connman_task_add_argument(task, "--user", username); - connman_task_add_argument(task, "--passwd-on-stdin", - NULL); + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; - /* Use stdout only when cookie is to be read. */ - use_stdout = true; - } else { - connman_task_add_argument(task, "--cookie-on-stdin", - NULL); + if (opt->type == OC_FORM_OPT_PASSWORD && + g_str_has_prefix(opt->name, + "openconnect_pkcs")) + break; } - break; - case OC_CONNECT_USERPASS: - username = vpn_provider_get_string(provider, - "OpenConnect.Username"); - password = vpn_provider_get_string(provider, - "OpenConnect.Password"); - if (!username || !password || !g_strcmp0(username, "-") || - !g_strcmp0(password, "-")) { - err = -EACCES; - goto done; + if (opt) { + if (password && g_strcmp0(password, "-")) { + opt->_value = strdup(password); + data->tried_passphrase = true; + form_data->status = OC_FORM_RESULT_OK; + goto out; + } else { + if (data->tried_passphrase) { + vpn_provider_add_error(data->provider, + VPN_PROVIDER_ERROR_AUTH_FAILED); + clear_provider_credentials( + data->provider, + true); + } + request_input_credentials_full(data, + request_input_pkcs_reply, + form_data); + return G_SOURCE_REMOVE; + } } - connman_task_add_argument(task, "--user", username); - connman_task_add_argument(task, "--passwd-on-stdin", NULL); - break; + /* fall-through */ + + /* + * In case of public key, reaching here means that the + * passphrase previously provided was incorrect. + */ case OC_CONNECT_PUBLICKEY: - certificate = vpn_provider_get_string(provider, - "OpenConnect.ClientCert"); - private_key = vpn_provider_get_string(provider, - "OpenConnect.UserPrivateKey"); + data->err = -EACCES; + clear_provider_credentials(data->provider, true); - if (!certificate || !private_key) { - err = -EACCES; - goto done; - } + /* fall-through */ + default: + form_data->status = OC_FORM_RESULT_ERR; + goto out; + } - connman_task_add_argument(task, "--certificate", certificate); - connman_task_add_argument(task, "--sslkey", private_key); - break; - case OC_CONNECT_PKCS: - certificate = vpn_provider_get_string(provider, - "OpenConnect.PKCSClientCert"); - if (!certificate) { - err = -EACCES; - goto done; + /* + * Form values are released with free(), so always use strdup() + * instead of g_strdup() + */ + for (opt = form_data->form->opts; opt; opt = opt->next) { + if (opt->flags & OC_FORM_OPT_IGNORE) + continue; + + if (opt->type == OC_FORM_OPT_TEXT && + g_str_has_prefix(opt->name, "user")) { + const char *user = vpn_provider_get_string( + data->provider, + "OpenConnect.Username"); + if (user) + opt->_value = strdup(user); + } else if (opt->type == OC_FORM_OPT_PASSWORD) { + const char *pass = vpn_provider_get_string( + data->provider, + "OpenConnect.Password"); + if (pass) + opt->_value = strdup(pass); } + } - connman_task_add_argument(task, "--certificate", certificate); + form_data->status = OC_FORM_RESULT_OK; - password = vpn_provider_get_string(data->provider, - "OpenConnect.PKCSPassword"); - /* Add password only if it is has been set */ - if (!password || !g_strcmp0(password, "-")) - break; +out: + form_data->processed = true; + g_cond_signal(&form_data->cond); + g_mutex_unlock(&form_data->mutex); + + return G_SOURCE_REMOVE; +} + +static int oc_process_auth_form(void *user_data, struct oc_auth_form *form) +{ + struct process_form_data data = { .form = form, + .data = user_data, + .processed = false }; + + DBG(""); + + g_cond_init(&data.cond); + g_mutex_init(&data.mutex); + + g_mutex_lock(&data.mutex); + g_idle_add(process_auth_form, &data); + + while (!data.processed) + g_cond_wait(&data.cond, &data.mutex); + + g_mutex_unlock(&data.mutex); + + g_mutex_clear(&data.mutex); + g_cond_clear(&data.cond); + + return data.status; +} + +static gboolean authenticated(void *user_data) +{ + struct oc_private_data *data = user_data; + int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread)); + + DBG(""); + + data->cookie_thread = NULL; + + if (rv == 0) + rv = run_connect(data, openconnect_get_cookie(data->vpninfo)); + else if (rv < 0) + clear_provider_credentials(data->provider, true); + + openconnect_vpninfo_free(data->vpninfo); + data->vpninfo = NULL; + + if (rv != -EINPROGRESS) { + oc_connect_done(data, data->err ? data->err : rv); + free_private_data(data); + } - connman_task_add_argument(task, "--passwd-on-stdin", NULL); + return G_SOURCE_REMOVE; +} + +static void *obtain_cookie_thread(void *user_data) +{ + struct oc_private_data *data = user_data; + int ret; + + DBG("%p", data->vpninfo); + + ret = openconnect_obtain_cookie(data->vpninfo); + + g_idle_add(authenticated, data); + + return GINT_TO_POINTER(ret); +} + +static int authenticate(struct oc_private_data *data) +{ + const char *cert = NULL; + const char *key = NULL; + const char *urlpath; + const char *vpnhost; + + DBG(""); + + switch (data->connect_type) { + case OC_CONNECT_PKCS: + cert = vpn_provider_get_string(data->provider, + "OpenConnect.PKCSClientCert"); break; + case OC_CONNECT_PUBLICKEY: + cert = vpn_provider_get_string(data->provider, + "OpenConnect.ClientCert"); + key = vpn_provider_get_string(data->provider, + "OpenConnect.UserPrivateKey"); + break; + + case OC_CONNECT_USERPASS: + case OC_CONNECT_COOKIE_WITH_USERPASS: + break; + + default: + return -EINVAL; } - vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost"); + openconnect_init_ssl(); + data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent", + oc_validate_peer_cert, + NULL, + oc_process_auth_form, + oc_progress, + data); + + /* Replicating how openconnect's --usergroup argument works */ + urlpath = vpn_provider_get_string(data->provider, + "OpenConnect.Usergroup"); + if (urlpath) + openconnect_set_urlpath(data->vpninfo, urlpath); + + if (vpn_provider_get_boolean(data->provider, + "OpenConnect.DisableIPv6", false)) + openconnect_disable_ipv6(data->vpninfo); + + vpnhost = vpn_provider_get_string(data->provider, + "OpenConnect.VPNHost"); if (!vpnhost || !*vpnhost) - vpnhost = vpn_provider_get_string(provider, "Host"); + vpnhost = vpn_provider_get_string(data->provider, "Host"); - task_append_config_data(provider, task); + openconnect_set_hostname(data->vpninfo, vpnhost); + + if (cert) + openconnect_set_client_cert(data->vpninfo, cert, key); + + data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo); /* - * To clarify complex situation, if cookie is expected to be printed - * to stdout all other output must go to syslog. But with PKCS all - * output must be caught in order to get message about file decryption - * error. For this reason, the mode has to be interactive as well. + * openconnect_obtain_cookie blocks, so run it in background thread + * instead */ - switch (data->connect_type) { - case OC_CONNECT_COOKIE: - /* fall through */ - case OC_CONNECT_COOKIE_WITH_USERPASS: - /* fall through */ - case OC_CONNECT_USERPASS: - /* fall through */ - case OC_CONNECT_PUBLICKEY: - connman_task_add_argument(task, "--syslog", NULL); + data->cookie_thread = g_thread_try_new("obtain_cookie", + obtain_cookie_thread, + data, NULL); + + if (!data->cookie_thread) + return -EIO; + + return -EINPROGRESS; +} - setting = vpn_provider_get_boolean(provider, +static int run_connect(struct oc_private_data *data, const char *cookie) +{ + struct vpn_provider *provider; + struct connman_task *task; + const char *vpnhost; + int fd_err; + int err = 0; + bool allow_self_signed; + const char *server_cert; + + if (!data || !cookie) + return -EINVAL; + + provider = data->provider; + task = data->task; + + server_cert = vpn_provider_get_string(provider, + "OpenConnect.ServerCert"); + allow_self_signed = vpn_provider_get_boolean(provider, "OpenConnect.AllowSelfSignedCert", false); - setting_str = vpn_provider_get_string(provider, - "OpenConnect.ServerCert"); - /* - * Run in interactive mode if self signed certificates are - * allowed and there is no set server SHA1 fingerprint. - */ - if (setting_str || !setting) - connman_task_add_argument(task, "--non-inter", NULL); - else - data->interactive = true; - break; - case OC_CONNECT_PKCS: - data->interactive = true; - break; - } + DBG("provider %p task %p", provider, task); + + connman_task_add_argument(task, "--cookie-on-stdin", NULL); + + vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost"); + if (!vpnhost || !*vpnhost) + vpnhost = vpn_provider_get_string(provider, "Host"); + + task_append_config_data(provider, task); connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script"); @@ -925,66 +1066,30 @@ static int run_connect(struct oc_private_data *data) connman_task_add_argument(task, (char *)vpnhost, NULL); - err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ? - &fd_out : NULL, &fd_err); + err = connman_task_run(task, oc_died, data, &data->fd_in, + NULL, &fd_err); if (err < 0) { err = -EIO; goto done; } - switch (data->connect_type) { - case OC_CONNECT_COOKIE: - if (write_data(data->fd_in, vpncookie) != 0) { - connman_error("openconnect failed to take cookie on " - "stdin"); - err = -EIO; - } - - break; - case OC_CONNECT_USERPASS: - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take password on " - "stdin"); - err = -EIO; - } - - break; - case OC_CONNECT_COOKIE_WITH_USERPASS: - if (!vpncookie || !g_strcmp0(vpncookie, "-")) { - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take " - "password on stdin"); - err = -EIO; - } - } else { - if (write_data(data->fd_in, vpncookie) != 0) { - connman_error("openconnect failed to take " - "cookie on stdin"); - err = -EIO; - } - } - - break; - case OC_CONNECT_PUBLICKEY: - break; - case OC_CONNECT_PKCS: - if (!password || !g_strcmp0(password, "-")) - break; + if (write_data(data->fd_in, cookie) != 0) { + connman_error("openconnect failed to take cookie on " + "stdin"); + err = -EIO; + } - if (write_data(data->fd_in, password) != 0) { - connman_error("openconnect failed to take PKCS " - "pass phrase on stdin"); + if (!server_cert || !allow_self_signed) { + if (write_data(data->fd_in, + (allow_self_signed ? "yes" : "no"))) { + connman_error("openconnect failed to take certificate " + "acknowledgement on stdin"); err = -EIO; } - - break; } if (err) { - if (fd_out > 0) - close(fd_out); - - if (fd_err > 0) + if (fd_err >= 0) close(fd_err); goto done; @@ -992,22 +1097,6 @@ static int run_connect(struct oc_private_data *data) err = -EINPROGRESS; - if (use_stdout) { - data->out_ch = g_io_channel_unix_new(fd_out); - - /* Use ASCII encoding only */ - if (g_io_channel_set_encoding(data->out_ch, NULL, NULL) != - G_IO_STATUS_NORMAL) { - close_io_channel(data, data->out_ch); - err = -EIO; - } else { - data->out_ch_id = g_io_add_watch(data->out_ch, - G_IO_IN | G_IO_ERR | G_IO_HUP, - (GIOFunc)io_channel_out_cb, - data); - } - } - data->err_ch = g_io_channel_unix_new(fd_err); /* Use ASCII encoding only */ @@ -1022,7 +1111,7 @@ static int run_connect(struct oc_private_data *data) } done: - clear_provider_credentials(data->provider); + clear_provider_credentials(data->provider, err != -EINPROGRESS); return err; } @@ -1119,8 +1208,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) connman_info("provider %p", data->provider); - if (!reply) + if (!reply) { + err = ENOENT; goto err; + } err = vpn_agent_check_and_process_reply_error(reply, data->provider, data->task, data->cb, data->user_data); @@ -1131,8 +1222,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) goto out; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -1224,41 +1317,53 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) switch (data->connect_type) { case OC_CONNECT_COOKIE: - if (!cookie) + if (!cookie) { + err = EACCES; goto err; + } break; case OC_CONNECT_USERPASS: /* fall through */ case OC_CONNECT_COOKIE_WITH_USERPASS: - if (!username || !password) + if (!username || !password) { + err = EACCES; goto err; + } break; case OC_CONNECT_PUBLICKEY: break; // This should not be reached. case OC_CONNECT_PKCS: - if (!pkcspassword) + if (!pkcspassword) { + err = EACCES; goto err; + } break; } - err = run_connect(data); + if (cookie) + err = run_connect(data, cookie); + else + err = authenticate(data); + if (err != -EINPROGRESS) goto err; return; err: - oc_connect_done(data, EACCES); + oc_connect_done(data, err); out: free_private_data(data); } -static int request_input_credentials(struct oc_private_data *data, - request_input_reply_cb_t cb) +static int request_input_credentials_full( + struct oc_private_data *data, + request_input_reply_cb_t cb, + void *user_data) { DBusMessage *message; const char *path; @@ -1299,7 +1404,7 @@ static int request_input_credentials(struct oc_private_data *data, /* * For backwards compatibility add OpenConnect.ServerCert and - * OpenConnect.VPNHost as madnatory only in the default authentication + * OpenConnect.VPNHost as mandatory only in the default authentication * mode. Otherwise. add the fields as informational. These should be * set in provider settings and not to be queried with every connection * attempt. @@ -1343,6 +1448,16 @@ static int request_input_credentials(struct oc_private_data *data, request_input_append_informational, "OpenConnect.PKCSClientCert"); + /* Do not allow to store or retrieve the encrypted PKCS pass */ + vpn_agent_append_allow_credential_storage(&dict, false); + vpn_agent_append_allow_credential_retrieval(&dict, false); + + /* + * Indicate to keep credentials, the PKCS password should not + * affect the credential storing. + */ + vpn_agent_append_keep_credentials(&dict, true); + request_input_append_to_dict(data->provider, &dict, request_input_append_password, "OpenConnect.PKCSPassword"); @@ -1354,7 +1469,7 @@ static int request_input_credentials(struct oc_private_data *data, connman_dbus_dict_close(&iter, &dict); err = connman_agent_queue_message(data->provider, message, - connman_timeout_input_request(), cb, data, agent); + connman_timeout_input_request(), cb, user_data, agent); dbus_message_unref(message); @@ -1366,6 +1481,12 @@ static int request_input_credentials(struct oc_private_data *data, return -EINPROGRESS; } +static int request_input_credentials(struct oc_private_data *data, + request_input_reply_cb_t cb) +{ + return request_input_credentials_full(data, cb, data); +} + static enum oc_connect_type get_authentication_type( struct vpn_provider *provider) { @@ -1395,7 +1516,7 @@ static int oc_connect(struct vpn_provider *provider, const char *dbus_sender, void *user_data) { struct oc_private_data *data; - const char *vpncookie; + const char *vpncookie = NULL; const char *certificate; const char *username; const char *password; @@ -1481,7 +1602,9 @@ static int oc_connect(struct vpn_provider *provider, break; } - return run_connect(data); + if (vpncookie && g_strcmp0(vpncookie, "-")) + return run_connect(data, vpncookie); + return authenticate(data); request_input: err = request_input_credentials(data, request_input_credentials_reply); @@ -1497,6 +1620,8 @@ request_input: static void oc_disconnect(struct vpn_provider *provider) { + struct oc_private_data *data; + connman_info("provider %p", provider); if (!provider) @@ -1508,6 +1633,19 @@ static void oc_disconnect(struct vpn_provider *provider) * agent request to avoid having multiple ones visible. */ connman_agent_cancel(provider); + + data = vpn_provider_get_plugin_data(provider); + + if (!data) + return; + + if (data->cookie_thread) { + char cmd = OC_CMD_CANCEL; + int w = write(data->fd_cmd, &cmd, 1); + if (w != 1) + DBG("Write failed, might be leaking a thread"); + } + } static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile) @@ -1547,7 +1685,7 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code) switch (exit_code) { case 2: /* Cookie has failed */ - clear_provider_credentials(provider); + clear_provider_credentials(provider, false); return VPN_PROVIDER_ERROR_LOGIN_FAILED; case 1: /* fall through */ @@ -1557,7 +1695,8 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code) } static int oc_route_env_parse(struct vpn_provider *provider, const char *key, - int *family, unsigned long *idx, enum vpn_provider_route_type *type) + int *family, unsigned long *idx, + enum vpn_provider_route_type *type) { char *end; const char *start; diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c index ef0bf782..8c8d3162 100755 --- a/vpn/plugins/openvpn.c +++ b/vpn/plugins/openvpn.c @@ -51,7 +51,6 @@ #include "../vpn-agent.h" #include "vpn.h" -#include "../vpn.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -84,6 +83,9 @@ struct { { "OpenVPN.ConfigFile", "--config", 1 }, { "OpenVPN.DeviceType", NULL, 1 }, { "OpenVPN.Verb", "--verb", 1 }, + { "OpenVPN.Ping", "--ping", 1}, + { "OpenVPN.PingExit", "--ping-exit", 1}, + { "OpenVPN.RemapUsr1", "--remap-usr1", 1}, }; struct ov_private_data { @@ -297,6 +299,7 @@ static int ov_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway); connman_ipaddress_set_peer(ipaddress, peer); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); if (nameserver_list) { @@ -506,16 +509,13 @@ static int run_connect(struct ov_private_data *data, #endif /* - * Disable client restarts because we can't handle this at the - * moment. The problem is that when OpenVPN decides to switch + * Disable client restarts with TCP because we can't handle this at + * the moment. The problem is that when OpenVPN decides to switch * from CONNECTED state to RECONNECTING and then to RESOLVE, * it is not possible to do a DNS lookup. The DNS server is * not accessible through the tunnel anymore and so we end up * trying to resolve the OpenVPN servers address. - */ - connman_task_add_argument(task, "--ping-restart", "0"); - - /* + * * Disable connetion retrying when OpenVPN is connected over TCP. * With TCP OpenVPN attempts to handle reconnection silently without * reporting the error back when establishing a connection or @@ -525,8 +525,24 @@ static int run_connect(struct ov_private_data *data, * including DNS. */ option = vpn_provider_get_string(provider, "OpenVPN.Proto"); - if (option && g_str_has_prefix(option, "tcp")) + if (option && g_str_has_prefix(option, "tcp")) { + option = vpn_provider_get_string(provider, "OpenVPN.PingExit"); + if (!option) + connman_task_add_argument(task, "--ping-restart", "0"); + connman_task_add_argument(task, "--connect-retry-max", "1"); + /* Apply defaults for --ping and --ping-exit only with UDP protocol. */ + } else { + /* Apply default of 10 second interval for ping if omitted. */ + option = vpn_provider_get_string(provider, "OpenVPN.Ping"); + if (!option) + connman_task_add_argument(task, "--ping", "10"); + + /* Apply default of 60 seconds for ping exit if omitted. */ + option = vpn_provider_get_string(provider, "OpenVPN.PingExit"); + if (!option) + connman_task_add_argument(task, "--ping-exit", "60"); + } err = connman_task_run(task, ov_died, data, NULL, NULL, NULL); if (err < 0) { diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c index 5fc861e4..4a704bb1 100755 --- a/vpn/plugins/pptp.c +++ b/vpn/plugins/pptp.c @@ -54,15 +54,18 @@ enum { OPT_STRING = 1, OPT_BOOL = 2, + OPT_PPTP_ONLY = 3, }; struct { const char *cm_opt; const char *pptp_opt; - const char *vpnc_default; + const char *pptp_default; int type; } pptp_options[] = { { "PPTP.User", "user", NULL, OPT_STRING }, + { "PPTP.IdleWait", "--idle-wait", NULL, OPT_PPTP_ONLY}, + { "PPTP.MaxEchoWait", "--max-echo-wait", NULL, OPT_PPTP_ONLY}, { "PPPD.EchoFailure", "lcp-echo-failure", "0", OPT_STRING }, { "PPPD.EchoInterval", "lcp-echo-interval", "0", OPT_STRING }, { "PPPD.Debug", "debug", NULL, OPT_STRING }, @@ -204,6 +207,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider) connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); vpn_provider_set_nameservers(provider, nameservers); @@ -436,7 +440,9 @@ static int run_connect(struct vpn_provider *provider, vpn_provider_connect_cb_t cb, void *user_data, const char *username, const char *password) { - const char *opt_s, *host; + GString *pptp_opt_s; + const char *opt_s; + const char *host; char *str; int err, i; @@ -450,16 +456,11 @@ static int run_connect(struct vpn_provider *provider, DBG("username %s password %p", username, password); host = vpn_provider_get_string(provider, "Host"); - str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2", - PPTP, host); - if (!str) { - connman_error("can not allocate memory"); - err = -ENOMEM; - goto done; - } - connman_task_add_argument(task, "pty", str); - g_free(str); + /* Create PPTP options for pppd "pty" */ + pptp_opt_s = g_string_new(NULL); + g_string_append_printf(pptp_opt_s, "%s %s --nolaunchpppd --loglevel 2", + PPTP, host); connman_task_add_argument(task, "nodetach", NULL); connman_task_add_argument(task, "lock", NULL); @@ -474,7 +475,7 @@ static int run_connect(struct vpn_provider *provider, opt_s = vpn_provider_get_string(provider, pptp_options[i].cm_opt); if (!opt_s) - opt_s = pptp_options[i].vpnc_default; + opt_s = pptp_options[i].pptp_default; if (!opt_s) continue; @@ -485,8 +486,15 @@ static int run_connect(struct vpn_provider *provider, else if (pptp_options[i].type == OPT_BOOL) pptp_write_bool_option(task, pptp_options[i].pptp_opt, opt_s); + else if (pptp_options[i].type == OPT_PPTP_ONLY) + g_string_append_printf(pptp_opt_s, " %s %s", + pptp_options[i].pptp_opt, opt_s); } + str = g_string_free(pptp_opt_s, FALSE); + connman_task_add_argument(task, "pty", str); + g_free(str); + connman_task_add_argument(task, "plugin", SCRIPTDIR "/libppp-plugin.so"); diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c index c0b29778..b89c2223 100755 --- a/vpn/plugins/vpn.c +++ b/vpn/plugins/vpn.c @@ -65,7 +65,7 @@ struct vpn_data { struct vpn_driver_data { const char *name; const char *program; - struct vpn_driver *vpn_driver; + const struct vpn_driver *vpn_driver; struct vpn_provider_driver provider_driver; }; @@ -471,61 +471,26 @@ exist_err: return ret; } -static gboolean is_numeric(const char *str) +static gid_t get_gid(const char *group_name) { - gint i; - - if(!str || !(*str)) - return false; - - for(i = 0; str[i] ; i++) { - if(!g_ascii_isdigit(str[i])) - return false; - } - - return true; -} - -static gint get_gid(const char *group_name) -{ - gint gid = -1; struct group *grp; - if(!group_name || !(*group_name)) - return gid; - - if (is_numeric(group_name)) { - gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10); - grp = getgrgid(group_id); - } else { - grp = getgrnam(group_name); - } - + grp = vpn_util_get_group(group_name); if (grp) - gid = grp->gr_gid; + return grp->gr_gid; - return gid; + return -1; } -static gint get_uid(const char *user_name) +static uid_t get_uid(const char *user_name) { - gint uid = -1; struct passwd *pw; - if(!user_name || !(*user_name)) - return uid; - - if (is_numeric(user_name)) { - uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10); - pw = getpwuid(user_id); - } else { - pw = getpwnam(user_name); - } - + pw = vpn_util_get_passwd(user_name); if (pw) - uid = pw->pw_uid; + return pw->pw_uid; - return uid; + return -1; } static gint get_supplementary_gids(gchar **groups, gid_t **gid_list) @@ -558,8 +523,8 @@ static gint get_supplementary_gids(gchar **groups, gid_t **gid_list) static void vpn_task_setup(gpointer user_data) { struct vpn_plugin_data *data; - gint uid; - gint gid; + uid_t uid; + gid_t gid; gid_t *gid_list = NULL; size_t gid_list_size; const gchar *user; @@ -682,7 +647,7 @@ static int vpn_connect(struct vpn_provider *provider, vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) { ret = vpn_driver_data->vpn_driver->connect(provider, - NULL, NULL, NULL, NULL, NULL); + NULL, NULL, cb, dbus_sender, user_data); if (ret) { stop_vpn(provider); goto exist_err; @@ -717,7 +682,6 @@ static int vpn_connect(struct vpn_provider *provider, goto exist_err; } - #if defined TIZEN_EXT if(vpn_driver_data->vpn_driver->set_event_cb) vpn_driver_data->vpn_driver->set_event_cb(vpn_event, provider); @@ -868,7 +832,7 @@ static int vpn_route_env_parse(struct vpn_provider *provider, const char *key, return 0; } -int vpn_register(const char *name, struct vpn_driver *vpn_driver, +int vpn_register(const char *name, const struct vpn_driver *vpn_driver, const char *program) { struct vpn_driver_data *data; diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h index 893e9a1d..7956ffaf 100755 --- a/vpn/plugins/vpn.h +++ b/vpn/plugins/vpn.h @@ -65,7 +65,7 @@ struct vpn_driver { enum vpn_provider_route_type *type); }; -int vpn_register(const char *name, struct vpn_driver *driver, +int vpn_register(const char *name, const struct vpn_driver *driver, const char *program); void vpn_unregister(const char *provider_name); void vpn_died(struct connman_task *task, int exit_code, void *user_data); diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c index 8350fc3c..d11b9111 100755 --- a/vpn/plugins/vpnc.c +++ b/vpn/plugins/vpnc.c @@ -30,6 +30,10 @@ #include <stdio.h> #include <net/if.h> #include <linux/if_tun.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> #include <glib.h> @@ -50,6 +54,7 @@ #include "../vpn.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#define PID_PATH_ROOT "/var/run/user" enum { OPT_STRING = 1, @@ -240,6 +245,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider *provider) } connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway); + connman_ipaddress_set_p2p(ipaddress, true); vpn_provider_set_ipaddress(provider, ipaddress); g_free(address); @@ -430,14 +436,49 @@ static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition, return G_SOURCE_CONTINUE; } +static char *create_pid_path(const char *user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + char *uid_str; + char *pid_path = NULL; + int mode = S_IRWXU|S_IRWXG; + gid_t gid; + + if (!user || !*user) + return NULL; + + if (vpn_settings_is_system_user(user)) + return NULL; + + pwd = vpn_util_get_passwd(user); + uid_str = g_strdup_printf("%d", pwd->pw_uid); + + grp = vpn_util_get_group(group); + gid = grp ? grp->gr_gid : pwd->pw_gid; + + pid_path = g_build_filename(PID_PATH_ROOT, uid_str, "vpnc", "pid", + NULL); + if (vpn_util_create_path(pid_path, pwd->pw_uid, gid, mode)) { + g_free(pid_path); + pid_path = NULL; + } + + g_free(uid_str); + + return pid_path; +} + static int run_connect(struct vc_private_data *data) { struct vpn_provider *provider; struct connman_task *task; + struct vpn_plugin_data *plugin_data; const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username", "VPNC.Xauth.Password", NULL}; const char *if_name; const char *option; + char *pid_path; int err; int fd_in; int fd_err; @@ -473,6 +514,20 @@ static int run_connect(struct vc_private_data *data) connman_task_add_argument(task, "--ifmode", "tun"); } + plugin_data = vpn_settings_get_vpn_plugin_config("vpnc"); + + option = vpn_settings_get_binary_user(plugin_data); + if (option) { + pid_path = create_pid_path(option, + vpn_settings_get_binary_group( + plugin_data)); + if (pid_path) + connman_task_add_argument(task, "--pid-file", + pid_path); + + g_free(pid_path); + } + connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script"); option = vpn_provider_get_string(provider, "VPNC.Debug"); @@ -619,8 +674,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) DBG("provider %p", data->provider); - if (!reply) + if (!reply) { + err = ENOENT; goto err; + } err = vpn_agent_check_and_process_reply_error(reply, data->provider, data->task, data->cb, data->user_data); @@ -631,8 +688,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) return; } - if (!vpn_agent_check_reply_has_dict(reply)) + if (!vpn_agent_check_reply_has_dict(reply)) { + err = ENOENT; goto err; + } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &dict); @@ -687,17 +746,22 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data) dbus_message_iter_next(&dict); } - if (!secret || !username || !password) + if (!secret || !username || !password) { + vpn_provider_indicate_error(data->provider, + VPN_PROVIDER_ERROR_AUTH_FAILED); + err = EACCES; goto err; + } - err = run_connect(data); - if (err != -EINPROGRESS) + /* vpn_provider.c:connect_cb() expects positive errors */ + err = -run_connect(data); + if (err != EINPROGRESS) goto err; return; err: - vc_connect_done(data, EACCES); + vc_connect_done(data, err); } static int request_input_credentials(struct vc_private_data *data, diff --git a/vpn/plugins/wireguard.c b/vpn/plugins/wireguard.c index e7ffbd1a..6ec18eb4 100644 --- a/vpn/plugins/wireguard.c +++ b/vpn/plugins/wireguard.c @@ -260,6 +260,8 @@ static int parse_address(const char *address, const char *gateway, err = -EINVAL; } + connman_ipaddress_set_p2p(*ipaddress, true); + g_strfreev(tokens); if (err) connman_ipaddress_free(*ipaddress); diff --git a/vpn/vpn-config.c b/vpn/vpn-config.c index 97e072c6..e412b89c 100755 --- a/vpn/vpn-config.c +++ b/vpn/vpn-config.c @@ -265,7 +265,7 @@ static int load_provider(GKeyFile *keyfile, const char *group, #if !defined TIZEN_EXT DBG("invalid values host %s domain %s", host, domain); #else - DBG("invalid values host %s domain %s name %s", host, domain, name); + DBG("invalid configuration: no host specified"); #endif err = -EINVAL; goto err; @@ -594,3 +594,18 @@ char **__vpn_config_get_string_list(GKeyFile *key_file, return strlist; } + +bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name, + const char *key, bool default_value) +{ + GError *error = NULL; + bool val; + + val = g_key_file_get_boolean(key_file, group_name, key, &error); + if (error) { + g_error_free(error); + return default_value; + } + + return val; +} diff --git a/vpn/vpn-dbus.conf b/vpn/vpn-dbus.conf index 476be1c1..db3d49e5 100755 --- a/vpn/vpn-dbus.conf +++ b/vpn/vpn-dbus.conf @@ -4,7 +4,11 @@ <policy user="root"> <allow own="net.connman.vpn"/> <allow send_destination="net.connman.vpn"/> + <allow send_interface="net.connman.vpn.Agent"/> </policy> + <policy at_console="true"> + <allow send_destination="net.connman.vpn"/> + </policy> <policy user="network_fw"> <allow own="net.connman.vpn"/> <allow send_destination="net.connman.vpn"/> diff --git a/vpn/vpn-ipconfig.c b/vpn/vpn-ipconfig.c index c4fa5482..825b43c4 100755 --- a/vpn/vpn-ipconfig.c +++ b/vpn/vpn-ipconfig.c @@ -211,7 +211,7 @@ int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family) if (family == AF_INET) return connman_inet_set_address(ipconfig->index, - ipconfig->address); + ipconfig->address); else if (family == AF_INET6) return connman_inet_set_ipv6_address(ipconfig->index, ipconfig->address); @@ -282,7 +282,10 @@ static struct vpn_ipconfig *create_ipv6config(int index) return NULL; } + connman_ipaddress_set_p2p(ipv6config->address, true); + ipv6config->system = connman_ipaddress_alloc(AF_INET6); + connman_ipaddress_set_p2p(ipv6config->system, true); DBG("ipconfig %p", ipv6config); @@ -314,7 +317,10 @@ struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family) return NULL; } + connman_ipaddress_set_p2p(ipconfig->address, true); + ipconfig->system = connman_ipaddress_alloc(AF_INET); + connman_ipaddress_set_p2p(ipconfig->system, true); DBG("ipconfig %p", ipconfig); diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c index 6a6b6617..8092b5d6 100755 --- a/vpn/vpn-provider.c +++ b/vpn/vpn-provider.c @@ -44,7 +44,6 @@ static DBusConnection *connection; static GHashTable *provider_hash; static GSList *driver_list; static int configuration_count; -static bool handle_routes; struct vpn_route { int family; @@ -71,6 +70,7 @@ struct vpn_provider { char *host; char *domain; int family; + bool do_split_routing; GHashTable *routes; struct vpn_provider_driver *driver; void *driver_data; @@ -91,6 +91,7 @@ struct vpn_provider { void *plugin_data; unsigned int auth_error_counter; unsigned int conn_error_counter; + unsigned int signal_watch; }; static void append_properties(DBusMessageIter *iter, @@ -366,22 +367,8 @@ static void set_user_networks(struct vpn_provider *provider, GSList *networks) static void del_routes(struct vpn_provider *provider) { GHashTableIter hash; - gpointer value, key; g_hash_table_iter_init(&hash, provider->user_routes); - while (handle_routes && g_hash_table_iter_next(&hash, - &key, &value)) { - struct vpn_route *route = value; - if (route->family == AF_INET6) { - unsigned char prefixlen = atoi(route->netmask); - connman_inet_del_ipv6_network_route(provider->index, - route->network, - prefixlen); - } else - connman_inet_del_host_route(provider->index, - route->network); - } - g_hash_table_remove_all(provider->user_routes); g_slist_free_full(provider->user_networks, free_route); provider->user_networks = NULL; @@ -404,6 +391,16 @@ static void send_value(const char *path, const char *key, const char *value) &str); } +static void send_value_boolean(const char *path, const char *key, + dbus_bool_t value) +{ + connman_dbus_property_changed_basic(path, + VPN_CONNECTION_INTERFACE, + key, + DBUS_TYPE_BOOLEAN, + &value); +} + static gboolean provider_send_changed(gpointer data) { struct vpn_provider *provider = data; @@ -474,6 +471,11 @@ static bool compare_network_lists(GSList *a, GSList *b) return true; } +static const char *bool2str(bool value) +{ + return value ? "true" : "false"; +} + static int set_provider_property(struct vpn_provider *provider, const char *name, DBusMessageIter *value, int type) { @@ -500,10 +502,17 @@ static int set_provider_property(struct vpn_provider *provider, del_routes(provider); provider->user_networks = networks; set_user_networks(provider, provider->user_networks); + send_routes(provider, provider->user_routes, "UserRoutes"); + } else if (g_str_equal(name, "SplitRouting")) { + dbus_bool_t split_routing; + + if (type != DBUS_TYPE_BOOLEAN) + return -EINVAL; - if (!handle_routes) - send_routes(provider, provider->user_routes, - "UserRoutes"); + dbus_message_iter_get_basic(value, &split_routing); + + DBG("property %s value %s ", name, bool2str(split_routing)); + vpn_provider_set_boolean(provider, name, split_routing, false); } else { const char *str; @@ -576,8 +585,13 @@ static DBusMessage *set_properties(DBusMessageIter *iter, DBusMessage *msg, dbus_message_iter_recurse(&entry, &value); type = dbus_message_iter_get_arg_type(&value); - /* Ignore and report back all invalid property types */ - if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_ARRAY) { + switch (type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_BOOLEAN: + break; + default: + /* Ignore and report back all invalid property types */ invalid = append_to_gstring(invalid, key); continue; } @@ -702,8 +716,7 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, del_routes(provider); - if (!handle_routes) - send_routes(provider, provider->user_routes, name); + send_routes(provider, provider->user_routes, name); } else if (vpn_provider_get_string(provider, name)) { err = vpn_provider_set_string(provider, name, NULL); switch (err) { @@ -841,6 +854,8 @@ static void provider_resolv_host_addr(struct vpn_provider *provider) void __vpn_provider_append_properties(struct vpn_provider *provider, DBusMessageIter *iter) { + dbus_bool_t split_routing; + if (provider->host) connman_dbus_dict_append_basic(iter, "Host", DBUS_TYPE_STRING, &provider->host); @@ -852,6 +867,10 @@ void __vpn_provider_append_properties(struct vpn_provider *provider, if (provider->type) connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &provider->type); + + split_routing = provider->do_split_routing; + connman_dbus_dict_append_basic(iter, "SplitRouting", DBUS_TYPE_BOOLEAN, + &split_routing); } int __vpn_provider_append_user_route(struct vpn_provider *provider, @@ -984,7 +1003,7 @@ static GSList *get_routes(gchar **networks) static int provider_load_from_keyfile(struct vpn_provider *provider, GKeyFile *keyfile) { - gsize idx = 0; + gsize idx; gchar **settings; gchar *key, *value; gsize length, num_user_networks; @@ -997,28 +1016,26 @@ static int provider_load_from_keyfile(struct vpn_provider *provider, return -ENOENT; } - while (idx < length) { + for (idx = 0; idx < length; idx++) { key = settings[idx]; - if (key) { - if (g_str_equal(key, "Networks")) { - networks = __vpn_config_get_string_list(keyfile, - provider->identifier, - key, - &num_user_networks, + if (!key) + continue; + + if (g_str_equal(key, "Networks")) { + networks = __vpn_config_get_string_list(keyfile, + provider->identifier,key, + &num_user_networks, NULL); + provider->user_networks = get_routes(networks); + } else { + value = __vpn_config_get_string(keyfile, + provider->identifier, key, NULL); - provider->user_networks = get_routes(networks); - - } else { - value = __vpn_config_get_string(keyfile, - provider->identifier, - key, NULL); - vpn_provider_set_string(provider, key, - value); - g_free(value); - } + + vpn_provider_set_string(provider, key, value); + g_free(value); } - idx += 1; } + g_strfreev(settings); g_strfreev(networks); @@ -1135,6 +1152,7 @@ static int vpn_provider_save(struct vpn_provider *provider) "Host", provider->host); g_key_file_set_string(keyfile, provider->identifier, "VPN.Domain", provider->domain); + if (provider->user_networks) { gchar **networks; gsize network_count; @@ -1725,6 +1743,7 @@ static void append_properties(DBusMessageIter *iter, GHashTableIter hash; gpointer value, key; dbus_bool_t immutable; + dbus_bool_t split_routing; connman_dbus_dict_open(iter, &dict); @@ -1752,6 +1771,10 @@ static void append_properties(DBusMessageIter *iter, connman_dbus_dict_append_basic(&dict, "Immutable", DBUS_TYPE_BOOLEAN, &immutable); + split_routing = provider->do_split_routing; + connman_dbus_dict_append_basic(&dict, "SplitRouting", + DBUS_TYPE_BOOLEAN, &split_routing); + if (provider->family == AF_INET) connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4, provider); @@ -1776,8 +1799,7 @@ static void append_properties(DBusMessageIter *iter, while (g_hash_table_iter_next(&hash, &key, &value)) { struct vpn_setting *setting = value; - if (!setting->hide_value && - setting->value) + if (!setting->hide_value && setting->value) connman_dbus_dict_append_basic(&dict, key, DBUS_TYPE_STRING, &setting->value); @@ -1806,55 +1828,6 @@ static void connection_added_signal(struct vpn_provider *provider) dbus_message_unref(signal); } -static bool check_host(char **hosts, char *host) -{ - int i; - - if (!hosts) - return false; - - for (i = 0; hosts[i]; i++) { - if (g_strcmp0(hosts[i], host) == 0) - return true; - } - - return false; -} - -static void provider_append_routes(gpointer key, gpointer value, - gpointer user_data) -{ - struct vpn_route *route = value; - struct vpn_provider *provider = user_data; - int index = provider->index; - - if (!handle_routes) - return; - - /* - * If the VPN administrator/user has given a route to - * VPN server, then we must discard that because the - * server cannot be contacted via VPN tunnel. - */ - if (check_host(provider->host_ip, route->network)) { - DBG("Discarding VPN route to %s via %s at index %d", - route->network, route->gateway, index); - return; - } - - if (route->family == AF_INET6) { - unsigned char prefix_len = atoi(route->netmask); - - connman_inet_add_ipv6_network_route(index, route->network, - route->gateway, - prefix_len); - } else { - connman_inet_add_network_route(index, route->network, - route->gateway, - route->netmask); - } -} - static int set_connected(struct vpn_provider *provider, bool connected) { @@ -1871,18 +1844,8 @@ static int set_connected(struct vpn_provider *provider, __vpn_ipconfig_address_add(ipconfig, provider->family); - if (handle_routes) - __vpn_ipconfig_gateway_add(ipconfig, provider->family); - provider_indicate_state(provider, VPN_PROVIDER_STATE_READY); - - g_hash_table_foreach(provider->routes, provider_append_routes, - provider); - - g_hash_table_foreach(provider->user_routes, - provider_append_routes, provider); - } else { provider_indicate_state(provider, VPN_PROVIDER_STATE_DISCONNECT); @@ -1949,10 +1912,61 @@ int vpn_provider_indicate_error(struct vpn_provider *provider, return 0; } +static gboolean provider_property_changed(DBusConnection *conn, + DBusMessage *message, void *user_data) +{ + DBusMessageIter iter; + DBusMessageIter value; + struct vpn_provider *provider = user_data; + const char *key; + + if (!dbus_message_iter_init(message, &iter)) + return TRUE; + + dbus_message_iter_get_basic(&iter, &key); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + DBG("provider %p key %s", provider, key); + + if (g_str_equal(key, "SplitRouting")) { + dbus_bool_t split_routing; + + if (dbus_message_iter_get_arg_type(&value) != + DBUS_TYPE_BOOLEAN) + goto out; + + dbus_message_iter_get_basic(&value, &split_routing); + + DBG("property %s value %s", key, bool2str(split_routing)); + + /* + * Even though this is coming from connmand, signal the value + * for other components listening to the changes via VPN API + * only. provider.c will skip setting the same value in order + * to avoid signaling loop. This is needed for ensuring that + * all components using VPN API will be informed about the + * correct status of SplitRouting. Especially when loading the + * services after a crash, for instance. + */ + vpn_provider_set_boolean(provider, "SplitRouting", + split_routing, true); + } + +out: + return TRUE; +} + static int connection_unregister(struct vpn_provider *provider) { DBG("provider %p path %s", provider, provider->path); + if (provider->signal_watch) { + g_dbus_remove_watch(connection, provider->signal_watch); + provider->signal_watch = 0; + } + if (!provider->path) return -EALREADY; @@ -1967,6 +1981,8 @@ static int connection_unregister(struct vpn_provider *provider) static int connection_register(struct vpn_provider *provider) { + char *connmand_vpn_path; + DBG("provider %p path %s", provider, provider->path); if (provider->path) @@ -1980,6 +1996,18 @@ static int connection_register(struct vpn_provider *provider) connection_methods, connection_signals, NULL, provider, NULL); + connmand_vpn_path = g_strdup_printf("%s/service/vpn_%s", CONNMAN_PATH, + provider->identifier); + + provider->signal_watch = g_dbus_add_signal_watch(connection, + CONNMAN_SERVICE, connmand_vpn_path, + CONNMAN_SERVICE_INTERFACE, + PROPERTY_CHANGED, + provider_property_changed, + provider, NULL); + + g_free(connmand_vpn_path); + return 0; } @@ -2017,6 +2045,7 @@ static void provider_initialize(struct vpn_provider *provider) provider->domain = NULL; provider->identifier = NULL; provider->immutable = false; + provider->do_split_routing = false; provider->user_networks = NULL; provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_route); @@ -2112,9 +2141,12 @@ static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile, return NULL; } - if (provider_register(provider) == 0) + if (!provider_register(provider)) { connection_register(provider); + connection_added_signal(provider); + } } + return provider; } @@ -2203,6 +2235,7 @@ int __vpn_provider_create(DBusMessage *msg) GSList *networks = NULL; char *ident; int err; + dbus_bool_t split_routing = false; dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &array); @@ -2229,6 +2262,11 @@ int __vpn_provider_create(DBusMessage *msg) g_str_equal(key, "Domain")) dbus_message_iter_get_basic(&value, &domain); break; + case DBUS_TYPE_BOOLEAN: + if (g_str_equal(key, "SplitRouting")) + dbus_message_iter_get_basic(&value, + &split_routing); + break; case DBUS_TYPE_ARRAY: if (g_str_equal(key, "UserRoutes")) networks = get_user_networks(&value); @@ -2266,6 +2304,7 @@ int __vpn_provider_create(DBusMessage *msg) provider->domain = g_strdup(domain); provider->name = g_strdup(name); provider->type = g_strdup(type); + provider->do_split_routing = split_routing; if (provider_register(provider) == 0) vpn_provider_load(provider); @@ -2594,6 +2633,10 @@ static int set_string(struct vpn_provider *provider, g_free(provider->domain); provider->domain = g_strdup(value); send_value(provider->path, "Domain", provider->domain); + } else if (g_str_equal(key, "SplitRouting")) { + connman_warn("VPN SplitRouting value attempted to set as " + "string, is boolean"); + return -EINVAL; } else { struct vpn_setting *setting; bool replace = true; @@ -2680,6 +2723,25 @@ const char *vpn_provider_get_string(struct vpn_provider *provider, return setting->value; } +int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key, + bool value, bool force_change) +{ + DBG("provider %p key %s", provider, key); + + if (g_str_equal(key, "SplitRouting")) { + if (provider->do_split_routing == value && !force_change) + return -EALREADY; + + DBG("SplitRouting set to %s", bool2str(value)); + + provider->do_split_routing = value; + send_value_boolean(provider->path, key, + provider->do_split_routing); + } + + return 0; +} + bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key, bool default_value) { @@ -2952,11 +3014,8 @@ int vpn_provider_append_route(struct vpn_provider *provider, break; } - if (!handle_routes) { - if (route->netmask && route->gateway && - route->network) - provider_schedule_changed(provider); - } + if (route->netmask && route->gateway && route->network) + provider_schedule_changed(provider); return 0; } @@ -3015,6 +3074,12 @@ void vpn_provider_driver_unregister(struct vpn_provider_driver *driver) if (provider && provider->driver && g_strcmp0(provider->driver->name, driver->name) == 0) { + /* + * Cancel VPN agent request to avoid segfault at + * shutdown as the callback, if set can point to a + * function in the plugin that is to be removed. + */ + connman_agent_cancel(provider); provider->driver = NULL; } } @@ -3093,7 +3158,7 @@ void vpn_provider_clear_address(struct vpn_provider *provider, int family) DBG("ipv6 %s/%d", address, len); connman_inet_clear_ipv6_address(provider->index, - address, len); + provider->prev_ipv6_addr); connman_ipaddress_free(provider->prev_ipv6_addr); provider->prev_ipv6_addr = NULL; @@ -3183,14 +3248,12 @@ static void remove_unprovisioned_providers(void) g_strfreev(providers); } -int __vpn_provider_init(bool do_routes) +int __vpn_provider_init(void) { int err; DBG(""); - handle_routes = do_routes; - err = connman_agent_driver_register(&agent_driver); if (err < 0) { connman_error("Cannot register agent driver for %s", diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h index 0275d51a..f7fa8591 100755 --- a/vpn/vpn-provider.h +++ b/vpn/vpn-provider.h @@ -83,6 +83,9 @@ const char *vpn_provider_get_string(struct vpn_provider *provider, const char *key); bool vpn_provider_get_string_immutable(struct vpn_provider *provider, const char *key); +int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key, + bool value, + bool force_change); bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key, bool default_value); @@ -118,6 +121,7 @@ const char *vpn_provider_get_save_group(struct vpn_provider *provider); const char *vpn_provider_get_name(struct vpn_provider *provider); const char *vpn_provider_get_host(struct vpn_provider *provider); const char *vpn_provider_get_path(struct vpn_provider *provider); +const char *vpn_provider_get_ident(struct vpn_provider *provider); unsigned int vpn_provider_get_authentication_errors( struct vpn_provider *provider); diff --git a/vpn/vpn-settings.c b/vpn/vpn-settings.c index 0eca2bc8..e78e5019 100644 --- a/vpn/vpn-settings.c +++ b/vpn/vpn-settings.c @@ -2,7 +2,7 @@ * ConnMan VPN daemon settings * * Copyright (C) 2012-2013 Intel Corporation. All rights reserved. - * Copyright (C) 2018-2019 Jolla Ltd. All rights reserved. + * Copyright (C) 2018-2020 Jolla Ltd. All rights reserved. * Contact: jussi.laakkonen@jolla.com * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,9 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <pwd.h> +#include <unistd.h> +#include <sys/types.h> #include <connman/log.h> @@ -37,11 +40,13 @@ static struct { char *binary_user; char *binary_group; char **binary_supplementary_groups; + char **system_binary_users; } connman_vpn_settings = { .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT, .binary_user = NULL, .binary_group = NULL, .binary_supplementary_groups = NULL, + .system_binary_users = NULL, }; struct vpn_plugin_data { @@ -52,6 +57,58 @@ struct vpn_plugin_data { GHashTable *plugin_hash = NULL; +bool vpn_settings_is_system_user(const char *user) +{ + struct passwd *pwd; + struct passwd *system_pwd; + int i; + + /* + * The username is not set = override should not be used. This is the + * case after the override is reset. + */ + if (!user) + return true; + + DBG("check user \"%s\"", user); + + /* + * Ignore errors if no entry was found. Treat as system user to + * prevent using an invalid override. + */ + pwd = vpn_util_get_passwd(user); + if (!pwd) + return true; + + if (!connman_vpn_settings.system_binary_users) { + DBG("no binary users set"); + + /* + * Check if the user is root, or the uid equals to process + * effective uid. + */ + return !pwd->pw_uid || pwd->pw_uid == geteuid(); + } + + /* Root set as user or the effective user id */ + if (!pwd->pw_uid || pwd->pw_uid == geteuid()) + return true; + + for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) { + const char *system_user = + connman_vpn_settings.system_binary_users[i]; + + system_pwd = vpn_util_get_passwd(system_user); + if (!system_pwd) + continue; + + if (pwd->pw_uid == system_pwd->pw_uid) + return true; + } + + return false; +} + const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data) { if (data && data->binary_user) @@ -129,6 +186,9 @@ static void parse_config(GKeyFile *config, const char *file) connman_vpn_settings.binary_supplementary_groups = get_string_list( config, VPN_GROUP, "SupplementaryGroups"); + connman_vpn_settings.system_binary_users = get_string_list( + config, VPN_GROUP, + "SystemBinaryUsers"); } struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name) @@ -245,6 +305,7 @@ void __vpn_settings_cleanup() g_free(connman_vpn_settings.binary_user); g_free(connman_vpn_settings.binary_group); g_strfreev(connman_vpn_settings.binary_supplementary_groups); + g_strfreev(connman_vpn_settings.system_binary_users); if (plugin_hash) { g_hash_table_destroy(plugin_hash); diff --git a/vpn/vpn-util.c b/vpn/vpn-util.c new file mode 100644 index 00000000..9ef14d38 --- /dev/null +++ b/vpn/vpn-util.c @@ -0,0 +1,224 @@ +/* + * ConnMan VPN daemon utils + * + * Copyright (C) 2020 Jolla Ltd. All rights reserved. + * Copyright (C) 2020 Open Mobile Platform LLC. + * Contact: jussi.laakkonen@jolla.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> + +#include <glib/gstdio.h> + +#include <connman/log.h> + +#include "vpn.h" + +static bool is_string_digits(const char *str) +{ + int i; + + if (!str || !*str) + return false; + + for (i = 0; str[i]; i++) { + if (!g_ascii_isdigit(str[i])) + return false; + } + + return true; +} + +static uid_t get_str_id(const char *username) +{ + if (!username || !*username) + return 0; + + return (uid_t)g_ascii_strtoull(username, NULL, 10); +} + +struct passwd *vpn_util_get_passwd(const char *username) +{ + struct passwd *pwd; + uid_t uid; + + if (!username || !*username) + return NULL; + + if (is_string_digits(username)) { + uid = get_str_id(username); + pwd = getpwuid(uid); + } else { + pwd = getpwnam(username); + } + + return pwd; +} + +struct group *vpn_util_get_group(const char *groupname) +{ + struct group *grp; + gid_t gid; + + if (!groupname || !*groupname) + return NULL; + + if (is_string_digits(groupname)) { + gid = get_str_id(groupname); + grp = getgrgid(gid); + } else { + grp = getgrnam(groupname); + } + + return grp; +} + +/* + * These prefixes are used for checking if the requested path for + * vpn_util_create_path() is acceptable. Allow only prefixes meant for run-time + * or temporary use to limit the access to any system resources. + * + * VPN core and plugins would need to create only temporary dirs for the + * run-time use. The requested dirs can be created for a specific user when + * running a VPN plugin as a different user and thus, user specific run dir is + * allowed and limitation to access any other system dir is restricted. + */ +static const char *allowed_prefixes[] = { "/var/run/connman-vpn/", + "/var/run/user/", "/tmp/", NULL }; + +static int is_path_allowed(const char *path) +{ + int err = -EPERM; + int i; + + if (!path || !*path || !g_path_is_absolute(path)) + return -EINVAL; + + if (g_strrstr(path, "..") || g_strrstr(path, "./")) + return -EPERM; + + for (i = 0; allowed_prefixes[i]; i++) { + if (g_str_has_prefix(path, allowed_prefixes[i])) { + const char *suffix = path + + strlen(allowed_prefixes[i]); + + /* + * Don't allow plain prefixes, an additional dir must + * be included after the prexix in the requested path. + */ + if (suffix && *suffix != G_DIR_SEPARATOR && + g_strrstr(suffix, + G_DIR_SEPARATOR_S)) { + DBG("allowed %s, has suffix %s", path, suffix); + err = 0; + } + + break; + } + } + + return err; +} + +int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode) +{ + mode_t old_umask; + char *dir_p; + int err; + + err = is_path_allowed(path); + if (err) + return err; + + dir_p = g_path_get_dirname(path); + if (!dir_p) + return -ENOMEM; + + err = g_unlink(dir_p); + if (err) + err = -errno; + + switch (err) { + case 0: + /* Removed */ + case -ENOENT: + /* Did not exist */ + break; + case -EACCES: + /* + * Cannot get write access to the containing directory, check + * if the path exists. + */ + if (!g_file_test(dir_p, G_FILE_TEST_EXISTS)) + goto out; + + /* If the dir does not exist new one cannot be created */ + if (!g_file_test(dir_p, G_FILE_TEST_IS_DIR)) + goto out; + + /* Do a chmod as the dir exists */ + /* fallthrough */ + case -EISDIR: + /* Exists as dir, just chmod and change owner */ + err = g_chmod(dir_p, mode); + if (err) { + connman_warn("chmod %s failed, err %d", dir_p, err); + err = -errno; + } + + goto chown; + default: + /* Any other error that is not handled here */ + connman_warn("remove %s failed, err %d", dir_p, err); + goto out; + } + + /* Set dir creation mask to correspond to the mode */ + old_umask = umask(~mode & 0777); + + DBG("mkdir %s", dir_p); + err = g_mkdir_with_parents(dir_p, mode); + + umask(old_umask); + + if (err) { + connman_warn("mkdir %s failed, err %d", dir_p, err); + err = -errno; + goto out; + } + +chown: + if (uid && grp) { + err = chown(dir_p, uid, grp); + if (err) { + err = -errno; + connman_warn("chown %s failed for %d/%d, err %d", + dir_p, uid, grp, err); + } + } + +out: + g_free(dir_p); + + return err; +} + @@ -88,7 +88,6 @@ int __vpn_provider_create_from_config(GHashTable *settings, int __vpn_provider_set_string_immutable(struct vpn_provider *provider, const char *key, const char *value); DBusMessage *__vpn_provider_get_connections(DBusMessage *msg); -const char *vpn_provider_get_ident(struct vpn_provider *provider); struct vpn_provider *__vpn_provider_lookup(const char *identifier); int __vpn_provider_indicate_state(struct vpn_provider *provider, enum vpn_provider_state state); @@ -100,7 +99,7 @@ int __vpn_provider_disconnect(struct vpn_provider *provider); int __vpn_provider_remove(const char *path); int __vpn_provider_delete(struct vpn_provider *provider); void __vpn_provider_cleanup(void); -int __vpn_provider_init(bool handle_routes); +int __vpn_provider_init(); #include "vpn-rtnl.h" @@ -115,10 +114,12 @@ int __vpn_rtnl_send(const void *buf, size_t len); int __vpn_config_init(void); void __vpn_config_cleanup(void); -char *__vpn_config_get_string(GKeyFile *key_file, - const char *group_name, const char *key, GError **error); -char **__vpn_config_get_string_list(GKeyFile *key_file, - const char *group_name, const char *key, gsize *length, GError **error); +char *__vpn_config_get_string(GKeyFile *key_file, const char *group_name, + const char *key, GError **error); +char **__vpn_config_get_string_list(GKeyFile *key_file, const char *group_name, + const char *key, gsize *length, GError **error); +bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name, + const char *key, bool default_value); int __vpn_settings_init(const char *file); void __vpn_settings_cleanup(void); @@ -135,3 +136,8 @@ const char * vpn_settings_get_binary_user(struct vpn_plugin_data *data); const char * vpn_settings_get_binary_group(struct vpn_plugin_data *data); char ** vpn_settings_get_binary_supplementary_groups( struct vpn_plugin_data *data); +bool vpn_settings_is_system_user(const char *user); + +struct passwd *vpn_util_get_passwd(const char *username); +struct group *vpn_util_get_group(const char *groupname); +int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode); |