Merge tag 'upstream/1.40' into tizen. 79/271279/4 submit/tizen/20220224.061208
authorNiraj Kumar Goit <niraj.g@samsung.com>
Thu, 17 Feb 2022 16:13:13 +0000 (21:43 +0530)
committerNiraj Kumar Goit <niraj.g@samsung.com>
Wed, 23 Feb 2022 15:30:32 +0000 (21:00 +0530)
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:
.mailmap
AUTHORS
ChangeLog
Makefile.am
Makefile.plugins
README
configure.ac
doc/clock-api.txt
doc/connman.conf.5.in
doc/vpn-connection-api.txt
gdbus/watch.c
gdhcp/client.c
gsupplicant/supplicant.c
include/inet.h
include/ipaddress.h
include/network.h
include/option.h [deleted file]
include/provider.h
include/service.h
include/setting.h
packaging/connman.spec
plugins/bluetooth.c
plugins/dundee.c
plugins/ethernet.c
plugins/iwd.c
plugins/loopback.c
plugins/neard.c
plugins/ofono.c
plugins/vpn.c
plugins/wifi.c
src/bridge.c
src/clock.c
src/connection.c
src/connman.h
src/dbus.c
src/device.c
src/dhcp.c
src/dhcpv6.c
src/dns-systemd-resolved.c
src/dnsproxy.c
src/inet.c
src/ipaddress.c
src/ipconfig.c
src/iptables.c
src/main.c
src/main.conf
src/manager.c
src/network.c
src/peer.c
src/provider.c
src/rtnl.c
src/service.c
src/session.c
src/shared/util.c
src/technology.c
src/tethering.c
src/timeserver.c
src/wispr.c
test/monitor-connman
test/monitor-services
test/p2p-on-supplicant
test/simple-agent
test/test-counter
test/test-session
vpn/main.c
vpn/plugins/l2tp.c
vpn/plugins/openconnect.c
vpn/plugins/openvpn.c
vpn/plugins/pptp.c
vpn/plugins/vpn.c
vpn/plugins/vpn.h
vpn/plugins/vpnc.c
vpn/plugins/wireguard.c
vpn/vpn-config.c
vpn/vpn-dbus.conf
vpn/vpn-ipconfig.c
vpn/vpn-provider.c
vpn/vpn-provider.h
vpn/vpn-settings.c
vpn/vpn-util.c [new file with mode: 0644]
vpn/vpn.h

index 1e374a6..ef44bd6 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -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>
diff --git a/AUTHORS b/AUTHORS
index e03a071..438d45f 100755 (executable)
--- a/AUTHORS
+++ b/AUTHORS
@@ -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>
index dedc1fe..69207d2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index 14d6ca1..e8662e4 100644 (file)
@@ -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
index 004bbe9..a0c17cf 100755 (executable)
@@ -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
 
diff --git a/README b/README
index e911bc2..b8154e6 100755 (executable)
--- a/README
+++ b/README
@@ -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
index 791e2a1..220d62b 100644 (file)
@@ -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")
index 6818f5a..a7fdf55 100755 (executable)
@@ -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.
index a90c229..2e06b3e 100644 (file)
@@ -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.
index ec55788..6e6293e 100755 (executable)
@@ -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]
 
index 1ca3c4b..8fa76cd 100755 (executable)
@@ -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;
 }
 
index bdaa882..cc0379e 100755 (executable)
@@ -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;
index 9ecaf06..c76c142 100755 (executable)
@@ -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;
        }
index 09f8454..579f7f7 100644 (file)
@@ -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);
index 3655ca8..652db0f 100755 (executable)
@@ -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,
index 9f5bb1f..4d09dff 100755 (executable)
@@ -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 (executable)
index 5e97ed4..0000000
+++ /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 */
index b585665..1f12099 100755 (executable)
@@ -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);
index acd8852..041949f 100755 (executable)
@@ -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;
index 3625f3e..f141bf6 100755 (executable)
@@ -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);
 
index ff92bd6..8fbb03d 100644 (file)
@@ -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
index 704d216..a8383e7 100755 (executable)
@@ -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;
index b5420ac..57571ec 100755 (executable)
@@ -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;
 
index 4dda80c..766f8e4 100644 (file)
@@ -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("");
 
index bf6a2c2..194a99d 100644 (file)
@@ -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)
index 28a59c9..3809d8f 100755 (executable)
@@ -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 &&
index 69586df..45effd4 100755 (executable)
@@ -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;
 
index 36873d5..f0bd3c5 100755 (executable)
@@ -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;
 
index 5668c00..d708d1f 100755 (executable)
@@ -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;
        }
 }
 
index 26b988e..bcb9344 100755 (executable)
@@ -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>
 #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)
index cd2d9ce..df19a6a 100755 (executable)
@@ -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;
 
index 40729b2..58a52c0 100755 (executable)
@@ -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);
 
index 6036db3..33f6103 100755 (executable)
@@ -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;
index e92f2b1..18c4fe0 100755 (executable)
@@ -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,
index d80a46c..c454a58 100755 (executable)
@@ -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,
index 0c3eea9..5116900 100755 (executable)
@@ -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;
index 951836f..5cef038 100644 (file)
@@ -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);
index 4c07c76..ba54b89 100755 (executable)
@@ -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;
index 5fe306c..912ab3f 100644 (file)
@@ -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;
                        }
index 7956e7f..18dc648 100755 (executable)
@@ -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;
index e0ef7db..268dc51 100644 (file)
@@ -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;
index d63d95c..201d834 100755 (executable)
@@ -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;
 }
index 7d4be73..4a0e4ad 100755 (executable)
@@ -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;
        }
 
index 2ee9485..90a296e 100755 (executable)
@@ -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;
index 772f7b8..13e26d4 100755 (executable)
 #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);
index f761da7..e734ecf 100755 (executable)
@@ -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
index 4f8306a..0000f78 100755 (executable)
@@ -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);
 
index 6849c1b..7046137 100755 (executable)
@@ -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;
 }
 
index 2102f11..bad5c84 100755 (executable)
@@ -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);
index c437c91..195ae22 100755 (executable)
@@ -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;
index 7c8c68b..40ede4f 100644 (file)
@@ -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;
index 19056a8..411b617 100755 (executable)
@@ -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)
index 6000b6d..69adba9 100644 (file)
@@ -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);
 
index 73c24ae..bda2d2b 100755 (executable)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "src/shared/util.h"
 
index f16ee38..7480ce9 100644 (file)
@@ -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;
index e2687b6..f930a26 100755 (executable)
@@ -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;
        }
index a9a73a2..b2707fa 100755 (executable)
@@ -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, &timestamp);
 }
 
 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;
 }
index 3b203fb..fb101a1 100755 (executable)
@@ -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;
 }
 
index 8403f91..c6edd76 100755 (executable)
@@ -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()
index d570e5f..c520a8c 100755 (executable)
@@ -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()
index 339d5eb..22501fc 100755 (executable)
@@ -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)
 
index 282785e..04de3f6 100755 (executable)
@@ -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)
index c09aabc..4c55162 100755 (executable)
@@ -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)
index e45d22b..112074f 100755 (executable)
@@ -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)
 
index ae9c945..e4f4a10 100755 (executable)
 
 #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();
index 48894aa..1e4fcd1 100755 (executable)
@@ -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);
 
index d600e61..fc6ceff 100755 (executable)
@@ -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;
index ef0bf78..8c8d316 100755 (executable)
@@ -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) {
index 5fc861e..4a704bb 100755 (executable)
 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");
 
index c0b2977..b89c222 100755 (executable)
@@ -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;
index 893e9a1..7956ffa 100755 (executable)
@@ -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);
index 8350fc3..d11b911 100755 (executable)
 #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,
index e7ffbd1..6ec18eb 100644 (file)
@@ -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);
index 97e072c..e412b89 100755 (executable)
@@ -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;
+}
index 476be1c..db3d49e 100755 (executable)
@@ -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"/>
index c4fa548..825b43c 100755 (executable)
@@ -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);
 
index 6a6b661..8092b5d 100755 (executable)
@@ -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",
index 0275d51..f7fa859 100755 (executable)
@@ -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);
index 0eca2bc..e78e501 100644 (file)
@@ -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 (file)
index 0000000..9ef14d3
--- /dev/null
@@ -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;
+}
+
index e68b8ad..94cee25 100755 (executable)
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -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);