Imported Upstream version 1.41 86/289886/1 upstream/1.41
authorAnjali Nijhara <a.nijhara@samsung.com>
Wed, 15 Mar 2023 06:29:53 +0000 (11:59 +0530)
committerAnjali Nijhara <a.nijhara@samsung.com>
Wed, 15 Mar 2023 06:31:07 +0000 (12:01 +0530)
Change-Id: Ida2a62a81c771f833353d1a9a77604e6f2b06b88

46 files changed:
AUTHORS
ChangeLog
README
acinclude.m4
client/agent.c
client/commands.c
configure.ac
doc/connman.conf.5.in
doc/technology-api.txt
doc/vpn-agent-api.txt
doc/vpn-connection-api.txt
gdhcp/client.c
gsupplicant/gsupplicant.h
gsupplicant/supplicant.c
include/technology.h
plugins/bluetooth.c
plugins/ethernet.c
plugins/gadget.c
plugins/iwd.c
plugins/neard.c
plugins/vpn.c
plugins/wifi.c
src/agent.c
src/config.c
src/dbus.c
src/dnsproxy.c
src/inet.c
src/ipconfig.c
src/main.c
src/main.conf
src/manager.c
src/network.c
src/rtnl.c
src/service.c
src/technology.c
src/wispr.c
tools/ip6tables-test.c
tools/iptables-test.c
unit/test-iptables.c
vpn/plugins/l2tp.c
vpn/plugins/openconnect.c
vpn/plugins/openvpn.c
vpn/plugins/pptp.c
vpn/vpn-provider.c
vpn/vpn-provider.h
vpn/vpn-rtnl.c

diff --git a/AUTHORS b/AUTHORS
index 438d45f..3ed0985 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -145,7 +145,7 @@ André Draszik <andre.draszik@jci.com>
 Chris Novakovic <chris@chrisn.me.uk>
 Ryan Schaefer <ryan.schaefer@flukenetworks.com>
 Nicolas Cornu <n.cornu@overkiz.com>
-Rahul Jain <rahul.jain@samsung.com
+Rahul Jain <rahul.jain@samsung.com>
 Benoît Monin <benoit.monin@gmx.fr>
 Jussi Laakkonen <jussi.laakkonen@jolla.com>
 Vivien Henriet <v.henriet@overkiz.com>
@@ -171,3 +171,8 @@ Gabriel FORTE <gforte@wyplay.com>
 Colin Wee <cwee@tesla.com>
 Valery Kashcheev <v.kascheev@omp.ru>
 Alyssa Ross <hi@alyssa.is>
+Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+Lukáš Karas <lukas.karas@centrum.cz>
+Michael Nazzareno Trimarchi <michael@amarulasolutions.com>
+Christian Taedcke <christian.taedcke@lemonbeat.com>
+Matthias Gerstner <mgerstner@suse.de>
index 69207d2..11566a5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+ver 1.41:
+       Fix issue with RTNL netlink message alignment.
+       Fix issue with dnsproxy and timeout for TCP feature.
+       Fix issue with dnsproxy and busy loop in TCP server.
+       Fix issue with WiFi connection with no passphrase.
+       Add support for wpa_supplicant and WPA3-SAE functionality.
+       Add support for D-Bus ObjectManager interface.
+
 ver 1.40:
        Fix issue with handling WiFi disconnecting status.
        Fix issue with handling WiFi auto-connect and iwd backend.
diff --git a/README b/README
index b8154e6..e3268c8 100644 (file)
--- a/README
+++ b/README
@@ -408,8 +408,17 @@ from ipv4.connman.net (for IPv4 connectivity) and ipv6.connman.net
 (for IPv6 connectivity). The used URL looks like this
 http://ipv{4|6}.connman.net/online/status.html
 
+When an online check request fails, another one is triggered after a
+longer interval. The intervals follow the square series of numbers
+in a specific range, by default [1, 12], corresponding to the following
+intervals, in seconds: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 and 144.
+
 See connman.conf(5) for the EnableOnlineCheck option, if you need to
 disable the feature.
+It is also possible to specify other URLs via OnlineCheckIPv4URL and
+OnlineCheckIPv6URL options.
+The range of intervals between two online check requests can be fine-tuned
+via OnlineCheckInitialInterval and OnlineCheckMaxInterval options.
 
 During the online check procedure, ConnMan will temporarily install
 a host route to both the ipv4.connman.net and ipv6.connman.net so that
@@ -453,3 +462,7 @@ send a (empty) message from your email account to
 
 Mailing list archive:
        https://lore.kernel.org/connman
+
+IRC:
+       ircs://irc.oftc.net:6697/#connman (for SSL)
+       irc://irc.oftc.net:6667/#connman (for non-SSL)
index 9e8e0dc..262465d 100644 (file)
@@ -21,7 +21,9 @@ AC_DEFUN([COMPILER_FLAGS], [
                CFLAGS+=" -Wdeclaration-after-statement"
                CFLAGS+=" -Wmissing-declarations"
                CFLAGS+=" -Wredundant-decls"
-               CFLAGS+=" -Wcast-align"
+                if ( $CC -v 2>/dev/null | grep "gcc version" ); then
+                        CFLAGS+=" -Wcast-align"
+                fi
                CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
        fi
 ])
index 1cad3e0..94ace7c 100644 (file)
@@ -100,6 +100,8 @@ static struct agent_input_data vpnagent_input_handler[] = {
          request_input_string_return },
        { "OpenConnect.VPNHost", false, "OpenConnect VPN server? ",
          request_input_string_return },
+       { "OpenConnect.SecondPassword", false, "VPN one-time password? ",
+         request_input_string_return },
        { "Username", false, "VPN username? ", request_input_string_return },
        { "Password", false, "VPN password? ", request_input_string_return },
        { },
index 94c375d..53cc14c 100644 (file)
@@ -550,20 +550,23 @@ struct tether_properties {
        int ssid_result;
        int passphrase_result;
        int set_tethering;
+       int freq_result;
 };
 
 static int tether_update(struct tether_properties *tether)
 {
        int ret;
 
-       if (tether->ssid_result == 0 && tether->passphrase_result == 0) {
+       if (tether->ssid_result == 0 && tether->passphrase_result == 0 &&
+                       tether->freq_result == 0) {
                ret = tether_set("wifi", tether->set_tethering);
                g_free(tether);
                return ret;
        }
 
        if (tether->ssid_result != -EINPROGRESS &&
-                       tether->passphrase_result != -EINPROGRESS) {
+                       tether->passphrase_result != -EINPROGRESS &&
+                       tether->freq_result != -EINPROGRESS) {
                g_free(tether);
                return 0;
        }
@@ -603,9 +606,25 @@ static int tether_set_passphrase_return(DBusMessageIter *iter, int errnum,
        return tether_update(tether);
 }
 
-static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
+static int tether_set_freq_return(DBusMessageIter *iter, int errnum,
+                                 const char *error, void *user_data)
 {
-       struct tether_properties *tether = g_new(struct tether_properties, 1);
+       struct tether_properties *tether = user_data;
+
+       if (!error) {
+               fprintf(stdout, "Wifi access point frequency set\n");
+               tether->freq_result = 0;
+       } else {
+               fprintf(stderr, "Error setting wifi frequency: %s\n", error);
+               tether->freq_result = -EINVAL;
+       }
+
+       return tether_update(tether);
+}
+
+static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering, int freq)
+{
+       struct tether_properties *tether = g_new0(struct tether_properties, 1);
 
        tether->set_tethering = set_tethering;
 
@@ -621,8 +640,17 @@ static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
                        tether_set_passphrase_return, tether,
                        "TetheringPassphrase", DBUS_TYPE_STRING, &passphrase);
 
+       if (freq > 0) {
+               tether->freq_result =__connmanctl_dbus_set_property(connection,
+                               "/net/connman/technology/wifi",
+                               "net.connman.Technology",
+                               tether_set_freq_return, tether,
+                               "TetheringFreq", DBUS_TYPE_INT32, &freq);
+       }
+
        if (tether->ssid_result != -EINPROGRESS &&
-                       tether->passphrase_result != -EINPROGRESS) {
+                       tether->passphrase_result != -EINPROGRESS &&
+                       tether->freq_result != -EINPROGRESS) {
                g_free(tether);
                return -ENXIO;
        }
@@ -638,24 +666,30 @@ static int cmd_tether(char *args[], int num, struct connman_option *options)
        if (num < 3)
                return -EINVAL;
 
-       passphrase = args[num - 1];
-       ssid = args[num - 2];
-
        set_tethering = parse_boolean(args[2]);
 
        if (strcmp(args[1], "wifi") == 0) {
+               int freq = 0;
 
-               if (num > 5)
+               if (num > 6)
                        return -E2BIG;
 
-               if (num == 5 && set_tethering == -1)
+               if (num >= 5 && set_tethering == -1)
                        return -EINVAL;
 
                if (num == 4)
                        set_tethering = -1;
 
+               if (num == 6) {
+                       freq = atoi(args[num - 1]);
+                       num --;
+               }
+
+               passphrase = args[num - 1];
+               ssid = args[num - 2];
+
                if (num > 3)
-                       return tether_set_ssid(ssid, passphrase, set_tethering);
+                       return tether_set_ssid(ssid, passphrase, set_tethering, freq);
        }
 
        if (num > 3)
@@ -1425,7 +1459,6 @@ static void monitor_del(char *interface)
        int i;
        char *rule;
 
-
        for (i = 0; monitor[i].interface; i++) {
                if (g_strcmp0(interface, monitor[i].interface) == 0) {
                        if (monitor[i].enabled == false)
@@ -1811,7 +1844,6 @@ static int session_connect_cb(DBusMessageIter *iter, int errnum,
        return -EINPROGRESS;
 }
 
-
 static int session_connect(void)
 {
        return __connmanctl_dbus_method_call(connection, "net.connman",
@@ -2768,7 +2800,7 @@ static const struct {
          "Disables given technology or offline mode",
          lookup_technology_offline },
        { "tether", "<technology> on|off\n"
-                   "            wifi [on|off] <ssid> <passphrase> ",
+                   "            wifi [on|off] <ssid> <passphrase> [<freq>] ",
                                          NULL,            cmd_tether,
          "Enable, disable tethering, set SSID and passphrase for wifi",
          lookup_tether },
index b55d248..a573cef 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(connman, 1.40)
+AC_INIT(connman, 1.41)
 
 AC_CONFIG_MACRO_DIR([m4])
 
index 2e06b3e..82cceb7 100644 (file)
@@ -167,13 +167,16 @@ transitioned to ONLINE state.
 If this setting is false, the default service will remain in READY state.
 Default value is true.
 .TP
+.BI OnlineCheckIPv4URL= url, OnlineCheckIPv6URL= url
+Urls (IPv4 and IPv6 respectively) used during the online status check.
+Please refer to the README for more detailed information.
+Default values are http://ipv4.connman.net/online/status.html and
+http://ipv6.connman.net/online/status.html respectively.
+.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.
+Please refer to the README for more detailed information.
+Default values are 1 and 12 respectively.
 .TP
 .BI EnableOnlineToReadyTransition=true\ \fR|\fB\ false
 WARNING: Experimental feature!!!
index f22e9b2..cdf3039 100644 (file)
@@ -100,3 +100,10 @@ Properties boolean Powered [readwrite]
                        This property is only valid for the WiFi technology,
                        and is then mapped to the WPA pre-shared key clients
                        will have to use in order to establish a connection.
+
+               int TetheringFreq [readwrite]
+
+                       The tethering access point frequency
+
+                       This property is only valid for the WiFi technology and it's
+                       optional. Default frequency is 2412 Mhz.
index ffa6fad..ecc8faa 100644 (file)
@@ -85,6 +85,10 @@ Fields               string Username
                        Return the OpenConnect cookie value that is used for
                        authenticating the VPN session.
 
+               string OpenConnect.Group
+
+                       Choose authentication login group.
+
                string OpenConnect.PKCSClientCert
 
                        Informational field containing a PKCS#1/PKCS#8/PKCS#12
@@ -96,12 +100,21 @@ Fields             string Username
                        Password for decrypting PKCS#8/PKCS#12 client
                        certificate.
 
+               string OpenConnect.SecondPassword
+
+                       Second factor password for authentication.
+
                string OpenConnect.ServerCert
 
                        Return the OpenConnect server hash used to identify
                        the final server after possible web authentication
                        logins, selections and redirections.
 
+               boolean OpenConnect.UseSecondPassword
+
+                       Indicates that second factor password is used
+                       for selected authentication group.
+
                string OpenConnect.VPNHost
 
                        Return the final VPN server to use after possible
index 6e6293e..2d3e007 100644 (file)
@@ -235,6 +235,19 @@ Properties string State [readonly]
                        The VPN server activated route. These routes
                        are pushed to connman by VPN server.
 
+               string AuthErrorLimit
+
+                       This value defines the amount of authentication errors
+                       that are allowed before informing VPN agent to clear
+                       the credentials in case there was a previous successful
+                       VPN connection made within one hour. This is to be used
+                       with providers that allow only one login from one
+                       account at a time to prevent clearing of credentials
+                       when networks are rapidly changed. This value is used
+                       as an integer and if unset this default to "1" for all
+                       except OpenVPN that uses value "10". Setting value "0"
+                       disables the feature for the provider.
+
                There can be other properties also but as the VPN
                technologies are so different, they have different
                kind of options that they need, so not all options
index c7b85e5..3016dfc 100644 (file)
@@ -2381,6 +2381,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                dhcp_client->retry_times = 0;
 
                option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
+               if (!option)
+                       return TRUE;
+
                dhcp_client->server_ip = get_be32(option);
                dhcp_client->requested_ip = ntohl(packet.yiaddr);
 
@@ -2442,6 +2445,8 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
                        if (dhcp_client->state == REBOOTING) {
                                option = dhcp_get_option(&packet, pkt_len,
                                                        DHCP_SERVER_ID);
+                               if (!option)
+                                       return TRUE;
                                dhcp_client->server_ip = get_be32(option);
                        }
 
index 7935c3a..eab6293 100644 (file)
@@ -61,6 +61,7 @@ extern "C" {
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP   (1 << 7)
 #define G_SUPPLICANT_KEYMGMT_WPA_EAP_256       (1 << 8)
 #define G_SUPPLICANT_KEYMGMT_WPS               (1 << 9)
+#define G_SUPPLICANT_KEYMGMT_SAE               (1 << 10)
 
 #define G_SUPPLICANT_PROTO_WPA         (1 << 0)
 #define G_SUPPLICANT_PROTO_RSN         (1 << 1)
@@ -129,6 +130,12 @@ typedef enum {
        G_SUPPLICANT_PEER_GROUP_FAILED,
 } GSupplicantPeerState;
 
+typedef enum {
+       G_SUPPLICANT_MFP_NONE,
+       G_SUPPLICANT_MFP_OPTIONAL,
+       G_SUPPLICANT_MFP_REQUIRED,
+} GSupplicantMfpOptions;
+
 struct _GSupplicantSSID {
        const void *ssid;
        unsigned int ssid_len;
@@ -155,6 +162,8 @@ struct _GSupplicantSSID {
        dbus_bool_t use_wps;
        const char *pin_wps;
        const char *bgscan;
+       unsigned int keymgmt;
+       GSupplicantMfpOptions ieee80211w;
 };
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
@@ -339,6 +348,7 @@ bool g_supplicant_peer_is_in_a_group(GSupplicantPeer *peer);
 GSupplicantInterface *g_supplicant_peer_get_group_interface(GSupplicantPeer *peer);
 bool g_supplicant_peer_is_client(GSupplicantPeer *peer);
 bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer);
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network);
 
 struct _GSupplicantCallbacks {
        void (*system_ready) (void);
index f56b595..470d99e 100644 (file)
@@ -92,6 +92,7 @@ static struct strvalmap keymgmt_map[] = {
        { "wpa-eap",            G_SUPPLICANT_KEYMGMT_WPA_EAP    },
        { "wpa-eap-sha256",     G_SUPPLICANT_KEYMGMT_WPA_EAP_256        },
        { "wps",                G_SUPPLICANT_KEYMGMT_WPS                },
+       { "sae",                G_SUPPLICANT_KEYMGMT_SAE                },
        { }
 };
 
@@ -234,6 +235,7 @@ struct _GSupplicantNetwork {
        unsigned int wps_capabilities;
        GHashTable *bss_table;
        GHashTable *config_table;
+       unsigned int keymgmt;
 };
 
 struct _GSupplicantPeer {
@@ -1215,7 +1217,7 @@ const char *g_supplicant_network_get_path(GSupplicantNetwork *network)
 const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
 {
        if (!network)
-               return G_SUPPLICANT_MODE_UNKNOWN;
+               return NULL;
 
        return mode2string(network->mode);
 }
@@ -1223,7 +1225,7 @@ const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
 const char *g_supplicant_network_get_security(GSupplicantNetwork *network)
 {
        if (!network)
-               return G_SUPPLICANT_SECURITY_UNKNOWN;
+               return NULL;
 
        return security2string(network->security);
 }
@@ -1427,6 +1429,14 @@ bool g_supplicant_peer_has_requested_connection(GSupplicantPeer *peer)
        return peer->connection_requested;
 }
 
+unsigned int g_supplicant_network_get_keymgmt(GSupplicantNetwork *network)
+{
+       if (!network)
+               return 0;
+
+       return network->keymgmt;
+}
+
 static void merge_network(GSupplicantNetwork *network)
 {
        GString *str;
@@ -1457,7 +1467,8 @@ static void merge_network(GSupplicantNetwork *network)
        else if (g_strcmp0(mode, "1") == 0)
                g_string_append_printf(str, "_adhoc");
 
-       if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+       if ((g_strcmp0(key_mgmt, "WPA-PSK") == 0) ||
+           (g_strcmp0(key_mgmt, "SAE") == 0))
                g_string_append_printf(str, "_psk");
 
        group = g_string_free(str, FALSE);
@@ -1650,6 +1661,7 @@ static int add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
        network->name = create_name(bss->ssid, bss->ssid_len);
        network->mode = bss->mode;
        network->security = bss->security;
+       network->keymgmt = bss->keymgmt;
        network->ssid_len = bss->ssid_len;
        memcpy(network->ssid, bss->ssid, bss->ssid_len);
        network->signal = bss->signal;
@@ -1931,7 +1943,8 @@ static void bss_compute_security(struct g_supplicant_bss *bss)
        if (bss->keymgmt &
                        (G_SUPPLICANT_KEYMGMT_WPA_PSK |
                                G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
-                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256 |
+                               G_SUPPLICANT_KEYMGMT_SAE))
                bss->psk = TRUE;
 
        if (bss->ieee8021x)
@@ -4890,8 +4903,16 @@ static void add_network_security_proto(DBusMessageIter *dict,
        g_free(proto);
 }
 
+static void add_network_ieee80211w(DBusMessageIter *dict, GSupplicantSSID *ssid,
+                                  GSupplicantMfpOptions ieee80211w)
+{
+       supplicant_dbus_dict_append_basic(dict, "ieee80211w", DBUS_TYPE_UINT32,
+                                         &ieee80211w);
+}
+
 static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
 {
+       GSupplicantMfpOptions ieee80211w;
        char *key_mgmt;
 
        switch (ssid->security) {
@@ -4907,7 +4928,22 @@ static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
                add_network_security_ciphers(dict, ssid);
                break;
        case G_SUPPLICANT_SECURITY_PSK:
-               key_mgmt = "WPA-PSK";
+               if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_SAE) {
+                       if (ssid->keymgmt & G_SUPPLICANT_KEYMGMT_WPA_PSK) {
+                               /*
+                                * WPA3-Personal transition mode: supports both
+                                * WPA2-Personal (PSK) and WPA3-Personal (SAE)
+                                */
+                               key_mgmt = "SAE WPA-PSK";
+                               ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
+                       } else {
+                               key_mgmt = "SAE";
+                               ieee80211w = G_SUPPLICANT_MFP_REQUIRED;
+                       }
+                       add_network_ieee80211w(dict, ssid, ieee80211w);
+               } else {
+                       key_mgmt = "WPA-PSK";
+               }
                add_network_security_psk(dict, ssid);
                add_network_security_ciphers(dict, ssid);
                add_network_security_proto(dict, ssid);
index 7508a9a..fcd658e 100644 (file)
@@ -44,8 +44,10 @@ void connman_technology_regdom_notify(struct connman_technology *technology,
 
 enum connman_service_type connman_technology_get_type
                                (struct connman_technology *technology);
-bool connman_technology_get_wifi_tethering(const char **ssid,
-                                                       const char **psk);
+
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+                                       const char **ssid, const char **psk, int *freq);
+
 bool connman_technology_is_tethering_allowed(enum connman_service_type type);
 
 struct connman_technology_driver {
@@ -60,7 +62,6 @@ struct connman_technology_driver {
        void (*remove_interface) (struct connman_technology *technology,
                                                                int index);
        int (*set_tethering) (struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled);
        int (*set_regdom) (struct connman_technology *technology,
                                                const char *alpha2);
index 5336103..57cc8a2 100644 (file)
@@ -882,7 +882,6 @@ static void bluetooth_tech_remove(struct connman_technology *technology)
 }
 
 static int bluetooth_tech_set_tethering(struct connman_technology *technology,
-               const char *identifier, const char *passphrase,
                const char *bridge, bool enabled)
 {
        GHashTableIter hash_iter;
index 6146b1c..0bf7fc4 100644 (file)
@@ -407,7 +407,6 @@ static void eth_tech_disable_tethering(struct connman_technology *technology,
 }
 
 static int eth_tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        if (!connman_technology_is_tethering_allowed(
index 1b44bbb..2d58df3 100644 (file)
@@ -294,7 +294,6 @@ static void gadget_tech_disable_tethering(struct connman_technology *technology,
 }
 
 static int gadget_tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        DBG("bridge %s enabled %d", bridge, enabled);
index 4ba107f..ac3d1e1 100644 (file)
@@ -209,18 +209,13 @@ static int cm_network_probe(struct connman_network *network)
 static void update_network_connected(struct iwd_network *iwdn)
 {
        struct iwd_device *iwdd;
-       int index;
 
        iwdd = g_hash_table_lookup(devices, iwdn->device);
        if (!iwdd)
                return;
 
-       index = connman_inet_ifindex(iwdd->name);
-       if (index < 0)
-               return;
-
-       DBG("interface name %s index %d", iwdd->name, index);
-       connman_network_set_index(iwdn->network, index);
+       DBG("interface name %s index %d", iwdd->name,
+               connman_network_get_index(iwdn->network));
        connman_network_set_connected(iwdn->network, true);
 }
 
@@ -316,11 +311,13 @@ static int cm_network_disconnect(struct connman_network *network)
        if (!iwds)
                return -EIO;
 
+       connman_network_set_associating(network, false);
+
        if (!g_dbus_proxy_method_call(iwds->proxy, "Disconnect",
                        NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
                return -EIO;
 
-       return -EINPROGRESS;
+       return 0;
 }
 
 struct auto_connect_cb_data {
@@ -558,7 +555,7 @@ static void cm_device_scan_cb(DBusMessage *message, void *user_data)
        const char *path = user_data;
        struct iwd_station *iwds;
 
-       iwds = g_hash_table_lookup(networks, path);
+       iwds = g_hash_table_lookup(stations, path);
        if (!iwds)
                return;
 
@@ -818,12 +815,16 @@ static int cm_change_tethering(struct iwd_device *iwdd,
 }
 
 static int cm_tech_tethering(struct connman_technology *technology,
-                       const char *identifier, const char *passphrase,
                        const char *bridge, bool enabled)
 {
        GHashTableIter iter;
        gpointer key, value;
        int err = 0, res;
+       const char *ssid;
+       const char *psk;
+       int freq;
+
+       connman_technology_get_wifi_tethering(technology, &ssid, &psk, &freq);
 
        g_hash_table_iter_init(&iter, devices);
 
@@ -840,8 +841,8 @@ static int cm_tech_tethering(struct connman_technology *technology,
                        continue;
 
                if (!enabled && !g_strcmp0("ap", iwdd->mode)) {
-                       res = cm_change_tethering(iwdd, technology, identifier,
-                                               passphrase, bridge, enabled);
+                       res = cm_change_tethering(iwdd, technology, ssid,
+                                               psk, bridge, enabled);
                        if (res)
                                connman_warn("%s switching to Station mode failed",
                                        iwdd->path);
@@ -851,8 +852,8 @@ static int cm_tech_tethering(struct connman_technology *technology,
                }
 
                if (enabled && !g_strcmp0("station", iwdd->mode)) {
-                       err = cm_change_tethering(iwdd, technology, identifier,
-                                       passphrase, bridge, enabled);
+                       err = cm_change_tethering(iwdd, technology, ssid,
+                                       psk, bridge, enabled);
                        if (err)
                                connman_warn("%s switching to AccessPoint mode failed",
                                        iwdd->path);
@@ -912,6 +913,7 @@ static void add_network(const char *path, struct iwd_network *iwdn)
 {
        struct iwd_device *iwdd;
        char *identifier;
+       int index;
 
        iwdd = g_hash_table_lookup(devices, iwdn->device);
        if (!iwdd)
@@ -920,6 +922,11 @@ static void add_network(const char *path, struct iwd_network *iwdn)
        identifier = create_identifier(path, iwdn->type);
        iwdn->network = connman_network_create(identifier,
                                        CONNMAN_NETWORK_TYPE_WIFI);
+
+       index = connman_inet_ifindex(iwdd->name);
+       if (index >= 0)
+               connman_network_set_index(iwdn->network, index);
+
        connman_network_set_data(iwdn->network, iwdn);
 
        connman_network_set_name(iwdn->network, iwdn->name);
@@ -936,7 +943,9 @@ static void add_network(const char *path, struct iwd_network *iwdn)
        }
        iwdn->iwdd = iwdd;
 
-       connman_network_set_available(iwdn->network, true);
+       if (connman_network_get_strength(iwdn->network))
+               connman_network_set_available(iwdn->network, true);
+
        connman_network_set_group(iwdn->network, identifier);
 
        g_free(identifier);
@@ -1372,13 +1381,7 @@ static void create_adapter(GDBusProxy *proxy)
        struct iwd_adapter *iwda;
        GSList *modes, *list;
 
-       iwda = g_try_new0(struct iwd_adapter, 1);
-
-       if (!iwda) {
-               connman_error("Out of memory creating IWD adapter");
-               return;
-       }
-
+       iwda = g_new0(struct iwd_adapter, 1);
        iwda->path = g_strdup(path);
        g_hash_table_replace(adapters, iwda->path, iwda);
 
@@ -1423,13 +1426,7 @@ static void create_device(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_device *iwdd;
 
-       iwdd = g_try_new0(struct iwd_device, 1);
-
-       if (!iwdd) {
-               connman_error("Out of memory creating IWD device");
-               return;
-       }
-
+       iwdd = g_new0(struct iwd_device, 1);
        iwdd->path = g_strdup(path);
        g_hash_table_replace(devices, iwdd->path, iwdd);
 
@@ -1494,6 +1491,8 @@ static DBusMessage *agent_request_passphrase(DBusConnection *dbus_conn,
                return get_reply_on_error(message, EINVAL);
 
        passwd = connman_network_get_string(iwdn->network, "WiFi.Passphrase");
+       if (!passwd)
+               return get_reply_on_error(message, ENOKEY);
 
        return g_dbus_create_reply(message, DBUS_TYPE_STRING, &passwd,
                                        DBUS_TYPE_INVALID);
@@ -1596,13 +1595,7 @@ static void create_network(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_network *iwdn;
 
-       iwdn = g_try_new0(struct iwd_network, 1);
-
-       if (!iwdn) {
-               connman_error("Out of memory creating IWD network");
-               return;
-       }
-
+       iwdn = g_new0(struct iwd_network, 1);
        iwdn->path = g_strdup(path);
        g_hash_table_replace(networks, iwdn->path, iwdn);
 
@@ -1682,12 +1675,7 @@ static void create_know_network(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_known_network *iwdkn;
 
-       iwdkn = g_try_new0(struct iwd_known_network, 1);
-       if (!iwdkn) {
-               connman_error("Out of memory creating IWD known network");
-               return;
-       }
-
+       iwdkn = g_new0(struct iwd_known_network, 1);
        iwdkn->path = g_strdup(path);
        g_hash_table_replace(known_networks, iwdkn->path, iwdkn);
 
@@ -1721,12 +1709,7 @@ static void create_station(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_station *iwds;
 
-       iwds = g_try_new0(struct iwd_station, 1);
-       if (!iwds) {
-               connman_error("Out of memory creating IWD station");
-               return;
-       }
-
+       iwds = g_new0(struct iwd_station, 1);
        iwds->path = g_strdup(path);
        g_hash_table_replace(stations, iwds->path, iwds);
 
@@ -1754,11 +1737,7 @@ static void create_ap(GDBusProxy *proxy)
        const char *path = g_dbus_proxy_get_path(proxy);
        struct iwd_ap *iwdap;
 
-       iwdap = g_try_new0(struct iwd_ap, 1);
-       if (!iwdap) {
-               connman_error("Out of memory creating IWD access point");
-               return;
-       }
+       iwdap = g_new0(struct iwd_ap, 1);
        iwdap->index = -1;
 
        iwdap->path = g_strdup(path);
index 45effd4..caaea85 100644 (file)
@@ -223,9 +223,9 @@ static DBusMessage *create_request_oob_reply(DBusMessage *message)
        DBusMessageIter dict;
        const char *ssid, *psk;
        uint8_t *tlv_msg;
-       int length;
+       int length, freq;
 
-       if (!connman_technology_get_wifi_tethering(&ssid, &psk))
+       if (!connman_technology_get_wifi_tethering(NULL, &ssid, &psk, &freq))
                return get_reply_on_error(message, ENOTSUP);
 
        tlv_msg = encode_to_tlv(ssid, psk, &length);
index d708d1f..42396d2 100644 (file)
@@ -3,6 +3,7 @@
  *  Connection Manager
  *
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd. 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
@@ -491,6 +492,9 @@ static int errorstr2val(const char *error) {
        if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0)
                return -EISCONN;
 
+       if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".NoCarrier") == 0)
+               return -ENOLINK;
+
        if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".OperationCanceled") == 0)
                return -ECANCELED;
 
@@ -529,16 +533,23 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
        if (dbus_set_error_from_message(&error, reply)) {
                int err = errorstr2val(error.name);
 
+               switch (err) {
+               case -EINPROGRESS:
+                       break;
                /*
                 * ECANCELED means that user has canceled authentication
                 * dialog. That's not really an error, it's part of a normal
                 * workflow. We also take it as a request to turn autoconnect
                 * off, in case if it was on.
                 */
-               if (err == -ECANCELED) {
+               case -ECANCELED:
                        DBG("%s connect canceled", data->path);
                        connman_provider_set_autoconnect(data->provider, false);
-               } else if (err != -EINPROGRESS) {
+                       break;
+               case -ENOLINK: /* vpnd reports that connmand is not online. */
+               case -EISCONN:
+               case -ECONNREFUSED:
+               default:
                        connman_error("Connect reply: %s (%s)", error.message,
                                                                error.name);
                        DBG("data %p cb_data %p", data, cb_data);
@@ -1016,7 +1027,7 @@ static int disconnect_provider(struct connection_data *data)
 
        if (data->disconnect_call) {
                DBG("already disconnecting");
-               return -EINVAL;
+               return -EALREADY;
        }
 
        message = dbus_message_new_method_call(VPN_SERVICE, data->path,
index e701451..e947b16 100644 (file)
@@ -166,6 +166,10 @@ struct wifi_data {
        int assoc_code;
 };
 
+struct wifi_network {
+       unsigned int keymgmt;
+};
+
 struct disconnect_data {
        struct wifi_data *wifi;
        struct connman_network *network;
@@ -179,7 +183,6 @@ static bool wfd_service_registered = false;
 
 static void start_autoscan(struct connman_device *device);
 static int tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled);
 
 static int p2p_tech_probe(struct connman_technology *technology)
@@ -816,6 +819,7 @@ static void remove_networks(struct connman_device *device,
        for (list = wifi->networks; list; list = list->next) {
                struct connman_network *network = list->data;
 
+               g_free(connman_network_get_data(network));
                connman_device_remove_network(device, network);
                connman_network_unref(network);
        }
@@ -2158,6 +2162,7 @@ static GSupplicantSecurity network_security(const char *security)
 
 static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
 {
+       struct wifi_network *network_data = connman_network_get_data(network);
        const char *security;
 
        memset(ssid, 0, sizeof(*ssid));
@@ -2167,6 +2172,8 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->scan_ssid = 1;
        security = connman_network_get_string(network, "WiFi.Security");
        ssid->security = network_security(security);
+       ssid->keymgmt = network_data->keymgmt;
+       ssid->ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
        ssid->passphrase = connman_network_get_string(network,
                                                "WiFi.Passphrase");
 
@@ -2783,8 +2790,6 @@ static void ap_create_fail(GSupplicantInterface *interface)
                wifi->tethering = false;
 
                ret = tech_set_tethering(wifi->tethering_param->technology,
-                               wifi->tethering_param->ssid->ssid,
-                               wifi->tethering_param->ssid->passphrase,
                                wifi->bridge, true);
 
                if ((ret == -EOPNOTSUPP) && (wifi_technology)) {
@@ -2813,6 +2818,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        struct connman_network *network;
        GSupplicantInterface *interface;
        struct wifi_data *wifi;
+       struct wifi_network *network_data;
        const char *name, *identifier, *security, *group, *mode;
        const unsigned char *ssid;
        unsigned int ssid_len;
@@ -2861,8 +2867,15 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                }
 
                wifi->networks = g_slist_prepend(wifi->networks, network);
+
+               network_data = g_new0(struct wifi_network, 1);
+               connman_network_set_data(network, network_data);
        }
 
+       network_data = connman_network_get_data(network);
+       network_data->keymgmt =
+               g_supplicant_network_get_keymgmt(supplicant_network);
+
        if (name && name[0] != '\0')
                connman_network_set_name(network, name);
 
@@ -2930,6 +2943,7 @@ static void network_removed(GSupplicantNetwork *network)
 
        wifi->networks = g_slist_remove(wifi->networks, connman_network);
 
+       g_free(connman_network_get_data(connman_network));
        connman_device_remove_network(wifi->device, connman_network);
        connman_network_unref(connman_network);
 }
@@ -3319,19 +3333,31 @@ static void tech_remove(struct connman_technology *technology)
        wifi_technology = NULL;
 }
 
-static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
+static GSupplicantSSID *ssid_ap_init(const struct connman_technology *technology)
 {
        GSupplicantSSID *ap;
+       const char *ssid, *passphrase;
+       int freq;
+       bool ret;
 
        ap = g_try_malloc0(sizeof(GSupplicantSSID));
        if (!ap)
                return NULL;
 
+       ret = connman_technology_get_wifi_tethering(technology,
+                                               &ssid, &passphrase,
+                                               &freq);
+       if (ret == false)
+               return NULL;
+
        ap->mode = G_SUPPLICANT_MODE_MASTER;
        ap->ssid = ssid;
        ap->ssid_len = strlen(ssid);
        ap->scan_ssid = 0;
-       ap->freq = 2412;
+       if (freq)
+               ap->freq = freq;
+       else
+               ap->freq = 2412;
 
        if (!passphrase || strlen(passphrase) == 0) {
                ap->security = G_SUPPLICANT_SECURITY_NONE;
@@ -3441,8 +3467,7 @@ static void sta_remove_callback(int result,
 }
 
 static int enable_wifi_tethering(struct connman_technology *technology,
-                               const char *bridge, const char *identifier,
-                               const char *passphrase, bool available)
+                               const char *bridge, bool available)
 {
        GList *list;
        GSupplicantInterface *interface;
@@ -3495,14 +3520,14 @@ static int enable_wifi_tethering(struct connman_technology *technology,
                info->wifi = wifi;
                info->technology = technology;
                info->wifi->bridge = bridge;
-               info->ssid = ssid_ap_init(identifier, passphrase);
+               info->ssid = ssid_ap_init(technology);
                if (!info->ssid)
                        goto failed;
 
                info->ifname = g_strdup(ifname);
 
                wifi->tethering_param->technology = technology;
-               wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase);
+               wifi->tethering_param->ssid = ssid_ap_init(technology);
                if (!wifi->tethering_param->ssid)
                        goto failed;
 
@@ -3544,7 +3569,6 @@ static int enable_wifi_tethering(struct connman_technology *technology,
 }
 
 static int tech_set_tethering(struct connman_technology *technology,
-                               const char *identifier, const char *passphrase,
                                const char *bridge, bool enabled)
 {
        GList *list;
@@ -3572,13 +3596,11 @@ static int tech_set_tethering(struct connman_technology *technology,
        }
 
        DBG("trying tethering for available devices");
-       err = enable_wifi_tethering(technology, bridge, identifier, passphrase,
-                               true);
+       err = enable_wifi_tethering(technology, bridge, true);
 
        if (err < 0) {
                DBG("trying tethering for any device");
-               err = enable_wifi_tethering(technology, bridge, identifier,
-                                       passphrase, false);
+               err = enable_wifi_tethering(technology, bridge, false);
        }
 
        return err;
index d6113af..d4f9add 100644 (file)
@@ -366,9 +366,9 @@ static void report_error_reply(DBusMessage *reply, void *user_data)
                        retry = true;
        }
 
+out:
        report_error->callback(report_error->user_context, retry,
                        report_error->user_data);
-out:
        g_free(report_error);
 }
 
index 62023b1..33fdc73 100644 (file)
@@ -1106,8 +1106,7 @@ static char *config_pem_fsid(const char *pem_file)
 
 static void provision_service_wifi(struct connman_config_service *config,
                                struct connman_service *service,
-                               struct connman_network *network,
-                               const void *ssid, unsigned int ssid_len)
+                               struct connman_network *network)
 {
        if (config->eap)
                __connman_service_set_string(service, "EAP", config->eap);
@@ -1418,8 +1417,7 @@ static int try_provision_service(struct connman_config_service *config,
                                                config->timeservers);
 
        if (type == CONNMAN_SERVICE_TYPE_WIFI) {
-               provision_service_wifi(config, service, network,
-                                                       ssid, ssid_len);
+               provision_service_wifi(config, service, network);
        }
 
        __connman_service_mark_dirty();
index c454a58..74b3157 100644 (file)
@@ -687,7 +687,7 @@ int __connman_dbus_init(DBusConnection *conn)
 
        connection = conn;
 
-       return 0;
+       return g_dbus_attach_object_manager(conn);
 }
 
 void __connman_dbus_cleanup(void)
index 38dbdd7..cf1d36c 100644 (file)
@@ -1791,7 +1791,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                ulen = strlen(name) + 1;
                if ((uptr + ulen) > uncomp_end)
                        goto out;
-               strncpy(uptr, name, ulen);
+               memcpy(uptr, name, ulen);
 
                debug("pos %d ulen %d left %d name %s", pos, ulen,
                        (int)(uncomp_end - (uptr + ulen)), uptr);
@@ -1951,6 +1951,12 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
 
        if (offset < 0)
                return offset;
+       if (reply_len < 0)
+               return -EINVAL;
+       if (reply_len < offset + 1)
+               return -EINVAL;
+       if ((size_t)reply_len < sizeof(struct domain_hdr))
+               return -EINVAL;
 
        hdr = (void *)(reply + offset);
        dns_id = reply[offset] | reply[offset + 1] << 8;
@@ -1986,23 +1992,31 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                 */
                if (req->append_domain && ntohs(hdr->qdcount) == 1) {
                        uint16_t domain_len = 0;
-                       uint16_t header_len;
+                       uint16_t header_len, payload_len;
                        uint16_t dns_type, dns_class;
                        uint8_t host_len, dns_type_pos;
                        char uncompressed[NS_MAXDNAME], *uptr;
                        char *ptr, *eom = (char *)reply + reply_len;
+                       char *domain;
 
                        /*
                         * ptr points to the first char of the hostname.
                         * ->hostname.domain.net
                         */
                        header_len = offset + sizeof(struct domain_hdr);
+                       if (reply_len < header_len)
+                               return -EINVAL;
+                       payload_len = reply_len - header_len;
+
                        ptr = (char *)reply + header_len;
 
                        host_len = *ptr;
+                       domain = ptr + 1 + host_len;
+                       if (domain > eom)
+                               return -EINVAL;
+
                        if (host_len > 0)
-                               domain_len = strnlen(ptr + 1 + host_len,
-                                               reply_len - header_len);
+                               domain_len = strnlen(domain, eom - domain);
 
                        /*
                         * If the query type is anything other than A or AAAA,
@@ -2011,6 +2025,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                         */
                        dns_type_pos = host_len + 1 + domain_len + 1;
 
+                       if (ptr + (dns_type_pos + 3) > eom)
+                               return -EINVAL;
                        dns_type = ptr[dns_type_pos] << 8 |
                                                        ptr[dns_type_pos + 1];
                        dns_class = ptr[dns_type_pos + 2] << 8 |
@@ -2040,6 +2056,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                int new_len, fixed_len;
                                char *answers;
 
+                               if (len > payload_len)
+                                       return -EINVAL;
                                /*
                                 * First copy host (without domain name) into
                                 * tmp buffer.
@@ -2054,6 +2072,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                 * Copy type and class fields of the question.
                                 */
                                ptr += len + domain_len + 1;
+                               if (ptr + NS_QFIXEDSZ > eom)
+                                       return -EINVAL;
                                memcpy(uptr, ptr, NS_QFIXEDSZ);
 
                                /*
@@ -2063,6 +2083,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                uptr += NS_QFIXEDSZ;
                                answers = uptr;
                                fixed_len = answers - uncompressed;
+                               if (ptr + offset > eom)
+                                       return -EINVAL;
 
                                /*
                                 * We then uncompress the result to buffer
@@ -2166,6 +2188,9 @@ out:
                        err = sendto(sk, req->resp, req->resplen, 0,
                                &req->sa, req->sa_len);
        } else {
+               uint16_t tcp_len = htons(req->resplen - 2);
+               /* correct TCP message length */
+               memcpy(req->resp, &tcp_len, sizeof(tcp_len));
                sk = req->client_sk;
                err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
        }
@@ -2254,8 +2279,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
 
        len = recv(sk, buf, sizeof(buf), 0);
 
-       if (len >= 12)
-               forward_dns_reply(buf, len, IPPROTO_UDP, data);
+       forward_dns_reply(buf, len, IPPROTO_UDP, data);
 
        return TRUE;
 }
@@ -2336,14 +2360,21 @@ hangup:
                        }
                }
 
+               /*
+                * Remove the G_IO_OUT flag from the watch, otherwise we end
+                * up in a busy loop, because the socket is constantly writable.
+                *
+                * There seems to be no better way in g_io to do that than
+                * re-adding the watch.
+                */
+               g_source_remove(server->watch);
+               server->watch = g_io_add_watch(server->channel,
+                       G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                       tcp_server_event, server);
+
                server->connected = true;
                server_list = g_slist_append(server_list, server);
 
-               if (server->timeout > 0) {
-                       g_source_remove(server->timeout);
-                       server->timeout = 0;
-               }
-
                for (list = request_list; list; ) {
                        struct request_data *req = list->data;
                        int status;
@@ -2707,7 +2738,7 @@ static void update_domain(int index, const char *domain, bool append)
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
                GList *dom_list;
-               char *dom;
+               char *dom = NULL;
                bool dom_found = false;
 
                if (data->index < 0)
index df94d1e..4039a73 100644 (file)
@@ -1836,13 +1836,6 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
        return 0;
 }
 
-static inline void ipv6_addr_advert_mult(const struct in6_addr *addr,
-                                       struct in6_addr *advert)
-{
-       ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2),
-                       htonl(0xFF000000) | addr->s6_addr32[3]);
-}
-
 #define MSG_SIZE_SEND 1452
 
 static int inc_len(int len, int inc)
index 1551826..34b1724 100644 (file)
@@ -1593,6 +1593,9 @@ static void disable_ipv6(struct connman_ipconfig *ipconfig)
 
        ifname = connman_inet_ifname(ipconfig->index);
 
+       if (!ifname)
+               return;
+
        set_ipv6_state(ifname, false);
 
        g_free(ifname);
@@ -1612,6 +1615,9 @@ static void enable_ipv6(struct connman_ipconfig *ipconfig)
 
        ifname = connman_inet_ifname(ipconfig->index);
 
+       if (!ifname)
+               return;
+
        if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO)
                set_ipv6_privacy(ifname, ipconfig->ipv6_privacy_config);
 
index 6480caa..e209cf2 100644 (file)
@@ -44,6 +44,9 @@
 #define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
 #define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
 
+#define DEFAULT_ONLINE_CHECK_IPV4_URL "http://ipv4.connman.net/online/status.html"
+#define DEFAULT_ONLINE_CHECK_IPV6_URL "http://ipv6.connman.net/online/status.html"
+
 /*
  * We set the integer to 1 sec so that we have a chance to get
  * necessary IPv6 router advertisement messages that might have
@@ -97,6 +100,8 @@ static struct {
        char *vendor_class_id;
        bool enable_online_check;
        bool enable_online_to_ready_transition;
+       char *online_check_ipv4_url;
+       char *online_check_ipv6_url;
        unsigned int online_check_initial_interval;
        unsigned int online_check_max_interval;
        bool auto_connect_roaming_services;
@@ -122,6 +127,8 @@ static struct {
        .vendor_class_id = NULL,
        .enable_online_check = true,
        .enable_online_to_ready_transition = false,
+       .online_check_ipv4_url = NULL,
+       .online_check_ipv6_url = NULL,
        .online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL,
        .online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL,
        .auto_connect_roaming_services = false,
@@ -148,6 +155,8 @@ static struct {
 #define CONF_VENDOR_CLASS_ID            "VendorClassID"
 #define CONF_ENABLE_ONLINE_CHECK        "EnableOnlineCheck"
 #define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_IPV4_URL      "OnlineCheckIPv4URL"
+#define CONF_ONLINE_CHECK_IPV6_URL      "OnlineCheckIPv6URL"
 #define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval"
 #define CONF_ONLINE_CHECK_MAX_INTERVAL     "OnlineCheckMaxInterval"
 #define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
@@ -174,6 +183,8 @@ static const char *supported_options[] = {
        CONF_VENDOR_CLASS_ID,
        CONF_ENABLE_ONLINE_CHECK,
        CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+       CONF_ONLINE_CHECK_IPV4_URL,
+       CONF_ONLINE_CHECK_IPV6_URL,
        CONF_ONLINE_CHECK_INITIAL_INTERVAL,
        CONF_ONLINE_CHECK_MAX_INTERVAL,
        CONF_AUTO_CONNECT_ROAMING_SERVICES,
@@ -341,6 +352,8 @@ static void parse_config(GKeyFile *config)
                connman_settings.auto_connect =
                        parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
 
+       g_strfreev(str_list);
+
        g_clear_error(&error);
 
        str_list = __connman_config_get_string_list(config, "General",
@@ -486,6 +499,27 @@ static void parse_config(GKeyFile *config)
 
        g_clear_error(&error);
 
+       string = __connman_config_get_string(config, "General",
+                                       CONF_ONLINE_CHECK_IPV4_URL, &error);
+       if (!error)
+               connman_settings.online_check_ipv4_url = string;
+       else
+               connman_settings.online_check_ipv4_url =
+                       g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+
+       g_clear_error(&error);
+
+       string = __connman_config_get_string(config, "General",
+                                       CONF_ONLINE_CHECK_IPV6_URL, &error);
+       if (!error)
+               connman_settings.online_check_ipv6_url = string;
+       else
+               connman_settings.online_check_ipv6_url =
+                       g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
+
+
+       g_clear_error(&error);
+
        integer = g_key_file_get_integer(config, "General",
                        CONF_ONLINE_CHECK_INITIAL_INTERVAL, &error);
        if (!error && integer >= 0)
@@ -708,6 +742,12 @@ char *connman_setting_get_string(const char *key)
        if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
                return connman_settings.vendor_class_id;
 
+       if (g_str_equal(key, CONF_ONLINE_CHECK_IPV4_URL))
+               return connman_settings.online_check_ipv4_url;
+
+       if (g_str_equal(key, CONF_ONLINE_CHECK_IPV6_URL))
+               return connman_settings.online_check_ipv6_url;
+
        if (g_strcmp0(key, "wifi") == 0) {
                if (!option_wifi)
                        return "nl80211,wext";
@@ -989,6 +1029,8 @@ int main(int argc, char *argv[])
        g_strfreev(connman_settings.blacklisted_interfaces);
        g_strfreev(connman_settings.tethering_technologies);
        g_free(connman_settings.vendor_class_id);
+       g_free(connman_settings.online_check_ipv4_url);
+       g_free(connman_settings.online_check_ipv6_url);
 
        g_free(option_debug);
        g_free(option_wifi);
index df70e17..ddd5799 100644 (file)
 # Default value is true.
 # EnableOnlineCheck = false
 
+# Urls (IPv4 and IPv6 respectively) used during the online status check.
+# Please refer to the README for more detailed information.
+# Default values are http://ipv4.connman.net/online/status.html and
+# http://ipv6.connman.net/online/status.html respectively.
+# OnlineCheckIPv4URL= http://ipv4.connman.net/online/status.html
+# OnlineCheckIPv6URL= http://ipv6.connman.net/online/status.html
+
 # 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.
+# Please refer to the README for more detailed information.
+# Default values are 1 and 12 respectively.
 # OnlineCheckInitialInterval = 1
 # OnlineCheckMaxInterval = 12
 
index 3bf8f4e..892d3a4 100644 (file)
@@ -600,6 +600,9 @@ static const GDBusSignalTable manager_signals[] = {
        { GDBUS_SIGNAL("PeersChanged",
                        GDBUS_ARGS({ "changed", "a(oa{sv})" },
                                        { "removed", "ao" })) },
+       { GDBUS_SIGNAL("TetheringClientsChanged",
+                       GDBUS_ARGS({ "registered", "as" },
+                                       { "removed", "as" })) },
        { },
 };
 
index b12bbc0..1cbdf9c 100644 (file)
@@ -1797,8 +1797,6 @@ int __connman_network_connect(struct connman_network *network)
        if (!network->device)
                return -ENODEV;
 
-       __connman_device_disconnect(network->device);
-
        network->connecting = true;
 
        err = network->driver->connect(network);
index c9e84da..a87296f 100644 (file)
@@ -1308,75 +1308,71 @@ static const char *type2string(uint16_t type)
 static GIOChannel *channel = NULL;
 static guint channel_watch = 0;
 
-struct rtnl_request {
-       struct nlmsghdr hdr;
-       struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
 
 static GSList *request_list = NULL;
 static guint32 request_seq = 0;
 
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
 {
        GSList *list;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
-               if (req->hdr.nlmsg_seq == seq)
-                       return req;
+               if (hdr->nlmsg_seq == seq)
+                       return hdr;
        }
 
        return NULL;
 }
 
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
 {
        struct sockaddr_nl addr;
        int sk;
 
        DBG("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
        sk = g_io_channel_unix_get_fd(channel);
 
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
 
-       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+       return sendto(sk, hdr, hdr->nlmsg_len, 0,
                                (struct sockaddr *) &addr, sizeof(addr));
 }
 
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
 {
-       request_list = g_slist_append(request_list, req);
+       request_list = g_slist_append(request_list, hdr);
 
        if (g_slist_length(request_list) > 1)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static int process_response(guint32 seq)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
 
        DBG("seq %d", seq);
 
-       req = find_request(seq);
-       if (req) {
-               request_list = g_slist_remove(request_list, req);
-               g_free(req);
+       hdr = find_request(seq);
+       if (hdr) {
+               request_list = g_slist_remove(request_list, hdr);
+               g_free(hdr);
        }
 
-       req = g_slist_nth_data(request_list, 0);
-       if (!req)
+       hdr = g_slist_nth_data(request_list, 0);
+       if (!hdr)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static void rtnl_message(void *buf, size_t len)
@@ -1474,62 +1470,65 @@ static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data
 
 static int send_getlink(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETLINK;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETLINK;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static int send_getaddr(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETADDR;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETADDR;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       return queue_request(req);
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
+
+       return queue_request(hdr);
 }
 
 static int send_getroute(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        DBG("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETROUTE;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETROUTE;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static gboolean update_timeout_cb(gpointer user_data)
@@ -1673,14 +1672,14 @@ void __connman_rtnl_cleanup(void)
        update_list = NULL;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr= list->data;
 
                DBG("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
-               g_free(req);
+               g_free(hdr);
                list->data = NULL;
        }
 
index 20917a8..1d2b78a 100644 (file)
@@ -49,6 +49,7 @@ static DBusConnection *connection = NULL;
 
 static GList *service_list = NULL;
 static GHashTable *service_hash = NULL;
+static GHashTable *passphrase_requested = NULL;
 static GSList *counter_list = NULL;
 static unsigned int autoconnect_id = 0;
 static unsigned int vpn_autoconnect_id = 0;
@@ -1594,6 +1595,25 @@ bool __connman_service_index_is_default(int index)
        return __connman_service_get_index(service) == index;
 }
 
+static void start_wispr_when_connected(struct connman_service *service)
+{
+       if (!connman_setting_get_bool("EnableOnlineCheck")) {
+               connman_info("Online check disabled. "
+                       "Default service remains in READY state.");
+               return;
+       }
+
+       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);
+}
+
 static void default_changed(void)
 {
        struct connman_service *service = connman_service_get_default();
@@ -1618,15 +1638,7 @@ 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);
+               start_wispr_when_connected(service);
 
                /*
                 * Connect VPN automatically when new default service
@@ -3725,13 +3737,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL);
                dns_configuration_changed(service);
 
-               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);
+               start_wispr_when_connected(service);
 
                service_save(service);
        } else if (g_str_equal(name, "Timeservers.Configuration")) {
@@ -4209,6 +4215,7 @@ static bool auto_connect_service(GList *services,
        bool ignore[MAX_CONNMAN_SERVICE_TYPES] = { };
        bool autoconnecting = false;
        GList *list;
+       int index;
 
        DBG("preferred %d sessions %d reason %s", preferred, active_count,
                reason2string(reason));
@@ -4230,6 +4237,11 @@ static bool auto_connect_service(GList *services,
                        continue;
                }
 
+               index = __connman_service_get_index(service);
+               if (g_hash_table_lookup(passphrase_requested,
+                                       GINT_TO_POINTER(index)))
+                       return true;
+
                if (service->pending ||
                                is_connecting(service->state) ||
                                is_connected(service->state)) {
@@ -4711,15 +4723,11 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
        struct connman_service *def_service;
 
        def_service = connman_service_get_default();
-       if (!def_service)
+       if (!def_service || def_service != service ||
+               def_service->state != CONNMAN_SERVICE_STATE_ONLINE)
                return;
 
-       if (def_service == service &&
-                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE) {
-               def_service->state = CONNMAN_SERVICE_STATE_READY;
-               __connman_notifier_leave_online(def_service->type);
-               state_changed(def_service);
-       }
+       downgrade_state(def_service);
 }
 
 static void switch_default_service(struct connman_service *default_service,
@@ -5316,6 +5324,25 @@ static gint service_compare_vpn(struct connman_service *a,
        return service_compare(transport, service);
 }
 
+static gint service_compare_preferred(struct connman_service *service_a,
+                                       struct connman_service *service_b)
+{
+       unsigned int *tech_array;
+       int i;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array) {
+               for (i = 0; tech_array[i]; i++) {
+                       if (tech_array[i] == service_a->type)
+                               return -1;
+
+                       if (tech_array[i] == service_b->type)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
 static gint service_compare(gconstpointer a, gconstpointer b)
 {
        struct connman_service *service_a = (void *) a;
@@ -5346,6 +5373,10 @@ static gint service_compare(gconstpointer a, gconstpointer b)
 
                if (service_a->order < service_b->order)
                        return 1;
+
+               rval = service_compare_preferred(service_a, service_b);
+               if (rval)
+                       return rval;
        }
 
        if (state_a != state_b) {
@@ -5376,20 +5407,11 @@ static gint service_compare(gconstpointer a, gconstpointer b)
                return 1;
 
        if (service_a->type != service_b->type) {
-               unsigned int *tech_array;
-               int i;
-
-               tech_array = connman_setting_get_uint_list(
-                                               "PreferredTechnologies");
-               if (tech_array) {
-                       for (i = 0; tech_array[i]; i++) {
-                               if (tech_array[i] == service_a->type)
-                                       return -1;
+               int rval;
 
-                               if (tech_array[i] == service_b->type)
-                                       return 1;
-                       }
-               }
+               rval = service_compare_preferred(service_a, service_b);
+               if (rval)
+                       return rval;
 
                if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET)
                        return -1;
@@ -5780,6 +5802,7 @@ static void request_input_cb(struct connman_service *service,
        struct connman_device *device;
        const char *security;
        int err = 0;
+       int index;
 
        DBG("RequestInput return, %p", service);
 
@@ -5842,6 +5865,10 @@ static void request_input_cb(struct connman_service *service,
                err = __connman_service_set_passphrase(service, passphrase);
 
  done:
+       index = __connman_service_get_index(service);
+       g_hash_table_remove(passphrase_requested,
+                               GINT_TO_POINTER(index));
+
        if (err >= 0) {
                /* We forget any previous error. */
                set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
@@ -5895,27 +5922,14 @@ static int service_update_preferred_order(struct connman_service *default_servic
                struct connman_service *new_service,
                enum connman_service_state new_state)
 {
-       unsigned int *tech_array;
-       int i;
-
-       if (!default_service || default_service == new_service ||
-                       default_service->state != new_state)
+       if (!default_service || default_service == new_service)
                return 0;
 
-       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
-       if (tech_array) {
-
-               for (i = 0; tech_array[i] != 0; i += 1) {
-                       if (default_service->type == tech_array[i])
-                               return -EALREADY;
-
-                       if (new_service->type == tech_array[i]) {
-                               switch_default_service(default_service,
-                                               new_service);
-                               __connman_connection_update_gateway();
-                               return 0;
-                       }
-               }
+       if (service_compare_preferred(default_service, new_service) > 0) {
+               switch_default_service(default_service,
+                               new_service);
+               __connman_connection_update_gateway();
+               return 0;
        }
 
        return -EALREADY;
@@ -6009,6 +6023,15 @@ static int service_indicate_state(struct connman_service *service)
                break;
 
        case CONNMAN_SERVICE_STATE_IDLE:
+               if (old_state == CONNMAN_SERVICE_STATE_FAILURE &&
+                               service->connect_reason ==
+                                       CONNMAN_SERVICE_CONNECT_REASON_NATIVE &&
+                               service->error ==
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+                       __connman_service_clear_error(service);
+                       service_complete(service);
+               }
+
                if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT)
                        __connman_service_disconnect(service);
 
@@ -6051,12 +6074,12 @@ static int service_indicate_state(struct connman_service *service)
 
                service->new_service = false;
 
-               default_changed();
-
                def_service = connman_service_get_default();
 
                service_update_preferred_order(def_service, service, new_state);
 
+               default_changed();
+
                __connman_service_set_favorite(service, true);
 
                reply_pending(service, 0);
@@ -6125,7 +6148,8 @@ static int service_indicate_state(struct connman_service *service)
                break;
 
        case CONNMAN_SERVICE_STATE_FAILURE:
-               if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+               if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+                       service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
                        connman_agent_report_error(service, service->path,
                                                error2string(service->error),
                                                report_error_cb,
@@ -6693,6 +6717,7 @@ static int service_connect(struct connman_service *service)
 int __connman_service_connect(struct connman_service *service,
                        enum connman_service_connect_reason reason)
 {
+       int index;
        int err;
 
        DBG("service %p state %s connect reason %s -> %s",
@@ -6756,7 +6781,8 @@ int __connman_service_connect(struct connman_service *service,
                                service->provider)
                        connman_provider_disconnect(service->provider);
 
-       if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+       if (reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+                       reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
                if (err == -ENOKEY || err == -EPERM) {
                        DBusMessage *pending = NULL;
                        const char *dbus_sender = get_dbus_sender(service);
@@ -6779,6 +6805,13 @@ int __connman_service_connect(struct connman_service *service,
                        if (service->hidden && err != -EINPROGRESS)
                                service->pending = pending;
 
+                       if (err == -EINPROGRESS) {
+                               index = __connman_service_get_index(service);
+                               g_hash_table_replace(passphrase_requested,
+                                               GINT_TO_POINTER(index),
+                                               GINT_TO_POINTER(true));
+                       }
+
                        return err;
                }
        }
@@ -7440,8 +7473,19 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
        if (__connman_network_get_weakness(network))
                return service;
 
+       index = connman_network_get_index(network);
+
        if (service->path) {
                update_from_network(service, network);
+
+               if (service->ipconfig_ipv4)
+                       __connman_ipconfig_set_index(service->ipconfig_ipv4,
+                                                                       index);
+
+               if (service->ipconfig_ipv6)
+                       __connman_ipconfig_set_index(service->ipconfig_ipv6,
+                                                                       index);
+
                __connman_connection_update_gateway();
                return service;
        }
@@ -7472,14 +7516,16 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
 
        update_from_network(service, network);
 
-       index = connman_network_get_index(network);
-
        if (!service->ipconfig_ipv4)
                service->ipconfig_ipv4 = create_ip4config(service, index,
                                CONNMAN_IPCONFIG_METHOD_DHCP);
+       else
+               __connman_ipconfig_set_index(service->ipconfig_ipv4, index);
 
        if (!service->ipconfig_ipv6)
                service->ipconfig_ipv6 = create_ip6config(service, index);
+       else
+               __connman_ipconfig_set_index(service->ipconfig_ipv6, index);
 
        service_register(service);
        service_schedule_added(service);
@@ -7750,6 +7796,8 @@ int __connman_service_init(void)
        service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        NULL, service_free);
 
+       passphrase_requested = g_hash_table_new(g_direct_hash, g_direct_equal);
+
        services_notify = g_new0(struct _services_notify, 1);
        services_notify->remove = g_hash_table_new_full(g_str_hash,
                        g_str_equal, g_free, NULL);
@@ -7782,6 +7830,9 @@ void __connman_service_cleanup(void)
        g_hash_table_destroy(service_hash);
        service_hash = NULL;
 
+       g_hash_table_destroy(passphrase_requested);
+       passphrase_requested = NULL;
+
        g_slist_free(counter_list);
        counter_list = NULL;
 
index 672d6ea..5c46911 100644 (file)
@@ -66,6 +66,7 @@ struct connman_technology {
                                              */
        char *tethering_ident;
        char *tethering_passphrase;
+       int tethering_freq;
 
        bool enable_persistent; /* Save the tech state */
 
@@ -192,6 +193,13 @@ static void technology_save(struct connman_technology *technology)
                g_free(enc);
        }
 
+       if (technology->tethering_freq == 0)
+               technology->tethering_freq = 2412;
+
+       g_key_file_set_integer(keyfile, identifier,
+                               "Tethering.Freq",
+                               technology->tethering_freq);
+
 done:
        g_free(identifier);
 
@@ -264,8 +272,7 @@ static int set_tethering(struct connman_technology *technology,
                if (!driver || !driver->set_tethering)
                        continue;
 
-               err = driver->set_tethering(technology, ident, passphrase,
-                               bridge, enabled);
+               err = driver->set_tethering(technology, bridge, enabled);
 
                if (result == -EINPROGRESS)
                        continue;
@@ -356,25 +363,32 @@ enum connman_service_type connman_technology_get_type
        return technology->type;
 }
 
-bool connman_technology_get_wifi_tethering(const char **ssid,
-                                                       const char **psk)
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+                                       const char **ssid, const char **psk,
+                                       int *freq)
 {
-       struct connman_technology *technology;
+       bool force = true;
 
        if (!ssid || !psk)
                return false;
 
        *ssid = *psk = NULL;
 
-       technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+       /* Workaround for the neard plugin */
+       if (!technology) {
+               technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+               force = false;
+       }
+
        if (!technology)
                return false;
 
-       if (!technology->tethering)
+       if (!force && !technology->tethering)
                return false;
 
        *ssid = technology->tethering_ident;
        *psk = technology->tethering_passphrase;
+       *freq = technology->tethering_freq;
 
        return true;
 }
@@ -443,6 +457,10 @@ static void technology_load(struct connman_technology *technology)
                                identifier, "Tethering.Passphrase", NULL);
        if (enc)
                technology->tethering_passphrase = g_strcompress(enc);
+
+       technology->tethering_freq = g_key_file_get_integer(keyfile,
+                               identifier, "Tethering.Freq", NULL);
+
 done:
        g_free(identifier);
 
@@ -554,6 +572,10 @@ static void append_properties(DBusMessageIter *iter,
                                        DBUS_TYPE_STRING,
                                        &technology->tethering_passphrase);
 
+       connman_dbus_dict_append_basic(&dict, "TetheringFreq",
+                               DBUS_TYPE_INT32,
+                               &technology->tethering_freq);
+
        connman_dbus_dict_close(iter, &dict);
 }
 
@@ -968,6 +990,27 @@ static DBusMessage *set_property(DBusConnection *conn,
                                        DBUS_TYPE_STRING,
                                        &technology->tethering_passphrase);
                }
+       } else if (g_str_equal(name, "TetheringFreq")) {
+               dbus_int32_t freq;
+
+               if (type != DBUS_TYPE_INT32)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &freq);
+
+               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       return __connman_error_not_supported(msg);
+
+               if (freq >= 0) {
+                       technology->tethering_freq = freq;
+                       technology_save(technology);
+
+                       connman_dbus_property_changed_basic(technology->path,
+                                       CONNMAN_TECHNOLOGY_INTERFACE,
+                                       "TetheringFreq",
+                                       DBUS_TYPE_INT32,
+                                       &technology->tethering_freq);
+               }
        } else if (g_str_equal(name, "Powered")) {
                dbus_bool_t enable;
 
index c63dc81..56007a3 100644 (file)
@@ -30,9 +30,6 @@
 
 #include "connman.h"
 
-#define STATUS_URL_IPV4  "http://ipv4.connman.net/online/status.html"
-#define STATUS_URL_IPV6  "http://ipv6.connman.net/online/status.html"
-
 struct connman_wispr_message {
        bool has_error;
        const char *current_element;
@@ -96,6 +93,8 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data);
 
 static GHashTable *wispr_portal_list = NULL;
 
+static char *online_check_ipv4_url = NULL;
+static char *online_check_ipv6_url = NULL;
 static bool enable_online_to_ready_transition = false;
 
 static void connman_wispr_message_init(struct connman_wispr_message *msg)
@@ -910,10 +909,10 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
 
        if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                g_web_set_address_family(wp_context->web, AF_INET);
-               wp_context->status_url = STATUS_URL_IPV4;
+               wp_context->status_url = online_check_ipv4_url;
        } else {
                g_web_set_address_family(wp_context->web, AF_INET6);
-               wp_context->status_url = STATUS_URL_IPV6;
+               wp_context->status_url = online_check_ipv6_url;
        }
 
        for (i = 0; nameservers[i]; i++)
@@ -1036,6 +1035,11 @@ int __connman_wispr_init(void)
                                                g_direct_equal, NULL,
                                                free_connman_wispr_portal);
 
+       online_check_ipv4_url =
+               connman_setting_get_string("OnlineCheckIPv4URL");
+       online_check_ipv6_url =
+               connman_setting_get_string("OnlineCheckIPv6URL");
+
        enable_online_to_ready_transition =
                connman_setting_get_bool("EnableOnlineToReadyTransition");
 
index 41e842d..a52f4af 100644 (file)
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
 {
        enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
        char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
-       int err, c, i;
+       int err = -EINVAL, c, i;
 
        opterr = 0;
 
index e9b7cb2..f9d091e 100644 (file)
@@ -44,7 +44,7 @@ int main(int argc, char *argv[])
 {
        enum iptables_command cmd = IPTABLES_COMMAND_UNKNOWN;
        char *table = NULL, *chain = NULL, *rule = NULL, *tmp;
-       int err, c, i;
+       int err = -EINVAL, c, i;
 
        opterr = 0;
 
index cd261d0..f08736e 100644 (file)
@@ -69,13 +69,13 @@ static void set_test_config(enum configtype type)
 #define IP6T_SO_GET_INFO               (IP6T_BASE_CTL)
 #define IP6T_SO_GET_ENTRIES            (IP6T_BASE_CTL + 1)
 
-int xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
                        const void *entry, struct xt_entry_match **match)
 {
        return 0;
 }
 
-int xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
+int static xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
                        const void *entry, struct xt_entry_target **targetinfo)
 {
        return 0;
index 1e4fcd1..ee40dd7 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2010,2013  BMW Car IT GmbH.
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd.
  *
  *  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
@@ -121,12 +122,40 @@ struct {
 static DBusConnection *connection;
 
 struct l2tp_private_data {
+       struct vpn_provider *provider;
        struct connman_task *task;
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
 };
 
+static void l2tp_connect_done(struct l2tp_private_data *data, int err)
+{
+       vpn_provider_connect_cb_t cb;
+       void *user_data;
+
+       if (!data || !data->cb)
+               return;
+
+       /* Ensure that callback is called only once */
+       cb = data->cb;
+       user_data = data->user_data;
+       data->cb = NULL;
+       data->user_data = NULL;
+       cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct l2tp_private_data *data)
+{
+       if (vpn_provider_get_plugin_data(data->provider) == data)
+               vpn_provider_set_plugin_data(data->provider, NULL);
+
+       l2tp_connect_done(data, EIO);
+       vpn_provider_unref(data->provider);
+       g_free(data->if_name);
+       g_free(data);
+}
+
 static DBusMessage *l2tp_get_sec(struct connman_task *task,
                        DBusMessage *msg, void *user_data)
 {
@@ -164,6 +193,9 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
        char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
        char *ifname = NULL, *nameservers = NULL;
        struct connman_ipaddress *ipaddress = NULL;
+       struct l2tp_private_data *data;
+
+       data = vpn_provider_get_plugin_data(provider);
 
        dbus_message_iter_init(msg, &iter);
 
@@ -182,11 +214,22 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
                vpn_provider_set_string_hide_value(provider, "L2TP.Password",
                                        NULL);
 
+               l2tp_connect_done(data, EACCES);
                return VPN_STATE_AUTH_FAILURE;
        }
 
-       if (strcmp(reason, "connect"))
+       if (strcmp(reason, "connect")) {
+               l2tp_connect_done(data, EIO);
+
+               /*
+                * Stop the task to avoid potential looping of this state when
+                * authentication fails.
+                */
+               if (data && data->task)
+                       connman_task_stop(data->task);
+
                return VPN_STATE_DISCONNECT;
+       }
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -257,6 +300,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
        g_free(nameservers);
        connman_ipaddress_free(ipaddress);
 
+       l2tp_connect_done(data, 0);
+
        return VPN_STATE_CONNECT;
 }
 
@@ -476,9 +521,10 @@ static int l2tp_write_config(struct vpn_provider *provider,
 
 static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
 {
+       struct l2tp_private_data *data = user_data;
        char *conf_file;
 
-       vpn_died(task, exit_code, user_data);
+       vpn_died(task, exit_code, data->provider);
 
        conf_file = g_strdup_printf(VPN_STATEDIR "/connman-xl2tpd.conf");
        unlink(conf_file);
@@ -487,6 +533,8 @@ static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
        conf_file = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
        unlink(conf_file);
        g_free(conf_file);
+
+       free_private_data(data);
 }
 
 struct request_input_reply {
@@ -646,12 +694,12 @@ static int request_input(struct vpn_provider *provider,
        return -EINPROGRESS;
 }
 
-static int run_connect(struct vpn_provider *provider,
-                       struct connman_task *task, const char *if_name,
-                       vpn_provider_connect_cb_t cb, void *user_data,
+static int run_connect(struct l2tp_private_data *data,
                        const char *username, const char *password)
 {
-       char *l2tp_name, *pppd_name;
+       struct vpn_provider *provider = data->provider;
+       struct connman_task *task = data->task;
+       char *l2tp_name, *ctrl_name, *pppd_name;
        int l2tp_fd, pppd_fd;
        int err;
 
@@ -674,12 +722,24 @@ static int run_connect(struct vpn_provider *provider,
                goto done;
        }
 
-       pppd_name = g_strdup_printf(VPN_STATEDIR "/connman-ppp-option.conf");
+       ctrl_name = g_strconcat(VPN_STATEDIR, "/connman-xl2tpd-control", NULL);
+
+       if (mkfifo(ctrl_name, S_IRUSR|S_IWUSR) != 0 && errno != EEXIST) {
+               connman_error("Error creating xl2tp control pipe");
+               g_free(l2tp_name);
+               g_free(ctrl_name);
+               close(l2tp_fd);
+               err = -EIO;
+               goto done;
+       }
+
+       pppd_name = g_strconcat(VPN_STATEDIR, "/connman-ppp-option.conf", NULL);
 
        pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
        if (pppd_fd < 0) {
                connman_error("Error writing pppd config");
                g_free(l2tp_name);
+               g_free(ctrl_name);
                g_free(pppd_name);
                close(l2tp_fd);
                err = -EIO;
@@ -691,34 +751,28 @@ static int run_connect(struct vpn_provider *provider,
        write_pppd_option(provider, pppd_fd);
 
        connman_task_add_argument(task, "-D", NULL);
+       connman_task_add_argument(task, "-C", ctrl_name);
        connman_task_add_argument(task, "-c", l2tp_name);
 
        g_free(l2tp_name);
+       g_free(ctrl_name);
        g_free(pppd_name);
        close(l2tp_fd);
        close(pppd_fd);
 
-       err = connman_task_run(task, l2tp_died, provider,
-                               NULL, NULL, NULL);
+       err = connman_task_run(task, l2tp_died, data, NULL, NULL, NULL);
        if (err < 0) {
                connman_error("l2tp failed to start");
                err = -EIO;
-               goto done;
        }
 
 done:
-       if (cb)
-               cb(provider, user_data, err);
+       if (err)
+               l2tp_connect_done(data, -err);
 
        return err;
 }
 
-static void free_private_data(struct l2tp_private_data *data)
-{
-       g_free(data->if_name);
-       g_free(data);
-}
-
 static void request_input_cb(struct vpn_provider *provider,
                        const char *username,
                        const char *password,
@@ -736,10 +790,7 @@ static void request_input_cb(struct vpn_provider *provider,
        vpn_provider_set_string_hide_value(provider, "L2TP.Password",
                                                                password);
 
-       run_connect(provider, data->task, data->if_name, data->cb,
-               data->user_data, username, password);
-
-       free_private_data(data);
+       run_connect(data, username, password);
 }
 
 static int l2tp_connect(struct vpn_provider *provider,
@@ -747,9 +798,21 @@ static int l2tp_connect(struct vpn_provider *provider,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
                        void *user_data)
 {
+       struct l2tp_private_data *data;
        const char *username, *password;
        int err;
 
+       data = g_try_new0(struct l2tp_private_data, 1);
+       if (!data)
+               return -ENOMEM;
+
+       data->provider = vpn_provider_ref(provider);
+       data->task = task;
+       data->if_name = g_strdup(if_name);
+       data->cb = cb;
+       data->user_data = user_data;
+       vpn_provider_set_plugin_data(provider, data);
+
        if (connman_task_set_notify(task, "getsec",
                                        l2tp_get_sec, provider) != 0) {
                err = -ENOMEM;
@@ -762,33 +825,19 @@ static int l2tp_connect(struct vpn_provider *provider,
        DBG("user %s password %p", username, password);
 
        if (!username || !*username || !password || !*password) {
-               struct l2tp_private_data *data;
-
-               data = g_try_new0(struct l2tp_private_data, 1);
-               if (!data)
-                       return -ENOMEM;
-
-               data->task = task;
-               data->if_name = g_strdup(if_name);
-               data->cb = cb;
-               data->user_data = user_data;
-
                err = request_input(provider, request_input_cb, dbus_sender,
                                                                        data);
-               if (err != -EINPROGRESS) {
-                       free_private_data(data);
-                       goto done;
-               }
+               if (err != -EINPROGRESS)
+                       goto error;
+
                return err;
        }
 
-done:
-       return run_connect(provider, task, if_name, cb, user_data,
-                                                       username, password);
+       return run_connect(data, username, password);
 
 error:
-       if (cb)
-               cb(provider, user_data, err);
+       l2tp_connect_done(data, -err);
+       free_private_data(data);
 
        return err;
 }
index fc6ceff..fb63a1a 100644 (file)
@@ -110,6 +110,7 @@ struct oc_private_data {
        GIOChannel *err_ch;
        enum oc_connect_type connect_type;
        bool tried_passphrase;
+       bool group_set;
 };
 
 typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
@@ -473,6 +474,7 @@ static void clear_provider_credentials(struct vpn_provider *provider,
        const char *keys[] = { "OpenConnect.PKCSPassword",
                                "OpenConnect.Username",
                                "OpenConnect.Password",
+                               "OpenConnect.SecondPassword",
                                "OpenConnect.Cookie",
                                NULL
        };
@@ -794,13 +796,46 @@ static gboolean process_auth_form(void *user_data)
 {
        struct process_form_data *form_data = user_data;
        struct oc_private_data *data = form_data->data;
+       struct oc_form_opt_select *authgroup_opt;
        struct oc_form_opt *opt;
        const char *password;
+       const char *group;
+       int i;
 
        g_mutex_lock(&form_data->mutex);
 
        DBG("");
 
+       /*
+        * Special handling for "GROUP:" field, if present.
+        * Different group selections can make other fields disappear/appear
+        */
+       if (form_data->form->authgroup_opt) {
+               group = vpn_provider_get_string(data->provider, "OpenConnect.Group");
+               authgroup_opt = form_data->form->authgroup_opt;
+
+               if (group && !data->group_set) {
+                       for (i = 0; i < authgroup_opt->nr_choices; i++) {
+                               struct oc_choice *choice = authgroup_opt->choices[i];
+
+                               if (!strcmp(group, choice->label)) {
+                                       DBG("Switching to auth group: %s", group);
+                                       openconnect_set_option_value(&authgroup_opt->form,
+                                                                       choice->name);
+                                       data->group_set = true;
+                                       form_data->status = OC_FORM_RESULT_NEWGROUP;
+                                       goto out;
+                               }
+                       }
+
+                       connman_warn("Group choice %s not present", group);
+                       data->err = -EACCES;
+                       clear_provider_credentials(data->provider, true);
+                       form_data->status = OC_FORM_RESULT_ERR;
+                       goto out;
+               }
+       }
+
        switch (data->connect_type) {
        case OC_CONNECT_USERPASS:
        case OC_CONNECT_COOKIE_WITH_USERPASS:
@@ -872,12 +907,21 @@ static gboolean process_auth_form(void *user_data)
                                                "OpenConnect.Username");
                        if (user)
                                opt->_value = strdup(user);
-               } else if (opt->type == OC_FORM_OPT_PASSWORD) {
+               } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+                               g_str_has_prefix(opt->name, "password")) {
+
                        const char *pass = vpn_provider_get_string(
                                                data->provider,
                                                "OpenConnect.Password");
                        if (pass)
                                opt->_value = strdup(pass);
+               } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+                               g_str_has_prefix(opt->name, "secondary_password")) {
+                       const char *pass = vpn_provider_get_string(
+                                               data->provider,
+                                               "OpenConnect.SecondPassword");
+                       if (pass)
+                               opt->_value = strdup(pass);
                }
        }
 
@@ -1201,6 +1245,7 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
        const char *vpnhost = NULL;
        const char *username = NULL;
        const char *password = NULL;
+       const char *second_password = NULL;
        const char *pkcspassword = NULL;
        const char *key;
        DBusMessageIter iter, dict;
@@ -1298,6 +1343,19 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
                        dbus_message_iter_get_basic(&value, &password);
                        vpn_provider_set_string_hide_value(data->provider,
                                        "OpenConnect.Password", password);
+               } else if (g_str_equal(key, "OpenConnect.SecondPassword")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_STRING)
+                               break;
+                       dbus_message_iter_get_basic(&value, &second_password);
+                       vpn_provider_set_string_hide_value(data->provider,
+                                       "OpenConnect.SecondPassword",
+                                       second_password);
                } else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
                        dbus_message_iter_next(&entry);
                        if (dbus_message_iter_get_arg_type(&entry)
@@ -1374,6 +1432,7 @@ static int request_input_credentials_full(
        DBusMessageIter dict;
        int err;
        void *agent;
+       bool use_second_password = false;
 
        if (!data || !cb)
                return -ESRCH;
@@ -1440,6 +1499,16 @@ static int request_input_credentials_full(
                username = vpn_provider_get_string(data->provider,
                                        "OpenConnect.Username");
                vpn_agent_append_user_info(&dict, data->provider, username);
+
+               use_second_password = vpn_provider_get_boolean(data->provider,
+                                       "OpenConnect.UseSecondPassword",
+                                       false);
+
+               if (use_second_password)
+                       request_input_append_to_dict(data->provider, &dict,
+                                       request_input_append_password,
+                                       "OpenConnect.SecondPassword");
+
                break;
        case OC_CONNECT_PUBLICKEY:
                return -EINVAL;
@@ -1520,8 +1589,10 @@ static int oc_connect(struct vpn_provider *provider,
        const char *certificate;
        const char *username;
        const char *password;
+       const char *second_password = NULL;
        const char *private_key;
        int err;
+       bool use_second_password = false;
 
        connman_info("provider %p task %p", provider, task);
 
@@ -1551,8 +1622,18 @@ static int oc_connect(struct vpn_provider *provider,
                                        "OpenConnect.Username");
                password = vpn_provider_get_string(provider,
                                        "OpenConnect.Password");
+
+               use_second_password = vpn_provider_get_boolean(provider,
+                                       "OpenConnect.UseSecondPassword",
+                                       false);
+
+               if (use_second_password)
+                       second_password = vpn_provider_get_string(provider,
+                                       "OpenConnect.SecondPassword");
+
                if (!username || !password || !g_strcmp0(username, "-") ||
-                                       !g_strcmp0(password, "-"))
+                                       !g_strcmp0(password, "-") ||
+                                       (use_second_password && !second_password))
                        goto request_input;
 
                break;
index daf66cd..6e288ef 100644 (file)
 
 static DBusConnection *connection;
 
+enum opt_type {
+       OPT_NONE = 0,
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
 struct {
-       const char *cm_opt;
-       const char *ov_opt;
-       char       has_value;
+       const char      *cm_opt;
+       const char      *ov_opt;
+       const char      *ov_opt_to_null;
+       enum opt_type   opt_type;
 } ov_options[] = {
-       { "Host", "--remote", 1 },
-       { "OpenVPN.CACert", "--ca", 1 },
-       { "OpenVPN.Cert", "--cert", 1 },
-       { "OpenVPN.Key", "--key", 1 },
-       { "OpenVPN.MTU", "--tun-mtu", 1 },
-       { "OpenVPN.NSCertType", "--ns-cert-type", 1 },
-       { "OpenVPN.Proto", "--proto", 1 },
-       { "OpenVPN.Port", "--port", 1 },
-       { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
-       { "OpenVPN.AskPass", "--askpass", 1 },
-       { "OpenVPN.AuthNoCache", "--auth-nocache", 0 },
-       { "OpenVPN.TLSRemote", "--tls-remote", 1 },
-       { "OpenVPN.TLSAuth", NULL, 1 },
-       { "OpenVPN.TLSAuthDir", NULL, 1 },
-       { "OpenVPN.TLSCipher", "--tls-cipher", 1},
-       { "OpenVPN.Cipher", "--cipher", 1 },
-       { "OpenVPN.Auth", "--auth", 1 },
-       { "OpenVPN.CompLZO", "--comp-lzo", 0 },
-       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
-       { "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},
+       { "Host", "--remote", NULL, OPT_STRING},
+       { "OpenVPN.CACert", "--ca", NULL, OPT_STRING},
+       { "OpenVPN.Cert", "--cert", NULL, OPT_STRING},
+       { "OpenVPN.Key", "--key", NULL, OPT_STRING},
+       { "OpenVPN.MTU", "--tun-mtu", NULL, OPT_STRING},
+       { "OpenVPN.NSCertType", "--ns-cert-type", NULL, OPT_STRING},
+       { "OpenVPN.Proto", "--proto", NULL, OPT_STRING},
+       { "OpenVPN.Port", "--port", NULL, OPT_STRING},
+       /*
+        * If the AuthUserPass option is "-", provide the input  via management
+        * interface. To facilitate this set the option as NULL.
+        */
+       { "OpenVPN.AuthUserPass", "--auth-user-pass", "-", OPT_STRING},
+       { "OpenVPN.AskPass", "--askpass", NULL, OPT_STRING},
+       { "OpenVPN.AuthNoCache", "--auth-nocache", NULL, OPT_BOOL},
+       { "OpenVPN.TLSRemote", "--tls-remote", NULL, OPT_STRING},
+       { "OpenVPN.TLSAuth", NULL, NULL, OPT_NONE},
+       { "OpenVPN.TLSCipher", "--tls-cipher", NULL, OPT_STRING},
+       { "OpenVPN.TLSAuthDir", NULL, NULL, OPT_NONE},
+       { "OpenVPN.Cipher", "--cipher", NULL, OPT_STRING},
+       { "OpenVPN.Auth", "--auth", NULL, OPT_STRING},
+       /* Is set to adaptive by default if value is omitted */
+       { "OpenVPN.CompLZO", "--comp-lzo", NULL, OPT_STRING},
+       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", NULL, OPT_STRING},
+       { "OpenVPN.ConfigFile", "--config", NULL, OPT_STRING},
+       { "OpenVPN.DeviceType", NULL, NULL, OPT_NONE},
+       { "OpenVPN.Verb", "--verb", NULL, OPT_STRING},
+       { "OpenVPN.Ping", "--ping", NULL, OPT_STRING},
+       { "OpenVPN.PingExit", "--ping-exit", NULL, OPT_STRING},
+       { "OpenVPN.RemapUsr1", "--remap-usr1", NULL, OPT_STRING},
 };
 
 struct ov_private_data {
@@ -355,29 +367,49 @@ static int ov_save(struct vpn_provider *provider, GKeyFile *keyfile)
 static int task_append_config_data(struct vpn_provider *provider,
                                        struct connman_task *task)
 {
-       const char *option;
        int i;
 
        for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
-               if (!ov_options[i].ov_opt)
-                       continue;
+               const char *ov_opt = ov_options[i].ov_opt;
+               const char *cm_opt = ov_options[i].cm_opt;
+               const char *option = NULL;
+               const char *opt_to_null;
 
-               option = vpn_provider_get_string(provider,
-                                       ov_options[i].cm_opt);
-               if (!option)
+               switch (ov_options[i].opt_type) {
+               case OPT_NONE:
                        continue;
 
-               /*
-                * If the AuthUserPass option is "-", provide the input
-                * via management interface
-                */
-               if (!strcmp(ov_options[i].cm_opt, "OpenVPN.AuthUserPass") &&
-                                               !strcmp(option, "-"))
-                       option = NULL;
+               case OPT_STRING:
+                       if (!ov_opt)
+                               continue;
+
+                       option = vpn_provider_get_string(provider, cm_opt);
+                       /*
+                        * A string option may be used alone without a value
+                        * in which case the default value is used by OpenVPN.
+                        */
+                       if (!option && !vpn_provider_setting_key_exists(
+                                                       provider, ov_opt))
+                               continue;
 
-               if (connman_task_add_argument(task,
-                               ov_options[i].ov_opt,
-                               ov_options[i].has_value ? option : NULL) < 0)
+                       opt_to_null = ov_options[i].ov_opt_to_null;
+                       if (opt_to_null && !g_strcmp0(option, opt_to_null))
+                               option = NULL;
+
+                       break;
+
+               case OPT_BOOL:
+                       if (!ov_opt)
+                               continue;
+
+                       /* Ignore the boolean toggle if option is disabled. */
+                       if (!vpn_provider_get_boolean(provider, cm_opt, false))
+                               continue;
+
+                       break;
+               }
+
+               if (connman_task_add_argument(task, ov_opt, option))
                        return -EIO;
 
        }
@@ -1099,6 +1131,15 @@ static int ov_connect(struct vpn_provider *provider,
        const char *tmpdir;
        struct ov_private_data *data;
 
+       /*
+        * Explicitly set limit of 10 for authentication errors. This defines
+        * the authentication error message limit from the server before VPN
+        * agent is instructed to clear the credentials. This is effective only
+        * after a successful connection has been made within CONNECT_OK_DIFF
+        * time. User defined value for "AuthErrorLimit" overrides this.
+        */
+       vpn_provider_set_auth_error_limit(provider, 10);
+
        data = g_try_new0(struct ov_private_data, 1);
        if (!data)
                return -ENOMEM;
index 4a704bb..7274376 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2010,2013-2014  BMW Car IT GmbH.
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd.
  *
  *  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
@@ -86,12 +87,40 @@ struct {
 static DBusConnection *connection;
 
 struct pptp_private_data {
+       struct vpn_provider *provider;
        struct connman_task *task;
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
 };
 
+static void pptp_connect_done(struct pptp_private_data *data, int err)
+{
+       vpn_provider_connect_cb_t cb;
+       void *user_data;
+
+       if (!data || !data->cb)
+               return;
+
+       /* Ensure that callback is called only once */
+       cb = data->cb;
+       user_data = data->user_data;
+       data->cb = NULL;
+       data->user_data = NULL;
+       cb(data->provider, user_data, err);
+}
+
+static void free_private_data(struct pptp_private_data *data)
+{
+       if (vpn_provider_get_plugin_data(data->provider) == data)
+               vpn_provider_set_plugin_data(data->provider, NULL);
+
+       pptp_connect_done(data, EIO);
+       vpn_provider_unref(data->provider);
+       g_free(data->if_name);
+       g_free(data);
+}
+
 static DBusMessage *pptp_get_sec(struct connman_task *task,
                                DBusMessage *msg, void *user_data)
 {
@@ -125,6 +154,9 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
        char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
        char *ifname = NULL, *nameservers = NULL;
        struct connman_ipaddress *ipaddress = NULL;
+       struct pptp_private_data *data;
+
+       data = vpn_provider_get_plugin_data(provider);
 
        dbus_message_iter_init(msg, &iter);
 
@@ -143,11 +175,22 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
                vpn_provider_set_string_hide_value(provider, "PPTP.Password",
                                        NULL);
 
+               pptp_connect_done(data, EACCES);
                return VPN_STATE_AUTH_FAILURE;
        }
 
-       if (strcmp(reason, "connect"))
+       if (strcmp(reason, "connect")) {
+               pptp_connect_done(data, EIO);
+
+               /*
+                * Stop the task to avoid potential looping of this state when
+                * authentication fails.
+                */
+               if (data && data->task)
+                       connman_task_stop(data->task);
+
                return VPN_STATE_DISCONNECT;
+       }
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -217,6 +260,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
        g_free(nameservers);
        connman_ipaddress_free(ipaddress);
 
+       pptp_connect_done(data, 0);
        return VPN_STATE_CONNECT;
 }
 
@@ -278,6 +322,16 @@ static void pptp_write_bool_option(struct connman_task *task,
        }
 }
 
+static void pptp_died(struct connman_task *task, int exit_code,
+                                                       void *user_data)
+{
+       struct pptp_private_data *data = user_data;
+
+       vpn_died(task, exit_code, data->provider);
+
+       free_private_data(data);
+}
+
 struct request_input_reply {
        struct vpn_provider *provider;
        vpn_provider_password_cb_t callback;
@@ -435,18 +489,18 @@ static int request_input(struct vpn_provider *provider,
        return -EINPROGRESS;
 }
 
-static int run_connect(struct vpn_provider *provider,
-                       struct connman_task *task, const char *if_name,
-                       vpn_provider_connect_cb_t cb, void *user_data,
-                       const char *username, const char *password)
+static int run_connect(struct pptp_private_data *data, const char *username,
+                                                       const char *password)
 {
+       struct vpn_provider *provider = data->provider;
+       struct connman_task *task = data->task;
        GString *pptp_opt_s;
        const char *opt_s;
        const char *host;
        char *str;
        int err, i;
 
-       if (!username || !password) {
+       if (!username || !*username || !password || !*password) {
                DBG("Cannot connect username %s password %p",
                                                username, password);
                err = -EINVAL;
@@ -498,27 +552,19 @@ static int run_connect(struct vpn_provider *provider,
        connman_task_add_argument(task, "plugin",
                                SCRIPTDIR "/libppp-plugin.so");
 
-       err = connman_task_run(task, vpn_died, provider,
-                               NULL, NULL, NULL);
+       err = connman_task_run(task, pptp_died, data, NULL, NULL, NULL);
        if (err < 0) {
                connman_error("pptp failed to start");
                err = -EIO;
-               goto done;
        }
 
 done:
-       if (cb)
-               cb(provider, user_data, err);
+       if (err)
+               pptp_connect_done(data, -err);
 
        return err;
 }
 
-static void free_private_data(struct pptp_private_data *data)
-{
-       g_free(data->if_name);
-       g_free(data);
-}
-
 static void request_input_cb(struct vpn_provider *provider,
                        const char *username,
                        const char *password,
@@ -526,7 +572,7 @@ static void request_input_cb(struct vpn_provider *provider,
 {
        struct pptp_private_data *data = user_data;
 
-       if (!username || !password)
+       if (!username || !*username || !password || !*password)
                DBG("Requesting username %s or password failed, error %s",
                        username, error);
        else if (error)
@@ -536,10 +582,7 @@ static void request_input_cb(struct vpn_provider *provider,
        vpn_provider_set_string_hide_value(provider, "PPTP.Password",
                                                                password);
 
-       run_connect(provider, data->task, data->if_name, data->cb,
-               data->user_data, username, password);
-
-       free_private_data(data);
+       run_connect(data, username, password);
 }
 
 static int pptp_connect(struct vpn_provider *provider,
@@ -547,9 +590,21 @@ static int pptp_connect(struct vpn_provider *provider,
                        vpn_provider_connect_cb_t cb, const char *dbus_sender,
                        void *user_data)
 {
+       struct pptp_private_data *data;
        const char *username, *password;
        int err;
 
+       data = g_try_new0(struct pptp_private_data, 1);
+       if (!data)
+               return -ENOMEM;
+
+       data->provider = vpn_provider_ref(provider);
+       data->task = task;
+       data->if_name = g_strdup(if_name);
+       data->cb = cb;
+       data->user_data = user_data;
+       vpn_provider_set_plugin_data(provider, data);
+
        DBG("iface %s provider %p user %p", if_name, provider, user_data);
 
        if (connman_task_set_notify(task, "getsec",
@@ -563,34 +618,20 @@ static int pptp_connect(struct vpn_provider *provider,
 
        DBG("user %s password %p", username, password);
 
-       if (!username || !password) {
-               struct pptp_private_data *data;
-
-               data = g_try_new0(struct pptp_private_data, 1);
-               if (!data)
-                       return -ENOMEM;
-
-               data->task = task;
-               data->if_name = g_strdup(if_name);
-               data->cb = cb;
-               data->user_data = user_data;
-
+       if (!username || !*username || !password || !*password) {
                err = request_input(provider, request_input_cb, dbus_sender,
                                                                        data);
-               if (err != -EINPROGRESS) {
-                       free_private_data(data);
-                       goto done;
-               }
+               if (err != -EINPROGRESS)
+                       goto error;
+
                return err;
        }
 
-done:
-       return run_connect(provider, task, if_name, cb, user_data,
-                                                       username, password);
+       return run_connect(data, username, password);
 
 error:
-       if (cb)
-               cb(provider, user_data, err);
+       pptp_connect_done(data, -err);
+       free_private_data(data);
 
        return err;
 }
index 59c805c..cc32596 100644 (file)
@@ -3,7 +3,7 @@
  *  ConnMan VPN daemon
  *
  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
- *  Copyright (C) 2019  Jolla Ltd. All rights reserved.
+ *  Copyright (C) 2019-2021  Jolla Ltd. 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
 #include <config.h>
 #endif
 
+/*
+ * One hour difference in seconds between connections for checking whether to
+ * treat the authentication error as a real error or as a result of rapid
+ * transport change.
+ */
+#define CONNECT_OK_DIFF                ((time_t)60*60)
+#define AUTH_ERROR_LIMIT_DEFAULT       1
+
+#define STATE_INTERVAL_DEFAULT         0
+
+#define CONNMAN_STATE_ONLINE           "online"
+#define CONNMAN_STATE_OFFLINE          "offline"
+#define CONNMAN_STATE_READY            "ready"
+#define CONNMAN_STATE_IDLE             "idle"
+
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -32,6 +47,7 @@
 #include <connman/log.h>
 #include <gweb/gresolv.h>
 #include <netdb.h>
+#include <time.h>
 
 #include "../src/connman.h"
 #include "connman/agent.h"
@@ -89,15 +105,57 @@ struct vpn_provider {
        struct connman_ipaddress *prev_ipv4_addr;
        struct connman_ipaddress *prev_ipv6_addr;
        void *plugin_data;
+       unsigned int do_connect_timeout;
        unsigned int auth_error_counter;
        unsigned int conn_error_counter;
        unsigned int signal_watch;
+       unsigned int auth_error_limit;
+       time_t previous_connect_time;
+};
+
+struct vpn_provider_connect_data {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       struct vpn_provider *provider;
 };
 
+static unsigned int get_connman_state_timeout;
+
+static guint connman_signal_watch;
+static guint connman_service_watch;
+
+static bool connman_online;
+static bool state_query_completed;
+
 static void append_properties(DBusMessageIter *iter,
                                struct vpn_provider *provider);
 static int vpn_provider_save(struct vpn_provider *provider);
 
+static void get_connman_state(void);
+
+static void set_state(const char *new_state)
+{
+       if (!new_state || !*new_state)
+               return;
+
+       DBG("old state %s new state %s",
+                               connman_online ?
+                               CONNMAN_STATE_ONLINE "/" CONNMAN_STATE_READY :
+                               CONNMAN_STATE_OFFLINE "/" CONNMAN_STATE_IDLE,
+                               new_state);
+
+       /* States "online" and "ready" mean connman is online */
+       if (!g_ascii_strcasecmp(new_state, CONNMAN_STATE_ONLINE) ||
+                       !g_ascii_strcasecmp(new_state, CONNMAN_STATE_READY))
+               connman_online = true;
+       /* Everything else means connman is offline */
+       else
+               connman_online = false;
+
+       DBG("set state %s connman_online=%s ", new_state,
+                               connman_online ? "true" : "false");
+}
+
 static void free_route(gpointer data)
 {
        struct vpn_route *route = data;
@@ -740,6 +798,61 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
+static gboolean do_connect_timeout_function(gpointer data)
+{
+       struct vpn_provider_connect_data *cdata = data;
+       struct vpn_provider *provider = cdata->provider;
+       int err;
+
+       DBG("");
+
+       /* Keep in main loop if connman is not online. */
+       if (!connman_online)
+               return G_SOURCE_CONTINUE;
+
+       provider->do_connect_timeout = 0;
+       err = __vpn_provider_connect(provider, cdata->msg);
+
+       if (err < 0 && err != -EINPROGRESS) {
+               g_dbus_send_message(cdata->conn,
+                               __connman_error_failed(cdata->msg, -err));
+               cdata->msg = NULL;
+       }
+
+       return G_SOURCE_REMOVE;
+}
+
+static void do_connect_timeout_free(gpointer data)
+{
+       struct vpn_provider_connect_data *cdata = data;
+
+       if (cdata->msg)
+               g_dbus_send_message(cdata->conn,
+                               __connman_error_operation_aborted(cdata->msg));
+
+       dbus_connection_unref(cdata->conn);
+       g_free(data);
+}
+
+static void do_connect_later(struct vpn_provider *provider,
+                                       DBusConnection *conn, DBusMessage *msg)
+{
+       struct vpn_provider_connect_data *cdata =
+                               g_new0(struct vpn_provider_connect_data, 1);
+
+       cdata->conn = dbus_connection_ref(conn);
+       cdata->msg = dbus_message_ref(msg);
+       cdata->provider = provider;
+
+       if (provider->do_connect_timeout)
+               g_source_remove(provider->do_connect_timeout);
+
+       provider->do_connect_timeout =
+                       g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
+                                       do_connect_timeout_function, cdata,
+                                       do_connect_timeout_free);
+}
+
 static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
                                                                void *data)
 {
@@ -748,6 +861,25 @@ static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
 
        DBG("conn %p provider %p", conn, provider);
 
+       if (!connman_online) {
+               if (state_query_completed) {
+                       DBG("%s not started - ConnMan not online/ready",
+                               provider->identifier);
+                       return __connman_error_failed(msg, ENOLINK);
+               }
+
+               DBG("%s start delayed - ConnMan state not queried",
+                       provider->identifier);
+               do_connect_later(provider, conn, msg);
+               return NULL;
+       }
+
+       /* Cancel delayed connection if connmand is online. */
+       if (provider->do_connect_timeout) {
+               g_source_remove(provider->do_connect_timeout);
+               provider->do_connect_timeout = 0;
+       }
+
        err = __vpn_provider_connect(provider, msg);
        if (err < 0 && err != -EINPROGRESS)
                return __connman_error_failed(msg, -err);
@@ -1117,12 +1249,15 @@ static void reset_error_counters(struct vpn_provider *provider)
        if (!provider)
                return;
 
+       DBG("provider %p", provider);
+
        provider->auth_error_counter = provider->conn_error_counter = 0;
 }
 
 static int vpn_provider_save(struct vpn_provider *provider)
 {
        GKeyFile *keyfile;
+       const char *value;
 
        DBG("provider %p immutable %s", provider,
                                        provider->immutable ? "yes" : "no");
@@ -1153,6 +1288,11 @@ static int vpn_provider_save(struct vpn_provider *provider)
        g_key_file_set_string(keyfile, provider->identifier,
                        "VPN.Domain", provider->domain);
 
+       value = vpn_provider_get_string(provider, "AuthErrorLimit");
+       if (value)
+               g_key_file_set_string(keyfile, provider->identifier,
+                                               "AuthErrorLimit", value);
+
        if (provider->user_networks) {
                gchar **networks;
                gsize network_count;
@@ -1269,6 +1409,9 @@ static void provider_destruct(struct vpn_provider *provider)
 {
        DBG("provider %p", provider);
 
+       if (provider->do_connect_timeout)
+               g_source_remove(provider->do_connect_timeout);
+
        if (provider->notify_id != 0)
                g_source_remove(provider->notify_id);
 
@@ -1658,6 +1801,18 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
                append_nameservers(iter, provider->nameservers);
 }
 
+static time_t get_uptime(void)
+{
+       struct timespec t = { 0 };
+
+       if (clock_gettime(CLOCK_BOOTTIME, &t) == -1) {
+               connman_warn("clock_gettime() error %d, uptime failed", errno);
+               return 0;
+       }
+
+       return t.tv_sec;
+}
+
 static int provider_indicate_state(struct vpn_provider *provider,
                                enum vpn_provider_state state)
 {
@@ -1673,6 +1828,8 @@ static int provider_indicate_state(struct vpn_provider *provider,
        provider->state = state;
 
        if (state == VPN_PROVIDER_STATE_READY) {
+               provider->previous_connect_time = get_uptime();
+
                connman_dbus_property_changed_basic(provider->path,
                                        VPN_CONNECTION_INTERFACE, "Index",
                                        DBUS_TYPE_INT32, &provider->index);
@@ -1885,8 +2042,10 @@ void vpn_provider_add_error(struct vpn_provider *provider,
 {
        switch (error) {
        case VPN_PROVIDER_ERROR_UNKNOWN:
+               provider->previous_connect_time = 0;
                break;
        case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+               provider->previous_connect_time = 0;
                ++provider->conn_error_counter;
                break;
        case VPN_PROVIDER_ERROR_LOGIN_FAILED:
@@ -1894,6 +2053,10 @@ void vpn_provider_add_error(struct vpn_provider *provider,
                ++provider->auth_error_counter;
                break;
        }
+
+       DBG("%p connect errors %d auth errors %d", provider,
+                                               provider->conn_error_counter,
+                                               provider->auth_error_counter);
 }
 
 int vpn_provider_indicate_error(struct vpn_provider *provider,
@@ -1902,6 +2065,21 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
        DBG("provider %p id %s error %d", provider, provider->identifier,
                                                                        error);
 
+       /*
+        * Ignore adding of errors when the VPN is idle or not set. Calls may
+        * happen in a case when networks are rapidly changed and the call to
+        * vpn_died() is done before executing the connect_cb() from the
+        * plugin. Then vpn.c:vpn_died() executes the plugin specific died()
+        * function which may free the plugin private data, containing also
+        * the callback which hasn't yet been called. As a result the provider
+        * might already been reset to idle state when the callback is executed
+        * resulting in unnecessary reset of the previous successful connect
+        * timer and adding of an error for already disconnected VPN.
+        */
+       if (provider->state == VPN_PROVIDER_STATE_IDLE ||
+                               provider->state == VPN_PROVIDER_STATE_UNKNOWN)
+               return 0;
+
        vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
 
        vpn_provider_add_error(provider, error);
@@ -2053,6 +2231,7 @@ static void provider_initialize(struct vpn_provider *provider)
                                        g_free, free_route);
        provider->setting_strings = g_hash_table_new_full(g_str_hash,
                                        g_str_equal, g_free, free_setting);
+       provider->auth_error_limit = AUTH_ERROR_LIMIT_DEFAULT;
 }
 
 static struct vpn_provider *vpn_provider_new(void)
@@ -2758,6 +2937,21 @@ bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
        return setting->immutable;
 }
 
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+                                                       const char *key)
+{
+       return g_hash_table_contains(provider->setting_strings, key);
+}
+
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+                                                       unsigned int limit)
+{
+       if (!provider)
+               return;
+
+       provider->auth_error_limit = limit;
+}
+
 bool __vpn_provider_check_routes(struct vpn_provider *provider)
 {
        if (!provider)
@@ -3066,9 +3260,70 @@ const char *vpn_provider_get_path(struct vpn_provider *provider)
        return provider->path;
 }
 
+/*
+ * This crude heuristic is meant to mitigate an issue with certain VPN
+ * providers that allow only one authentication per account at a time. These
+ * providers require the VPN client shuts down cleanly by sending an exit
+ * notification. In many cases this is not possible as the transport may
+ * already be gone and there is no route to the VPN server. In such case server
+ * may return authentication error to indicate that an other client is active
+ * and reserves the slot.
+ *
+ * By allowing the VPN client to try again with the following conditons the
+ * unnecessary credential resets done by VPN agent can be avoided. VPN client
+ * is allowed to retry if 1) there was a successful connection to the server
+ * within the specified CONNECT_OK_DIFF time and 2) the provider specific
+ * limit for auth errors is not reached the unnecessary credential resets in
+ * this case are avoided.
+ *
+ * This feature is controlled by the provider specific value for
+ * "AuthErrorLimit". Setting the value to 0 feature is disabled. User defined
+ * value is preferred if set, otherwise plugin default set with
+ * vpn_provider_set_auth_error_limit() is used, which defaults to
+ * AUTH_ERROR_LIMIT_DEFAULT.
+ */
+static bool ignore_authentication_errors(struct vpn_provider *provider)
+{
+       const char *val;
+       unsigned int limit;
+       time_t uptime;
+       time_t diff;
+
+       val = vpn_provider_get_string(provider, "AuthErrorLimit");
+       if (val)
+               limit = g_ascii_strtoull(val, NULL, 10);
+       else
+               limit = provider->auth_error_limit;
+
+       if (!limit || !provider->previous_connect_time) {
+               DBG("%p errors %u %s", provider, provider->auth_error_counter,
+                                       !limit ?
+                                       "disabled by 0 limit" :
+                                       "no previous ok conn");
+               return false;
+       }
+
+       uptime = get_uptime();
+       diff = uptime - provider->previous_connect_time;
+
+       DBG("%p errors %u limit %u uptime %jd time diff %jd", provider,
+                               provider->auth_error_counter, limit,
+                               (intmax_t)uptime, (intmax_t)diff);
+
+       if (diff <= CONNECT_OK_DIFF && provider->auth_error_counter <= limit) {
+               DBG("ignore auth errors");
+               return true;
+       }
+
+       return false;
+}
+
 unsigned int vpn_provider_get_authentication_errors(
                                                struct vpn_provider *provider)
 {
+       if (ignore_authentication_errors(provider))
+               return 0;
+
        return provider->auth_error_counter;
 }
 
@@ -3214,6 +3469,215 @@ static void remove_unprovisioned_providers(void)
        g_strfreev(providers);
 }
 
+static gboolean connman_property_changed(DBusConnection *conn,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       DBusMessageIter iter, value;
+       const char *key;
+       const char *signature = DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING;
+
+       if (!dbus_message_has_signature(message, signature)) {
+               connman_error("vpn connman property signature \"%s\" "
+                               "does not match expected \"%s\"",
+                       dbus_message_get_signature(message),
+                       signature);
+               return TRUE;
+       }
+
+       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("key %s", key);
+
+       if (g_str_equal(key, "State")) {
+               const char *str;
+
+               if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_STRING)
+                       return TRUE;
+
+               dbus_message_iter_get_basic(&value, &str);
+               set_state(str);
+       }
+
+       return TRUE;
+}
+
+static void get_connman_state_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = NULL;
+       DBusError error;
+       DBusMessageIter iter, array, dict, value;
+
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       const char *key;
+       const char *str;
+
+       DBG("");
+
+       if (!dbus_pending_call_get_completed(call))
+               goto done;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("%s", error.message);
+
+               /*
+                * In case of timeout re-add the state function to main
+                * event loop.
+                */
+               if (g_ascii_strcasecmp(error.name, DBUS_ERROR_TIMEOUT) == 0) {
+                       DBG("D-Bus timeout, re-add get_connman_state()");
+                       get_connman_state();
+               } else {
+                       dbus_error_free(&error);
+                       goto done;
+               }
+       }
+
+       if (!dbus_message_has_signature(reply, signature)) {
+               connman_error("vpnd signature \"%s\" does not match "
+                               "expected \"%s\"",
+                       dbus_message_get_signature(reply),
+                       signature);
+
+               goto done;
+       }
+
+       if (!dbus_message_iter_init(reply, &array))
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               dbus_message_iter_recurse(&dict, &iter);
+
+               dbus_message_iter_get_basic(&iter, &key);
+
+               dbus_message_iter_next(&iter);
+               dbus_message_iter_recurse(&iter, &value);
+
+               if (g_ascii_strcasecmp(key, "State") == 0 &&
+                               dbus_message_iter_get_arg_type(&value) ==
+                                               DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&value, &str);
+
+                       DBG("Got initial state %s", str);
+
+                       set_state(str);
+
+                       /* No need to process further */
+                       break;
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       state_query_completed = true;
+
+done:
+       if (reply)
+               dbus_message_unref(reply);
+
+       if (call)
+               dbus_pending_call_unref(call);
+}
+
+static gboolean run_get_connman_state(gpointer user_data)
+{
+       const char *path = "/";
+       const char *method = "GetProperties";
+       gboolean rval = FALSE;
+
+       DBusMessage *msg = NULL;
+       DBusPendingCall *call = NULL;
+
+       DBG("");
+
+       msg = dbus_message_new_method_call(CONNMAN_SERVICE, path,
+                                       CONNMAN_MANAGER_INTERFACE, method);
+       if (!msg)
+               goto out;
+
+       rval = g_dbus_send_message_with_reply(connection, msg, &call, -1);
+       if (!rval) {
+               connman_error("Cannot call %s on %s", method,
+                                               CONNMAN_MANAGER_INTERFACE);
+               goto out;
+       }
+
+       if (!call) {
+               connman_error("set pending call failed");
+               rval = FALSE;
+               goto out;
+       }
+
+       if (!dbus_pending_call_set_notify(call, get_connman_state_reply,
+                                                               NULL, NULL)) {
+               connman_error("set notify to pending call failed");
+
+               if (call)
+                       dbus_pending_call_unref(call);
+
+               rval = FALSE;
+       }
+
+out:
+       if (msg)
+               dbus_message_unref(msg);
+
+       /* In case sending was success, unset timeout function id */
+       if (rval) {
+               DBG("unsetting get_connman_state_timeout id");
+               get_connman_state_timeout = 0;
+       }
+
+       /* Return FALSE in case of success to remove from main event loop */
+       return !rval;
+}
+
+static void get_connman_state(void)
+{
+       if (get_connman_state_timeout)
+               return;
+
+       get_connman_state_timeout = g_timeout_add(STATE_INTERVAL_DEFAULT,
+                                               run_get_connman_state, NULL);
+}
+
+static void connman_service_watch_connected(DBusConnection *conn,
+                               void *user_data)
+{
+       DBG("");
+
+       get_connman_state();
+}
+
+static void connman_service_watch_disconnected(DBusConnection *conn,
+                               void *user_data)
+{
+       DBG("");
+
+       set_state(CONNMAN_STATE_IDLE);
+
+       /* Set state query variable to initial state */
+       state_query_completed = false;
+}
+
 int __vpn_provider_init(void)
 {
        int err;
@@ -3233,6 +3697,20 @@ int __vpn_provider_init(void)
 
        provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                NULL, unregister_provider);
+
+       connman_service_watch = g_dbus_add_service_watch(connection,
+                                       CONNMAN_SERVICE,
+                                       connman_service_watch_connected,
+                                       connman_service_watch_disconnected,
+                                       NULL, NULL);
+
+       connman_signal_watch = g_dbus_add_signal_watch(connection,
+                                       CONNMAN_SERVICE, NULL,
+                                       CONNMAN_MANAGER_INTERFACE,
+                                       PROPERTY_CHANGED,
+                                       connman_property_changed,
+                                       NULL, NULL);
+
        return 0;
 }
 
@@ -3247,5 +3725,13 @@ void __vpn_provider_cleanup(void)
        g_hash_table_destroy(provider_hash);
        provider_hash = NULL;
 
+       if (get_connman_state_timeout) {
+               if (!g_source_remove(get_connman_state_timeout))
+                       connman_error("connman state timeout not removed");
+       }
+
+       g_dbus_remove_watch(connection, connman_service_watch);
+       g_dbus_remove_watch(connection, connman_signal_watch);
+
        dbus_connection_unref(connection);
 }
index f7fa859..5d1455d 100644 (file)
@@ -88,6 +88,10 @@ int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
                                                        bool force_change);
 bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
                                                        bool default_value);
+bool vpn_provider_setting_key_exists(struct vpn_provider *provider,
+                                                       const char *key);
+void vpn_provider_set_auth_error_limit(struct vpn_provider *provider,
+                                                       unsigned int limit);
 
 int vpn_provider_set_state(struct vpn_provider *provider,
                                        enum vpn_provider_state state);
index 295c05c..5a02d77 100644 (file)
@@ -797,75 +797,71 @@ static const char *type2string(uint16_t type)
 
 static GIOChannel *channel = NULL;
 
-struct rtnl_request {
-       struct nlmsghdr hdr;
-       struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
 
 static GSList *request_list = NULL;
 static guint32 request_seq = 0;
 
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
 {
        GSList *list;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
-               if (req->hdr.nlmsg_seq == seq)
-                       return req;
+               if (hdr->nlmsg_seq == seq)
+                       return hdr;
        }
 
        return NULL;
 }
 
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
 {
        struct sockaddr_nl addr;
        int sk;
 
        debug("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
        sk = g_io_channel_unix_get_fd(channel);
 
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
 
-       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+       return sendto(sk, hdr, hdr->nlmsg_len, 0,
                                (struct sockaddr *) &addr, sizeof(addr));
 }
 
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
 {
-       request_list = g_slist_append(request_list, req);
+       request_list = g_slist_append(request_list, hdr);
 
        if (g_slist_length(request_list) > 1)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static int process_response(guint32 seq)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
 
        debug("seq %d", seq);
 
-       req = find_request(seq);
-       if (req) {
-               request_list = g_slist_remove(request_list, req);
-               g_free(req);
+       hdr = find_request(seq);
+       if (hdr) {
+               request_list = g_slist_remove(request_list, hdr);
+               g_free(hdr);
        }
 
-       req = g_slist_nth_data(request_list, 0);
-       if (!req)
+       hdr = g_slist_nth_data(request_list, 0);
+       if (!hdr)
                return 0;
 
-       return send_request(req);
+       return send_request(hdr);
 }
 
 static void rtnl_message(void *buf, size_t len)
@@ -960,62 +956,65 @@ static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data
 
 static int send_getlink(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETLINK;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETLINK;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static int send_getaddr(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETADDR;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETADDR;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       return queue_request(req);
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
+
+       return queue_request(hdr);
 }
 
 static int send_getroute(void)
 {
-       struct rtnl_request *req;
+       struct nlmsghdr *hdr;
+       struct rtgenmsg *msg;
 
        debug("");
 
-       req = g_try_malloc0(RTNL_REQUEST_SIZE);
-       if (!req)
-               return -ENOMEM;
+       hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+       hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+       hdr->nlmsg_type = RTM_GETROUTE;
+       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       hdr->nlmsg_pid = 0;
+       hdr->nlmsg_seq = request_seq++;
 
-       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
-       req->hdr.nlmsg_type = RTM_GETROUTE;
-       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
-       req->hdr.nlmsg_pid = 0;
-       req->hdr.nlmsg_seq = request_seq++;
-       req->msg.rtgen_family = AF_INET;
+       msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+       msg->rtgen_family = AF_INET;
 
-       return queue_request(req);
+       return queue_request(hdr);
 }
 
 static gboolean update_timeout_cb(gpointer user_data)
@@ -1158,14 +1157,14 @@ void __vpn_rtnl_cleanup(void)
        update_list = NULL;
 
        for (list = request_list; list; list = list->next) {
-               struct rtnl_request *req = list->data;
+               struct nlmsghdr *hdr = list->data;
 
                debug("%s len %d type %d flags 0x%04x seq %d",
-                               type2string(req->hdr.nlmsg_type),
-                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
-                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+                               type2string(hdr->nlmsg_type),
+                               hdr->nlmsg_len, hdr->nlmsg_type,
+                               hdr->nlmsg_flags, hdr->nlmsg_seq);
 
-               g_free(req);
+               g_free(hdr);
                list->data = NULL;
        }