summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.mailmap3
-rw-r--r--AUTHORS26
-rw-r--r--ChangeLog35
-rw-r--r--Makefile.am21
-rw-r--r--Makefile.plugins9
-rw-r--r--README25
-rw-r--r--acinclude.m44
-rwxr-xr-xbootstrap-configure3
-rw-r--r--client/agent.c2
-rw-r--r--client/commands.c60
-rw-r--r--configure.ac48
-rw-r--r--doc/clock-api.txt9
-rw-r--r--doc/connman-vpn-provider.config.5.in2
-rw-r--r--doc/connman-vpn.8.in4
-rw-r--r--doc/connman.8.in11
-rw-r--r--doc/connman.conf.5.in44
-rw-r--r--doc/technology-api.txt7
-rw-r--r--doc/vpn-agent-api.txt13
-rw-r--r--doc/vpn-config-format.txt2
-rw-r--r--doc/vpn-connection-api.txt22
-rw-r--r--gdbus/watch.c65
-rw-r--r--gdhcp/client.c45
-rw-r--r--gdhcp/common.c24
-rw-r--r--gdhcp/common.h2
-rw-r--r--gdhcp/server.c12
-rw-r--r--gsupplicant/gsupplicant.h10
-rw-r--r--gsupplicant/supplicant.c88
-rw-r--r--gweb/gresolv.c1
-rw-r--r--gweb/gweb.c2
-rw-r--r--include/agent.h1
-rw-r--r--include/inet.h10
-rw-r--r--include/ipaddress.h4
-rw-r--r--include/network.h5
-rw-r--r--include/option.h35
-rw-r--r--include/provider.h4
-rw-r--r--include/service.h1
-rw-r--r--include/setting.h2
-rw-r--r--include/technology.h7
-rw-r--r--include/timeserver.h7
-rw-r--r--plugins/bluetooth.c5
-rw-r--r--plugins/dundee.c2
-rw-r--r--plugins/ethernet.c13
-rw-r--r--plugins/gadget.c1
-rw-r--r--plugins/iwd.c548
-rw-r--r--plugins/neard.c6
-rw-r--r--plugins/ofono.c9
-rw-r--r--plugins/vpn.c314
-rw-r--r--plugins/wifi.c273
-rw-r--r--scripts/libppp-compat.h127
-rw-r--r--scripts/libppp-plugin.c15
-rw-r--r--src/agent-connman.c14
-rw-r--r--src/agent.c24
-rw-r--r--src/bridge.c3
-rw-r--r--src/clock.c12
-rw-r--r--src/config.c6
-rw-r--r--src/connection.c23
-rw-r--r--src/connman.h32
-rw-r--r--src/dbus.c26
-rw-r--r--src/dhcp.c5
-rw-r--r--src/dhcpv6.c2
-rw-r--r--src/dns-systemd-resolved.c4
-rw-r--r--src/dnsproxy.c1994
-rw-r--r--src/inet.c669
-rw-r--r--src/ipaddress.c12
-rw-r--r--src/ipconfig.c243
-rw-r--r--src/iptables.c6
-rw-r--r--src/main.c191
-rw-r--r--src/main.conf25
-rw-r--r--src/manager.c3
-rw-r--r--src/network.c52
-rw-r--r--src/ntp.c2
-rw-r--r--src/peer.c7
-rw-r--r--src/provider.c110
-rw-r--r--src/resolver.c33
-rw-r--r--src/rtnl.c145
-rw-r--r--src/service.c637
-rw-r--r--src/session.c2
-rw-r--r--src/shared/util.c1
-rw-r--r--src/technology.c70
-rw-r--r--src/tethering.c3
-rw-r--r--src/timeserver.c194
-rw-r--r--src/timezone.c105
-rw-r--r--src/wispr.c246
-rwxr-xr-xtest/monitor-connman4
-rwxr-xr-xtest/monitor-services4
-rwxr-xr-xtest/p2p-on-supplicant13
-rwxr-xr-xtest/simple-agent4
-rwxr-xr-xtest/test-counter5
-rwxr-xr-xtest/test-session10
-rwxr-xr-xtools/dnsproxy-simple-test195
-rw-r--r--tools/dnsproxy-standalone.c155
-rw-r--r--tools/dnsproxy-test.c11
-rw-r--r--tools/ip6tables-test.c2
-rw-r--r--tools/iptables-test.c2
-rw-r--r--unit/test-iptables.c4
-rw-r--r--vpn/connman-vpn.service.in2
-rw-r--r--vpn/main.c5
-rw-r--r--vpn/plugins/l2tp.c135
-rw-r--r--vpn/plugins/openconnect.c978
-rw-r--r--vpn/plugins/openvpn.c155
-rw-r--r--vpn/plugins/pptp.c159
-rw-r--r--vpn/plugins/vpn.c61
-rw-r--r--vpn/plugins/vpn.h2
-rw-r--r--vpn/plugins/vpnc.c76
-rw-r--r--vpn/plugins/wireguard.c96
-rw-r--r--vpn/vpn-config.c19
-rw-r--r--vpn/vpn-ipconfig.c10
-rw-r--r--vpn/vpn-polkit.policy4
-rw-r--r--vpn/vpn-provider.c786
-rw-r--r--vpn/vpn-provider.h8
-rw-r--r--vpn/vpn-rtnl.c127
-rw-r--r--vpn/vpn-settings.c63
-rw-r--r--vpn/vpn-util.c224
-rw-r--r--vpn/vpn.h20
115 files changed, 7137 insertions, 3092 deletions
diff --git a/.gitignore b/.gitignore
index cd7d8810..9e8d1a40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*.a
*.lo
*.la
+*~
.deps
.libs
.dirstamp
diff --git a/.mailmap b/.mailmap
index 1e374a64..ef44bd6e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -11,3 +11,6 @@ Bing Niu <bing.niu@intel.com> <bing.niu@intel.com>
Naveen Singh <naveensingh0977@gmail.com> <naveensingh0977@gmail.com>
Mylène Josserand <josserand.mylene@gmail.com> <josserand.mylene@gmail.com>
Måns Rullgård <mans@mansr.com> <mans@mansr.com>
+Emmanuel VAUTRIN <Emmanuel.VAUTRIN@cpexterne.org> <Emmanuel.VAUTRIN@cpexterne.org>
+Simon Holesch <Simon.Holesch@bshg.com> <Simon.Holesch@bshg.com>
+Nishant Chaprana <n.chaprana@samsung.com> <n.chaprana@samsung.com>
diff --git a/AUTHORS b/AUTHORS
index e03a071a..70cb07ce 100644
--- 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>
@@ -159,3 +159,27 @@ Yasser <yasser.toor@gmail.com>
Matt Vogt <matthew.vogt@jollamobile.com>
David Llewellyn-Jones <david.llewellyn-jones@jolla.com>
David Weidenkopf <David.Weidenkopf@Arthrex.com>
+Maxime Roussin-Bélanger <maxime.roussinbelanger@gmail.com>
+Holesch, Simon (GED-SDD1) <Simon.Holesch@bshg.com>
+Christoph Steiger <c.steiger@lemonage.de>
+Markus Held <mjh42@gmx.de>
+Sergey Matyukevich <geomatsi@gmail.com>
+Pieter Cardoen <P.Cardoen@TELEVIC.com>
+Emmanuel Vautrin <emmanuel.vautrin@cpexterne.org>
+Boleslaw Tokarski <boleslaw.tokarski@jolla.com>
+Gabriel FORTE <gforte@wyplay.com>
+Colin Wee <cwee@tesla.com>
+Valery Kashcheev <v.kascheev@omp.ru>
+Alyssa Ross <hi@alyssa.is>
+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>
+Sebastian Pipping <sebastian@pipping.org>
+Daniel Linjama <daniel@dev.linjama.com>
+Nathan Crandall <ncrandall@tesla.com>
+Ben Kohler <bkohler@gentoo.org>
+Polina Smirnova <moe.hwr@gmail.com>
+Eivind Næss <eivnaes@yahoo.com>
+Oskar Roesler <o.roesler@oscloud.info>
diff --git a/ChangeLog b/ChangeLog
index dedc1fee..d3819e66 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+ver 1.42:
+ Fix issue with iwd and signal strength calculation.
+ Fix issue with iwd and handling service removal.
+ Fix issue with iwd and handling new connections.
+ Fix issue with handling default online check URL.
+ Fix issue with handling nameservers refresh.
+ Fix issue with handling proxy from DHCP lease.
+ Fix issue with handling multiple proxies from PAC.
+ Fix issue with handling manual time update changes.
+ Fix issue with handling invalid gateway routes.
+ Fix issue with handling hidden WiFi agent requests.
+ Fix issue with handling WiFi SAE authentication failure.
+ Fix issue with handling DNS Proxy and TCP server replies.
+ Add support for regulatory domain following timezone.
+ Add support for localtime configuration option.
+
+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.
+ Fix issue with DNS Proxy stack-based buffer overflow attack.
+
+ver 1.39:
+ Fix issue with scanning state synchronization and iwd.
+ Fix issue with invalid key with 4-way handshake offloading.
+ Fix issue with DNS proxy length checks to prevent buffer overflow.
+ Fix issue with DHCP leaking stack data via uninitialized variable.
+
ver 1.38:
Fix issue with online check on IP address update.
Fix issue with OpenVPN and encrypted private keys.
diff --git a/Makefile.am b/Makefile.am
index 5971ca9b..1a3dbe3c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,7 +17,7 @@ include_HEADERS = include/log.h include/plugin.h \
nodist_include_HEADERS = include/version.h
noinst_HEADERS = include/rtnl.h include/task.h \
- include/dbus.h include/option.h \
+ include/dbus.h \
include/provider.h include/vpn-dbus.h \
include/utsname.h include/timeserver.h include/proxy.h \
include/technology.h include/setting.h \
@@ -181,7 +181,7 @@ vpn_connman_vpnd_SOURCES = $(builtin_vpn_sources) $(backtrace_sources) \
vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
vpn/vpn-agent.c vpn/vpn-agent.h src/inotify.c \
- vpn/vpn-config.c vpn/vpn-settings.c
+ vpn/vpn-config.c vpn/vpn-settings.c vpn/vpn-util.c
vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
@@ -243,6 +243,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
-DSCRIPTDIR=\""$(build_scriptdir)"\" \
-DSTORAGEDIR=\""$(storagedir)\"" \
-DVPN_STORAGEDIR=\""$(vpn_storagedir)\"" \
+ -DRUNSTATEDIR=\""$(runstatedir)"\" \
-DCONFIGDIR=\""$(configdir)\""
if VPN
@@ -275,6 +276,7 @@ vpn_connman_vpnd_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
-DSCRIPTDIR=\""$(build_scriptdir)"\" \
-DSTORAGEDIR=\""$(storagedir)\"" \
-DVPN_STORAGEDIR=\""$(vpn_storagedir)\"" \
+ -DRUNSTATEDIR=\""$(runstatedir)"\" \
-DCONFIGDIR=\""$(configdir)\"" \
-I$(builddir)/vpn
@@ -434,6 +436,21 @@ test_scripts += test/vpn-connect test/vpn-disconnect test/vpn-get \
if TEST
testdir = $(pkglibdir)/test
test_SCRIPTS = $(test_scripts)
+
+if INTERNAL_DNS_BACKEND
+tools_dnsproxy_standalone_CFLAGS = $(src_connmand_CFLAGS) -I$(srcdir)/src -DDNSPROXY_DEBUG
+tools_dnsproxy_standalone_SOURCES = tools/dnsproxy-standalone.c $(src_connmand_SOURCES)
+# for EXTRA_PROGRAMS the BUILT_SOURCES aren't automatically added as
+# dependency, so let's do it explicitly
+tools/dnsproxy-standalone.c: $(BUILT_SOURCES)
+tools_dnsproxy_standalone_LDADD = $(src_connmand_LDADD)
+# pass -zmuldefs to let the linker tolerate the duplicate definition of
+# main(), the first definition from dnsproxy-standalone should be used
+tools_dnsproxy_standalone_LDFLAGS = $(src_connmand_LDFLAGS) -Wl,-zmuldefs
+
+noinst_PROGRAMS += tools/dnsproxy-standalone
+endif
+
endif
EXTRA_DIST += $(test_scripts)
diff --git a/Makefile.plugins b/Makefile.plugins
index ab2bbe04..bd5049ec 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -92,7 +92,9 @@ builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
if OPENCONNECT_BUILTIN
builtin_vpn_modules += openconnect
builtin_vpn_sources += vpn/plugins/openconnect.c
-builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
+builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\" \
+ @LIBOPENCONNECT_CFLAGS@
+builtin_vpn_libadd += @LIBOPENCONNECT_LIBS@
else
vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la
vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS)
@@ -100,8 +102,10 @@ vpn_plugins_openconnect_la_SOURCES = vpn/plugins/openconnect.c
vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \
-DOPENCONNECT=\"@OPENCONNECT@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
- -DSCRIPTDIR=\""$(build_scriptdir)"\"
+ -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+ @LIBOPENCONNECT_CFLAGS@
vpn_plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_openconnect_la_LIBADD = @LIBOPENCONNECT_LIBS@
endif
endif
@@ -134,6 +138,7 @@ vpn_plugin_objects += $(plugins_vpnc_la_OBJECTS)
vpn_plugins_vpnc_la_SOURCES = vpn/plugins/vpnc.c
vpn_plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
+ -DRUNSTATEDIR=\""$(runstatedir)"\" \
-DSCRIPTDIR=\""$(build_scriptdir)"\"
vpn_plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
endif
diff --git a/README b/README
index e911bc2d..e3268c82 100644
--- 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
@@ -444,10 +453,16 @@ Information
===========
Mailing list:
- connman@connman.net
+ connman@lists.linux.dev
+
+If you would like to subscribe to receive mail in your inbox, just
+send a (empty) message from your email account to
+
+ connman+subscribe@lists.linux.dev
-For additional information about the project visit ConnMan web site:
- https://01.org/connman
- http://www.connman.net
+Mailing list archive:
+ https://lore.kernel.org/connman
-You can report bugs at https://01.org/jira/browse/CM
+IRC:
+ ircs://irc.oftc.net:6697/#connman (for SSL)
+ irc://irc.oftc.net:6667/#connman (for non-SSL)
diff --git a/acinclude.m4 b/acinclude.m4
index 9e8e0dc5..262465d8 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -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
])
diff --git a/bootstrap-configure b/bootstrap-configure
index 3f697987..ed5ecd78 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -18,4 +18,5 @@ fi
--enable-vpnc=builtin \
--enable-session-policy-local=builtin \
--enable-nmcompat \
- --enable-polkit $*
+ --enable-polkit $* \
+ --enable-test
diff --git a/client/agent.c b/client/agent.c
index 1cad3e03..94ace7cd 100644
--- a/client/agent.c
+++ b/client/agent.c
@@ -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 },
{ },
diff --git a/client/commands.c b/client/commands.c
index 94c375dd..53cc14c8 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -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 },
diff --git a/configure.ac b/configure.ac
index 5041e69f..f224bcc7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(connman, 1.38)
+AC_INIT(connman, 1.42)
AC_CONFIG_MACRO_DIR([m4])
@@ -22,7 +22,7 @@ AC_SUBST(abs_top_builddir)
AC_LANG_C
AC_USE_SYSTEM_EXTENSIONS
-AC_PROG_CC
+AC_PROG_CC_C99
AM_PROG_CC_C_O
AC_PROG_CC_PIE
AC_PROG_INSTALL
@@ -82,6 +82,8 @@ if (test "${enable_openconnect}" != "no"); then
OPENCONNECT="${path_openconnect}"
AC_SUBST(OPENCONNECT)
fi
+ PKG_CHECK_MODULES(LIBOPENCONNECT, openconnect >= 8, [],
+ AC_MSG_ERROR(openconnect >= 8 is required))
fi
AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no")
AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin")
@@ -133,14 +135,6 @@ AC_ARG_ENABLE(l2tp,
AC_HELP_STRING([--enable-l2tp], [enable l2tp support]),
[enable_l2tp=${enableval}], [enable_l2tp="no"])
if (test "${enable_l2tp}" != "no"); then
- if (test -z "${path_pppd}"); then
- AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
- else
- PPPD="${path_pppd}"
- AC_SUBST(PPPD)
- fi
- AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
- AC_MSG_ERROR(ppp header files are required))
if (test -z "${path_l2tp}"); then
AC_PATH_PROG(L2TP, [xl2tpd], [/usr/sbin/xl2tpd], $PATH:/sbin:/usr/sbin)
else
@@ -158,6 +152,18 @@ AC_ARG_ENABLE(pptp,
AC_HELP_STRING([--enable-pptp], [enable pptp support]),
[enable_pptp=${enableval}], [enable_pptp="no"])
if (test "${enable_pptp}" != "no"); then
+ if (test -z "${path_pptp}"); then
+ AC_PATH_PROG(PPTP, [pptp], [/usr/sbin/pptp], $PATH:/sbin:/usr/sbin)
+ else
+ PPTP="${path_pptp}"
+ AC_SUBST(PPTP)
+ fi
+fi
+AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
+AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
+
+if (test "${enable_pptp}" != "no" || test "${enable_l2tp}" != "no"); then
+
if (test -z "${path_pppd}"); then
AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
else
@@ -166,15 +172,23 @@ if (test "${enable_pptp}" != "no"); then
fi
AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
AC_MSG_ERROR(ppp header files are required))
- if (test -z "${path_pptp}"); then
- AC_PATH_PROG(PPTP, [pptp], [/usr/sbin/pptp], $PATH:/sbin:/usr/sbin)
- else
- PPTP="${path_pptp}"
- AC_SUBST(PPTP)
+ AC_CHECK_HEADERS([pppd/chap.h pppd/chap-new.h pppd/chap_ms.h])
+
+ PKG_CHECK_EXISTS([pppd],
+ [AS_VAR_SET([pppd_pkgconfig_support],[yes])])
+
+ PPPD_VERSION=2.4.9
+ if test x"$pppd_pkgconfig_support" = xyes; then
+ PPPD_VERSION=`$PKG_CONFIG --modversion pppd`
fi
+
+ AC_DEFINE_UNQUOTED([PPP_VERSION(x,y,z)],
+ [((x & 0xFF) << 16 | (y & 0xFF) << 8 | (z & 0xFF) << 0)],
+ [Macro to help determine the particular version of pppd])
+ PPP_VERSION=$(echo $PPPD_VERSION | sed -e "s/\./\,/g")
+ AC_DEFINE_UNQUOTED(WITH_PPP_VERSION, PPP_VERSION($PPP_VERSION),
+ [The real version of pppd represented as an int])
fi
-AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
-AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
AC_CHECK_HEADERS(resolv.h, dummy=yes,
AC_MSG_ERROR(resolver header files are required))
diff --git a/doc/clock-api.txt b/doc/clock-api.txt
index 6818f5a8..a7fdf555 100644
--- a/doc/clock-api.txt
+++ b/doc/clock-api.txt
@@ -85,3 +85,12 @@ Properties uint64 Time [readonly or readwrite] [experimental]
This list of servers is used when TimeUpdates is set
to auto.
+
+ boolean TimeserverSynced [readonly] [experimental]
+
+ This value indicates if the current system time
+ is synced via NTP servers.
+
+ True when TimeUpdates is set to auto and Time value
+ results from the system time synchronization with a NTP
+ server. Otherwise False.
diff --git a/doc/connman-vpn-provider.config.5.in b/doc/connman-vpn-provider.config.5.in
index cea99e65..036b60c1 100644
--- a/doc/connman-vpn-provider.config.5.in
+++ b/doc/connman-vpn-provider.config.5.in
@@ -105,7 +105,7 @@ MTU of the tunnel.
.B OpenVPN.NSCertType=client \fR|\fB server
Peer certificate type, either \fBclient\fP or \fBserver\fP.
.TP
-.BI OpenVPN.Protocol= protocol
+.BI OpenVPN.Proto= protocol
Use \fIprotocol\fP.
.TP
.BI OpenVPN.Port= port
diff --git a/doc/connman-vpn.8.in b/doc/connman-vpn.8.in
index 6130b3b8..829c4983 100644
--- a/doc/connman-vpn.8.in
+++ b/doc/connman-vpn.8.in
@@ -19,7 +19,6 @@ ConnMan-VPN \- VPN management daemon
.RB [\| \-P
.IR plugin [,...]\|]
.RB [\| \-n \|]
-.RB [\| \-r \|]
.SH DESCRIPTION
The \fIConnMan-VPN\fP provides a daemon for managing vpn connections together
with \fBconnmand\fP(8). The Connection Manager is designed to be slim and to
@@ -54,9 +53,6 @@ present, then only debug prints from that source file are printed. Example:
.BR \-n ", " \-\-nodaemon
Do not daemonize. This is useful for debugging, and directs log output to
the controlling terminal in addition to syslog.
-.TP
-.BR \-r ", " \-\-routes
-Manage VPN routes instead of telling \fBconnmand\fP(8) to do it.
.SH SEE ALSO
.BR connmanctl (1), \ connman-vpn.conf (5), \c
.BR \ connman-vpn-provider.config (5), \ connmand (8)
diff --git a/doc/connman.8.in b/doc/connman.8.in
index 85e7c5e0..ffee8d3f 100644
--- a/doc/connman.8.in
+++ b/doc/connman.8.in
@@ -66,7 +66,8 @@ Only manage these network interfaces. By default all network interfaces
are managed.
.TP
.BR \-I\ \fIinterface \fR[,...],\ \-\-nodevice= \fIinterface \fR[,...]
-Never manage these network interfaces.
+Never manage these network interfaces. The option can be a pattern
+containing "*" and "?" characters.
.TP
.BI \-p\ plugin \fR[,...],\ \fB\-\-plugin= plugin \fR[,...]
Load these plugins only. The option can be a pattern containing
@@ -94,9 +95,11 @@ to itself by setting nameserver to 127.0.0.1 in \fBresolv.conf\fP(5)
file or leave DNS management to an external entity, such as
systemd-resolved. If this is not desired and you want that all programs
call directly some DNS server, then you can use the \fB--nodnsproxy\fP
-option. If this option is used, then ConnMan is not able to cache the
-DNS queries because the DNS traffic is not going through ConnMan and that
-can cause some extra network traffic.
+option. ConnMan then figures out the DNS server and search domain
+on startup and sets them in \fBresolv.conf\fP(5). If this option is
+used, then ConnMan is not able to cache the DNS queries because the DNS
+traffic is not going through ConnMan and that can cause some extra
+network traffic.
.SH SEE ALSO
.BR connmanctl (1), \ connman.conf (5), \ connman-service.config (5), \c
.BR \ connman-vpn (8)
diff --git a/doc/connman.conf.5.in b/doc/connman.conf.5.in
index a90c2291..1f9b2908 100644
--- a/doc/connman.conf.5.in
+++ b/doc/connman.conf.5.in
@@ -129,7 +129,8 @@ are in 'online' or 'ready' states, the newly connected service is
the only one that will be kept connected. A service connected by the
user will be used until going out of network coverage. With this
setting enabled applications will notice more network breaks than
-normal. Default value is false.
+normal. Note this options can't be used with VPNs.
+Default value is false.
.TP
.BI TetheringTechnologies= technology\fR[,...]
List of technologies that are allowed to enable tethering separated by ",".
@@ -167,6 +168,30 @@ 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.
+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!!!
+In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET
+to detect the loss of end-to-end connectivity.
+If this setting is false, when the default service transitions to ONLINE
+state, the HTTP GET request is no more called until next cycle, initiated
+by a transition of the default service to DISCONNECT state.
+If this setting is true, the HTTP GET request keeps beeing called to guarantee
+that end-to-end connectivity is still successful. If not, the default service
+will transition to READY state, enabling another service to become the
+default one, in replacement.
+Default value is false.
+.TP
.BI AutoConnectRoamingServices=true\ \fR|\fB\ false
Automatically connect roaming services. This is not recommended unless you know
you won't have any billing problem.
@@ -181,6 +206,23 @@ address will be chosen instead (according to RFC3927). If an address conflict
occurs for an address offered via DHCP, ConnMan send a DHCP DECLINE once and
for the second conflict resort to finding an IPv4LL address.
Default value is false.
+.TP
+.BI Localtime= string
+Path to localtime file. Defaults to /etc/localtime.
+.TP
+.BI RegdomFollowsTimezone= true\ \fR|\fB\ false
+Enable regdomain to be changed along timezone changes. With this option set to
+true each time the timezone changes the first present ISO3166 country code is
+being read from /usr/share/zoneinfo/zone1970.tab and set as regdom value.
+Default value is false.
+.TP
+.BI ResolvConf= string
+Path to resolv.conf file. If the file does not exist, but intermediate
+directories exist, it will be created.
+If this option is not set, it tries to write into
+@runstatedir@/connman/resolv.conf and fallbacks to @sysconfdir@/resolv.conf if
+it fails (@runstatedir@/connman does not exist or is not writeable).
+If you do not want to update resolv.conf, you can set /dev/null.
.SH "EXAMPLE"
The following example configuration disables hostname updates and enables
ethernet tethering.
diff --git a/doc/technology-api.txt b/doc/technology-api.txt
index f22e9b29..cdf30396 100644
--- a/doc/technology-api.txt
+++ b/doc/technology-api.txt
@@ -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.
diff --git a/doc/vpn-agent-api.txt b/doc/vpn-agent-api.txt
index ffa6fadd..ecc8faaa 100644
--- a/doc/vpn-agent-api.txt
+++ b/doc/vpn-agent-api.txt
@@ -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
diff --git a/doc/vpn-config-format.txt b/doc/vpn-config-format.txt
index 91e2a636..f2adf299 100644
--- a/doc/vpn-config-format.txt
+++ b/doc/vpn-config-format.txt
@@ -38,7 +38,7 @@ Allowed fields:
VPN related parameters (M = mandatory, O = optional):
- Name: A user defined name for the VPN (M)
- Host: VPN server IP address (M)
-- Domain: Domain name for the VPN service (M)
+- Domain: Domain name for the VPN service (O)
- Networks: The networks behind the VPN link can be defined here. This can
be missing if all traffic should go via VPN tunnel. If there are more
than one network, then separate them by comma. Format of the entry
diff --git a/doc/vpn-connection-api.txt b/doc/vpn-connection-api.txt
index ec557889..2d3e0078 100644
--- a/doc/vpn-connection-api.txt
+++ b/doc/vpn-connection-api.txt
@@ -130,7 +130,14 @@ Properties string State [readonly]
configured externally via a configuration file.
The only valid operation are Connect(), Disconnect()
- and GetProperties()
+ and GetProperties()
+
+ boolean SplitRouting
+
+ This value reflects the split routing setting on
+ connmand side. By default, this value is omitted and
+ defaults to false. The value needs to be explicitly
+ set to true for VPN to be split routed.
int Index [readonly]
@@ -228,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
diff --git a/gdbus/watch.c b/gdbus/watch.c
index 447e4867..8fa76cda 100644
--- a/gdbus/watch.c
+++ b/gdbus/watch.c
@@ -136,38 +136,55 @@ static struct filter_data *filter_data_find(DBusConnection *connection)
return NULL;
}
-static void format_rule(struct filter_data *data, char *rule, size_t size)
+static char *format_rule(struct filter_data *data)
{
+ char *rule, *tmp;
const char *sender;
- int offset;
- offset = snprintf(rule, size, "type='signal'");
+ rule = g_strdup("type='signal'");
sender = data->name ? : data->owner;
- if (sender)
- offset += snprintf(rule + offset, size - offset,
- ",sender='%s'", sender);
- if (data->path)
- offset += snprintf(rule + offset, size - offset,
- ",path='%s'", data->path);
- if (data->interface)
- offset += snprintf(rule + offset, size - offset,
- ",interface='%s'", data->interface);
- if (data->member)
- offset += snprintf(rule + offset, size - offset,
- ",member='%s'", data->member);
- if (data->argument)
- snprintf(rule + offset, size - offset,
- ",arg0='%s'", data->argument);
+ if (sender) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,sender='%s'", rule, sender);
+ g_free(tmp);
+ }
+
+ if (data->path) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,path='%s'", rule, data->path);
+ g_free(tmp);
+ }
+
+ if (data->interface){
+ tmp = rule;
+ rule = g_strdup_printf("%s,interface='%s'", rule,
+ data->interface);
+ g_free(tmp);
+ }
+
+ if (data->member) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,member='%s'", rule, data->member);
+ g_free(tmp);
+ }
+
+ if (data->argument) {
+ tmp = rule;
+ rule = g_strdup_printf("%s,arg0='%s'", rule, data->argument);
+ g_free(tmp);
+ }
+
+ return rule;
}
static gboolean add_match(struct filter_data *data,
DBusHandleMessageFunction filter)
{
DBusError err;
- char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+ char *rule;
- format_rule(data, rule, sizeof(rule));
+ rule = format_rule(data);
dbus_error_init(&err);
dbus_bus_add_match(data->connection, rule, &err);
@@ -175,21 +192,23 @@ static gboolean add_match(struct filter_data *data,
error("Adding match rule \"%s\" failed: %s", rule,
err.message);
dbus_error_free(&err);
+ g_free(rule);
return FALSE;
}
data->handle_func = filter;
data->registered = TRUE;
+ g_free(rule);
return TRUE;
}
static gboolean remove_match(struct filter_data *data)
{
DBusError err;
- char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+ char *rule;
- format_rule(data, rule, sizeof(rule));
+ rule = format_rule(data);
dbus_error_init(&err);
@@ -198,9 +217,11 @@ static gboolean remove_match(struct filter_data *data)
error("Removing owner match rule for %s failed: %s",
rule, err.message);
dbus_error_free(&err);
+ g_free(rule);
return FALSE;
}
+ g_free(rule);
return TRUE;
}
diff --git a/gdhcp/client.c b/gdhcp/client.c
index 09dfe5ec..82017692 100644
--- a/gdhcp/client.c
+++ b/gdhcp/client.c
@@ -1319,9 +1319,9 @@ static bool sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd,
struct sockaddr_in *dst_addr)
{
- int bytes;
struct ip_udp_dhcp_packet packet;
uint16_t check;
+ int bytes, tot_len;
memset(&packet, 0, sizeof(packet));
@@ -1329,15 +1329,17 @@ static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd,
if (bytes < 0)
return -1;
- if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
- return -1;
-
- if (bytes < ntohs(packet.ip.tot_len))
+ tot_len = ntohs(packet.ip.tot_len);
+ if (bytes > tot_len) {
+ /* ignore any extra garbage bytes */
+ bytes = tot_len;
+ } else if (bytes < tot_len) {
/* packet is bigger than sizeof(packet), we did partial read */
return -1;
+ }
- /* ignore any extra garbage bytes */
- bytes = ntohs(packet.ip.tot_len);
+ if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
+ return -1;
if (!sanity_check(&packet, bytes))
return -1;
@@ -1629,12 +1631,12 @@ static void start_request(GDHCPClient *dhcp_client)
NULL);
}
-static uint32_t get_lease(struct dhcp_packet *packet)
+static uint32_t get_lease(struct dhcp_packet *packet, uint16_t packet_len)
{
uint8_t *option;
uint32_t lease_seconds;
- option = dhcp_get_option(packet, DHCP_LEASE_TIME);
+ option = dhcp_get_option(packet, packet_len, DHCP_LEASE_TIME);
if (!option)
return 3600;
@@ -2226,7 +2228,8 @@ static void get_dhcpv6_request(GDHCPClient *dhcp_client,
}
}
-static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
+static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet,
+ uint16_t packet_len)
{
GDHCPOptionType type;
GList *list, *value_list;
@@ -2237,7 +2240,7 @@ static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
for (list = dhcp_client->request_list; list; list = list->next) {
code = (uint8_t) GPOINTER_TO_INT(list->data);
- option = dhcp_get_option(packet, code);
+ option = dhcp_get_option(packet, packet_len, code);
if (!option) {
g_hash_table_remove(dhcp_client->code_value_hash,
GINT_TO_POINTER((int) code));
@@ -2269,7 +2272,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
{
GDHCPClient *dhcp_client = user_data;
struct sockaddr_in dst_addr = { 0 };
- struct dhcp_packet packet;
+ struct dhcp_packet packet = { 0 };
struct dhcpv6_packet *packet6 = NULL;
uint8_t *message_type = NULL, *client_id = NULL, *option,
*server_id = NULL;
@@ -2297,6 +2300,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
re = dhcp_recv_l2_packet(&packet,
dhcp_client->listener_sockfd,
&dst_addr);
+ pkt_len = (uint16_t)(unsigned int)re;
xid = packet.xid;
} else if (dhcp_client->listen_mode == L3) {
if (dhcp_client->type == G_DHCP_IPV6) {
@@ -2361,7 +2365,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->status_code = status;
}
} else {
- message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+ message_type = dhcp_get_option(&packet, pkt_len, DHCP_MESSAGE_TYPE);
if (!message_type)
return TRUE;
}
@@ -2378,7 +2382,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->timeout = 0;
dhcp_client->retry_times = 0;
- option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+ 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);
@@ -2428,9 +2435,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
remove_timeouts(dhcp_client);
- dhcp_client->lease_seconds = get_lease(&packet);
+ dhcp_client->lease_seconds = get_lease(&packet, pkt_len);
- get_request(dhcp_client, &packet);
+ get_request(dhcp_client, &packet, pkt_len);
switch_listening_mode(dhcp_client, L_NONE);
@@ -2438,8 +2445,10 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
dhcp_client->assigned_ip = get_ip(packet.yiaddr);
if (dhcp_client->state == REBOOTING) {
- option = dhcp_get_option(&packet,
+ option = dhcp_get_option(&packet, pkt_len,
DHCP_SERVER_ID);
+ if (!option)
+ return TRUE;
dhcp_client->server_ip = get_be32(option);
}
@@ -3029,7 +3038,7 @@ char *g_dhcp_client_get_server_address(GDHCPClient *dhcp_client)
if (!dhcp_client)
return NULL;
- return get_ip(dhcp_client->server_ip);
+ return get_ip(htonl(dhcp_client->server_ip));
}
char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
diff --git a/gdhcp/common.c b/gdhcp/common.c
index 1d667d17..c8916aa8 100644
--- a/gdhcp/common.c
+++ b/gdhcp/common.c
@@ -73,18 +73,21 @@ GDHCPOptionType dhcp_get_code_type(uint8_t code)
return OPTION_UNKNOWN;
}
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code)
{
int len, rem;
- uint8_t *optionptr;
+ uint8_t *optionptr, *options_end;
+ size_t options_len;
uint8_t overload = 0;
/* option bytes: [code][len][data1][data2]..[dataLEN] */
optionptr = packet->options;
rem = sizeof(packet->options);
+ options_len = packet_len - (sizeof(*packet) - sizeof(packet->options));
+ options_end = optionptr + options_len - 1;
while (1) {
- if (rem <= 0)
+ if ((rem <= 0) && (optionptr + OPT_CODE > options_end))
/* Bad packet, malformed option field */
return NULL;
@@ -115,14 +118,25 @@ uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
break;
}
+ if (optionptr + OPT_LEN > options_end) {
+ /* bad packet, would read length field from OOB */
+ return NULL;
+ }
+
len = 2 + optionptr[OPT_LEN];
rem -= len;
if (rem < 0)
continue; /* complain and return NULL */
- if (optionptr[OPT_CODE] == code)
- return optionptr + OPT_DATA;
+ if (optionptr[OPT_CODE] == code) {
+ if (optionptr + len > options_end) {
+ /* bad packet, option length points OOB */
+ return NULL;
+ } else {
+ return optionptr + OPT_DATA;
+ }
+ }
if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
overload |= optionptr[OPT_DATA];
diff --git a/gdhcp/common.h b/gdhcp/common.h
index 9660231c..8f63fd75 100644
--- a/gdhcp/common.h
+++ b/gdhcp/common.h
@@ -179,7 +179,7 @@ struct in6_pktinfo {
};
#endif
-uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code);
uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
int code, uint16_t *option_len, int *option_count);
uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
diff --git a/gdhcp/server.c b/gdhcp/server.c
index 85405f19..52ea2a55 100644
--- a/gdhcp/server.c
+++ b/gdhcp/server.c
@@ -413,7 +413,7 @@ error:
}
-static uint8_t check_packet_type(struct dhcp_packet *packet)
+static uint8_t check_packet_type(struct dhcp_packet *packet, uint16_t packet_len)
{
uint8_t *type;
@@ -423,7 +423,7 @@ static uint8_t check_packet_type(struct dhcp_packet *packet)
if (packet->op != BOOTREQUEST)
return 0;
- type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
+ type = dhcp_get_option(packet, packet_len, DHCP_MESSAGE_TYPE);
if (!type)
return 0;
@@ -651,6 +651,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
struct dhcp_lease *lease;
uint32_t requested_nip = 0;
uint8_t type, *server_id_option, *request_ip_option;
+ uint16_t packet_len;
int re;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
@@ -661,12 +662,13 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
if (re < 0)
return TRUE;
+ packet_len = (uint16_t)(unsigned int)re;
- type = check_packet_type(&packet);
+ type = check_packet_type(&packet, packet_len);
if (type == 0)
return TRUE;
- server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+ server_id_option = dhcp_get_option(&packet, packet_len, DHCP_SERVER_ID);
if (server_id_option) {
uint32_t server_nid =
get_unaligned((const uint32_t *) server_id_option);
@@ -675,7 +677,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
return TRUE;
}
- request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
+ request_ip_option = dhcp_get_option(&packet, packet_len, DHCP_REQUESTED_IP);
if (request_ip_option)
requested_nip = get_be32(request_ip_option);
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index 7935c3a6..eab6293f 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -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);
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index f56b595f..1b92ec44 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -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 {
@@ -808,6 +810,33 @@ static void remove_peer(gpointer data)
g_free(peer);
}
+static void remove_ssid(gpointer data)
+{
+ GSupplicantSSID *ssid = data;
+
+ if (!ssid)
+ return;
+
+ g_free((void *) ssid->ssid);
+ g_free((char *) ssid->eap);
+ g_free((char *) ssid->passphrase);
+ g_free((char *) ssid->identity);
+ g_free((char *) ssid->anonymous_identity);
+ g_free((char *) ssid->ca_cert_path);
+ g_free((char *) ssid->subject_match);
+ g_free((char *) ssid->altsubject_match);
+ g_free((char *) ssid->domain_suffix_match);
+ g_free((char *) ssid->domain_match);
+ g_free((char *) ssid->client_cert_path);
+ g_free((char *) ssid->private_key_path);
+ g_free((char *) ssid->private_key_passphrase);
+ g_free((char *) ssid->phase2_auth);
+ g_free((char *) ssid->pin_wps);
+ g_free((char *) ssid->bgscan);
+
+ g_free(ssid);
+}
+
static void debug_strvalmap(const char *label, struct strvalmap *map,
unsigned int val)
{
@@ -1215,7 +1244,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 +1252,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 +1456,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 +1494,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 +1688,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 +1970,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)
@@ -4138,6 +4178,7 @@ done:
if (data->callback)
data->callback(err, NULL, data->user_data);
+ remove_ssid(data->ssid);
dbus_free(data);
}
@@ -4456,7 +4497,7 @@ static void interface_select_network_result(const char *error,
if (data->callback)
data->callback(err, data->interface, data->user_data);
- g_free(data->ssid);
+ remove_ssid(data->ssid);
dbus_free(data);
}
@@ -4512,7 +4553,7 @@ error:
}
g_free(data->path);
- g_free(data->ssid);
+ remove_ssid(data->ssid);
g_free(data);
}
@@ -4890,8 +4931,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 +4956,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);
@@ -4994,7 +5058,7 @@ static void interface_wps_start_result(const char *error,
data->callback(err, data->interface, data->user_data);
g_free(data->path);
- g_free(data->ssid);
+ remove_ssid(data->ssid);
dbus_free(data);
}
@@ -5034,7 +5098,7 @@ static void wps_start(const char *error, DBusMessageIter *iter, void *user_data)
if (error) {
SUPPLICANT_DBG("error: %s", error);
g_free(data->path);
- g_free(data->ssid);
+ remove_ssid(data->ssid);
dbus_free(data);
return;
}
@@ -5123,6 +5187,7 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
* type is 802.11x).
*/
if (compare_network_parameters(interface, ssid)) {
+ remove_ssid(ssid);
return -EALREADY;
}
@@ -5148,6 +5213,7 @@ int g_supplicant_interface_connect(GSupplicantInterface *interface,
if (ret < 0) {
g_free(data->path);
+ remove_ssid(data->ssid);
dbus_free(data);
return ret;
}
@@ -5198,6 +5264,7 @@ static void network_remove_result(const char *error,
} else {
if (data->callback)
data->callback(result, data->interface, data->user_data);
+ remove_ssid(data->ssid);
}
g_free(data->path);
dbus_free(data);
@@ -5251,6 +5318,7 @@ static void interface_disconnect_result(const char *error,
data->user_data);
g_free(data->path);
+ remove_ssid(data->ssid);
dbus_free(data);
return;
}
@@ -5263,10 +5331,12 @@ static void interface_disconnect_result(const char *error,
if (result != -ECONNABORTED) {
if (network_remove(data) < 0) {
g_free(data->path);
+ remove_ssid(data->ssid);
dbus_free(data);
}
} else {
g_free(data->path);
+ remove_ssid(data->ssid);
dbus_free(data);
}
}
diff --git a/gweb/gresolv.c b/gweb/gresolv.c
index 954e7cfe..8101d718 100644
--- a/gweb/gresolv.c
+++ b/gweb/gresolv.c
@@ -36,6 +36,7 @@
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <net/if.h>
+#include <ctype.h>
#include "gresolv.h"
diff --git a/gweb/gweb.c b/gweb/gweb.c
index 12fcb1d8..13c6c5f2 100644
--- a/gweb/gweb.c
+++ b/gweb/gweb.c
@@ -918,7 +918,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
}
*pos = '\0';
- count = strlen((char *) ptr);
+ count = pos - ptr;
if (count > 0 && ptr[count - 1] == '\r') {
ptr[--count] = '\0';
bytes_read--;
diff --git a/include/agent.h b/include/agent.h
index 6961f7a1..27020203 100644
--- a/include/agent.h
+++ b/include/agent.h
@@ -71,6 +71,7 @@ int connman_agent_queue_message(void *user_context,
DBusMessage *msg, int timeout,
agent_queue_cb callback, void *user_data,
void *agent_data);
+bool connman_agent_queue_search(void *user_context, void *agent_data);
void *connman_agent_get_info(const char *dbus_sender, const char **sender,
const char **path);
diff --git a/include/inet.h b/include/inet.h
index fdc2155f..9245eef8 100644
--- a/include/inet.h
+++ b/include/inet.h
@@ -55,7 +55,7 @@ bool connman_inet_compare_ipv6_subnet(int index, const char *host);
int connman_inet_set_ipv6_address(int index,
struct connman_ipaddress *ipaddress);
int connman_inet_clear_ipv6_address(int index,
- const char *address, int prefix_len);
+ struct connman_ipaddress *ipaddress);
int connman_inet_add_ipv6_network_route(int index, const char *host,
const char *gateway, unsigned char prefix_len);
int connman_inet_add_ipv6_host_route(int index, const char *host,
@@ -78,6 +78,14 @@ int connman_inet_ipv6_get_dest_addr(int index, char **dest);
int connman_inet_check_ipaddress(const char *host);
bool connman_inet_check_hostname(const char *ptr, size_t len);
bool connman_inet_is_ipv6_supported();
+bool connman_inet_is_default_route(int family, const char *host,
+ const char *gateway, const char *netmask);
+
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+ char **destination);
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+ char **netmask,
+ char **destination);
#ifdef __cplusplus
}
diff --git a/include/ipaddress.h b/include/ipaddress.h
index 3655ca86..652db0f0 100644
--- a/include/ipaddress.h
+++ b/include/ipaddress.h
@@ -22,6 +22,8 @@
#ifndef __CONNMAN_IPADDRESS_H
#define __CONNMAN_IPADDRESS_H
+#include <stdbool.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -36,6 +38,8 @@ struct connman_ipaddress;
unsigned char connman_ipaddress_calc_netmask_len(const char *netmask);
struct connman_ipaddress *connman_ipaddress_alloc(int family);
+void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress,
+ bool value);
void connman_ipaddress_free(struct connman_ipaddress *ipaddress);
int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
const char *address, const char *netmask,
diff --git a/include/network.h b/include/network.h
index 8f87d7c5..5bca62ad 100644
--- a/include/network.h
+++ b/include/network.h
@@ -132,6 +132,8 @@ uint16_t connman_network_get_frequency(struct connman_network *network);
int connman_network_set_wifi_channel(struct connman_network *network,
uint16_t channel);
uint16_t connman_network_get_wifi_channel(struct connman_network *network);
+int connman_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect);
int connman_network_set_string(struct connman_network *network,
const char *key, const char *value);
@@ -161,6 +163,9 @@ struct connman_network_driver {
void (*remove) (struct connman_network *network);
int (*connect) (struct connman_network *network);
int (*disconnect) (struct connman_network *network);
+ int (*forget) (struct connman_network *network);
+ int (*set_autoconnect) (struct connman_network *network,
+ bool autoconnect);
};
int connman_network_driver_register(struct connman_network_driver *driver);
diff --git a/include/option.h b/include/option.h
deleted file mode 100644
index 5e97ed4c..00000000
--- a/include/option.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifndef __CONNMAN_OPTION_H
-#define __CONNMAN_OPTION_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-const char *connman_option_get_string(const char *key);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CONNMAN_OPTION_H */
diff --git a/include/provider.h b/include/provider.h
index b5856653..1f120990 100644
--- a/include/provider.h
+++ b/include/provider.h
@@ -113,6 +113,10 @@ int connman_provider_set_nameservers(struct connman_provider *provider,
char * const *nameservers);
void connman_provider_set_autoconnect(struct connman_provider *provider,
bool flag);
+bool connman_provider_is_split_routing(struct connman_provider *provider);
+int connman_provider_set_split_routing(struct connman_provider *provider,
+ bool split_routing);
+int connman_provider_get_family(struct connman_provider *provider);
const char *connman_provider_get_driver_name(struct connman_provider *provider);
const char *connman_provider_get_save_group(struct connman_provider *provider);
diff --git a/include/service.h b/include/service.h
index 4a129b49..8f6b36b4 100644
--- a/include/service.h
+++ b/include/service.h
@@ -94,6 +94,7 @@ enum connman_service_connect_reason {
CONNMAN_SERVICE_CONNECT_REASON_AUTO = 1,
CONNMAN_SERVICE_CONNECT_REASON_USER = 2,
CONNMAN_SERVICE_CONNECT_REASON_SESSION = 3,
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE = 4,
};
struct connman_service;
diff --git a/include/setting.h b/include/setting.h
index a8820217..920e6754 100644
--- a/include/setting.h
+++ b/include/setting.h
@@ -29,6 +29,8 @@ extern "C" {
#endif
bool connman_setting_get_bool(const char *key);
+unsigned int connman_setting_get_uint(const char *key);
+char *connman_setting_get_string(const char *key);
char **connman_setting_get_string_list(const char *key);
unsigned int *connman_setting_get_uint_list(const char *key);
diff --git a/include/technology.h b/include/technology.h
index 7508a9a1..fcd658e8 100644
--- a/include/technology.h
+++ b/include/technology.h
@@ -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);
diff --git a/include/timeserver.h b/include/timeserver.h
index 48ea1945..3177d4e5 100644
--- a/include/timeserver.h
+++ b/include/timeserver.h
@@ -26,6 +26,13 @@
extern "C" {
#endif
+enum connman_timeserver_sync_reason {
+ CONNMAN_TIMESERVER_SYNC_REASON_START = 0,
+ CONNMAN_TIMESERVER_SYNC_REASON_ADDRESS_UPDATE = 1,
+ CONNMAN_TIMESERVER_SYNC_REASON_STATE_UPDATE = 2,
+ CONNMAN_TIMESERVER_SYNC_REASON_TS_CHANGE = 3,
+};
+
int __connman_timeserver_system_set(char **server);
#ifdef __cplusplus
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c
index f759a902..57cc8a29 100644
--- a/plugins/bluetooth.c
+++ b/plugins/bluetooth.c
@@ -717,8 +717,6 @@ static bool tethering_create(const char *path,
const char *method;
bool result;
- DBG("path %s bridge %s", path, bridge);
-
if (!bridge) {
g_free(tethering);
return false;
@@ -730,6 +728,8 @@ static bool tethering_create(const char *path,
return false;
}
+ DBG("path %s bridge %s", path, bridge);
+
tethering->technology = technology;
tethering->bridge = g_strdup(bridge);
tethering->enable = enabled;
@@ -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;
diff --git a/plugins/dundee.c b/plugins/dundee.c
index b5420acf..57571ec3 100644
--- a/plugins/dundee.c
+++ b/plugins/dundee.c
@@ -488,7 +488,7 @@ static void extract_settings(DBusMessageIter *array,
if (index < 0)
goto out;
- info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+ info->address = connman_ipaddress_alloc(AF_INET);
if (!info->address)
goto out;
diff --git a/plugins/ethernet.c b/plugins/ethernet.c
index ed4208ad..0bf7fc41 100644
--- a/plugins/ethernet.c
+++ b/plugins/ethernet.c
@@ -73,7 +73,7 @@ static int get_vlan_vid(const char *ifname)
return -errno;
vifr.cmd = GET_VLAN_VID_CMD;
- stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1);
if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
vid = vifr.u.VID;
@@ -99,14 +99,16 @@ static int get_dsa_port(const char *ifname)
return -errno;
memset(&ifr, 0, sizeof(ifr));
- stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
/* check if it is a vlan and get physical interface name*/
vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
- stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1) - 1);
- if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
- stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
+ if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0) {
+ stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name) - 1);
+ ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
+ }
/* get driver info */
drvinfocmd.cmd = ETHTOOL_GDRVINFO;
@@ -405,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(
diff --git a/plugins/gadget.c b/plugins/gadget.c
index 1b44bbb5..2d58df3e 100644
--- a/plugins/gadget.c
+++ b/plugins/gadget.c
@@ -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);
diff --git a/plugins/iwd.c b/plugins/iwd.c
index bf6a2c26..2fe49a23 100644
--- a/plugins/iwd.c
+++ b/plugins/iwd.c
@@ -41,7 +41,15 @@ static GDBusClient *client;
static GDBusProxy *agent_proxy;
static GHashTable *adapters;
static GHashTable *devices;
+/*
+ * Mapping from dbus path -> struct iwd_network, tracking the set of Network
+ * objects seen by iwd.
+ */
static GHashTable *networks;
+/*
+ * Mapping from dbus path -> struct iwd_network, tracking the set of iwd
+ * KnownNetwork objects.
+ */
static GHashTable *known_networks;
static GHashTable *stations;
static GHashTable *access_points;
@@ -84,6 +92,11 @@ struct iwd_device {
struct connman_device *device;
};
+/*
+ * Structure tracking an net.connman.iwd.Network D-Bus object.
+ *
+ * This is mapped one-to-one to a connman_network object.
+ */
struct iwd_network {
GDBusProxy *proxy;
char *path;
@@ -95,8 +108,17 @@ struct iwd_network {
struct iwd_device *iwdd;
struct connman_network *network;
+ /*
+ * connman_service's autoconnect.
+ *
+ * See Note [Managing autoconnect state] for more details.
+ */
+ bool cm_autoconnect;
};
+/*
+ * Structure tracking a net.connman.iwd.KnownNetwork D-Bus object.
+ */
struct iwd_known_network {
GDBusProxy *proxy;
char *path;
@@ -104,8 +126,15 @@ struct iwd_known_network {
char *type;
bool hidden;
char *last_connected_time;
- bool auto_connect;
+ bool iwd_auto_connect;
int auto_connect_id;
+
+ /*
+ * connman_service's autoconnect.
+ *
+ * See Note [Managing autoconnect state] for more details.
+ */
+ bool cm_autoconnect;
};
struct iwd_station {
@@ -204,18 +233,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);
}
@@ -241,10 +265,12 @@ static void cm_network_connect_cb(DBusMessage *message, void *user_data)
return;
DBG("%s connect failed: %s", path, dbus_error);
- if (!strcmp(dbus_error, "net.connman.iwd.Failed"))
+ if (!strcmp(dbus_error, "net.connman.iwd.Failed") ||
+ !strcmp(dbus_error,
+ "net.connman.iwd.InvalidFormat"))
connman_network_set_error(iwdn->network,
CONNMAN_NETWORK_ERROR_INVALID_KEY);
- else
+ else if (!iwdn->cm_autoconnect)
connman_network_set_error(iwdn->network,
CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
return;
@@ -270,6 +296,46 @@ static int cm_network_connect(struct connman_network *network)
return -EINPROGRESS;
}
+static void cm_network_forget_cb(DBusMessage *message, void *user_data)
+{
+ struct iwd_known_network *iwdkn;
+ const char *path = user_data;
+
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!iwdkn)
+ return;
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *dbus_error = dbus_message_get_error_name(message);
+
+ DBG("%s failed: %s", path, dbus_error);
+ }
+}
+
+static int cm_network_forget(struct connman_network *network)
+{
+ struct iwd_network *iwdn = connman_network_get_data(network);
+ struct iwd_known_network *iwdkn;
+
+ if (!iwdn)
+ return -EINVAL;
+
+ if (!iwdn->known_network)
+ return 0;
+
+ iwdkn = g_hash_table_lookup(known_networks,
+ iwdn->known_network);
+ if (!iwdkn)
+ return 0;
+
+ if (!g_dbus_proxy_method_call(iwdkn->proxy, "Forget",
+ NULL, cm_network_forget_cb,
+ g_strdup(iwdkn->path), g_free))
+ return -EIO;
+
+ return 0;
+}
+
static void cm_network_disconnect_cb(DBusMessage *message, void *user_data)
{
const char *path = user_data;
@@ -311,19 +377,167 @@ 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 0;
+}
+
+struct auto_connect_cb_data {
+ char *path;
+ bool auto_connect;
+};
+
+static void auto_connect_cb_free(struct auto_connect_cb_data *cbd)
+{
+ g_free(cbd->path);
+ g_free(cbd);
+}
+
+static void auto_connect_cb(const DBusError *error, void *user_data)
+{
+ struct auto_connect_cb_data *cbd = user_data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, cbd->path);
+ if (!iwdkn)
+ goto out;
+
+ if (dbus_error_is_set(error))
+ connman_warn("WiFi known network %s property auto connect %s",
+ cbd->path, error->message);
+
+ /* property is updated via watch known_network_property_change() */
+out:
+ auto_connect_cb_free(cbd);
+}
+
+static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect)
+{
+ dbus_bool_t dbus_auto_connect = auto_connect;
+ struct auto_connect_cb_data *cbd;
+
+ if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect)
+ return -EALREADY;
+
+ cbd = g_new(struct auto_connect_cb_data, 1);
+ cbd->path = g_strdup(iwdkn->path);
+ cbd->auto_connect = auto_connect;
+
+ if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect",
+ DBUS_TYPE_BOOLEAN,
+ &dbus_auto_connect,
+ auto_connect_cb, cbd, NULL)) {
+ auto_connect_cb_free(cbd);
+ return -EIO;
+ }
+
return -EINPROGRESS;
}
+static gboolean disable_auto_connect_cb(gpointer data)
+{
+ char *path = data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!iwdkn)
+ return FALSE;
+
+ if (set_auto_connect(iwdkn, false) != -EINPROGRESS)
+ connman_warn("Failed to disable auto connect");
+
+ iwdkn->auto_connect_id = 0;
+ return FALSE;
+}
+
+static int disable_auto_connect(struct iwd_known_network *iwdkn)
+{
+ if (iwdkn->auto_connect_id)
+ return -EBUSY;
+
+ iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 0,
+ disable_auto_connect_cb,
+ g_strdup(iwdkn->path),
+ g_free);
+ return 0;
+}
+
+static gboolean enable_auto_connect_cb(gpointer data)
+{
+ char *path = data;
+ struct iwd_known_network *iwdkn;
+
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!iwdkn)
+ return FALSE;
+
+ if (set_auto_connect(iwdkn, true) != -EINPROGRESS)
+ connman_warn("Failed to enable auto connect");
+
+ iwdkn->auto_connect_id = 0;
+ return FALSE;
+}
+
+static int enable_auto_connect(struct iwd_known_network *iwdkn)
+{
+ if (iwdkn->auto_connect_id)
+ return -EBUSY;
+
+ iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 0,
+ enable_auto_connect_cb,
+ g_strdup(iwdkn->path),
+ g_free);
+ return 0;
+}
+
+static int update_auto_connect(struct iwd_known_network *iwdkn)
+{
+ DBG("iwd_auto_connect %d cm_autoconnect %d", iwdkn->iwd_auto_connect, iwdkn->cm_autoconnect);
+
+ if (iwdkn->iwd_auto_connect == iwdkn->cm_autoconnect)
+ return -EALREADY;
+
+ if (iwdkn->cm_autoconnect)
+ return enable_auto_connect(iwdkn);
+ return disable_auto_connect(iwdkn);
+}
+
+static int cm_network_set_autoconnect(struct connman_network *network,
+ bool cm_autoconnect)
+{
+ struct iwd_network *iwdn = connman_network_get_data(network);
+ struct iwd_known_network *iwdkn;
+
+ DBG("cm_autoconnect %d", cm_autoconnect);
+
+ iwdn->cm_autoconnect = cm_autoconnect;
+
+ if (!iwdn->known_network)
+ return -ENOENT;
+
+ iwdkn = g_hash_table_lookup(known_networks, iwdn->known_network);
+ if (!iwdkn)
+ return -ENOENT;
+
+ iwdkn->cm_autoconnect = cm_autoconnect;
+
+ return update_auto_connect(iwdkn);
+}
+
static struct connman_network_driver network_driver = {
- .name = "iwd",
- .type = CONNMAN_NETWORK_TYPE_WIFI,
- .probe = cm_network_probe,
- .connect = cm_network_connect,
- .disconnect = cm_network_disconnect,
+ .name = "iwd",
+ .type = CONNMAN_NETWORK_TYPE_WIFI,
+ .probe = cm_network_probe,
+ .connect = cm_network_connect,
+ .disconnect = cm_network_disconnect,
+ .forget = cm_network_forget,
+ .set_autoconnect = cm_network_set_autoconnect,
};
static int cm_device_probe(struct connman_device *device)
@@ -408,7 +622,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;
@@ -535,7 +749,7 @@ static void tech_enable_tethering_cb(const DBusError *error, void *user_data)
{
struct tech_cb_data *cbd = user_data;
struct iwd_device *iwdd;
- struct iwd_ap *iwdap;
+ struct iwd_ap *iwdap = NULL;
DBG("");
@@ -546,7 +760,7 @@ static void tech_enable_tethering_cb(const DBusError *error, void *user_data)
}
if (dbus_error_is_set(error)) {
- connman_warn("iwd device %s could not enable AcessPoint mode: %s",
+ connman_warn("iwd device %s could not enable AccessPoint mode: %s",
cbd->path, error->message);
goto out;
}
@@ -598,6 +812,7 @@ static void tech_disable_tethering_cb(const DBusError *error, void *user_data)
goto out;
}
+ connman_technology_tethering_notify(cbd->tech, false);
iwdap = g_hash_table_lookup(access_points, iwdd->path);
if (!iwdap) {
DBG("%s no ap object found", iwdd->path);
@@ -609,14 +824,9 @@ static void tech_disable_tethering_cb(const DBusError *error, void *user_data)
iwdap->bridge = NULL;
iwdap->tech = NULL;
- if (!connman_inet_remove_from_bridge(cbd->index, cbd->bridge))
- goto out;
-
- connman_technology_tethering_notify(cbd->tech, false);
-
if (!g_dbus_proxy_method_call(iwdap->proxy, "Stop",
NULL, tech_ap_stop_cb, cbd, NULL)) {
- connman_warn("iwd ap %s could not start AccessPoint mode: %s",
+ connman_warn("iwd ap %s could not stop AccessPoint mode: %s",
cbd->path, error->message);
goto out;
}
@@ -640,6 +850,9 @@ static int cm_change_tethering(struct iwd_device *iwdd,
if (index < 0)
return -ENODEV;
+ if (!enabled && connman_inet_remove_from_bridge(index, bridge))
+ return -EIO;
+
cbd = g_new(struct tech_cb_data, 1);
cbd->iwdd = iwdd;
cbd->path = g_strdup(iwdd->path);
@@ -668,12 +881,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);
@@ -690,8 +907,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);
@@ -701,8 +918,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);
@@ -762,6 +979,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)
@@ -770,6 +988,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);
@@ -786,7 +1009,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);
@@ -923,6 +1148,7 @@ static void network_property_change(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
struct iwd_network *iwdn;
+ struct iwd_known_network *iwdkn;
const char *path;
path = g_dbus_proxy_get_path(proxy);
@@ -942,9 +1168,67 @@ static void network_property_change(GDBusProxy *proxy, const char *name,
update_network_connected(iwdn);
else
update_network_disconnected(iwdn);
+ } else if (!strcmp(name, "KnownNetwork")) {
+ g_free(iwdn->known_network);
+ iwdn->known_network =
+ g_strdup(proxy_get_string(proxy, "KnownNetwork"));
+ if (!iwdn->known_network)
+ return;
+
+ iwdkn = g_hash_table_lookup(known_networks,
+ iwdn->known_network);
+ if (iwdkn) {
+ /* See Note [Managing autoconnect state] */
+ iwdkn->cm_autoconnect = iwdn->cm_autoconnect;
+ update_auto_connect(iwdkn);
+ }
}
}
+/*
+ * Note [Managing autoconnect state]:
+ *
+ * We need to set the iwd_known_network's cm_autoconnect status from the
+ * iwd_network, which has in turn been set to the corresponding
+ * connman_service's state when it first appeared (due to
+ * __connman_service_create_from_network).
+ *
+ * The management of the autoconnect state between ConnMan and its plugins and
+ * iwd is rather subtle and prone to bugs:
+ * - ConnMan itself determines the autoconnect state in struct connman_service,
+ * which we cannot directly see; we only see cm_network_set_autoconnect
+ * callbacks.
+ *
+ * - The iwd plugin maintains an independent state machine tracking iwd's view of
+ * the world, which processes events in a non atomic fashion; for
+ * instance, a iwd.KnownNetwork created event will appear before the
+ * corresponding PropertyChanged setting the KnownNetwork property of the
+ * iwd.Network corresponding to the created iwd.KnownNetwork.
+ *
+ * A typical flow of a network being newly connected to looks like so:
+ * - An iwd.Network appears, and add_network registers a connman_network
+ * structure with ConnMan. ConnMan then in turn creates a service via
+ * __connman_service_create_from_network.
+ *
+ * - The iwd plugin receives a callback from ConnMan to set the autoconnect
+ * state, setting the cm_autoconnect state of the iwd_network. At this point,
+ * there is no iwd_known_network yet.
+ *
+ * - ConnMan receives a Connect() request on the connman.Service, which is
+ * forwarded to the iwd plugin via cm_network_connect. The iwd plugin calls
+ * Connect() on the corresponding iwd.Network (possibly using the iwd
+ * plugin's agent to get credentials if necessary).
+ *
+ * - Around the time that the connection completes, a iwd.KnownNetwork created
+ * event appears, followed by a PropertyChanged event noting the change in
+ * the iwd.Network's KnownNetwork property.
+ *
+ * This is the first time that we can associate the iwd.KnownNetwork with the
+ * corresponding iwd.Network and iwd_network. We have the ConnMan-side
+ * autoconnect status in the iwd_network structure at this point, so we
+ * synchronize the autoconnect state with iwd here.
+ */
+
static unsigned char calculate_strength(int strength)
{
unsigned char res;
@@ -957,7 +1241,9 @@ static unsigned char calculate_strength(int strength)
* ConnMan expects it in the range from 100 (strongest) to 0
* (weakest).
*/
- res = (unsigned char)((strength + 10000) / 100);
+ res = (unsigned char)(120 + strength / 100);
+ if (res > 100)
+ res = 100;
return res;
}
@@ -972,12 +1258,15 @@ static void _update_signal_strength(const char *path, int16_t signal_strength)
connman_network_set_strength(iwdn->network,
calculate_strength(signal_strength));
+ connman_network_set_available(iwdn->network, true);
connman_network_update(iwdn->network);
}
static void ordered_networks_cb(DBusMessage *message, void *user_data)
{
DBusMessageIter array, entry;
+ struct iwd_device *iwdd;
+ char *path = user_data;
DBG("");
@@ -1005,6 +1294,11 @@ static void ordered_networks_cb(DBusMessage *message, void *user_data)
dbus_message_iter_next(&entry);
}
+
+ iwdd = g_hash_table_lookup(devices, path);
+ if (iwdd)
+ connman_device_set_scanning(iwdd->device,
+ CONNMAN_SERVICE_TYPE_WIFI, false);
}
static void update_signal_strength(struct iwd_station *iwds)
@@ -1012,7 +1306,7 @@ static void update_signal_strength(struct iwd_station *iwds)
if (!g_dbus_proxy_method_call(iwds->proxy,
"GetOrderedNetworks",
NULL, ordered_networks_cb,
- NULL, NULL))
+ g_strdup(iwds->path), g_free))
DBG("GetOrderedNetworks() failed");
}
@@ -1020,6 +1314,7 @@ static void station_property_change(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
struct iwd_station *iwds;
+ struct iwd_device *iwdd;
const char *path;
path = g_dbus_proxy_get_path(proxy);
@@ -1053,8 +1348,15 @@ static void station_property_change(GDBusProxy *proxy, const char *name,
dbus_message_iter_get_basic(iter, &scanning);
iwds->scanning = scanning;
- if (!iwds->scanning)
+ if (iwds->scanning) {
+ iwdd = g_hash_table_lookup(devices, path);
+ if (iwdd)
+ connman_device_set_scanning(iwdd->device,
+ CONNMAN_SERVICE_TYPE_WIFI, true);
+ } else {
update_signal_strength(iwds);
+ }
+
DBG("%s scanning %d", path, iwds->scanning);
}
@@ -1194,13 +1496,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);
@@ -1245,13 +1541,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);
@@ -1316,6 +1606,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);
@@ -1418,13 +1710,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);
@@ -1452,121 +1738,59 @@ static void create_network(GDBusProxy *proxy)
add_network(path, iwdn);
}
-struct auto_connect_cb_data {
- char *path;
- bool auto_connect;
-};
-
-static void auto_connect_cb_free(struct auto_connect_cb_data *cbd)
-{
- g_free(cbd->path);
- g_free(cbd);
-}
-
-static void auto_connect_cb(const DBusError *error, void *user_data)
+static void known_network_property_change(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
{
- struct auto_connect_cb_data *cbd = user_data;
struct iwd_known_network *iwdkn;
+ const char *path;
- iwdkn = g_hash_table_lookup(known_networks, cbd->path);
+ path = g_dbus_proxy_get_path(proxy);
+ iwdkn = g_hash_table_lookup(known_networks, path);
if (!iwdkn)
- goto out;
-
- if (dbus_error_is_set(error))
- connman_warn("WiFi known network %s property auto connect %s",
- cbd->path, error->message);
-
- /* property is updated via watch known_network_property_change() */
-out:
- auto_connect_cb_free(cbd);
-}
+ return;
-static int set_auto_connect(struct iwd_known_network *iwdkn, bool auto_connect)
-{
- dbus_bool_t dbus_auto_connect = auto_connect;
- struct auto_connect_cb_data *cbd;
+ if (!strcmp(name, "AutoConnect")) {
+ dbus_bool_t iwd_auto_connect;
- if (proxy_get_bool(iwdkn->proxy, "AutoConnect") == auto_connect)
- return -EALREADY;
+ dbus_message_iter_get_basic(iter, &iwd_auto_connect);
+ iwdkn->iwd_auto_connect = iwd_auto_connect;
- cbd = g_new(struct auto_connect_cb_data, 1);
- cbd->path = g_strdup(iwdkn->path);
- cbd->auto_connect = auto_connect;
+ DBG("%s iwd_auto_connect %d", path, iwdkn->iwd_auto_connect);
- if (!g_dbus_proxy_set_property_basic(iwdkn->proxy, "AutoConnect",
- DBUS_TYPE_BOOLEAN,
- &dbus_auto_connect,
- auto_connect_cb, cbd, NULL)) {
- auto_connect_cb_free(cbd);
- return -EIO;
+ update_auto_connect(iwdkn);
}
-
- return -EINPROGRESS;
}
-static gboolean disable_auto_connect_cb(gpointer data)
+static void init_auto_connect(struct iwd_known_network *iwdkn)
{
- char *path = data;
- struct iwd_known_network *iwdkn;
-
- iwdkn = g_hash_table_lookup(known_networks, path);
- if (!iwdkn)
- return FALSE;
-
- if (set_auto_connect(iwdkn, false) != -EINPROGRESS)
- connman_warn("Failed to disable auto connect");
+ GHashTableIter iter;
+ gpointer key, value;
- iwdkn->auto_connect_id = 0;
- return FALSE;
-}
+ g_hash_table_iter_init(&iter, networks);
-static void disable_auto_connect(struct iwd_known_network *iwdkn)
-{
- if (iwdkn->auto_connect_id)
- return;
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct iwd_network *iwdn = value;
+ struct iwd_known_network *kn;
- iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
- 0,
- disable_auto_connect_cb,
- g_strdup(iwdkn->path),
- g_free);
-}
+ if (!iwdn->known_network)
+ continue;
-static void known_network_property_change(GDBusProxy *proxy, const char *name,
- DBusMessageIter *iter, void *user_data)
-{
- struct iwd_known_network *iwdkn;
- const char *path;
+ kn = g_hash_table_lookup(known_networks, iwdn->known_network);
+ if (iwdkn != kn)
+ continue;
- path = g_dbus_proxy_get_path(proxy);
- iwdkn = g_hash_table_lookup(known_networks, path);
- if (!iwdkn)
+ iwdkn->cm_autoconnect = iwdn->cm_autoconnect;
+ update_auto_connect(iwdkn);
return;
-
- if (!strcmp(name, "AutoConnect")) {
- dbus_bool_t auto_connect;
-
- dbus_message_iter_get_basic(iter, &auto_connect);
- iwdkn->auto_connect = auto_connect;
-
- DBG("%p auto_connect %d", path, iwdkn->auto_connect);
-
- if (iwdkn->auto_connect)
- disable_auto_connect(iwdkn);
}
}
-static void create_know_network(GDBusProxy *proxy)
+static void create_known_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);
@@ -1583,17 +1807,26 @@ static void create_know_network(GDBusProxy *proxy)
iwdkn->hidden = proxy_get_bool(proxy, "Hidden");
iwdkn->last_connected_time =
g_strdup(proxy_get_string(proxy, "LastConnectedTime"));
- iwdkn->auto_connect = proxy_get_bool(proxy, "AutoConnect");
+ iwdkn->iwd_auto_connect = proxy_get_bool(proxy, "AutoConnect");
- DBG("name '%s' type %s hidden %d, last_connection_time %s auto_connect %d",
+ DBG("name '%s' type %s hidden %d, last_connection_time %s iwd_auto_connect %d",
iwdkn->name, iwdkn->type, iwdkn->hidden,
- iwdkn->last_connected_time, iwdkn->auto_connect);
+ iwdkn->last_connected_time, iwdkn->iwd_auto_connect);
+
+ /*
+ * Although we initialize the autoconnect state of this
+ * iwd_known_network here, it is only initialized in the case of
+ * networks that already existed prior to startup: in the
+ * case of a new iwd.KnownNetwork appearing, we are called before the
+ * iwd_network.known_network field is initialized by a subsequent
+ * PropertyChanged event.
+ *
+ * See Note [Managing autoconnect state].
+ */
+ init_auto_connect(iwdkn);
g_dbus_proxy_set_property_watch(iwdkn->proxy,
known_network_property_change, NULL);
-
- if (iwdkn->auto_connect)
- disable_auto_connect(iwdkn);
}
static void create_station(GDBusProxy *proxy)
@@ -1601,12 +1834,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);
@@ -1634,11 +1862,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);
@@ -1682,7 +1906,7 @@ static void object_added(GDBusProxy *proxy, void *user_data)
else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
create_network(proxy);
else if (!strcmp(interface, IWD_KNOWN_NETWORK_INTERFACE))
- create_know_network(proxy);
+ create_known_network(proxy);
else if (!strcmp(interface, IWD_STATION_INTERFACE))
create_station(proxy);
else if (!strcmp(interface, IWD_AP_INTERFACE))
diff --git a/plugins/neard.c b/plugins/neard.c
index 69586df6..caaea853 100644
--- a/plugins/neard.c
+++ b/plugins/neard.c
@@ -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);
@@ -499,7 +499,7 @@ static void register_agent_cb(DBusPendingCall *pending, void *user_data)
DBusMessage *reply;
if (!dbus_pending_call_get_completed(pending))
- return;
+ goto out;
register_call = NULL;
diff --git a/plugins/ofono.c b/plugins/ofono.c
index 36873d5a..8bb53949 100644
--- a/plugins/ofono.c
+++ b/plugins/ofono.c
@@ -40,6 +40,7 @@
#include <connman/dbus.h>
#include <connman/log.h>
#include <connman/technology.h>
+#include <connman/setting.h>
#include "mcc.h"
@@ -910,7 +911,7 @@ static void extract_ipv4_settings(DBusMessageIter *array,
if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED)
goto out;
- context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+ context->ipv4_address = connman_ipaddress_alloc(AF_INET);
if (!context->ipv4_address) {
context->index = -1;
goto out;
@@ -998,7 +999,7 @@ static void extract_ipv6_settings(DBusMessageIter *array,
context->ipv6_method = CONNMAN_IPCONFIG_METHOD_AUTO;
context->ipv6_address =
- connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+ connman_ipaddress_alloc(AF_INET6);
if (!context->ipv6_address)
goto out;
@@ -1710,6 +1711,10 @@ static void netreg_update_regdom(struct modem_data *modem,
char *alpha2;
int mcc;
+ /* Do not change regdom here if it is set to follow timezone. */
+ if (connman_setting_get_bool("RegdomFollowsTimezone"))
+ return;
+
dbus_message_iter_get_basic(value, &mobile_country_code);
DBG("%s MobileContryCode %s", modem->path, mobile_country_code);
diff --git a/plugins/vpn.c b/plugins/vpn.c
index 5668c004..42396d2a 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -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
@@ -85,6 +86,7 @@ struct connection_data {
char *domain;
char **nameservers;
bool immutable;
+ bool default_route_set;
GHashTable *server_routes;
GHashTable *user_routes;
@@ -94,6 +96,7 @@ struct connection_data {
GResolv *resolv;
guint resolv_id;
+ guint remove_resolv_id;
};
static int set_string(struct connman_provider *provider,
@@ -151,6 +154,8 @@ static const char *get_string(struct connman_provider *provider,
return data->host_ip[0];
} else if (g_str_equal(key, "VPN.Domain"))
return data->domain;
+ else if (g_str_equal(key, "Transport"))
+ return data->service_ident;
return g_hash_table_lookup(data->setting_strings, key);
}
@@ -171,10 +176,15 @@ static char *get_ident(const char *path)
static void cancel_host_resolv(struct connection_data *data)
{
- if (data->resolv_id != 0)
+
+ if (data->remove_resolv_id)
+ g_source_remove(data->remove_resolv_id);
+
+ if (data->resolv && data->resolv_id)
g_resolv_cancel_lookup(data->resolv, data->resolv_id);
data->resolv_id = 0;
+ data->remove_resolv_id = 0;
g_resolv_unref(data->resolv);
data->resolv = NULL;
@@ -206,7 +216,7 @@ static void resolv_result(GResolvResultStatus status,
* We cannot unref the resolver here as resolv struct is manipulated
* by gresolv.c after we return from this callback.
*/
- g_idle_add(remove_resolv, data);
+ data->remove_resolv_id = g_idle_add(remove_resolv, data);
data->resolv_id = 0;
}
@@ -261,14 +271,12 @@ static bool provider_is_connected(struct connection_data *data)
static void set_provider_state(struct connection_data *data)
{
enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
+ bool connected;
int err = 0;
DBG("provider %p new state %s", data->provider, data->state);
- if (!provider_is_connected(data)) {
- g_free(data->service_ident);
- data->service_ident = NULL;
- }
+ connected = provider_is_connected(data);
if (g_str_equal(data->state, "ready")) {
state = CONNMAN_PROVIDER_STATE_READY;
@@ -288,7 +296,7 @@ static void set_provider_state(struct connection_data *data)
}
connman_provider_set_state(data->provider, state);
- return;
+ goto free;
set:
if (data->cb_data)
@@ -299,6 +307,12 @@ set:
free_config_cb_data(data->cb_data);
data->cb_data = NULL;
+
+free:
+ if (!connected) {
+ g_free(data->service_ident);
+ data->service_ident = NULL;
+ }
}
static int create_provider(struct connection_data *data, void *user_data)
@@ -431,6 +445,7 @@ static int extract_ip(DBusMessageIter *array, int family,
}
connman_ipaddress_set_peer(data->ip, peer);
+ connman_ipaddress_set_p2p(data->ip, true);
return 0;
}
@@ -477,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;
@@ -490,8 +508,12 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
struct connection_data *data = user_data;
struct config_create_data *cb_data = data->cb_data;
- if (!dbus_pending_call_get_completed(call))
- return;
+ DBG("");
+
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("vpn connect reply pending call incomplete");
+ goto out;
+ }
if (call != data->call) {
connman_error("invalid call %p to VPN connect_reply data %p "
@@ -511,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);
@@ -769,12 +798,16 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data)
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_STRUCT_END_CHAR_AS_STRING;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("");
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("get connections reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
@@ -814,6 +847,7 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data)
done:
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
@@ -861,12 +895,16 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data)
DBusMessage *reply;
DBusError error;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("");
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("remove connection reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
@@ -884,6 +922,7 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data)
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
@@ -988,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,
@@ -1010,11 +1049,14 @@ static int disconnect_provider(struct connection_data *data)
dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply,
data, NULL);
- g_free(data->service_ident);
- data->service_ident = NULL;
+ data->default_route_set = false;
connman_provider_set_state(data->provider,
CONNMAN_PROVIDER_STATE_DISCONNECT);
+
+ g_free(data->service_ident);
+ data->service_ident = NULL;
+
return -EINPROGRESS;
}
@@ -1055,12 +1097,16 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data)
struct connection_data *data;
struct config_create_data *cb_data = user_data;
- if (!dbus_pending_call_get_completed(call))
- return;
-
DBG("user %p", cb_data);
+ if (!dbus_pending_call_get_completed(call)) {
+ connman_warn("configuration create reply pending call incomplete");
+ goto out;
+ }
+
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
@@ -1114,6 +1160,7 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data)
done:
dbus_message_unref(reply);
+out:
dbus_pending_call_unref(call);
}
@@ -1461,6 +1508,17 @@ static void set_route(struct connection_data *data, struct vpn_route *route)
return;
}
+ DBG("set route provider %p %s/%s/%s", data->provider,
+ route->network, route->gateway,
+ route->netmask);
+
+ /* Do not add default route for split routed VPNs.*/
+ if (connman_provider_is_split_routing(data->provider) &&
+ connman_inet_is_default_route(route->family,
+ route->network, route->gateway,
+ route->netmask))
+ return;
+
if (route->family == AF_INET6) {
unsigned char prefix_len = atoi(route->netmask);
@@ -1473,6 +1531,126 @@ static void set_route(struct connection_data *data, struct vpn_route *route)
route->gateway,
route->netmask);
}
+
+ if (connman_inet_is_default_route(route->family, route->network,
+ route->gateway, route->netmask))
+ data->default_route_set = true;
+}
+
+static int save_route(GHashTable *routes, int family, const char *network,
+ const char *netmask, const char *gateway);
+
+static int add_network_route(struct connection_data *data)
+{
+ struct vpn_route rt = { 0, };
+ int err;
+
+ if (!data)
+ return -EINVAL;
+
+ rt.family = connman_provider_get_family(data->provider);
+ switch (rt.family) {
+ case PF_INET:
+ err = connman_inet_get_route_addresses(data->index,
+ &rt.network, &rt.netmask, &rt.gateway);
+ break;
+ case PF_INET6:
+ err = connman_inet_ipv6_get_route_addresses(data->index,
+ &rt.network, &rt.netmask, &rt.gateway);
+ break;
+ default:
+ connman_error("invalid protocol family %d", rt.family);
+ return -EINVAL;
+ }
+
+ DBG("network %s gateway %s netmask %s for provider %p",
+ rt.network, rt.gateway, rt.netmask,
+ data->provider);
+
+ if (err) {
+ connman_error("cannot get network/gateway/netmask for %p",
+ data->provider);
+ goto out;
+ }
+
+ err = save_route(data->server_routes, rt.family, rt.network, rt.netmask,
+ rt.gateway);
+ if (err) {
+ connman_warn("failed to add network route for provider"
+ "%p", data->provider);
+ goto out;
+ }
+
+ set_route(data, &rt);
+
+out:
+ g_free(rt.network);
+ g_free(rt.netmask);
+ g_free(rt.gateway);
+
+ return 0;
+}
+
+static bool is_valid_route_table(struct connman_provider *provider,
+ GHashTable *table)
+{
+ GHashTableIter iter;
+ gpointer value, key;
+ struct vpn_route *route;
+ size_t table_size;
+
+ if (!table)
+ return false;
+
+ table_size = g_hash_table_size(table);
+
+ /* Non-split routed may have only the default route */
+ if (table_size > 0 && !connman_provider_is_split_routing(provider))
+ return true;
+
+ /* Split routed has more than the default route */
+ if (table_size > 1)
+ return true;
+
+ /*
+ * Only one route for split routed VPN, which should not be the
+ * default route.
+ */
+ g_hash_table_iter_init(&iter, table);
+ if (!g_hash_table_iter_next(&iter, &key, &value)) /* First and only */
+ return false;
+
+ route = value;
+ if (!route)
+ return false;
+
+ DBG("check route %d %s/%s/%s", route->family, route->network,
+ route->gateway, route->netmask);
+
+ if (!connman_inet_is_default_route(route->family, route->network,
+ route->gateway, route->netmask))
+ return true;
+
+ return false;
+}
+
+static bool check_routes(struct connman_provider *provider)
+{
+ struct connection_data *data;;
+
+ DBG("provider %p", provider);
+
+ data = connman_provider_get_data(provider);
+ if (!data)
+ return false;
+
+ if (is_valid_route_table(provider, data->user_routes))
+ return true;
+
+ if (is_valid_route_table(provider, data->server_routes))
+ return true;
+
+ return false;
}
static int set_routes(struct connman_provider *provider,
@@ -1504,28 +1682,30 @@ static int set_routes(struct connman_provider *provider,
set_route(data, value);
}
- return 0;
-}
+ /* If non-split routed VPN does not have a default route, add it */
+ if (!connman_provider_is_split_routing(provider) &&
+ !data->default_route_set) {
+ int family = connman_provider_get_family(provider);
+ const char *ipaddr_any = family == AF_INET6 ?
+ "::" : "0.0.0.0";
+ struct vpn_route def_route = {family, (char*) ipaddr_any,
+ (char*) ipaddr_any, NULL};
-static bool check_routes(struct connman_provider *provider)
-{
- struct connection_data *data;
-
- DBG("provider %p", provider);
-
- data = connman_provider_get_data(provider);
- if (!data)
- return false;
-
- if (data->user_routes &&
- g_hash_table_size(data->user_routes) > 0)
- return true;
+ set_route(data, &def_route);
+ }
- if (data->server_routes &&
- g_hash_table_size(data->server_routes) > 0)
- return true;
+ /* Split routed VPN must have at least one route to the network */
+ if (connman_provider_is_split_routing(provider) &&
+ !check_routes(provider)) {
+ int err = add_network_route(data);
+ if (err) {
+ connman_warn("cannot add network route provider %p",
+ provider);
+ return err;
+ }
+ }
- return false;
+ return 0;
}
static struct connman_provider_driver provider_driver = {
@@ -1694,12 +1874,52 @@ static int save_route(GHashTable *routes, int family, const char *network,
route->gateway = g_strdup(gateway);
g_hash_table_replace(routes, key, route);
- } else
+ } else {
g_free(key);
+ return -EALREADY;
+ }
return 0;
}
+static void change_provider_split_routing(struct connman_provider *provider,
+ bool split_routing)
+{
+ struct connection_data *data;
+ int err;
+
+ if (!provider)
+ return;
+
+ if (connman_provider_is_split_routing(provider) == split_routing)
+ return;
+
+ data = connman_provider_get_data(provider);
+ if (split_routing && data && provider_is_connected(data) &&
+ !check_routes(provider)) {
+ err = add_network_route(data);
+ if (err) {
+ connman_warn("cannot add network route provider %p",
+ provider);
+ return;
+ }
+ }
+
+ err = connman_provider_set_split_routing(provider, split_routing);
+ switch (err) {
+ case 0:
+ /* fall through */
+ case -EALREADY:
+ break;
+ case -EINVAL:
+ /* fall through */
+ case -EOPNOTSUPP:
+ connman_warn("cannot change split routing %d", err);
+ default:
+ break;
+ }
+}
+
static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts)
{
DBusMessageIter dict;
@@ -1876,6 +2096,10 @@ static gboolean property_changed(DBusConnection *conn,
g_free(data->domain);
data->domain = g_strdup(str);
connman_provider_set_domain(data->provider, data->domain);
+ } else if (g_str_equal(key, "SplitRouting")) {
+ dbus_bool_t split_routing;
+ dbus_message_iter_get_basic(&value, &split_routing);
+ change_provider_split_routing(data->provider, split_routing);
}
if (ip_set && err == 0) {
@@ -1964,6 +2188,10 @@ static void vpn_disconnect_check_provider(struct connection_data *data)
if (!vpn_is_valid_transport(service)) {
connman_provider_disconnect(data->provider);
}
+
+ /* VPN moved to be split routed, default route is not set */
+ if (connman_provider_is_split_routing(data->provider))
+ data->default_route_set = false;
}
}
diff --git a/plugins/wifi.c b/plugins/wifi.c
index fc304e3b..ed7437f5 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -49,7 +49,6 @@
#include <connman/service.h>
#include <connman/peer.h>
#include <connman/log.h>
-#include <connman/option.h>
#include <connman/storage.h>
#include <include/setting.h>
#include <connman/provision.h>
@@ -68,12 +67,14 @@
#define BGSCAN_DEFAULT "simple:30:-65:300"
#define AUTOSCAN_EXPONENTIAL "exponential:3:300"
#define AUTOSCAN_SINGLE "single:3"
+#define SCAN_MAX_DURATION 10
#define P2P_FIND_TIMEOUT 30
#define P2P_CONNECTION_TIMEOUT 100
#define P2P_LISTEN_PERIOD 500
#define P2P_LISTEN_INTERVAL 2000
+#define ASSOC_STATUS_AUTH_TIMEOUT 16
#define ASSOC_STATUS_NO_CLIENT 17
#define LOAD_SHAPING_MAX_RETRIES 3
@@ -165,6 +166,15 @@ struct wifi_data {
int assoc_code;
};
+struct wifi_network {
+ unsigned int keymgmt;
+};
+
+struct disconnect_data {
+ struct wifi_data *wifi;
+ struct connman_network *network;
+};
+
static GList *iface_list = NULL;
static GList *pending_wifi_device = NULL;
@@ -173,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)
@@ -477,15 +486,23 @@ static GSupplicantP2PServiceParams *fill_in_peer_service_params(
if (version > 0) {
params->version = version;
- params->service = g_memdup(spec, spec_length);
+ if (spec_length > 0) {
+ params->service = g_malloc(spec_length);
+ memcpy(params->service, spec, spec_length);
+ }
} else if (query_length > 0 && spec_length > 0) {
- params->query = g_memdup(query, query_length);
+ params->query = g_malloc(query_length);
+ memcpy(params->query, query, query_length);
params->query_length = query_length;
- params->response = g_memdup(spec, spec_length);
+ params->response = g_malloc(spec_length);
+ memcpy(params->response, spec, spec_length);
params->response_length = spec_length;
} else {
- params->wfd_ies = g_memdup(spec, spec_length);
+ if (spec_length > 0) {
+ params->wfd_ies = g_malloc(spec_length);
+ memcpy(params->wfd_ies, spec, spec_length);
+ }
params->wfd_ies_length = spec_length;
}
@@ -752,14 +769,15 @@ static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
}
if ((wifi->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
- if (flags & IFF_LOWER_UP) {
+ if (flags & IFF_LOWER_UP)
DBG("carrier on");
-
- handle_tethering(wifi);
- } else
+ else
DBG("carrier off");
}
+ if (flags & IFF_LOWER_UP)
+ handle_tethering(wifi);
+
wifi->flags = flags;
}
@@ -801,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);
}
@@ -1178,11 +1197,14 @@ static int get_hidden_connections_params(struct wifi_data *wifi,
scan_params->num_ssids = i;
scan_params->ssids = g_slist_reverse(scan_params->ssids);
- scan_params->freqs = g_memdup(orig_params->freqs,
- sizeof(uint16_t) * orig_params->num_freqs);
- if (!scan_params->freqs)
+ if (orig_params->num_freqs <= 0)
goto err;
+ scan_params->freqs =
+ g_malloc(sizeof(uint16_t) * orig_params->num_freqs);
+ memcpy(scan_params->freqs, orig_params->freqs,
+ sizeof(uint16_t) *orig_params->num_freqs);
+
scan_params->num_freqs = orig_params->num_freqs;
} else
@@ -1524,6 +1546,8 @@ static void interface_create_callback(int result,
void *user_data)
{
struct wifi_data *wifi = user_data;
+ char *bgscan_range_max;
+ long value;
DBG("result %d ifname %s, wifi %p", result,
g_supplicant_interface_get_ifname(interface),
@@ -1539,6 +1563,24 @@ static void interface_create_callback(int result,
wifi->interface_ready = true;
finalize_interface_creation(wifi);
}
+
+ /*
+ * Set the BSS expiration age to match the long scanning
+ * interval to avoid the loss of unconnected networks between
+ * two scans.
+ */
+ bgscan_range_max = strrchr(BGSCAN_DEFAULT, ':');
+ if (!bgscan_range_max || strlen(bgscan_range_max) < 1)
+ return;
+
+ value = strtol(bgscan_range_max + 1, NULL, 10);
+ if (value <= 0 || errno == ERANGE)
+ return;
+
+ if (g_supplicant_interface_set_bss_expiration_age(interface,
+ value + SCAN_MAX_DURATION) < 0) {
+ connman_warn("Failed to set bss expiration age");
+ }
}
static int wifi_enable(struct connman_device *device)
@@ -1546,7 +1588,7 @@ static int wifi_enable(struct connman_device *device)
struct wifi_data *wifi = connman_device_get_data(device);
int index;
char *interface;
- const char *driver = connman_option_get_string("wifi");
+ const char *driver = connman_setting_get_string("wifi");
int ret;
DBG("device %p %p", device, wifi);
@@ -2120,19 +2162,30 @@ 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 *ssid_blob;
const char *security;
memset(ssid, 0, sizeof(*ssid));
ssid->mode = G_SUPPLICANT_MODE_INFRA;
- ssid->ssid = connman_network_get_blob(network, "WiFi.SSID",
- &ssid->ssid_len);
+ ssid->ssid_len = 0;
+ ssid_blob =
+ connman_network_get_blob(network, "WiFi.SSID", &ssid->ssid_len);
+ ssid->ssid = g_try_malloc(ssid->ssid_len);
+ if (ssid->ssid)
+ memcpy((void *)ssid->ssid, ssid_blob, ssid->ssid_len);
+ else
+ ssid->ssid_len = 0;
+
ssid->scan_ssid = 1;
security = connman_network_get_string(network, "WiFi.Security");
ssid->security = network_security(security);
- ssid->passphrase = connman_network_get_string(network,
- "WiFi.Passphrase");
+ ssid->keymgmt = network_data->keymgmt;
+ ssid->ieee80211w = G_SUPPLICANT_MFP_OPTIONAL;
+ ssid->passphrase = g_strdup(
+ connman_network_get_string(network, "WiFi.Passphrase"));
- ssid->eap = connman_network_get_string(network, "WiFi.EAP");
+ ssid->eap = g_strdup(connman_network_get_string(network, "WiFi.EAP"));
/*
* If our private key password is unset,
@@ -2141,42 +2194,44 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
* cert may have to be provided.
*/
if (!connman_network_get_string(network, "WiFi.PrivateKeyPassphrase"))
- connman_network_set_string(network,
- "WiFi.PrivateKeyPassphrase",
- ssid->passphrase);
+ connman_network_set_string(network, "WiFi.PrivateKeyPassphrase",
+ ssid->passphrase);
/* We must have an identity for both PEAP and TLS */
- ssid->identity = connman_network_get_string(network, "WiFi.Identity");
+ ssid->identity =
+ g_strdup(connman_network_get_string(network, "WiFi.Identity"));
/* Use agent provided identity as a fallback */
if (!ssid->identity || strlen(ssid->identity) == 0)
- ssid->identity = connman_network_get_string(network,
- "WiFi.AgentIdentity");
-
- ssid->anonymous_identity = connman_network_get_string(network,
- "WiFi.AnonymousIdentity");
- ssid->ca_cert_path = connman_network_get_string(network,
- "WiFi.CACertFile");
- ssid->subject_match = connman_network_get_string(network,
- "WiFi.SubjectMatch");
- ssid->altsubject_match = connman_network_get_string(network,
- "WiFi.AltSubjectMatch");
- ssid->domain_suffix_match = connman_network_get_string(network,
- "WiFi.DomainSuffixMatch");
- ssid->domain_match = connman_network_get_string(network,
- "WiFi.DomainMatch");
- ssid->client_cert_path = connman_network_get_string(network,
- "WiFi.ClientCertFile");
- ssid->private_key_path = connman_network_get_string(network,
- "WiFi.PrivateKeyFile");
- ssid->private_key_passphrase = connman_network_get_string(network,
- "WiFi.PrivateKeyPassphrase");
- ssid->phase2_auth = connman_network_get_string(network, "WiFi.Phase2");
+ ssid->identity = g_strdup(connman_network_get_string(
+ network, "WiFi.AgentIdentity"));
+
+ ssid->anonymous_identity = g_strdup(
+ connman_network_get_string(network, "WiFi.AnonymousIdentity"));
+ ssid->ca_cert_path = g_strdup(
+ connman_network_get_string(network, "WiFi.CACertFile"));
+ ssid->subject_match = g_strdup(
+ connman_network_get_string(network, "WiFi.SubjectMatch"));
+ ssid->altsubject_match = g_strdup(
+ connman_network_get_string(network, "WiFi.AltSubjectMatch"));
+ ssid->domain_suffix_match = g_strdup(
+ connman_network_get_string(network, "WiFi.DomainSuffixMatch"));
+ ssid->domain_match = g_strdup(
+ connman_network_get_string(network, "WiFi.DomainMatch"));
+ ssid->client_cert_path = g_strdup(
+ connman_network_get_string(network, "WiFi.ClientCertFile"));
+ ssid->private_key_path = g_strdup(
+ connman_network_get_string(network, "WiFi.PrivateKeyFile"));
+ ssid->private_key_passphrase = g_strdup(connman_network_get_string(
+ network, "WiFi.PrivateKeyPassphrase"));
+ ssid->phase2_auth =
+ g_strdup(connman_network_get_string(network, "WiFi.Phase2"));
ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
- ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
+ ssid->pin_wps =
+ g_strdup(connman_network_get_string(network, "WiFi.PinWPS"));
if (connman_setting_get_bool("BackgroundScanning"))
- ssid->bgscan = BGSCAN_DEFAULT;
+ ssid->bgscan = g_strdup(BGSCAN_DEFAULT);
}
static int network_connect(struct connman_network *network)
@@ -2201,12 +2256,12 @@ static int network_connect(struct connman_network *network)
interface = wifi->interface;
- ssid_init(ssid, network);
-
if (wifi->disconnecting) {
wifi->pending_network = network;
g_free(ssid);
} else {
+ ssid_init(ssid, network);
+
wifi->network = connman_network_ref(network);
wifi->retries = 0;
@@ -2220,21 +2275,35 @@ static int network_connect(struct connman_network *network)
static void disconnect_callback(int result, GSupplicantInterface *interface,
void *user_data)
{
- struct wifi_data *wifi = user_data;
+ struct disconnect_data *dd = user_data;
+ struct connman_network *network = dd->network;
+ struct wifi_data *wifi = dd->wifi;
- DBG("result %d supplicant interface %p wifi %p",
- result, interface, wifi);
+ g_free(dd);
+
+ DBG("result %d supplicant interface %p wifi %p networks: current %p "
+ "pending %p disconnected %p", result, interface, wifi,
+ wifi->network, wifi->pending_network, network);
if (result == -ECONNABORTED) {
DBG("wifi interface no longer available");
return;
}
- if (wifi->network && wifi->network != wifi->pending_network)
- connman_network_set_connected(wifi->network, false);
- wifi->network = NULL;
+ if (g_slist_find(wifi->networks, network))
+ connman_network_set_connected(network, false);
wifi->disconnecting = false;
+
+ if (network != wifi->network) {
+ if (network == wifi->pending_network)
+ wifi->pending_network = NULL;
+ DBG("current wifi network has changed since disconnection");
+ return;
+ }
+
+ wifi->network = NULL;
+
wifi->connected = false;
if (wifi->pending_network) {
@@ -2248,6 +2317,7 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
static int network_disconnect(struct connman_network *network)
{
struct connman_device *device = connman_network_get_device(network);
+ struct disconnect_data *dd;
struct wifi_data *wifi;
int err;
@@ -2264,10 +2334,16 @@ static int network_disconnect(struct connman_network *network)
wifi->disconnecting = true;
+ dd = g_malloc0(sizeof(*dd));
+ dd->wifi = wifi;
+ dd->network = network;
+
err = g_supplicant_interface_disconnect(wifi->interface,
- disconnect_callback, wifi);
- if (err < 0)
+ disconnect_callback, dd);
+ if (err < 0) {
wifi->disconnecting = false;
+ g_free(dd);
+ }
return err;
}
@@ -2375,6 +2451,7 @@ static bool handle_wps_completion(GSupplicantInterface *interface,
if (wps) {
const unsigned char *ssid, *wps_ssid;
unsigned int ssid_len, wps_ssid_len;
+ struct disconnect_data *dd;
const char *wps_key;
/* Checking if we got associated with requested
@@ -2387,9 +2464,13 @@ static bool handle_wps_completion(GSupplicantInterface *interface,
if (!wps_ssid || wps_ssid_len != ssid_len ||
memcmp(ssid, wps_ssid, ssid_len) != 0) {
+ dd = g_malloc0(sizeof(*dd));
+ dd->wifi = wifi;
+ dd->network = network;
+
connman_network_set_associating(network, false);
g_supplicant_interface_disconnect(wifi->interface,
- disconnect_callback, wifi);
+ disconnect_callback, dd);
return false;
}
@@ -2422,7 +2503,9 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
{
struct connman_service *service;
- if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
+ if ((wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) &&
+ !((wifi->state == G_SUPPLICANT_STATE_ASSOCIATING) &&
+ (wifi->assoc_code == ASSOC_STATUS_AUTH_TIMEOUT)))
return false;
if (wifi->connected)
@@ -2445,6 +2528,25 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
return false;
}
+static bool handle_sae_authentication_failure(struct connman_network *network,
+ struct wifi_data *wifi)
+{
+ struct wifi_network *network_data = connman_network_get_data(network);
+
+ if (!(network_data->keymgmt & G_SUPPLICANT_KEYMGMT_SAE))
+ return false;
+
+ if (wifi->state != G_SUPPLICANT_STATE_AUTHENTICATING)
+ return false;
+
+ if (wifi->connected)
+ return false;
+
+ connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+
+ return true;
+}
+
static void interface_state(GSupplicantInterface *interface)
{
struct connman_network *network;
@@ -2542,11 +2644,15 @@ static void interface_state(GSupplicantInterface *interface)
network, wifi))
break;
+ /*
+ * On WPA3-SAE authentication, wpa_supplicant goes directly from
+ * authenticating to disconnected state if the key was invalid.
+ */
+ if (handle_sae_authentication_failure(network, wifi))
+ break;
+
/* See table 8-36 Reason codes in IEEE Std 802.11 */
switch (wifi->disconnect_code) {
- case 1: /* Unspecified reason */
- /* Let's assume it's because we got blocked */
-
case 6: /* Class 2 frame received from nonauthenticated STA */
connman_network_set_error(network,
CONNMAN_NETWORK_ERROR_BLOCKED);
@@ -2720,8 +2826,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)) {
@@ -2750,6 +2854,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;
@@ -2798,8 +2903,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);
@@ -2867,6 +2979,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);
}
@@ -3256,19 +3369,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;
@@ -3350,7 +3475,7 @@ static void sta_remove_callback(int result,
void *user_data)
{
struct wifi_tethering_info *info = user_data;
- const char *driver = connman_option_get_string("wifi");
+ const char *driver = connman_setting_get_string("wifi");
DBG("ifname %s result %d ", info->ifname, result);
@@ -3378,8 +3503,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;
@@ -3432,14 +3556,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;
@@ -3481,7 +3605,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;
@@ -3509,13 +3632,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;
diff --git a/scripts/libppp-compat.h b/scripts/libppp-compat.h
new file mode 100644
index 00000000..eee1d09d
--- /dev/null
+++ b/scripts/libppp-compat.h
@@ -0,0 +1,127 @@
+/* Copyright (C) Eivind Naess, eivnaes@yahoo.com */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __LIBPPP_COMPAT_H__
+#define __LIBPPP_COMPAT_H__
+
+/* Define USE_EAPTLS compile with EAP TLS support against older pppd headers,
+ * pppd >= 2.5.0 use PPP_WITH_EAPTLS and is defined in pppdconf.h */
+#define USE_EAPTLS 1
+
+/* Define INET6 to compile with IPv6 support against older pppd headers,
+ * pppd >= 2.5.0 use PPP_WITH_IPV6CP and is defined in pppdconf.h */
+#define INET6 1
+
+/* PPP < 2.5.0 defines and exports VERSION which overlaps with current package VERSION define.
+ * this silly macro magic is to work around that. */
+#undef VERSION
+#include <pppd/pppd.h>
+
+#ifndef PPPD_VERSION
+#define PPPD_VERSION VERSION
+#endif
+
+#include <pppd/fsm.h>
+#include <pppd/ccp.h>
+#include <pppd/eui64.h>
+#include <pppd/ipcp.h>
+#include <pppd/ipv6cp.h>
+#include <pppd/eap.h>
+#include <pppd/upap.h>
+
+#ifdef HAVE_PPPD_CHAP_H
+#include <pppd/chap.h>
+#endif
+
+#ifdef HAVE_PPPD_CHAP_NEW_H
+#include <pppd/chap-new.h>
+#endif
+
+#ifdef HAVE_PPPD_CHAP_MS_H
+#include <pppd/chap_ms.h>
+#endif
+
+#ifndef PPP_PROTO_CHAP
+#define PPP_PROTO_CHAP 0xc223
+#endif
+
+#ifndef PPP_PROTO_EAP
+#define PPP_PROTO_EAP 0xc227
+#endif
+
+
+#if WITH_PPP_VERSION < PPP_VERSION(2,5,0)
+
+static inline bool
+debug_on (void)
+{
+ return debug;
+}
+
+static inline const char
+*ppp_ipparam (void)
+{
+ return ipparam;
+}
+
+static inline int
+ppp_ifunit (void)
+{
+ return ifunit;
+}
+
+static inline const char *
+ppp_ifname (void)
+{
+ return ifname;
+}
+
+static inline int
+ppp_get_mtu (int idx)
+{
+ return netif_get_mtu(idx);
+}
+
+typedef enum ppp_notify
+{
+ NF_PID_CHANGE,
+ NF_PHASE_CHANGE,
+ NF_EXIT,
+ NF_SIGNALED,
+ NF_IP_UP,
+ NF_IP_DOWN,
+ NF_IPV6_UP,
+ NF_IPV6_DOWN,
+ NF_AUTH_UP,
+ NF_LINK_DOWN,
+ NF_FORK,
+ NF_MAX_NOTIFY
+} ppp_notify_t;
+
+typedef void (ppp_notify_fn) (void *ctx, int arg);
+
+static inline void
+ppp_add_notify (ppp_notify_t type, ppp_notify_fn *func, void *ctx)
+{
+ struct notifier **list[NF_MAX_NOTIFY] = {
+ [NF_PID_CHANGE ] = &pidchange,
+ [NF_PHASE_CHANGE] = &phasechange,
+ [NF_EXIT ] = &exitnotify,
+ [NF_SIGNALED ] = &sigreceived,
+ [NF_IP_UP ] = &ip_up_notifier,
+ [NF_IP_DOWN ] = &ip_down_notifier,
+ [NF_IPV6_UP ] = &ipv6_up_notifier,
+ [NF_IPV6_DOWN ] = &ipv6_down_notifier,
+ [NF_AUTH_UP ] = &auth_up_notifier,
+ [NF_LINK_DOWN ] = &link_down_notifier,
+ [NF_FORK ] = &fork_notifier,
+ };
+
+ struct notifier **notify = list[type];
+ if (notify) {
+ add_notifier(notify, func, ctx);
+ }
+}
+
+#endif /* #if WITH_PPP_VERSION < PPP_VERSION(2,5,0) */
+#endif /* #if__LIBPPP_COMPAT_H__ */
diff --git a/scripts/libppp-plugin.c b/scripts/libppp-plugin.c
index 0dd8b471..61641b5b 100644
--- a/scripts/libppp-plugin.c
+++ b/scripts/libppp-plugin.c
@@ -29,14 +29,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <pppd/pppd.h>
-#include <pppd/fsm.h>
-#include <pppd/ipcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dbus/dbus.h>
+#include "libppp-compat.h"
+
#define INET_ADDRES_LEN (INET_ADDRSTRLEN + 5)
#define INET_DNS_LEN (2*INET_ADDRSTRLEN + 9)
@@ -47,7 +46,7 @@ static char *path;
static DBusConnection *connection;
static int prev_phase;
-char pppd_version[] = VERSION;
+char pppd_version[] = PPPD_VERSION;
int plugin_init(void);
@@ -170,7 +169,7 @@ static void ppp_up(void *data, int arg)
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
- append(&dict, "INTERNAL_IFNAME", ifname);
+ append(&dict, "INTERNAL_IFNAME", ppp_ifname());
inet_ntop(AF_INET, &ipcp_gotoptions[0].ouraddr, buf, INET_ADDRSTRLEN);
append(&dict, "INTERNAL_IP4_ADDRESS", buf);
@@ -309,9 +308,9 @@ int plugin_init(void)
chap_check_hook = ppp_have_secret;
pap_check_hook = ppp_have_secret;
- add_notifier(&ip_up_notifier, ppp_up, NULL);
- add_notifier(&phasechange, ppp_phase_change, NULL);
- add_notifier(&exitnotify, ppp_exit, connection);
+ ppp_add_notify(NF_IP_UP, ppp_up, NULL);
+ ppp_add_notify(NF_PHASE_CHANGE, ppp_phase_change, NULL);
+ ppp_add_notify(NF_EXIT, ppp_exit, connection);
return 0;
}
diff --git a/src/agent-connman.c b/src/agent-connman.c
index fca7cc1f..2bd33e04 100644
--- a/src/agent-connman.c
+++ b/src/agent-connman.c
@@ -865,3 +865,17 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer,
return -EINPROGRESS;
}
+
+bool __connman_agent_is_request_pending(struct connman_service *service,
+ const char *dbus_sender)
+{
+ void *agent;
+
+ /* Default agent will be returned if no dbus_sender */
+ agent = connman_agent_get_info(dbus_sender, NULL, NULL);
+
+ if (!service || !agent)
+ return false;
+
+ return connman_agent_queue_search(service, agent);
+}
diff --git a/src/agent.c b/src/agent.c
index d6113af7..23517d9b 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -257,6 +257,28 @@ int connman_agent_queue_message(void *user_context,
return err;
}
+bool connman_agent_queue_search(void *user_context, void *agent_data)
+{
+ struct connman_agent *agent = agent_data;
+ struct connman_agent_request *queue_data;
+ GList *iter;
+
+ if (!agent || !user_context)
+ return false;
+
+ if (agent->pending && agent->pending->user_context == user_context)
+ return true;
+
+ for (iter = agent->queue; iter; iter = iter->next) {
+ queue_data = iter->data;
+
+ if (queue_data && queue_data->user_context == user_context)
+ return true;
+ }
+
+ return false;
+}
+
static void set_default_agent(void)
{
struct connman_agent *agent = NULL;
@@ -366,9 +388,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);
}
diff --git a/src/bridge.c b/src/bridge.c
index cd2d9cee..df19a6af 100644
--- a/src/bridge.c
+++ b/src/bridge.c
@@ -122,7 +122,8 @@ int __connman_bridge_enable(const char *name, const char *ip_address,
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- ip_address, NULL, prefix_len, broadcast);
+ ip_address, NULL, prefix_len, broadcast,
+ false);
if (err < 0)
return err;
diff --git a/src/clock.c b/src/clock.c
index 0fde2c34..54ac274a 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -173,6 +173,7 @@ static DBusMessage *get_properties(DBusConnection *conn,
{
DBusMessage *reply;
DBusMessageIter array, dict;
+ dbus_bool_t is_synced;
struct timeval tv;
const char *str;
@@ -210,6 +211,10 @@ static DBusMessage *get_properties(DBusConnection *conn,
connman_dbus_dict_append_array(&dict, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ is_synced = __connman_timeserver_is_synced();
+ connman_dbus_dict_append_basic(&dict, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
+
connman_dbus_dict_close(&array, &dict);
return reply;
@@ -258,12 +263,14 @@ static DBusMessage *set_property(DBusConnection *conn,
if (settimeofday(&tv, NULL) < 0)
return __connman_error_invalid_arguments(msg);
+ __connman_timeserver_set_synced(false);
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Time",
DBUS_TYPE_UINT64, &newval);
} else if (g_str_equal(name, "TimeUpdates")) {
const char *strval;
enum time_updates newval;
+ struct connman_service *service;
if (type != DBUS_TYPE_STRING)
return __connman_error_invalid_arguments(msg);
@@ -283,6 +290,9 @@ static DBusMessage *set_property(DBusConnection *conn,
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
DBUS_TYPE_STRING, &strval);
+
+ service = connman_service_get_default();
+ __connman_timeserver_conf_update(service);
} else if (g_str_equal(name, "Timezone")) {
const char *strval;
@@ -362,6 +372,8 @@ static DBusMessage *set_property(DBusConnection *conn,
connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ } else if (g_str_equal(name, "TimeserverSynced")) {
+ return __connman_error_permission_denied(msg);
} else
return __connman_error_invalid_property(msg);
diff --git a/src/config.c b/src/config.c
index 62023b10..33fdc737 100644
--- a/src/config.c
+++ b/src/config.c
@@ -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();
diff --git a/src/connection.c b/src/connection.c
index 303e9922..9d2c6961 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1066,6 +1066,29 @@ int __connman_connection_get_vpn_index(int phy_index)
return -1;
}
+int __connman_connection_get_vpn_phy_index(int vpn_index)
+{
+ GHashTableIter iter;
+ gpointer value, key;
+
+ g_hash_table_iter_init(&iter, gateway_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct gateway_data *data = value;
+
+ if (data->index != vpn_index)
+ continue;
+
+ if (data->ipv4_gateway)
+ return data->ipv4_gateway->vpn_phy_index;
+
+ if (data->ipv6_gateway)
+ return data->ipv6_gateway->vpn_phy_index;
+ }
+
+ return -1;
+}
+
int __connman_connection_init(void)
{
int err;
diff --git a/src/connman.h b/src/connman.h
index 3bdc0dc7..b955d98b 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -126,6 +126,8 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer,
bool wps_requested,
const char *dbus_sender,
void *user_data);
+bool __connman_agent_is_request_pending(struct connman_service *service,
+ const char *dbus_sender);
#include <connman/log.h>
@@ -138,8 +140,6 @@ void __connman_log_enable(struct connman_debug_desc *start,
#include <connman/backtrace.h>
-#include <connman/option.h>
-
#include <connman/setting.h>
#include <connman/plugin.h>
@@ -159,7 +159,8 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family,
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast);
+ const char *broadcast,
+ bool is_p2p);
int __connman_inet_get_interface_address(int index, int family, void *address);
int __connman_inet_get_interface_ll_address(int index, int family, void *address);
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address);
@@ -302,6 +303,7 @@ struct connman_ipaddress {
char *peer;
char *broadcast;
char *gateway;
+ bool is_p2p; /* P2P connection or VPN, broadcast is excluded. */
};
struct connman_ipconfig_ops {
@@ -449,7 +451,12 @@ char **__connman_timeserver_system_get();
GSList *__connman_timeserver_add_list(GSList *server_list,
const char *timeserver);
GSList *__connman_timeserver_get_all(struct connman_service *service);
-int __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_sync(struct connman_service *service,
+ enum connman_timeserver_sync_reason reason);
+void __connman_timeserver_conf_update(struct connman_service *service);
+
+bool __connman_timeserver_is_synced(void);
+void __connman_timeserver_set_synced(bool status);
enum __connman_dhcpv6_status {
CONNMAN_DHCPV6_STATUS_FAIL = 0,
@@ -502,6 +509,7 @@ int __connman_connection_gateway_add(struct connman_service *service,
void __connman_connection_gateway_remove(struct connman_service *service,
enum connman_ipconfig_type type);
int __connman_connection_get_vpn_index(int phy_index);
+int __connman_connection_get_vpn_phy_index(int vpn_index);
bool __connman_connection_update_gateway(void);
@@ -600,6 +608,7 @@ void __connman_network_set_device(struct connman_network *network,
int __connman_network_connect(struct connman_network *network);
int __connman_network_disconnect(struct connman_network *network);
+int __connman_network_forget(struct connman_network *network);
int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig);
int __connman_network_enable_ipconfig(struct connman_network *network,
@@ -609,6 +618,7 @@ const char *__connman_network_get_type(struct connman_network *network);
const char *__connman_network_get_group(struct connman_network *network);
const char *__connman_network_get_ident(struct connman_network *network);
bool __connman_network_get_weakness(struct connman_network *network);
+bool __connman_network_native_autoconnect(struct connman_network *network);
int __connman_config_init();
void __connman_config_cleanup(void);
@@ -656,6 +666,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data);
bool __connman_provider_is_immutable(struct connman_provider *provider);
int __connman_provider_create_and_connect(DBusMessage *msg);
const char * __connman_provider_get_ident(struct connman_provider *provider);
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider);
int __connman_provider_indicate_state(struct connman_provider *provider,
enum connman_provider_state state);
int __connman_provider_indicate_error(struct connman_provider *provider,
@@ -670,6 +682,8 @@ int __connman_provider_init(void);
int __connman_service_init(void);
void __connman_service_cleanup(void);
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before);
int __connman_service_load_modifiable(struct connman_service *service);
void __connman_service_list_struct(DBusMessageIter *iter);
@@ -720,8 +734,9 @@ int __connman_service_set_mdns(struct connman_service *service,
void __connman_service_set_string(struct connman_service *service,
const char *key, const char *value);
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type);
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success);
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_service_state new_state,
enum connman_ipconfig_type type);
@@ -737,7 +752,6 @@ int __connman_service_indicate_default(struct connman_service *service);
int __connman_service_connect(struct connman_service *service,
enum connman_service_connect_reason reason);
int __connman_service_disconnect(struct connman_service *service);
-int __connman_service_disconnect_all(void);
void __connman_service_set_active_session(bool enable, GSList *list);
void __connman_service_auto_connect(enum connman_service_connect_reason reason);
bool __connman_service_remove(struct connman_service *service);
@@ -779,6 +793,9 @@ void __connman_service_set_pac(struct connman_service *service,
bool __connman_service_is_hidden(struct connman_service *service);
bool __connman_service_is_split_routing(struct connman_service *service);
bool __connman_service_index_is_split_routing(int index);
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool split_routing);
+void __connman_service_split_routing_changed(struct connman_service *service);
int __connman_service_get_index(struct connman_service *service);
void __connman_service_set_hidden(struct connman_service *service);
void __connman_service_set_hostname(struct connman_service *service,
@@ -970,6 +987,7 @@ void __connman_dnsproxy_remove_listener(int index);
int __connman_dnsproxy_append(int index, const char *domain, const char *server);
int __connman_dnsproxy_remove(int index, const char *domain, const char *server);
int __connman_dnsproxy_set_mdns(int index, bool enabled);
+void __connman_dnsproxy_set_listen_port(unsigned int port);
int __connman_6to4_probe(struct connman_service *service);
void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
diff --git a/src/dbus.c b/src/dbus.c
index d80a46ce..74b3157b 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -246,9 +246,7 @@ dbus_bool_t connman_dbus_property_changed_basic(const char *path,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_basic(&iter, key, type, val);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_dict(const char *path,
@@ -268,9 +266,7 @@ dbus_bool_t connman_dbus_property_changed_dict(const char *path,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_dict(&iter, key, function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_array(const char *path,
@@ -291,9 +287,7 @@ dbus_bool_t connman_dbus_property_changed_array(const char *path,
connman_dbus_property_append_array(&iter, key, type,
function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
@@ -319,9 +313,7 @@ dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
@@ -348,9 +340,7 @@ dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
@@ -377,9 +367,7 @@ dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
@@ -699,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)
diff --git a/src/dhcp.c b/src/dhcp.c
index 42e9f417..18dbab27 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -422,8 +422,7 @@ static bool apply_lease_available_on_network(GDHCPClient *dhcp_client,
g_free(dhcp->pac);
dhcp->pac = pac;
- __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig,
- dhcp->pac);
+ __connman_service_set_proxy_autoconfig(service, dhcp->pac);
}
if (connman_setting_get_bool("Enable6to4"))
@@ -616,7 +615,7 @@ static int dhcp_initialize(struct connman_dhcp *dhcp)
g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
- vendor_class_id = connman_option_get_string("VendorClassID");
+ vendor_class_id = connman_setting_get_string("VendorClassID");
if (vendor_class_id)
g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID,
vendor_class_id);
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 2d5f8f6a..8b683599 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -945,7 +945,7 @@ static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
ref_own_address(user_data);
- if (inet_pton(AF_INET6, address, &addr) < 0) {
+ if (inet_pton(AF_INET6, address, &addr) != 1) {
DBG("Invalid IPv6 address %s %d/%s", address,
-errno, strerror(errno));
goto fail;
diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c
index 5fe306c3..912ab3fe 100644
--- a/src/dns-systemd-resolved.c
+++ b/src/dns-systemd-resolved.c
@@ -106,7 +106,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
if (type == AF_INET) {
result = inet_pton(type, server, ipv4_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv4 address: %s",
server);
return;
@@ -128,7 +128,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
&byte_array);
} else if (type == AF_INET6) {
result = inet_pton(type, server, ipv6_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv6 address: %s", server);
return;
}
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index a7bf87a1..7ebffbc0 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -3,6 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2022 Matthias Gerstner of SUSE. 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
@@ -41,7 +42,13 @@
#include "connman.h"
-#define debug(fmt...) do { } while (0)
+#ifdef DNSPROXY_DEBUG
+# define debug(fmt...) do { fprintf(stderr, fmt); fprintf(stderr, "\n"); } while (0)
+#else
+# define debug(fmt...) do { } while (0)
+#endif
+
+#define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(a[0])
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct domain_hdr {
@@ -172,11 +179,17 @@ struct cache_data {
struct cache_entry {
char *key;
bool want_refresh;
- int hits;
+ size_t hits;
struct cache_data *ipv4;
struct cache_data *ipv6;
};
+struct cache_timeout {
+ time_t current_time;
+ time_t max_timeout;
+ bool try_harder;
+};
+
struct domain_question {
uint16_t type;
uint16_t class;
@@ -214,38 +227,91 @@ struct domain_rr {
*/
#define MAX_CACHE_SIZE 256
+#define DNS_HEADER_SIZE sizeof(struct domain_hdr)
+#define DNS_HEADER_TCP_EXTRA_BYTES 2
+#define DNS_TCP_HEADER_SIZE DNS_HEADER_SIZE + DNS_HEADER_TCP_EXTRA_BYTES
+#define DNS_QUESTION_SIZE sizeof(struct domain_question)
+#define DNS_RR_SIZE sizeof(struct domain_rr)
+#define DNS_QTYPE_QCLASS_SIZE sizeof(struct qtype_qclass)
+
+enum dns_type {
+ /* IPv4 address 32-bit */
+ DNS_TYPE_A = ns_t_a,
+ /* IPv6 address 128-bit */
+ DNS_TYPE_AAAA = ns_t_aaaa,
+ /* alias to another name */
+ DNS_TYPE_CNAME = ns_t_cname,
+ /* start of a zone of authority */
+ DNS_TYPE_SOA = ns_t_soa
+};
+
+enum dns_class {
+ DNS_CLASS_IN = ns_c_in,
+ DNS_CLASS_ANY = ns_c_any /* only valid for QCLASS fields */
+};
+
static int cache_size;
static GHashTable *cache;
static int cache_refcount;
-static GSList *server_list = NULL;
-static GSList *request_list = NULL;
-static GHashTable *listener_table = NULL;
+static GSList *server_list;
+static GSList *request_list;
+static GHashTable *listener_table;
static time_t next_refresh;
static GHashTable *partial_tcp_req_table;
-static guint cache_timer = 0;
+static guint cache_timer;
+static in_port_t dns_listen_port = 53;
+/* we can keep using the same resolve's */
+static GResolv *ipv4_resolve;
+static GResolv *ipv6_resolve;
static guint16 get_id(void)
{
uint64_t rand;
+ /* TODO: return code is ignored, should we rather abort() on error? */
__connman_util_get_random(&rand);
return rand;
}
-static int protocol_offset(int protocol)
+static size_t protocol_offset(int protocol)
{
switch (protocol) {
case IPPROTO_UDP:
return 0;
case IPPROTO_TCP:
- return 2;
+ return DNS_HEADER_TCP_EXTRA_BYTES;
default:
- return -EINVAL;
+ /* this should never happen */
+ abort();
}
+}
+static const char* protocol_label(int protocol)
+{
+ switch(protocol) {
+ case IPPROTO_UDP:
+ return "UDP";
+ case IPPROTO_TCP:
+ return "TCP";
+ default:
+ return "BAD_PROTOCOL";
+ }
+}
+
+static int socket_type(int protocol, int extra_flags)
+{
+ switch (protocol) {
+ case IPPROTO_UDP:
+ return SOCK_DGRAM | extra_flags;
+ case IPPROTO_TCP:
+ return SOCK_STREAM | extra_flags;
+ default:
+ /* this should never happen */
+ abort();
+ }
}
/*
@@ -271,9 +337,7 @@ static time_t round_down_ttl(time_t end_time, int ttl)
static struct request_data *find_request(guint16 id)
{
- GSList *list;
-
- for (list = request_list; list; list = list->next) {
+ for (GSList *list = request_list; list; list = list->next) {
struct request_data *req = list->data;
if (req->dstid == id || req->altid == id)
@@ -287,11 +351,9 @@ static struct server_data *find_server(int index,
const char *server,
int protocol)
{
- GSList *list;
-
debug("index %d server %s proto %d", index, server, protocol);
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (index < 0 && data->index < 0 &&
@@ -312,10 +374,6 @@ static struct server_data *find_server(int index,
return NULL;
}
-/* we can keep using the same resolve's */
-static GResolv *ipv4_resolve;
-static GResolv *ipv6_resolve;
-
static void dummy_resolve_func(GResolvResultStatus status,
char **results, gpointer user_data)
{
@@ -353,95 +411,86 @@ static void refresh_dns_entry(struct cache_entry *entry, char *name)
age = 4;
}
- entry->hits -= age;
- if (entry->hits < 0)
+ if (entry->hits > age)
+ entry->hits -= age;
+ else
entry->hits = 0;
}
-static int dns_name_length(unsigned char *buf)
+static size_t dns_name_length(const unsigned char *buf)
{
if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */
return 2;
- return strlen((char *)buf) + 1;
+ return strlen((const char *)buf) + 1;
}
-static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
+static void update_cached_ttl(unsigned char *ptr, int len, int new_ttl)
{
- unsigned char *c;
- uint16_t w;
- int l;
+ size_t name_len;
+ const uint32_t raw_ttl = ntohl((uint32_t)new_ttl);
+
+ if (new_ttl < 0)
+ return;
/* skip the header */
- c = buf + 12;
- len -= 12;
+ ptr += DNS_HEADER_SIZE;
+ len -= DNS_HEADER_SIZE;
+
+ if (len < DNS_QUESTION_SIZE + 1)
+ return;
- /* skip the query, which is a name and 2 16 bit words */
- l = dns_name_length(c);
- c += l;
- len -= l;
- c += 4;
- len -= 4;
+ /* skip the query, which is a name and a struct domain_question */
+ name_len = dns_name_length(ptr);
+
+ ptr += name_len + DNS_QUESTION_SIZE;
+ len -= name_len + DNS_QUESTION_SIZE;
/* now we get the answer records */
while (len > 0) {
+ struct domain_rr *rr = NULL;
+ size_t rr_len;
+
/* first a name */
- l = dns_name_length(c);
- c += l;
- len -= l;
- if (len < 0)
- break;
- /* then type + class, 2 bytes each */
- c += 4;
- len -= 4;
+ name_len = dns_name_length(ptr);
+ ptr += name_len;
+ len -= name_len;
if (len < 0)
break;
- /* now the 4 byte TTL field */
- c[0] = new_ttl >> 24 & 0xff;
- c[1] = new_ttl >> 16 & 0xff;
- c[2] = new_ttl >> 8 & 0xff;
- c[3] = new_ttl & 0xff;
- c += 4;
- len -= 4;
- if (len < 0)
+ rr = (void*)ptr;
+ if (len < sizeof(*rr))
+ /* incomplete record */
break;
- /* now the 2 byte rdlen field */
- w = c[0] << 8 | c[1];
- c += w + 2;
- len -= w + 2;
+ /* update the TTL field */
+ memcpy(&rr->ttl, &raw_ttl, sizeof(raw_ttl));
+
+ /* skip to the next record */
+ rr_len = sizeof(*rr) + ntohs(rr->rdlen);
+ ptr += rr_len;
+ len -= rr_len;
}
}
-static void send_cached_response(int sk, unsigned char *buf, int len,
+static void send_cached_response(int sk, const unsigned char *ptr, size_t len,
const struct sockaddr *to, socklen_t tolen,
int protocol, int id, uint16_t answers, int ttl)
{
- struct domain_hdr *hdr;
- unsigned char *ptr = buf;
- int err, offset, dns_len, adj_len = len - 2;
-
+ struct domain_hdr *hdr = NULL;
+ int err;
+ const size_t offset = protocol_offset(protocol);
/*
* The cached packet contains always the TCP offset (two bytes)
* so skip them for UDP.
*/
- switch (protocol) {
- case IPPROTO_UDP:
- ptr += 2;
- len -= 2;
- dns_len = len;
- offset = 0;
- break;
- case IPPROTO_TCP:
- offset = 2;
- dns_len = ptr[0] * 256 + ptr[1];
- break;
- default:
- return;
- }
+ const size_t skip_bytes = offset ? 0 : DNS_HEADER_TCP_EXTRA_BYTES;
+ ptr += skip_bytes;
+ len -= skip_bytes;
+ const size_t dns_len = protocol == IPPROTO_UDP ? len : ntohs(*((uint16_t*)ptr));
+
- if (len < 12)
+ if (len < DNS_HEADER_SIZE)
return;
hdr = (void *) (ptr + offset);
@@ -456,22 +505,21 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
/* if this is a negative reply, we are authoritative */
if (answers == 0)
hdr->aa = 1;
- else
+ else {
+ const int adj_len = len - 2;
update_cached_ttl((unsigned char *)hdr, adj_len, ttl);
+ }
- debug("sk %d id 0x%04x answers %d ptr %p length %d dns %d",
+ debug("sk %d id 0x%04x answers %d ptr %p length %zd dns %zd",
sk, hdr->id, answers, ptr, len, dns_len);
err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
connman_error("Cannot send cached DNS response: %s",
strerror(errno));
- return;
}
-
- if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) ||
- (dns_len != len && protocol == IPPROTO_UDP))
- debug("Packet length mismatch, sent %d wanted %d dns %d",
+ else if (err != len || dns_len != (len - offset))
+ debug("Packet length mismatch, sent %d wanted %zd dns %zd",
err, len, dns_len);
}
@@ -480,20 +528,19 @@ static void send_response(int sk, unsigned char *buf, size_t len,
int protocol)
{
struct domain_hdr *hdr;
- int err, offset = protocol_offset(protocol);
+ int err;
+ const size_t offset = protocol_offset(protocol);
+ const size_t send_size = DNS_HEADER_SIZE + offset;
debug("sk %d", sk);
- if (offset < 0)
- return;
-
- if (len < sizeof(*hdr) + offset)
+ if (len < send_size)
return;
hdr = (void *) (buf + offset);
if (offset) {
buf[0] = 0;
- buf[1] = sizeof(*hdr);
+ buf[1] = DNS_HEADER_SIZE;
}
debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
@@ -506,11 +553,10 @@ static void send_response(int sk, unsigned char *buf, size_t len,
hdr->nscount = 0;
hdr->arcount = 0;
- err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen);
+ err = sendto(sk, buf, send_size, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
connman_error("Failed to send DNS response to %d: %s",
sk, strerror(errno));
- return;
}
}
@@ -544,7 +590,7 @@ static gboolean request_timeout(gpointer user_data)
{
struct request_data *req = user_data;
struct sockaddr *sa;
- int sk;
+ int sk = -1;
if (!req)
return FALSE;
@@ -559,7 +605,9 @@ static gboolean request_timeout(gpointer user_data)
} else if (req->protocol == IPPROTO_TCP) {
sk = req->client_sk;
sa = NULL;
- } else
+ }
+
+ if (sk < 0)
goto out;
if (req->resplen > 0 && req->resp) {
@@ -568,22 +616,18 @@ static gboolean request_timeout(gpointer user_data)
* "not found" result), so send that back to client instead
* of more fatal server failed error.
*/
- if (sk >= 0)
- sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
- sa, req->sa_len);
+ sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
+ sa, req->sa_len);
} else if (req->request) {
/*
* There was not reply from server at all.
*/
- struct domain_hdr *hdr;
-
- hdr = (void *)(req->request + protocol_offset(req->protocol));
+ struct domain_hdr *hdr = (void *)(req->request + protocol_offset(req->protocol));
hdr->id = req->srcid;
- if (sk >= 0)
- send_response(sk, req->request, req->request_len,
- sa, req->sa_len, req->protocol);
+ send_response(sk, req->request, req->request_len,
+ sa, req->sa_len, req->protocol);
}
/*
@@ -603,73 +647,92 @@ out:
return FALSE;
}
-static int append_query(unsigned char *buf, unsigned int size,
- const char *query, const char *domain)
+static int append_data(unsigned char *buf, size_t size, const char *data)
{
unsigned char *ptr = buf;
- int len;
+ size_t len;
- debug("query %s domain %s", query, domain);
+ while (true) {
+ const char *dot = strchr(data, '.');
+ len = dot ? dot - data : strlen(data);
- while (query) {
- const char *tmp;
-
- tmp = strchr(query, '.');
- if (!tmp) {
- len = strlen(query);
- if (len == 0)
- break;
- *ptr = len;
- memcpy(ptr + 1, query, len);
- ptr += len + 1;
+ if (len == 0)
break;
- }
+ else if (size < len + 1)
+ return -1;
- *ptr = tmp - query;
- memcpy(ptr + 1, query, tmp - query);
- ptr += tmp - query + 1;
+ *ptr = len;
+ memcpy(ptr + 1, data, len);
+ ptr += len + 1;
+ size -= len + 1;
+
+ if (!dot)
+ break;
- query = tmp + 1;
+ data = dot + 1;
}
- while (domain) {
- const char *tmp;
+ return ptr - buf;
+}
- tmp = strchr(domain, '.');
- if (!tmp) {
- len = strlen(domain);
- if (len == 0)
- break;
- *ptr = len;
- memcpy(ptr + 1, domain, len);
- ptr += len + 1;
- break;
- }
+static int append_query(unsigned char *buf, size_t size,
+ const char *query, const char *domain)
+{
+ size_t added;
+ size_t left_size = size;
+ int res;
- *ptr = tmp - domain;
- memcpy(ptr + 1, domain, tmp - domain);
- ptr += tmp - domain + 1;
+ debug("query %s domain %s", query, domain);
- domain = tmp + 1;
- }
+ res = append_data(buf, left_size, query);
+ if (res < 0)
+ return -1;
+ left_size -= res;
- *ptr++ = 0x00;
+ res = append_data(buf + res, left_size, domain);
+ if (res < 0)
+ return -1;
+ left_size -= res;
- return ptr - buf;
+ if (left_size == 0)
+ return -1;
+
+ added = size - left_size;
+ *(buf + added) = 0x00;
+
+ return added;
}
-static bool cache_check_is_valid(struct cache_data *data,
- time_t current_time)
+static bool cache_check_is_valid(struct cache_data *data, time_t current_time)
{
if (!data)
return false;
-
- if (data->cache_until < current_time)
+ else if (data->cache_until < current_time)
return false;
return true;
}
+static void cache_free_ipv4(struct cache_entry *entry)
+{
+ if (!entry->ipv4)
+ return;
+
+ g_free(entry->ipv4->data);
+ g_free(entry->ipv4);
+ entry->ipv4 = NULL;
+}
+
+static void cache_free_ipv6(struct cache_entry *entry)
+{
+ if (!entry->ipv6)
+ return;
+
+ g_free(entry->ipv6->data);
+ g_free(entry->ipv6);
+ entry->ipv6 = NULL;
+}
+
/*
* remove stale cached entries so that they can be refreshed
*/
@@ -677,76 +740,65 @@ static void cache_enforce_validity(struct cache_entry *entry)
{
time_t current_time = time(NULL);
- if (!cache_check_is_valid(entry->ipv4, current_time)
- && entry->ipv4) {
+ if (entry->ipv4 && !cache_check_is_valid(entry->ipv4, current_time)) {
debug("cache timeout \"%s\" type A", entry->key);
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- entry->ipv4 = NULL;
-
+ cache_free_ipv4(entry);
}
- if (!cache_check_is_valid(entry->ipv6, current_time)
- && entry->ipv6) {
+ if (entry->ipv6 && !cache_check_is_valid(entry->ipv6, current_time)) {
debug("cache timeout \"%s\" type AAAA", entry->key);
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- entry->ipv6 = NULL;
+ cache_free_ipv6(entry);
}
}
-static uint16_t cache_check_validity(char *question, uint16_t type,
+static bool cache_check_validity(const char *question, uint16_t type,
struct cache_entry *entry)
{
- time_t current_time = time(NULL);
- bool want_refresh = false;
-
- /*
- * if we have a popular entry, we want a refresh instead of
- * total destruction of the entry.
- */
- if (entry->hits > 2)
- want_refresh = true;
+ struct cache_data *cached_ip = NULL, *other_ip = NULL;
+ const time_t current_time = time(NULL);
+ bool want_refresh;
cache_enforce_validity(entry);
switch (type) {
- case 1: /* IPv4 */
- if (!cache_check_is_valid(entry->ipv4, current_time)) {
- debug("cache %s \"%s\" type A", entry->ipv4 ?
- "timeout" : "entry missing", question);
-
- if (want_refresh)
- entry->want_refresh = true;
+ case DNS_TYPE_A: /* IPv4 */
+ cached_ip = entry->ipv4;
+ other_ip = entry->ipv6;
+ break;
- /*
- * We do not remove cache entry if there is still
- * valid IPv6 entry found in the cache.
- */
- if (!cache_check_is_valid(entry->ipv6, current_time) && !want_refresh) {
- g_hash_table_remove(cache, question);
- type = 0;
- }
- }
+ case DNS_TYPE_AAAA: /* IPv6 */
+ cached_ip = entry->ipv6;
+ other_ip = entry->ipv4;
break;
+ default:
+ return false;
+ }
- case 28: /* IPv6 */
- if (!cache_check_is_valid(entry->ipv6, current_time)) {
- debug("cache %s \"%s\" type AAAA", entry->ipv6 ?
- "timeout" : "entry missing", question);
+ /*
+ * if we have a popular entry, we want a refresh instead of
+ * total destruction of the entry.
+ */
+ want_refresh = entry->hits > 2 ? true : false;
- if (want_refresh)
- entry->want_refresh = true;
+ if (!cache_check_is_valid(cached_ip, current_time)) {
+ debug("cache %s \"%s\" type %s",
+ cached_ip ? "timeout" : "entry missing",
+ question,
+ cached_ip == entry->ipv4 ? "A" : "AAAA");
- if (!cache_check_is_valid(entry->ipv4, current_time) && !want_refresh) {
- g_hash_table_remove(cache, question);
- type = 0;
- }
+ if (want_refresh)
+ entry->want_refresh = true;
+ /*
+ * We do not remove cache entry if there is still a
+ * valid entry for another IP version found in the cache.
+ */
+ else if (!cache_check_is_valid(other_ip, current_time)) {
+ g_hash_table_remove(cache, question);
+ return false;
}
- break;
}
- return type;
+ return true;
}
static void cache_element_destroy(gpointer value)
@@ -756,19 +808,13 @@ static void cache_element_destroy(gpointer value)
if (!entry)
return;
- if (entry->ipv4) {
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- }
-
- if (entry->ipv6) {
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- }
+ cache_free_ipv4(entry);
+ cache_free_ipv6(entry);
g_free(entry->key);
g_free(entry);
+ /* TODO: this would be a worrying condition. Does this ever happen? */
if (--cache_size < 0)
cache_size = 0;
}
@@ -782,6 +828,7 @@ static gboolean try_remove_cache(gpointer user_data)
g_hash_table_destroy(cache);
cache = NULL;
+ cache_size = 0;
}
return FALSE;
@@ -789,36 +836,28 @@ static gboolean try_remove_cache(gpointer user_data)
static void create_cache(void)
{
- if (__sync_fetch_and_add(&cache_refcount, 1) == 0)
+ if (__sync_fetch_and_add(&cache_refcount, 1) == 0) {
cache = g_hash_table_new_full(g_str_hash,
g_str_equal,
NULL,
cache_element_destroy);
+ cache_size = 0;
+ }
}
-static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
+static struct cache_entry *cache_check(gpointer request, uint16_t *qtype, int proto)
{
- char *question;
- struct cache_entry *entry;
- struct domain_question *q;
- uint16_t type;
- int offset, proto_offset;
-
if (!request)
return NULL;
- proto_offset = protocol_offset(proto);
- if (proto_offset < 0)
- return NULL;
-
- question = request + proto_offset + 12;
-
- offset = strlen(question) + 1;
- q = (void *) (question + offset);
- type = ntohs(q->type);
+ const char *question = request + protocol_offset(proto) + DNS_HEADER_SIZE;
+ const size_t offset = strlen(question) + 1;
+ const struct domain_question *q = (void *) (question + offset);
+ const uint16_t type = ntohs(q->type);
+ struct cache_entry *entry;
/* We only cache either A (1) or AAAA (28) requests */
- if (type != 1 && type != 28)
+ if (type != DNS_TYPE_A && type != DNS_TYPE_AAAA)
return NULL;
if (!cache) {
@@ -830,8 +869,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
if (!entry)
return NULL;
- type = cache_check_validity(question, type, entry);
- if (type == 0)
+ if (!cache_check_validity(question, type, entry))
return NULL;
*qtype = type;
@@ -846,20 +884,19 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
* format so that we can cache the wire format string directly.
*/
static int get_name(int counter,
- unsigned char *pkt, unsigned char *start, unsigned char *max,
+ const unsigned char *pkt, const unsigned char *start, const unsigned char *max,
unsigned char *output, int output_max, int *output_len,
- unsigned char **end, char *name, size_t max_name, int *name_len)
+ const unsigned char **end, char *name, size_t max_name, int *name_len)
{
- unsigned char *p;
+ const unsigned char *p = start;
/* Limit recursion to 10 (this means up to 10 labels in domain name) */
if (counter > 10)
return -EINVAL;
- p = start;
while (*p) {
if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
- uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
+ const uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
if (offset >= max - pkt)
return -ENOBUFS;
@@ -875,11 +912,9 @@ static int get_name(int counter,
if (pkt + label_len > max)
return -ENOBUFS;
-
- if (*output_len > output_max)
+ else if (*output_len > output_max)
return -ENOBUFS;
-
- if ((*name_len + 1 + label_len + 1) > max_name)
+ else if ((*name_len + 1 + label_len + 1) > max_name)
return -ENOBUFS;
/*
@@ -908,25 +943,25 @@ static int get_name(int counter,
return 0;
}
-static int parse_rr(unsigned char *buf, unsigned char *start,
- unsigned char *max,
- unsigned char *response, unsigned int *response_size,
- uint16_t *type, uint16_t *class, int *ttl, int *rdlen,
- unsigned char **end,
+static int parse_rr(const unsigned char *buf, const unsigned char *start,
+ const unsigned char *max,
+ unsigned char *response, size_t *response_size,
+ uint16_t *type, uint16_t *class, int *ttl, uint16_t *rdlen,
+ const unsigned char **end,
char *name, size_t max_name)
{
struct domain_rr *rr;
- int err, offset;
+ size_t offset;
int name_len = 0, output_len = 0, max_rsp = *response_size;
+ int err = get_name(0, buf, start, max, response, max_rsp,
+ &output_len, end, name, max_name, &name_len);
- err = get_name(0, buf, start, max, response, max_rsp,
- &output_len, end, name, max_name, &name_len);
if (err < 0)
return err;
offset = output_len;
- if ((unsigned int) offset > *response_size)
+ if (offset > *response_size)
return -ENOBUFS;
rr = (void *) (*end);
@@ -942,31 +977,28 @@ static int parse_rr(unsigned char *buf, unsigned char *start,
if (*ttl < 0)
return -EINVAL;
- memcpy(response + offset, *end, sizeof(struct domain_rr));
+ memcpy(response + offset, *end, DNS_RR_SIZE);
- offset += sizeof(struct domain_rr);
- *end += sizeof(struct domain_rr);
+ offset += DNS_RR_SIZE;
+ *end += DNS_RR_SIZE;
- if ((unsigned int) (offset + *rdlen) > *response_size)
+ if ((offset + *rdlen) > *response_size)
return -ENOBUFS;
memcpy(response + offset, *end, *rdlen);
*end += *rdlen;
-
*response_size = offset + *rdlen;
return 0;
}
-static bool check_alias(GSList *aliases, char *name)
+static bool check_alias(GSList *aliases, const char *name)
{
- GSList *list;
-
if (aliases) {
- for (list = aliases; list; list = list->next) {
- int len = strlen((char *)list->data);
- if (strncmp((char *)list->data, name, len) == 0)
+ for (GSList *list = aliases; list; list = list->next) {
+ const char *cmpname = (const char*)list->data;
+ if (strncmp(cmpname, name, NS_MAXDNAME) == 0)
return true;
}
}
@@ -974,55 +1006,67 @@ static bool check_alias(GSList *aliases, char *name)
return false;
}
-static int parse_response(unsigned char *buf, int buflen,
- char *question, int qlen,
+/*
+ * Parses the DNS response packet found in 'buf' consisting of 'buflen' bytes.
+ *
+ * The parsed question label, response type and class, ttl and number of
+ * answer sections are output parameters. The response output buffer will
+ * receive all matching resource records to be cached.
+ *
+ * Return value is < 0 on error (negative errno) or zero on success.
+ */
+static int parse_response(const unsigned char *buf, size_t buflen,
+ char *question, size_t qlen,
uint16_t *type, uint16_t *class, int *ttl,
- unsigned char *response, unsigned int *response_len,
+ unsigned char *response, size_t *response_len,
uint16_t *answers)
{
struct domain_hdr *hdr = (void *) buf;
struct domain_question *q;
- unsigned char *ptr;
- uint16_t qdcount = ntohs(hdr->qdcount);
- uint16_t ancount = ntohs(hdr->ancount);
- int err, i;
- uint16_t qtype, qclass;
- unsigned char *next = NULL;
- unsigned int maxlen = *response_len;
- GSList *aliases = NULL, *list;
- char name[NS_MAXDNAME + 1];
-
- if (buflen < 12)
+ uint16_t qtype;
+ int err = -ENOMSG;
+ uint16_t ancount, qclass;
+ GSList *aliases = NULL;
+ const size_t maxlen = *response_len;
+
+ *response_len = 0;
+ *answers = 0;
+
+ if (buflen < DNS_HEADER_SIZE)
return -EINVAL;
+ const uint16_t qdcount = ntohs(hdr->qdcount);
+ const unsigned char *ptr = buf + DNS_HEADER_SIZE;
+ const unsigned char *eptr = buf + buflen;
+
debug("qr %d qdcount %d", hdr->qr, qdcount);
/* We currently only cache responses where question count is 1 */
if (hdr->qr != 1 || qdcount != 1)
return -EINVAL;
- ptr = buf + sizeof(struct domain_hdr);
-
- strncpy(question, (char *) ptr, qlen);
+ /*
+ * NOTE: currently the *caller* ensures that the `question' buffer is
+ * always zero terminated.
+ */
+ strncpy(question, (const char *) ptr, MIN(qlen, buflen - DNS_HEADER_SIZE));
qlen = strlen(question);
ptr += qlen + 1; /* skip \0 */
+ if ((eptr - ptr) < DNS_QUESTION_SIZE)
+ return -EINVAL;
+
q = (void *) ptr;
qtype = ntohs(q->type);
/* We cache only A and AAAA records */
- if (qtype != 1 && qtype != 28)
+ if (qtype != DNS_TYPE_A && qtype != DNS_TYPE_AAAA)
return -ENOMSG;
- qclass = ntohs(q->class);
-
- ptr += 2 + 2; /* ptr points now to answers */
+ ptr += DNS_QUESTION_SIZE; /* advance to answers section */
- err = -ENOMSG;
- *response_len = 0;
- *answers = 0;
-
- memset(name, 0, sizeof(name));
+ ancount = ntohs(hdr->ancount);
+ qclass = ntohs(q->class);
/*
* We have a bunch of answers (like A, AAAA, CNAME etc) to
@@ -1030,7 +1074,8 @@ static int parse_response(unsigned char *buf, int buflen,
* resource records. Only A and AAAA records are cached, all
* the other records in answers are skipped.
*/
- for (i = 0; i < ancount; i++) {
+ for (uint16_t i = 0; i < ancount; i++) {
+ char name[NS_MAXDNAME + 1] = {0};
/*
* Get one address at a time to this buffer.
* The max size of the answer is
@@ -1038,23 +1083,27 @@ static int parse_response(unsigned char *buf, int buflen,
* 4 (ttl) + 2 (rdlen) + addr (16 or 4) = 28
* for A or AAAA record.
* For CNAME the size can be bigger.
+ * TODO: why are we using the MAXCDNAME constant as buffer
+ * size then?
*/
- unsigned char rsp[NS_MAXCDNAME];
- unsigned int rsp_len = sizeof(rsp) - 1;
- int ret, rdlen;
-
- memset(rsp, 0, sizeof(rsp));
+ unsigned char rsp[NS_MAXCDNAME] = {0};
+ size_t rsp_len = sizeof(rsp) - 1;
+ const unsigned char *next = NULL;
+ uint16_t rdlen;
- ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
+ int ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
type, class, ttl, &rdlen, &next, name,
sizeof(name) - 1);
if (ret != 0) {
err = ret;
- goto out;
+ break;
}
+ /* set pointer to the next RR for the next iteration */
+ ptr = next;
+
/*
- * Now rsp contains compressed or uncompressed resource
+ * Now rsp contains a compressed or an uncompressed resource
* record. Next we check if this record answers the question.
* The name var contains the uncompressed label.
* One tricky bit is the CNAME records as they alias
@@ -1066,8 +1115,6 @@ static int parse_response(unsigned char *buf, int buflen,
* looking for.
*/
if (*class != qclass) {
- ptr = next;
- next = NULL;
continue;
}
@@ -1092,7 +1139,7 @@ static int parse_response(unsigned char *buf, int buflen,
* address of ipv6.l.google.com. For caching purposes this
* should not cause any issues.
*/
- if (*type == 5 && strncmp(question, name, qlen) == 0) {
+ if (*type == DNS_TYPE_CNAME && strncmp(question, name, qlen) == 0) {
/*
* So now the alias answered the question. This is
* not very useful from caching point of view as
@@ -1100,7 +1147,7 @@ static int parse_response(unsigned char *buf, int buflen,
* question. We need to find the real A/AAAA record
* of the alias and cache that.
*/
- unsigned char *end = NULL;
+ const unsigned char *end = NULL;
int name_len = 0, output_len = 0;
memset(rsp, 0, sizeof(rsp));
@@ -1116,8 +1163,6 @@ static int parse_response(unsigned char *buf, int buflen,
name, sizeof(name) - 1, &name_len);
if (ret != 0) {
/* just ignore the error at this point */
- ptr = next;
- next = NULL;
continue;
}
@@ -1129,12 +1174,8 @@ static int parse_response(unsigned char *buf, int buflen,
*/
aliases = g_slist_prepend(aliases, g_strdup(name));
- ptr = next;
- next = NULL;
continue;
- }
-
- if (*type == qtype) {
+ } else if (*type == qtype) {
/*
* We found correct type (A or AAAA)
*/
@@ -1151,7 +1192,7 @@ static int parse_response(unsigned char *buf, int buflen,
*/
if (*response_len + rsp_len > maxlen) {
err = -ENOBUFS;
- goto out;
+ break;
}
memcpy(response + *response_len, rsp, rsp_len);
*response_len += rsp_len;
@@ -1159,31 +1200,21 @@ static int parse_response(unsigned char *buf, int buflen,
err = 0;
}
}
-
- ptr = next;
- next = NULL;
}
-out:
- for (list = aliases; list; list = list->next)
+ for (GSList *list = aliases; list; list = list->next)
g_free(list->data);
g_slist_free(aliases);
return err;
}
-struct cache_timeout {
- time_t current_time;
- int max_timeout;
- int try_harder;
-};
-
static gboolean cache_check_entry(gpointer key, gpointer value,
gpointer user_data)
{
struct cache_timeout *data = user_data;
struct cache_entry *entry = value;
- int max_timeout;
+ time_t max_timeout;
/* Scale the number of hits by half as part of cache aging */
@@ -1224,14 +1255,14 @@ static gboolean cache_check_entry(gpointer key, gpointer value,
static void cache_cleanup(void)
{
- static int max_timeout;
- struct cache_timeout data;
+ static time_t max_timeout;
+ struct cache_timeout data = {
+ .current_time = time(NULL),
+ .max_timeout = 0,
+ .try_harder = false
+ };
int count = 0;
- data.current_time = time(NULL);
- data.max_timeout = 0;
- data.try_harder = 0;
-
/*
* In the first pass, we only remove entries that have timed out.
* We use a cache of the first time to expire to do this only
@@ -1248,7 +1279,7 @@ static void cache_cleanup(void)
* we also expire entries with a low hit count,
* while aging the hit count at the same time.
*/
- data.try_harder = 1;
+ data.try_harder = true;
if (count == 0)
count = g_hash_table_foreach_remove(cache, cache_check_entry,
&data);
@@ -1278,23 +1309,11 @@ static gboolean cache_invalidate_entry(gpointer key, gpointer value,
entry->want_refresh = true;
/* delete the cached data */
- if (entry->ipv4) {
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- entry->ipv4 = NULL;
- }
-
- if (entry->ipv6) {
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- entry->ipv6 = NULL;
- }
+ cache_free_ipv4(entry);
+ cache_free_ipv6(entry);
/* keep the entry if we want it refreshed, delete it otherwise */
- if (entry->want_refresh)
- return FALSE;
- else
- return TRUE;
+ return entry->want_refresh ? FALSE : TRUE;
}
/*
@@ -1315,25 +1334,24 @@ static void cache_invalidate(void)
static void cache_refresh_entry(struct cache_entry *entry)
{
-
cache_enforce_validity(entry);
- if (entry->hits > 2 && !entry->ipv4)
- entry->want_refresh = true;
- if (entry->hits > 2 && !entry->ipv6)
+ if (entry->hits > 2 && (!entry->ipv4 || !entry->ipv6))
entry->want_refresh = true;
if (entry->want_refresh) {
- char *c;
char dns_name[NS_MAXDNAME + 1];
+ char *c;
+
entry->want_refresh = false;
/* turn a DNS name into a hostname with dots */
strncpy(dns_name, entry->key, NS_MAXDNAME);
c = dns_name;
- while (c && *c) {
- int jump;
- jump = *c;
+ while (*c) {
+ /* fetch the size of the current component and replace
+ it by a dot */
+ int jump = *c;
*c = '.';
c += jump + 1;
}
@@ -1359,43 +1377,44 @@ static void cache_refresh(void)
g_hash_table_foreach(cache, cache_refresh_iterator, NULL);
}
-static int reply_query_type(unsigned char *msg, int len)
+static int reply_query_type(const unsigned char *msg, int len)
{
- unsigned char *c;
- int l;
- int type;
-
/* skip the header */
- c = msg + sizeof(struct domain_hdr);
- len -= sizeof(struct domain_hdr);
+ const unsigned char *c = msg + DNS_HEADER_SIZE;
+ int type;
+ len -= DNS_HEADER_SIZE;
if (len < 0)
return 0;
- /* now the query, which is a name and 2 16 bit words */
- l = dns_name_length(c);
- c += l;
+ /* now the query, which is a name and 2 16 bit words for type and class */
+ c += dns_name_length(c);
+
type = c[0] << 8 | c[1];
return type;
}
-static int cache_update(struct server_data *srv, unsigned char *msg,
- unsigned int msg_len)
+/*
+ * update the cache with the DNS reply found in msg
+ */
+static int cache_update(struct server_data *srv, const unsigned char *msg, size_t msg_len)
{
- int offset = protocol_offset(srv->protocol);
- int err, qlen, ttl = 0;
+ const size_t offset = protocol_offset(srv->protocol);
+ int err, ttl = 0;
+ uint16_t *lenhdr;
+ size_t qlen;
+ bool is_new_entry = false;
uint16_t answers = 0, type = 0, class = 0;
struct domain_hdr *hdr = (void *)(msg + offset);
- struct domain_question *q;
+ struct domain_question *q = NULL;
struct cache_entry *entry;
struct cache_data *data;
char question[NS_MAXDNAME + 1];
unsigned char response[NS_MAXDNAME + 1];
- unsigned char *ptr;
- unsigned int rsplen;
- bool new_entry = true;
- time_t current_time;
+ unsigned char *ptr = NULL;
+ size_t rsplen = sizeof(response) - 1;
+ const time_t current_time = time(NULL);
if (cache_size >= MAX_CACHE_SIZE) {
cache_cleanup();
@@ -1403,18 +1422,13 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
return 0;
}
- current_time = time(NULL);
-
/* don't do a cache refresh more than twice a minute */
if (next_refresh < current_time) {
cache_refresh();
next_refresh = current_time + 30;
}
- if (offset < 0)
- return 0;
-
- debug("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
+ debug("offset %zd hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
/* Continue only if response code is 0 (=ok) */
if (hdr->rcode != ns_r_noerror)
@@ -1423,9 +1437,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
if (!cache)
create_cache();
- rsplen = sizeof(response) - 1;
question[sizeof(question) - 1] = '\0';
-
err = parse_response(msg + offset, msg_len - offset,
question, sizeof(question) - 1,
&type, &class, &ttl,
@@ -1438,26 +1450,29 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
*/
if ((err == -ENOMSG || err == -ENOBUFS) &&
reply_query_type(msg + offset,
- msg_len - offset) == 28) {
+ msg_len - offset) == DNS_TYPE_AAAA) {
entry = g_hash_table_lookup(cache, question);
if (entry && entry->ipv4 && !entry->ipv6) {
- int cache_offset = 0;
+ struct cache_data *data = g_try_new(struct cache_data, 1);
- data = g_try_new(struct cache_data, 1);
if (!data)
return -ENOMEM;
data->inserted = entry->ipv4->inserted;
data->type = type;
data->answers = ntohs(hdr->ancount);
data->timeout = entry->ipv4->timeout;
- if (srv->protocol == IPPROTO_UDP)
- cache_offset = 2;
- data->data_len = msg_len + cache_offset;
- data->data = ptr = g_malloc(data->data_len);
- ptr[0] = (data->data_len - 2) / 256;
- ptr[1] = (data->data_len - 2) - ptr[0] * 256;
- if (srv->protocol == IPPROTO_UDP)
- ptr += 2;
+ data->data_len = msg_len +
+ (offset ? 0 : DNS_HEADER_TCP_EXTRA_BYTES);
+ data->data = g_malloc(data->data_len);
+ ptr = data->data;
+ if (srv->protocol == IPPROTO_UDP) {
+ /* add the two bytes length header also for
+ * UDP responses */
+ lenhdr = (void*)ptr;
+ *lenhdr = htons(data->data_len -
+ DNS_HEADER_TCP_EXTRA_BYTES);
+ ptr += DNS_HEADER_TCP_EXTRA_BYTES;
+ }
data->valid_until = entry->ipv4->valid_until;
data->cache_until = entry->ipv4->cache_until;
memcpy(ptr, msg, msg_len);
@@ -1466,9 +1481,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
* we will get a "hit" when we serve the response
* out of the cache
*/
- entry->hits--;
- if (entry->hits < 0)
- entry->hits = 0;
+ entry->hits = entry->hits ? entry->hits - 1 : 0;
return 0;
}
}
@@ -1476,8 +1489,6 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
if (err < 0 || ttl == 0)
return 0;
- qlen = strlen(question);
-
/*
* If the cache contains already data, check if the
* type of the cached data is the same and do not add
@@ -1485,7 +1496,11 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
* This is needed so that we can cache both A and AAAA
* records for the same name.
*/
+
entry = g_hash_table_lookup(cache, question);
+ data = NULL;
+ is_new_entry = !entry;
+
if (!entry) {
entry = g_try_new(struct cache_entry, 1);
if (!entry)
@@ -1502,37 +1517,28 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
entry->want_refresh = false;
entry->hits = 0;
- if (type == 1)
- entry->ipv4 = data;
- else
- entry->ipv6 = data;
} else {
- if (type == 1 && entry->ipv4)
+ if (type == DNS_TYPE_A && entry->ipv4)
return 0;
-
- if (type == 28 && entry->ipv6)
+ else if (type == DNS_TYPE_AAAA && entry->ipv6)
return 0;
data = g_try_new(struct cache_data, 1);
if (!data)
return -ENOMEM;
- if (type == 1)
- entry->ipv4 = data;
- else
- entry->ipv6 = data;
-
/*
* compensate for the hit we'll get for serving
* the response out of the cache
*/
- entry->hits--;
- if (entry->hits < 0)
- entry->hits = 0;
-
- new_entry = false;
+ entry->hits = entry->hits ? entry->hits - 1 : 0;
}
+ if (type == DNS_TYPE_A)
+ entry->ipv4 = data;
+ else
+ entry->ipv6 = data;
+
if (ttl < MIN_CACHE_TTL)
ttl = MIN_CACHE_TTL;
@@ -1540,14 +1546,21 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
data->type = type;
data->answers = answers;
data->timeout = ttl;
+ data->valid_until = current_time + ttl;
+
+ qlen = strlen(question);
/*
- * The "2" in start of the length is the TCP offset. We allocate it
- * here even for UDP packet because it simplifies the sending
- * of cached packet.
+ * We allocate the extra TCP header bytes here even for UDP packet
+ * because it simplifies the sending of cached packet.
*/
- data->data_len = 2 + 12 + qlen + 1 + 2 + 2 + rsplen;
- data->data = ptr = g_malloc(data->data_len);
- data->valid_until = current_time + ttl;
+ data->data_len = DNS_TCP_HEADER_SIZE + qlen + 1 + 2 + 2 + rsplen;
+ data->data = g_malloc(data->data_len);
+ if (!data->data) {
+ g_free(entry->key);
+ g_free(data);
+ g_free(entry);
+ return -ENOMEM;
+ }
/*
* Restrict the cached DNS record TTL to some sane value
@@ -1558,45 +1571,39 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
data->cache_until = round_down_ttl(current_time + ttl, ttl);
- if (!data->data) {
- g_free(entry->key);
- g_free(data);
- g_free(entry);
- return -ENOMEM;
- }
+ ptr = data->data;
/*
* We cache the two extra bytes at the start of the message
- * in a TCP packet. When sending UDP packet, we skip the first
+ * in a TCP packet. When sending UDP packet, we pad the first
* two bytes. This way we do not need to know the format
* (UDP/TCP) of the cached message.
*/
- if (srv->protocol == IPPROTO_UDP)
- memcpy(ptr + 2, msg, offset + 12);
- else
- memcpy(ptr, msg, offset + 12);
+ lenhdr = (void*)ptr;
+ *lenhdr = htons(data->data_len - DNS_HEADER_TCP_EXTRA_BYTES);
+ ptr += DNS_HEADER_TCP_EXTRA_BYTES;
- ptr[0] = (data->data_len - 2) / 256;
- ptr[1] = (data->data_len - 2) - ptr[0] * 256;
- if (srv->protocol == IPPROTO_UDP)
- ptr += 2;
+ memcpy(ptr, hdr, DNS_HEADER_SIZE);
+ ptr += DNS_HEADER_SIZE;
- memcpy(ptr + offset + 12, question, qlen + 1); /* copy also the \0 */
+ memcpy(ptr, question, qlen + 1); /* copy also the \0 */
+ ptr += qlen + 1;
- q = (void *) (ptr + offset + 12 + qlen + 1);
+ q = (void *)ptr;
q->type = htons(type);
q->class = htons(class);
- memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question),
- response, rsplen);
+ ptr += DNS_QUESTION_SIZE;
- if (new_entry) {
+ memcpy(ptr, response, rsplen);
+
+ if (is_new_entry) {
g_hash_table_replace(cache, entry->key, entry);
cache_size++;
}
debug("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u "
"dns len %u",
- cache_size, new_entry ? "new " : "old ",
+ cache_size, is_new_entry ? "new " : "old ",
question, type, ttl,
sizeof(*entry) + sizeof(*data) + data->data_len + qlen,
data->data_len,
@@ -1607,38 +1614,41 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
return 0;
}
-static int ns_resolv(struct server_data *server, struct request_data *req,
- gpointer request, gpointer name)
+/*
+ * attempts to answer the given request from cached replies.
+ *
+ * returns:
+ * > 0 on cache hit (answer is already sent out to client)
+ * == 0 on cache miss
+ * < 0 on error condition (errno)
+ */
+static int ns_try_resolv_from_cache(
+ struct request_data *req, gpointer request, const char *lookup)
{
- GList *list;
- int sk, err, type = 0;
- char *dot, *lookup = (char *) name;
- struct cache_entry *entry;
+ uint16_t type = 0;
+ int ttl_left;
+ struct cache_data *data;
+ struct cache_entry *entry = cache_check(request, &type, req->protocol);
+ if (!entry)
+ return 0;
- entry = cache_check(request, &type, req->protocol);
- if (entry) {
- int ttl_left = 0;
- struct cache_data *data;
+ debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
- debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
- if (type == 1)
- data = entry->ipv4;
- else
- data = entry->ipv6;
+ data = type == DNS_TYPE_A ? entry->ipv4 : entry->ipv6;
- if (data) {
- ttl_left = data->valid_until - time(NULL);
- entry->hits++;
- }
+ if (!data)
+ return 0;
- if (data && req->protocol == IPPROTO_TCP) {
+ ttl_left = data->valid_until - time(NULL);
+ entry->hits++;
+
+ switch(req->protocol) {
+ case IPPROTO_TCP:
send_cached_response(req->client_sk, data->data,
data->data_len, NULL, 0, IPPROTO_TCP,
req->srcid, data->answers, ttl_left);
return 1;
- }
-
- if (data && req->protocol == IPPROTO_UDP) {
+ case IPPROTO_UDP: {
int udp_sk = get_req_udp_socket(req);
if (udp_sk < 0)
@@ -1652,6 +1662,24 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
}
}
+ return -EINVAL;
+}
+
+static int ns_resolv(struct server_data *server, struct request_data *req,
+ gpointer request, gpointer name)
+{
+ int sk = -1;
+ const char *lookup = (const char *)name;
+ int err = ns_try_resolv_from_cache(req, request, lookup);
+
+ if (err > 0)
+ /* cache hit */
+ return 1;
+ else if (err != 0)
+ /* error other than cache miss, don't continue */
+ return err;
+
+ /* forward request to real DNS server */
sk = g_io_channel_unix_get_fd(server->channel);
err = sendto(sk, request, req->request_len, MSG_NOSIGNAL,
@@ -1667,54 +1695,51 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
req->numserv++;
/* If we have more than one dot, we don't add domains */
- dot = strchr(lookup, '.');
- if (dot && dot != lookup + strlen(lookup) - 1)
- return 0;
+ {
+ const char *dot = strchr(lookup, '.');
+ if (dot && dot != lookup + strlen(lookup) - 1)
+ return 0;
+ }
if (server->domains && server->domains->data)
req->append_domain = true;
- for (list = server->domains; list; list = list->next) {
- char *domain;
+ for (GList *list = server->domains; list; list = list->next) {
+ int domlen, altlen;
unsigned char alt[1024];
- struct domain_hdr *hdr = (void *) &alt;
- int altlen, domlen, offset;
-
- domain = list->data;
+ const char *domain = list->data;
+ const size_t offset = protocol_offset(server->protocol);
+ struct domain_hdr *hdr = (void *) (&alt[0] + offset);
if (!domain)
continue;
- offset = protocol_offset(server->protocol);
- if (offset < 0)
- return offset;
-
domlen = strlen(domain) + 1;
+
if (domlen < 5)
return -EINVAL;
- alt[offset] = req->altid & 0xff;
- alt[offset + 1] = req->altid >> 8;
+ memcpy(alt + offset, &req->altid, sizeof(req->altid));
- memcpy(alt + offset + 2, request + offset + 2, 10);
+ memcpy(alt + offset + 2, request + offset + 2, DNS_HEADER_SIZE - 2);
hdr->qdcount = htons(1);
- altlen = append_query(alt + offset + 12, sizeof(alt) - 12,
+ altlen = append_query(alt + offset + DNS_HEADER_SIZE, sizeof(alt) - DNS_HEADER_SIZE - offset,
name, domain);
if (altlen < 0)
return -EINVAL;
- altlen += 12;
+ altlen += DNS_HEADER_SIZE;
+ altlen += offset;
- memcpy(alt + offset + altlen,
- request + offset + altlen - domlen,
- req->request_len - altlen - offset + domlen);
+ memcpy(alt + altlen,
+ request + altlen - domlen,
+ req->request_len - altlen + domlen);
if (server->protocol == IPPROTO_TCP) {
- int req_len = req->request_len + domlen - 2;
-
- alt[0] = (req_len >> 8) & 0xff;
- alt[1] = req_len & 0xff;
+ uint16_t req_len = req->request_len + domlen - DNS_HEADER_TCP_EXTRA_BYTES;
+ uint16_t *len_hdr = (void*)alt;
+ *len_hdr = htons(req_len);
}
debug("req %p dstid 0x%04x altid 0x%04x", req, req->dstid,
@@ -1730,17 +1755,17 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
return 0;
}
-static char *convert_label(char *start, char *end, char *ptr, char *uptr,
+static bool convert_label(const char *start, const char *end, const char *ptr, char *uptr,
int remaining_len, int *used_comp, int *used_uncomp)
{
- int pos, comp_pos;
+ int comp_pos;
char name[NS_MAXLABEL];
- pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
+ const int pos = dn_expand((const u_char *)start, (const u_char *)end, (const u_char *)ptr,
name, NS_MAXLABEL);
if (pos < 0) {
debug("uncompress error [%d/%s]", errno, strerror(errno));
- goto out;
+ return false;
}
/*
@@ -1750,23 +1775,21 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr,
comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL);
if (comp_pos < 0) {
debug("compress error [%d/%s]", errno, strerror(errno));
- goto out;
+ return false;
}
*used_comp = pos;
*used_uncomp = comp_pos;
- return ptr;
-
-out:
- return NULL;
+ return true;
}
-static char *uncompress(int16_t field_count, char *start, char *end,
- char *ptr, char *uncompressed, int uncomp_len,
+static const char* uncompress(int16_t field_count, const char *start, const char *end,
+ const char *ptr, char *uncompressed, int uncomp_len,
char **uncompressed_ptr)
{
char *uptr = *uncompressed_ptr; /* position in result buffer */
+ char * const uncomp_end = uncompressed + uncomp_len - 1;
debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
@@ -1780,21 +1803,22 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
&pos, &comp_pos))
- goto out;
+ return NULL;
/*
* Copy the uncompressed resource record, type, class and \0 to
* tmp buffer.
*/
- ulen = strlen(name);
- strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+ ulen = strlen(name) + 1;
+ if ((uptr + ulen) > uncomp_end)
+ return NULL;
+ memcpy(uptr, name, ulen);
debug("pos %d ulen %d left %d name %s", pos, ulen,
- (int)(uncomp_len - (uptr - uncompressed)), uptr);
+ (int)(uncomp_end - (uptr + ulen)), uptr);
uptr += ulen;
- *uptr++ = '\0';
ptr += pos;
@@ -1802,13 +1826,17 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* We copy also the fixed portion of the result (type, class,
* ttl, address length and the address)
*/
+ if ((uptr + NS_RRFIXEDSZ) > uncomp_end) {
+ debug("uncompressed data too large for buffer");
+ return NULL;
+ }
memcpy(uptr, ptr, NS_RRFIXEDSZ);
dns_type = uptr[0] << 8 | uptr[1];
dns_class = uptr[2] << 8 | uptr[3];
- if (dns_class != ns_c_in)
- goto out;
+ if (dns_class != DNS_CLASS_IN)
+ return NULL;
ptr += NS_RRFIXEDSZ;
uptr += NS_RRFIXEDSZ;
@@ -1818,11 +1846,11 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* Typically this portion is also compressed
* so we need to uncompress it also when necessary.
*/
- if (dns_type == ns_t_cname) {
+ if (dns_type == DNS_TYPE_CNAME) {
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
uptr[-2] = comp_pos << 8;
uptr[-1] = comp_pos & 0xff;
@@ -1830,19 +1858,19 @@ static char *uncompress(int16_t field_count, char *start, char *end,
uptr += comp_pos;
ptr += pos;
- } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) {
+ } else if (dns_type == DNS_TYPE_A || dns_type == DNS_TYPE_AAAA) {
dlen = uptr[-2] << 8 | uptr[-1];
- if (ptr + dlen > end) {
+ if ((ptr + dlen) > end || (uptr + dlen) > uncomp_end) {
debug("data len %d too long", dlen);
- goto out;
+ return NULL;
}
memcpy(uptr, ptr, dlen);
uptr += dlen;
ptr += dlen;
- } else if (dns_type == ns_t_soa) {
+ } else if (dns_type == DNS_TYPE_SOA) {
int total_len = 0;
char *len_ptr;
@@ -1850,7 +1878,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
total_len += comp_pos;
len_ptr = &uptr[-2];
@@ -1861,7 +1889,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
total_len += comp_pos;
ptr += pos;
@@ -1872,6 +1900,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* refresh interval, retry interval, expiration
* limit and minimum ttl). They are 20 bytes long.
*/
+ if ((uptr + 20) > uncomp_end || (ptr + 20) > end) {
+ debug("soa record too long");
+ return NULL;
+ }
memcpy(uptr, ptr, 20);
uptr += 20;
ptr += 20;
@@ -1888,239 +1920,300 @@ static char *uncompress(int16_t field_count, char *start, char *end,
}
return ptr;
-
-out:
- return NULL;
}
-static int strip_domains(char *name, char *answers, int maxlen)
+/*
+ * removes the qualified domain name part from the given answer sections
+ * starting at 'answers', consisting of 'length' bytes.
+ *
+ * 'name' points the start of the unqualified host label including the leading
+ * length octet.
+ *
+ * returns the new (possibly shorter) length of remaining payload in the
+ * answers buffer, or a negative (errno) value to indicate error conditions.
+ */
+static int strip_domains(const char *name, char *answers, size_t length)
{
uint16_t data_len;
- int name_len = strlen(name);
- char *ptr, *start = answers, *end = answers + maxlen;
+ struct domain_rr *rr;
+ /* length of the name label including the length header octet */
+ const size_t name_len = strlen(name);
+ const char *end = answers + length;
- while (maxlen > 0) {
- ptr = strstr(answers, name);
+ while (answers < end) {
+ char *ptr = strstr(answers, name);
if (ptr) {
char *domain = ptr + name_len;
+ /* this now points to the domain part length octet. */
if (*domain) {
- int domain_len = strlen(domain);
+ /*
+ * length of the rest of the labels up to the
+ * null label (zero byte).
+ */
+ const size_t domain_len = strlen(domain);
+ char *remaining = domain + domain_len;
- memmove(answers + name_len,
- domain + domain_len,
- end - (domain + domain_len));
+ /*
+ * now shift the rest of the answer sections
+ * to the left to get rid of the domain label
+ * part
+ */
+ memmove(ptr + name_len,
+ remaining,
+ end - remaining);
end -= domain_len;
- maxlen -= domain_len;
+ length -= domain_len;
}
}
- answers += strlen(answers) + 1;
- answers += 2 + 2 + 4; /* skip type, class and ttl fields */
-
- data_len = answers[0] << 8 | answers[1];
- answers += 2; /* skip the length field */
+ /* skip to the next answer section */
- if (answers + data_len > end)
+ /* the labels up to the root null label */
+ answers += strlen(answers) + 1;
+ /* the fixed part of the RR */
+ rr = (void*)answers;
+ if (answers + sizeof(*rr) > end)
return -EINVAL;
-
+ data_len = htons(rr->rdlen);
+ /* skip the rest of the RR */
+ answers += sizeof(*rr);
answers += data_len;
- maxlen -= answers - ptr;
}
- return end - start;
+ if (answers > end)
+ return -EINVAL;
+
+ return length;
}
-static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
- struct server_data *data)
+/*
+ * Removes domain names from replies, if one has been appended during
+ * forwarding to the real DNS server.
+ *
+ * Returns:
+ * < 0 on error (abort processing reply)
+ * == 0 if the reply should be forwarded unmodified
+ * > 0 returns a new reply buffer in *new_reply on success. The return value
+ * indicates the new length of the data in *new_reply.
+ */
+static int dns_reply_fixup_domains(
+ const char *reply, size_t reply_len,
+ const size_t offset,
+ struct request_data *req,
+ char **new_reply)
{
- struct domain_hdr *hdr;
- struct request_data *req;
- int dns_id, sk, err, offset = protocol_offset(protocol);
+ char uncompressed[NS_MAXDNAME];
+ char *uptr, *answers;
+ size_t fixed_len;
+ int new_an_len;
+ const struct domain_hdr *hdr = (void *)(reply + offset);
+ const char *eom = reply + reply_len;
+ const uint16_t header_len = offset + DNS_HEADER_SIZE;
+ /* full header plus at least one byte for the hostname length */
+ if (reply_len < header_len + 1)
+ return -EINVAL;
- if (offset < 0)
- return offset;
+ const uint16_t section_counts[] = {
+ hdr->ancount,
+ hdr->nscount,
+ hdr->arcount
+ };
- hdr = (void *)(reply + offset);
- dns_id = reply[offset] | reply[offset + 1] << 8;
+ /*
+ * length octet of the hostname.
+ * ->hostname.domain.net
+ */
+ const char *ptr = reply + header_len;
+ const uint8_t host_len = *ptr;
+ const char *domain = ptr + host_len + 1;
+ if (domain >= eom)
+ return -EINVAL;
- debug("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+ const uint16_t domain_len = host_len ? strnlen(domain, eom - domain) : 0;
- req = find_request(dns_id);
- if (!req)
+ /*
+ * If the query type is anything other than A or AAAA, then bail out
+ * and pass the message as is. We only want to deal with IPv4 or IPv6
+ * addresses.
+ */
+ const struct qtype_qclass *qtc = (void*)(domain + domain_len + 1);
+ if (((const char*)(qtc + 1)) > eom)
return -EINVAL;
- debug("req %p dstid 0x%04x altid 0x%04x rcode %d",
- req, req->dstid, req->altid, hdr->rcode);
+ const uint16_t dns_type = ntohs(qtc->qtype);
+ const uint16_t dns_class = ntohs(qtc->qclass);
- reply[offset] = req->srcid & 0xff;
- reply[offset + 1] = req->srcid >> 8;
+ if (domain_len == 0) {
+ /* nothing to do */
+ return 0;
+ }
- req->numresp++;
+ /* TODO: This condition looks wrong. It should probably be
+ *
+ * (dns_type != A && dns_type != AAAA) || dns_class != IN
+ *
+ * doing so, however, changes the behaviour of dnsproxy, e.g. MX
+ * records will be passed back to the client, but without the
+ * adjustment of the appended domain name.
+ */
+ if (dns_type != DNS_TYPE_A && dns_type != DNS_TYPE_AAAA &&
+ dns_class != DNS_CLASS_IN) {
+ debug("Pass msg dns type %d class %d", dns_type, dns_class);
+ return 0;
+ }
- if (hdr->rcode == ns_r_noerror || !req->resp) {
- unsigned char *new_reply = NULL;
+ /*
+ * Remove the domain name and replace it by the end of reply. Check if
+ * the domain is really there before trying to copy the data. We also
+ * need to uncompress the answers if necessary. The domain_len can be
+ * 0 because if the original query did not contain a domain name, then
+ * we are sending two packets, first without the domain name and the
+ * second packet with domain name. The append_domain is set to true
+ * even if we sent the first packet without domain name. In this case
+ * we end up in this branch.
+ */
- /*
- * If the domain name was append
- * remove it before forwarding the reply.
- * If there were more than one question, then this
- * domain name ripping can be hairy so avoid that
- * and bail out in that that case.
- *
- * The reason we are doing this magic is that if the
- * user's DNS client tries to resolv hostname without
- * domain part, it also expects to get the result without
- * a domain name part.
- */
- if (req->append_domain && ntohs(hdr->qdcount) == 1) {
- uint16_t domain_len = 0;
- uint16_t header_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;
+ /* NOTE: length checks up and including to qtype_qclass have already
+ been done above */
- /*
- * ptr points to the first char of the hostname.
- * ->hostname.domain.net
- */
- header_len = offset + sizeof(struct domain_hdr);
- ptr = (char *)reply + header_len;
+ /*
+ * First copy host (without domain name) into tmp buffer.
+ */
+ uptr = &uncompressed[0];
+ memcpy(uptr, ptr, host_len + 1);
- host_len = *ptr;
- if (host_len > 0)
- domain_len = strnlen(ptr + 1 + host_len,
- reply_len - header_len);
+ uptr[host_len + 1] = '\0'; /* host termination */
+ uptr += host_len + 2;
- /*
- * If the query type is anything other than A or AAAA,
- * then bail out and pass the message as is.
- * We only want to deal with IPv4 or IPv6 addresses.
- */
- dns_type_pos = host_len + 1 + domain_len + 1;
-
- dns_type = ptr[dns_type_pos] << 8 |
- ptr[dns_type_pos + 1];
- dns_class = ptr[dns_type_pos + 2] << 8 |
- ptr[dns_type_pos + 3];
- if (dns_type != ns_t_a && dns_type != ns_t_aaaa &&
- dns_class != ns_c_in) {
- debug("Pass msg dns type %d class %d",
- dns_type, dns_class);
- goto pass;
- }
+ /*
+ * Copy type and class fields of the question.
+ */
+ memcpy(uptr, qtc, sizeof(*qtc));
- /*
- * Remove the domain name and replace it by the end
- * of reply. Check if the domain is really there
- * before trying to copy the data. We also need to
- * uncompress the answers if necessary.
- * The domain_len can be 0 because if the original
- * query did not contain a domain name, then we are
- * sending two packets, first without the domain name
- * and the second packet with domain name.
- * The append_domain is set to true even if we sent
- * the first packet without domain name. In this
- * case we end up in this branch.
- */
- if (domain_len > 0) {
- int len = host_len + 1;
- int new_len, fixed_len;
- char *answers;
+ /*
+ * ptr points to answers after this
+ */
+ ptr = (void*)(qtc + 1);
+ uptr += sizeof(*qtc);
+ answers = uptr;
+ fixed_len = answers - uncompressed;
- /*
- * First copy host (without domain name) into
- * tmp buffer.
- */
- uptr = &uncompressed[0];
- memcpy(uptr, ptr, len);
+ /*
+ * We then uncompress the result to buffer so that we can rip off the
+ * domain name part from the question. First answers, then name server
+ * (authority) information, and finally additional record info.
+ */
- uptr[len] = '\0'; /* host termination */
- uptr += len + 1;
+ for (size_t i = 0; i < NUM_ARRAY_ELEMENTS(section_counts); i++) {
+ ptr = uncompress(ntohs(section_counts[i]), reply + offset, eom,
+ ptr, uncompressed, NS_MAXDNAME, &uptr);
+ if (!ptr) {
+ /* failed to uncompress, pass on as is
+ * (TODO: good idea?) */
+ return 0;
+ }
+ }
- /*
- * Copy type and class fields of the question.
- */
- ptr += len + domain_len + 1;
- memcpy(uptr, ptr, NS_QFIXEDSZ);
+ /*
+ * The uncompressed buffer now contains an almost valid response.
+ * Final step is to get rid of the domain name because at least glibc
+ * gethostbyname() implementation does extra checks and expects to
+ * find an answer without domain name if we asked a query without
+ * domain part. Note that glibc getaddrinfo() works differently and
+ * accepts FQDN in answer
+ */
+ new_an_len = strip_domains(uncompressed, answers, uptr - answers);
+ if (new_an_len < 0) {
+ debug("Corrupted packet");
+ return -EINVAL;
+ }
- /*
- * ptr points to answers after this
- */
- ptr += NS_QFIXEDSZ;
- uptr += NS_QFIXEDSZ;
- answers = uptr;
- fixed_len = answers - uncompressed;
+ /*
+ * Because we have now uncompressed the answers we might have to
+ * create a bigger buffer to hold all that data.
+ *
+ * TODO: only create a bigger buffer if actually necessary, pass
+ * allocation size of input buffer via additional parameter.
+ */
- /*
- * We then uncompress the result to buffer
- * so that we can rip off the domain name
- * part from the question. First answers,
- * then name server (authority) information,
- * and finally additional record info.
- */
+ reply_len = header_len + new_an_len + fixed_len;
- ptr = uncompress(ntohs(hdr->ancount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
-
- ptr = uncompress(ntohs(hdr->nscount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
-
- ptr = uncompress(ntohs(hdr->arcount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
+ *new_reply = g_try_malloc(reply_len);
+ if (!*new_reply)
+ return -ENOMEM;
- /*
- * The uncompressed buffer now contains almost
- * valid response. Final step is to get rid of
- * the domain name because at least glibc
- * gethostbyname() implementation does extra
- * checks and expects to find an answer without
- * domain name if we asked a query without
- * domain part. Note that glibc getaddrinfo()
- * works differently and accepts FQDN in answer
- */
- new_len = strip_domains(uncompressed, answers,
- uptr - answers);
- if (new_len < 0) {
- debug("Corrupted packet");
- return -EINVAL;
- }
+ memcpy(*new_reply, reply, header_len);
+ memcpy(*new_reply + header_len, uncompressed, new_an_len + fixed_len);
- /*
- * Because we have now uncompressed the answers
- * we might have to create a bigger buffer to
- * hold all that data.
- */
+ return reply_len;
+}
+
+static struct request_data* lookup_request(
+ const unsigned char *reply, size_t len, int protocol)
+{
+ const size_t offset = protocol_offset(protocol);
+ struct request_data *req;
+ struct domain_hdr *hdr = (void *)(reply + offset);
+
+ debug("Received %zd bytes (id 0x%04x)", len, hdr->id);
- reply_len = header_len + new_len + fixed_len;
+ if (len < DNS_HEADER_SIZE + offset)
+ return NULL;
+
+ req = find_request(hdr->id);
+
+ if (!req)
+ return NULL;
+
+ debug("req %p dstid 0x%04x altid 0x%04x rcode %d",
+ req, req->dstid, req->altid, hdr->rcode);
+
+ req->numresp++;
+
+ return req;
+}
+
+static int forward_dns_reply(char *reply, size_t reply_len, int protocol,
+ struct server_data *data, struct request_data *req)
+{
+ const size_t offset = protocol_offset(protocol);
+ struct domain_hdr *hdr = (void *)(reply + offset);
+ int err, sk;
- new_reply = g_try_malloc(reply_len);
- if (!new_reply)
- return -ENOMEM;
+ /* replace with original request ID from our client */
+ hdr->id = req->srcid;
- memcpy(new_reply, reply, header_len);
- memcpy(new_reply + header_len, uncompressed,
- new_len + fixed_len);
+ if (hdr->rcode == ns_r_noerror || !req->resp) {
+ /*
+ * If the domain name was appended remove it before forwarding
+ * the reply. If there were more than one question, then this
+ * domain name ripping can be hairy so avoid that and bail out
+ * in that that case.
+ *
+ * The reason we are doing this magic is that if the user's
+ * DNS client tries to resolv hostname without domain part, it
+ * also expects to get the result without a domain name part.
+ */
+ char *new_reply = NULL;
+ if (req->append_domain && ntohs(hdr->qdcount) == 1) {
+ const int fixup_res = dns_reply_fixup_domains(
+ reply, reply_len,
+ offset, req, &new_reply);
+ if (fixup_res < 0) {
+ /* error occured */
+ return fixup_res;
+ } else if (fixup_res > 0 && new_reply) {
+ /* new reply length */
+ reply_len = fixup_res;
reply = new_reply;
+ } else {
+ /* keep message as is */
}
}
- pass:
g_free(req->resp);
req->resplen = 0;
@@ -2131,12 +2224,11 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
memcpy(req->resp, reply, reply_len);
req->resplen = reply_len;
- cache_update(data, reply, reply_len);
+ cache_update(data, (unsigned char*)reply, reply_len);
g_free(new_reply);
}
-out:
if (req->numresp < req->numserv) {
if (hdr->rcode > ns_r_noerror) {
return -EINVAL;
@@ -2156,6 +2248,9 @@ out:
err = sendto(sk, req->resp, req->resplen, 0,
&req->sa, req->sa_len);
} else {
+ const uint16_t tcp_len = htons(req->resplen - DNS_HEADER_TCP_EXTRA_BYTES);
+ /* 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);
}
@@ -2166,8 +2261,6 @@ out:
else
debug("proto %d sent %d bytes to %d", protocol, err, sk);
- destroy_request_data(req);
-
return err;
}
@@ -2231,8 +2324,10 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
unsigned char buf[4096];
- int sk, len;
+ int sk, res;
+ ssize_t len;
struct server_data *data = user_data;
+ struct request_data *req;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with UDP server %s", data->server);
@@ -2241,11 +2336,22 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
}
sk = g_io_channel_unix_get_fd(channel);
-
len = recv(sk, buf, sizeof(buf), 0);
- if (len >= 12)
- forward_dns_reply(buf, len, IPPROTO_UDP, data);
+ if (len <= 0)
+ return TRUE;
+
+ req = lookup_request(buf, len, IPPROTO_UDP);
+
+ if (!req)
+ /* invalid / corrupt request */
+ return TRUE;
+
+ res = forward_dns_reply((char*)buf, len, IPPROTO_UDP, data, req);
+
+ /* on success or no further responses are expected, destroy the req */
+ if (res == 0 || req->numresp >= req->numserv)
+ destroy_request_data(req);
return TRUE;
}
@@ -2253,10 +2359,9 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
- int sk;
+ struct request_data *req;
struct server_data *server = user_data;
-
- sk = g_io_channel_unix_get_fd(channel);
+ int sk = g_io_channel_unix_get_fd(channel);
if (sk == 0)
return FALSE;
@@ -2274,14 +2379,13 @@ hangup:
list = request_list;
while (list) {
- struct request_data *req = list->data;
struct domain_hdr *hdr;
+ req = list->data;
list = list->next;
if (req->protocol == IPPROTO_UDP)
continue;
-
- if (!req->request)
+ else if (!req->request)
continue;
/*
@@ -2292,7 +2396,7 @@ hangup:
if (req->numserv && --(req->numserv))
continue;
- hdr = (void *) (req->request + 2);
+ hdr = (void *)(req->request + DNS_HEADER_TCP_EXTRA_BYTES);
hdr->id = req->srcid;
send_response(req->client_sk, req->request,
req->request_len, NULL, 0, IPPROTO_TCP);
@@ -2306,17 +2410,14 @@ hangup:
}
if ((condition & G_IO_OUT) && !server->connected) {
- GSList *list;
- GList *domains;
bool no_request_sent = true;
- struct server_data *udp_server;
-
- udp_server = find_server(server->index, server->server,
- IPPROTO_UDP);
+ struct server_data *udp_server = find_server(
+ server->index, server->server,
+ IPPROTO_UDP);
if (udp_server) {
- for (domains = udp_server->domains; domains;
+ for (GList *domains = udp_server->domains; domains;
domains = domains->next) {
- char *dom = domains->data;
+ const char *dom = domains->data;
debug("Adding domain %s to %s",
dom, server->server);
@@ -2326,17 +2427,26 @@ 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;
+ /* don't advance the list in the for loop, because we might
+ * need to delete elements while iterating through it */
+ for (GSList *list = request_list; list; ) {
int status;
+ req = list->data;
if (req->protocol == IPPROTO_UDP) {
list = list->next;
@@ -2356,9 +2466,7 @@ hangup:
request_list = g_slist_remove(request_list, req);
destroy_request_data(req);
continue;
- }
-
- if (status < 0) {
+ } else if (status < 0) {
list = list->next;
continue;
}
@@ -2381,12 +2489,12 @@ hangup:
} else if (condition & G_IO_IN) {
struct partial_reply *reply = server->incoming_reply;
int bytes_recv;
+ int res;
if (!reply) {
- unsigned char reply_len_buf[2];
uint16_t reply_len;
- bytes_recv = recv(sk, reply_len_buf, 2, MSG_PEEK);
+ bytes_recv = recv(sk, &reply_len, sizeof(reply_len), MSG_PEEK);
if (!bytes_recv) {
goto hangup;
} else if (bytes_recv < 0) {
@@ -2396,11 +2504,12 @@ hangup:
connman_error("DNS proxy error %s",
strerror(errno));
goto hangup;
- } else if (bytes_recv < 2)
+ } else if (bytes_recv < sizeof(reply_len))
return TRUE;
- reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
- reply_len += 2;
+ /* the header contains the length of the message
+ * excluding the two length bytes */
+ reply_len = ntohs(reply_len) + DNS_HEADER_TCP_EXTRA_BYTES;
debug("TCP reply %d bytes from %d", reply_len, sk);
@@ -2409,6 +2518,8 @@ hangup:
return TRUE;
reply->len = reply_len;
+ /* we only peeked the two length bytes, so we have to
+ receive the complete message below proper. */
reply->received = 0;
server->incoming_reply = reply;
@@ -2431,15 +2542,30 @@ hangup:
reply->received += bytes_recv;
}
- forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP,
- server);
+ req = lookup_request(reply->buf, reply->received, IPPROTO_TCP);
+
+ if (!req)
+ /* invalid / corrupt request */
+ return TRUE;
+
+ res = forward_dns_reply((char*)reply->buf, reply->received, IPPROTO_TCP, server, req);
g_free(reply);
server->incoming_reply = NULL;
- destroy_server(server);
+ /* on success or if no further responses are expected close
+ * connection */
+ if (res == 0 || req->numresp >= req->numserv) {
+ destroy_request_data(req);
+ destroy_server(server);
+ return FALSE;
+ }
- return FALSE;
+ /*
+ * keep the TCP connection open, there are more
+ * requests to be answered
+ */
+ return TRUE;
}
return TRUE;
@@ -2449,7 +2575,7 @@ static gboolean tcp_idle_timeout(gpointer user_data)
{
struct server_data *server = user_data;
- debug("");
+ debug("\n");
if (!server)
return FALSE;
@@ -2461,15 +2587,15 @@ static gboolean tcp_idle_timeout(gpointer user_data)
static int server_create_socket(struct server_data *data)
{
- int sk, err;
+ int err;
char *interface;
+ int sk = socket(data->server_addr->sa_family,
+ data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
+ data->protocol);
debug("index %d server %s proto %d", data->index,
data->server, data->protocol);
- sk = socket(data->server_addr->sa_family,
- data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
- data->protocol);
if (sk < 0) {
err = errno;
connman_error("Failed to create server %s socket",
@@ -2540,9 +2666,7 @@ static int server_create_socket(struct server_data *data)
static void enable_fallback(bool enable)
{
- GSList *list;
-
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->index != -1)
@@ -2557,17 +2681,30 @@ static void enable_fallback(bool enable)
}
}
+static unsigned int get_enabled_server_number(void)
+{
+ GSList *list;
+ unsigned int result = 0;
+
+ for (list = server_list; list; list = list->next) {
+ struct server_data *data = list->data;
+
+ if (data->index != -1 && data->enabled == true)
+ result++;
+ }
+ return result;
+}
+
static struct server_data *create_server(int index,
const char *domain, const char *server,
int protocol)
{
- struct server_data *data;
+ struct server_data *data = g_try_new0(struct server_data, 1);
struct addrinfo hints, *rp;
int ret;
DBG("index %d server %s", index, server);
- data = g_try_new0(struct server_data, 1);
if (!data) {
connman_error("Failed to allocate server %s data", server);
return NULL;
@@ -2580,20 +2717,7 @@ static struct server_data *create_server(int index,
data->protocol = protocol;
memset(&hints, 0, sizeof(hints));
-
- switch (protocol) {
- case IPPROTO_UDP:
- hints.ai_socktype = SOCK_DGRAM;
- break;
-
- case IPPROTO_TCP:
- hints.ai_socktype = SOCK_STREAM;
- break;
-
- default:
- destroy_server(data);
- return NULL;
- }
+ hints.ai_socktype = socket_type(protocol, 0);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
@@ -2645,6 +2769,9 @@ static struct server_data *create_server(int index,
DBG("Adding DNS server %s", data->server);
enable_fallback(false);
+ } else if (data->index == -1 && get_enabled_server_number() == 0) {
+ data->enabled = true;
+ DBG("Adding fallback DNS server %s", data->server);
}
server_list = g_slist_append(server_list, data);
@@ -2656,9 +2783,7 @@ static struct server_data *create_server(int index,
static bool resolv(struct request_data *req,
gpointer request, gpointer name)
{
- GSList *list;
-
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->protocol == IPPROTO_TCP) {
@@ -2687,26 +2812,22 @@ static bool resolv(struct request_data *req,
static void update_domain(int index, const char *domain, bool append)
{
- GSList *list;
-
DBG("index %d domain %s", index, domain);
if (!domain)
return;
- for (list = server_list; list; list = list->next) {
+ for (GSList *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)
continue;
-
- if (data->index != index)
+ else if (data->index != index)
continue;
- for (dom_list = data->domains; dom_list;
+ for (GList *dom_list = data->domains; dom_list;
dom_list = dom_list->next) {
dom = dom_list->data;
@@ -2739,9 +2860,7 @@ static void remove_domain(int index, const char *domain)
static void flush_requests(struct server_data *server)
{
- GSList *list;
-
- list = request_list;
+ GSList *list = request_list;
while (list) {
struct request_data *req = list->data;
@@ -2769,22 +2888,20 @@ int __connman_dnsproxy_append(int index, const char *domain,
const char *server)
{
struct server_data *data;
-
DBG("index %d server %s", index, server);
- if (!server && !domain)
- return -EINVAL;
-
if (!server) {
- append_domain(index, domain);
-
- return 0;
+ if (!domain) {
+ return -EINVAL;
+ } else {
+ append_domain(index, domain);
+ return 0;
+ }
}
if (g_str_equal(server, "127.0.0.1"))
return -ENODEV;
-
- if (g_str_equal(server, "::1"))
+ else if (g_str_equal(server, "::1"))
return -ENODEV;
data = find_server(index, server, IPPROTO_UDP);
@@ -2802,11 +2919,9 @@ int __connman_dnsproxy_append(int index, const char *domain,
return 0;
}
-static void remove_server(int index, const char *domain,
- const char *server, int protocol)
+static void remove_server(int index, const char *server, int protocol)
{
struct server_data *data;
- GSList *list;
data = find_server(index, server, protocol);
if (!data)
@@ -2814,14 +2929,8 @@ static void remove_server(int index, const char *domain,
destroy_server(data);
- for (list = server_list; list; list = list->next) {
- struct server_data *data = list->data;
-
- if (data->index != -1 && data->enabled == true)
- return;
- }
-
- enable_fallback(true);
+ if (get_enabled_server_number() == 0)
+ enable_fallback(true);
}
int __connman_dnsproxy_remove(int index, const char *domain,
@@ -2829,34 +2938,31 @@ int __connman_dnsproxy_remove(int index, const char *domain,
{
DBG("index %d server %s", index, server);
- if (!server && !domain)
- return -EINVAL;
-
if (!server) {
- remove_domain(index, domain);
-
- return 0;
+ if (!domain) {
+ return -EINVAL;
+ } else {
+ remove_domain(index, domain);
+ return 0;
+ }
}
if (g_str_equal(server, "127.0.0.1"))
return -ENODEV;
-
- if (g_str_equal(server, "::1"))
+ else if (g_str_equal(server, "::1"))
return -ENODEV;
- remove_server(index, domain, server, IPPROTO_UDP);
- remove_server(index, domain, server, IPPROTO_TCP);
+ remove_server(index, server, IPPROTO_UDP);
+ remove_server(index, server, IPPROTO_TCP);
return 0;
}
static void dnsproxy_offline_mode(bool enabled)
{
- GSList *list;
-
DBG("enabled %d", enabled);
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (!enabled) {
@@ -2874,9 +2980,8 @@ static void dnsproxy_offline_mode(bool enabled)
static void dnsproxy_default_changed(struct connman_service *service)
{
- bool server_enabled = false;
- GSList *list;
- int index;
+ bool any_server_enabled = false;
+ int index, vpn_index;
DBG("service %p", service);
@@ -2893,20 +2998,30 @@ static void dnsproxy_default_changed(struct connman_service *service)
if (index < 0)
return;
- for (list = server_list; list; list = list->next) {
+ /*
+ * In case non-split-routed VPN is set as split routed the DNS servers
+ * the VPN must be enabled as well, when the transport becomes the
+ * default service.
+ */
+ vpn_index = __connman_connection_get_vpn_index(index);
+
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->index == index) {
DBG("Enabling DNS server %s", data->server);
data->enabled = true;
- server_enabled = true;
+ any_server_enabled = true;
+ } else if (data->index == vpn_index) {
+ DBG("Enabling DNS server of VPN %s", data->server);
+ data->enabled = true;
} else {
DBG("Disabling DNS server %s", data->server);
data->enabled = false;
}
}
- if (!server_enabled)
+ if (!any_server_enabled)
enable_fallback(true);
cache_refresh();
@@ -2954,47 +3069,59 @@ static const struct connman_notifier dnsproxy_notifier = {
.service_state_changed = dnsproxy_service_state_changed,
};
-static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
-
+/*
+ * Parses the given request buffer. `buf´ is expected to be the start of the
+ * domain_hdr structure i.e. the TCP length header is not handled by this
+ * function.
+ * Returns the ascii string dot representation of the query in `name´, which
+ * must be able to hold `size´ bytes.
+ *
+ * Returns < 0 on error (errno) or zero on success.
+ */
static int parse_request(unsigned char *buf, size_t len,
- char *name, unsigned int size)
+ char *name, size_t size)
{
+ static const unsigned char OPT_EDNS0_TYPE[2] = { 0x00, 0x29 };
struct domain_hdr *hdr = (void *) buf;
- uint16_t qdcount = ntohs(hdr->qdcount);
- uint16_t ancount = ntohs(hdr->ancount);
- uint16_t nscount = ntohs(hdr->nscount);
- uint16_t arcount = ntohs(hdr->arcount);
- unsigned char *ptr;
- unsigned int remain, used = 0;
-
- if (len < sizeof(*hdr) + sizeof(struct qtype_qclass) ||
- hdr->qr || qdcount != 1 || ancount || nscount) {
- DBG("Dropped DNS request qr %d with len %zd qdcount %d "
- "ancount %d nscount %d", hdr->qr, len, qdcount, ancount,
- nscount);
+ uint16_t qdcount, ancount, nscount, arcount;
+ unsigned char *ptr = buf + DNS_HEADER_SIZE;
+ size_t remain = len - DNS_HEADER_SIZE;
+ size_t used = 0;
+ if (len < DNS_HEADER_SIZE + DNS_QTYPE_QCLASS_SIZE) {
+ DBG("Dropped DNS request with short length %zd", len);
return -EINVAL;
}
if (!name || !size)
return -EINVAL;
+ qdcount = ntohs(hdr->qdcount);
+ ancount = ntohs(hdr->ancount);
+ nscount = ntohs(hdr->nscount);
+ arcount = ntohs(hdr->arcount);
+
+ if (hdr->qr || qdcount != 1 || ancount || nscount) {
+ DBG("Dropped DNS request with bad flags/counts qr %d "
+ "with len %zd qdcount %d ancount %d nscount %d",
+ hdr->qr, len, qdcount, ancount, nscount);
+
+ return -EINVAL;
+ }
+
debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
hdr->id, hdr->qr, hdr->opcode,
qdcount, arcount);
name[0] = '\0';
- ptr = buf + sizeof(struct domain_hdr);
- remain = len - sizeof(struct domain_hdr);
-
+ /* parse DNS query string into `name' out parameter */
while (remain > 0) {
uint8_t label_len = *ptr;
if (label_len == 0x00) {
- uint8_t class;
- struct qtype_qclass *q =
- (struct qtype_qclass *)(ptr + 1);
+ struct qtype_qclass *q = (struct qtype_qclass *)(ptr + 1);
+ uint16_t class;
if (remain < sizeof(*q)) {
DBG("Dropped malformed DNS query");
@@ -3002,7 +3129,7 @@ static int parse_request(unsigned char *buf, size_t len,
}
class = ntohs(q->qclass);
- if (class != 1 && class != 255) {
+ if (class != DNS_CLASS_IN && class != DNS_CLASS_ANY) {
DBG("Dropped non-IN DNS class %d", class);
return -EINVAL;
}
@@ -3019,18 +3146,17 @@ static int parse_request(unsigned char *buf, size_t len,
strcat(name, ".");
used += label_len + 1;
-
ptr += label_len + 1;
remain -= label_len + 1;
}
- if (arcount && remain >= sizeof(struct domain_rr) + 1 && !ptr[0] &&
- ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) {
+ if (arcount && remain >= DNS_RR_SIZE + 1 && !ptr[0] &&
+ ptr[1] == OPT_EDNS0_TYPE[0] && ptr[2] == OPT_EDNS0_TYPE[1]) {
struct domain_rr *edns0 = (struct domain_rr *)(ptr + 1);
DBG("EDNS0 buffer size %u", ntohs(edns0->class));
} else if (!arcount && remain) {
- DBG("DNS request with %d garbage bytes", remain);
+ DBG("DNS request with %zd garbage bytes", remain);
}
debug("query %s", name);
@@ -3067,7 +3193,7 @@ static void client_reset(struct tcp_partial_client_data *client)
client->buf_end = 0;
}
-static unsigned int get_msg_len(unsigned char *buf)
+static size_t get_msg_len(const unsigned char *buf)
{
return buf[0]<<8 | buf[1];
}
@@ -3078,15 +3204,14 @@ static bool read_tcp_data(struct tcp_partial_client_data *client,
{
char query[TCP_MAX_BUF_LEN];
struct request_data *req;
- int client_sk, err;
- unsigned int msg_len;
- GSList *list;
+ struct domain_hdr *hdr;
+ int client_sk = g_io_channel_unix_get_fd(client->channel);
+ int err;
+ size_t msg_len;
bool waiting_for_connect = false;
- int qtype = 0;
+ uint16_t qtype = 0;
struct cache_entry *entry;
- client_sk = g_io_channel_unix_get_fd(client->channel);
-
if (read_len == 0) {
debug("client %d closed, pending %d bytes",
client_sk, client->buf_end);
@@ -3099,34 +3224,36 @@ static bool read_tcp_data(struct tcp_partial_client_data *client,
client->buf_end += read_len;
- if (client->buf_end < 2)
+ /* we need at least the message length header */
+ if (client->buf_end < DNS_HEADER_TCP_EXTRA_BYTES)
return true;
msg_len = get_msg_len(client->buf);
if (msg_len > TCP_MAX_BUF_LEN) {
- debug("client %d sent too much data %d", client_sk, msg_len);
+ debug("client %d sent too much data %zd", client_sk, msg_len);
g_hash_table_remove(partial_tcp_req_table,
GINT_TO_POINTER(client_sk));
return false;
}
read_another:
- debug("client %d msg len %d end %d past end %d", client_sk, msg_len,
+ debug("client %d msg len %zd end %d past end %zd", client_sk, msg_len,
client->buf_end, client->buf_end - (msg_len + 2));
if (client->buf_end < (msg_len + 2)) {
- debug("client %d still missing %d bytes",
+ debug("client %d still missing %zd bytes",
client_sk,
msg_len + 2 - client->buf_end);
return true;
}
- debug("client %d all data %d received", client_sk, msg_len);
+ debug("client %d all data %zd received", client_sk, msg_len);
- err = parse_request(client->buf + 2, msg_len,
- query, sizeof(query));
+ err = parse_request(client->buf + DNS_HEADER_TCP_EXTRA_BYTES,
+ msg_len, query, sizeof(query));
if (err < 0 || (g_slist_length(server_list) == 0)) {
- send_response(client_sk, client->buf, msg_len + 2,
+ send_response(client_sk, client->buf,
+ msg_len + DNS_HEADER_TCP_EXTRA_BYTES,
NULL, 0, IPPROTO_TCP);
return true;
}
@@ -3141,13 +3268,15 @@ read_another:
req->protocol = IPPROTO_TCP;
req->family = client->family;
- req->srcid = client->buf[2] | (client->buf[3] << 8);
+ hdr = (void*)(client->buf + DNS_HEADER_TCP_EXTRA_BYTES);
+
+ memcpy(&req->srcid, &hdr->id, sizeof(req->srcid));
req->dstid = get_id();
req->altid = get_id();
- req->request_len = msg_len + 2;
+ req->request_len = msg_len + DNS_HEADER_TCP_EXTRA_BYTES;
- client->buf[2] = req->dstid & 0xff;
- client->buf[3] = req->dstid >> 8;
+ /* replace ID the request for forwarding */
+ memcpy(&hdr->id, &req->dstid, sizeof(hdr->id));
req->numserv = 0;
req->ifdata = client->ifdata;
@@ -3159,17 +3288,12 @@ read_another:
*/
entry = cache_check(client->buf, &qtype, IPPROTO_TCP);
if (entry) {
- int ttl_left = 0;
- struct cache_data *data;
-
- debug("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
- if (qtype == 1)
- data = entry->ipv4;
- else
- data = entry->ipv6;
+ debug("cache hit %s type %s", query, qtype == DNS_TYPE_A ? "A" : "AAAA");
+ struct cache_data *data = qtype == DNS_TYPE_A ?
+ entry->ipv4 : entry->ipv6;
if (data) {
- ttl_left = data->valid_until - time(NULL);
+ int ttl_left = data->valid_until - time(NULL);
entry->hits++;
send_cached_response(client_sk, data->data,
@@ -3182,7 +3306,7 @@ read_another:
debug("data missing, ignoring cache for this query");
}
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->protocol != IPPROTO_UDP || !data->enabled)
@@ -3233,8 +3357,8 @@ read_another:
request_list = g_slist_append(request_list, req);
out:
- if (client->buf_end > (msg_len + 2)) {
- debug("client %d buf %p -> %p end %d len %d new %d",
+ if (client->buf_end > (msg_len + DNS_HEADER_TCP_EXTRA_BYTES)) {
+ debug("client %d buf %p -> %p end %d len %d new %zd",
client_sk,
client->buf + msg_len + 2,
client->buf, client->buf_end,
@@ -3250,7 +3374,7 @@ out:
*/
msg_len = get_msg_len(client->buf);
if ((msg_len + 2) == client->buf_end) {
- debug("client %d reading another %d bytes", client_sk,
+ debug("client %d reading another %zd bytes", client_sk,
msg_len + 2);
goto read_another;
}
@@ -3276,15 +3400,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
struct tcp_partial_client_data *client = user_data;
- struct sockaddr_in6 client_addr6;
- socklen_t client_addr6_len = sizeof(client_addr6);
- struct sockaddr_in client_addr4;
- socklen_t client_addr4_len = sizeof(client_addr4);
- void *client_addr;
- socklen_t *client_addr_len;
- int len, client_sk;
-
- client_sk = g_io_channel_unix_get_fd(channel);
+ int client_sk = g_io_channel_unix_get_fd(channel);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
g_hash_table_remove(partial_tcp_req_table,
@@ -3294,6 +3410,13 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
return FALSE;
}
+ struct sockaddr_in6 client_addr6;
+ socklen_t client_addr6_len = sizeof(client_addr6);
+ struct sockaddr_in client_addr4;
+ socklen_t client_addr4_len = sizeof(client_addr4);
+ void *client_addr;
+ socklen_t *client_addr_len;
+
switch (client->family) {
case AF_INET:
client_addr = &client_addr4;
@@ -3310,7 +3433,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
return FALSE;
}
- len = recvfrom(client_sk, client->buf + client->buf_end,
+ const int len = recvfrom(client_sk, client->buf + client->buf_end,
TCP_MAX_BUF_LEN - client->buf_end, 0,
client_addr, client_addr_len);
if (len < 0) {
@@ -3330,9 +3453,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
static gboolean client_timeout(gpointer user_data)
{
struct tcp_partial_client_data *client = user_data;
- int sock;
-
- sock = g_io_channel_unix_get_fd(client->channel);
+ int sock = g_io_channel_unix_get_fd(client->channel);
debug("client %d timeout pending %d bytes", sock, client->buf_end);
@@ -3345,8 +3466,12 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
struct listener_data *ifdata, int family,
guint *listener_watch)
{
- int sk, client_sk, len;
- unsigned int msg_len;
+ int sk = -1, client_sk = -1;
+ int recv_len;
+ size_t msg_len;
+ fd_set readfds;
+ struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
+
struct tcp_partial_client_data *client;
struct sockaddr_in6 client_addr6;
socklen_t client_addr6_len = sizeof(client_addr6);
@@ -3354,8 +3479,6 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
socklen_t client_addr4_len = sizeof(client_addr4);
void *client_addr;
socklen_t *client_addr_len;
- struct timeval tv;
- fd_set readfds;
debug("condition 0x%02x channel %p ifdata %p family %d",
condition, channel, ifdata, family);
@@ -3380,29 +3503,27 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
client_addr_len = &client_addr6_len;
}
- tv.tv_sec = tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(sk, &readfds);
+ /* TODO: check select return code */
select(sk + 1, &readfds, NULL, NULL, &tv);
- if (FD_ISSET(sk, &readfds)) {
- client_sk = accept(sk, client_addr, client_addr_len);
- debug("client %d accepted", client_sk);
- } else {
+ if (!FD_ISSET(sk, &readfds)) {
debug("No data to read from master %d, waiting.", sk);
return true;
}
+ client_sk = accept(sk, client_addr, client_addr_len);
if (client_sk < 0) {
connman_error("Accept failure on TCP listener");
*listener_watch = 0;
return false;
}
+ debug("client %d accepted", client_sk);
fcntl(client_sk, F_SETFL, O_NONBLOCK);
- client = g_hash_table_lookup(partial_tcp_req_table,
- GINT_TO_POINTER(client_sk));
+ client = g_hash_table_lookup(partial_tcp_req_table, GINT_TO_POINTER(client_sk));
if (!client) {
client = g_try_new0(struct tcp_partial_client_data, 1);
if (!client) {
@@ -3446,8 +3567,8 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
* proceed normally, otherwise read the bits until everything
* is received or timeout occurs.
*/
- len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0);
- if (len < 0) {
+ recv_len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0);
+ if (recv_len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
debug("client %d no data to read, waiting", client_sk);
return true;
@@ -3460,15 +3581,15 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
return true;
}
- if (len < 2) {
+ if (recv_len < DNS_HEADER_TCP_EXTRA_BYTES) {
debug("client %d not enough data to read, waiting", client_sk);
- client->buf_end += len;
+ client->buf_end += recv_len;
return true;
}
msg_len = get_msg_len(client->buf);
if (msg_len > TCP_MAX_BUF_LEN) {
- debug("client %d invalid message length %u ignoring packet",
+ debug("client %d invalid message length %zd ignoring packet",
client_sk, msg_len);
g_hash_table_remove(partial_tcp_req_table,
GINT_TO_POINTER(client_sk));
@@ -3479,15 +3600,15 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
* The packet length bytes do not contain the total message length,
* that is the reason to -2 below.
*/
- if (msg_len != (unsigned int)(len - 2)) {
- debug("client %d sent %d bytes but expecting %u pending %d",
- client_sk, len, msg_len + 2, msg_len + 2 - len);
+ if (msg_len != (size_t)(recv_len - DNS_HEADER_TCP_EXTRA_BYTES)) {
+ debug("client %d sent %d bytes but expecting %zd pending %zd",
+ client_sk, recv_len, msg_len + 2, msg_len + 2 - recv_len);
- client->buf_end += len;
+ client->buf_end += recv_len;
return true;
}
- return read_tcp_data(client, client_addr, *client_addr_len, len);
+ return read_tcp_data(client, client_addr, *client_addr_len, recv_len);
}
static gboolean tcp4_listener_event(GIOChannel *channel, GIOCondition condition,
@@ -3514,14 +3635,16 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
{
unsigned char buf[768];
char query[512];
- struct request_data *req;
+ struct request_data *req = NULL;
+ struct domain_hdr *hdr = NULL;
+ int sk = -1, err, len;
+
struct sockaddr_in6 client_addr6;
socklen_t client_addr6_len = sizeof(client_addr6);
struct sockaddr_in client_addr4;
socklen_t client_addr4_len = sizeof(client_addr4);
void *client_addr;
socklen_t *client_addr_len;
- int sk, err, len;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with UDP listener channel");
@@ -3529,8 +3652,6 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
return false;
}
- sk = g_io_channel_unix_get_fd(channel);
-
if (family == AF_INET) {
client_addr = &client_addr4;
client_addr_len = &client_addr4_len;
@@ -3540,6 +3661,7 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
}
memset(client_addr, 0, *client_addr_len);
+ sk = g_io_channel_unix_get_fd(channel);
len = recvfrom(sk, buf, sizeof(buf), 0, client_addr, client_addr_len);
if (len < 2)
return true;
@@ -3563,13 +3685,14 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
req->protocol = IPPROTO_UDP;
req->family = family;
- req->srcid = buf[0] | (buf[1] << 8);
+ hdr = (void*)buf;
+
+ req->srcid = hdr->id;
req->dstid = get_id();
req->altid = get_id();
req->request_len = len;
- buf[0] = req->dstid & 0xff;
- buf[1] = req->dstid >> 8;
+ hdr->id = req->dstid;
req->numserv = 0;
req->ifdata = ifdata;
@@ -3610,42 +3733,26 @@ static gboolean udp6_listener_event(GIOChannel *channel, GIOCondition condition,
static GIOChannel *get_listener(int family, int protocol, int index)
{
- GIOChannel *channel;
- const char *proto;
+ GIOChannel *channel = NULL;
union {
struct sockaddr sa;
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
} s;
socklen_t slen;
- int sk, type;
+ const char *proto = protocol_label(protocol);
+ const int type = socket_type(protocol, SOCK_CLOEXEC);
char *interface;
+ int sk = socket(family, type, protocol);
debug("family %d protocol %d index %d", family, protocol, index);
- switch (protocol) {
- case IPPROTO_UDP:
- proto = "UDP";
- type = SOCK_DGRAM | SOCK_CLOEXEC;
- break;
-
- case IPPROTO_TCP:
- proto = "TCP";
- type = SOCK_STREAM | SOCK_CLOEXEC;
- break;
-
- default:
- return NULL;
- }
-
- sk = socket(family, type, protocol);
- if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
- connman_error("No IPv6 support");
- return NULL;
- }
-
if (sk < 0) {
- connman_error("Failed to create %s listener socket", proto);
+ if (family == AF_INET6 && errno == EAFNOSUPPORT) {
+ connman_error("No IPv6 support");
+ } else {
+ connman_error("Failed to create %s listener socket", proto);
+ }
return NULL;
}
@@ -3666,7 +3773,7 @@ static GIOChannel *get_listener(int family, int protocol, int index)
if (family == AF_INET6) {
memset(&s.sin6, 0, sizeof(s.sin6));
s.sin6.sin6_family = AF_INET6;
- s.sin6.sin6_port = htons(53);
+ s.sin6.sin6_port = htons(dns_listen_port);
slen = sizeof(s.sin6);
if (__connman_inet_get_interface_address(index,
@@ -3683,7 +3790,7 @@ static GIOChannel *get_listener(int family, int protocol, int index)
} else if (family == AF_INET) {
memset(&s.sin, 0, sizeof(s.sin));
s.sin.sin_family = AF_INET;
- s.sin.sin_port = htons(53);
+ s.sin.sin_port = htons(dns_listen_port);
slen = sizeof(s.sin);
if (__connman_inet_get_interface_address(index,
@@ -3704,7 +3811,6 @@ static GIOChannel *get_listener(int family, int protocol, int index)
}
if (protocol == IPPROTO_TCP) {
-
if (listen(sk, 10) < 0) {
connman_error("Failed to listen on TCP socket %d/%s",
-errno, strerror(errno));
@@ -3818,9 +3924,7 @@ static void destroy_tcp_listener(struct listener_data *ifdata)
static int create_listener(struct listener_data *ifdata)
{
- int err, index;
-
- err = create_dns_listener(IPPROTO_UDP, ifdata);
+ int err = create_dns_listener(IPPROTO_UDP, ifdata);
if ((err & UDP_FAILED) == UDP_FAILED)
return -EIO;
@@ -3830,7 +3934,7 @@ static int create_listener(struct listener_data *ifdata)
return -EIO;
}
- index = connman_inet_ifindex("lo");
+ int index = connman_inet_ifindex("lo");
if (ifdata->index == index) {
if ((err & IPv6_FAILED) != IPv6_FAILED)
__connman_resolvfile_append(index, NULL, "::1");
@@ -3844,16 +3948,14 @@ static int create_listener(struct listener_data *ifdata)
static void destroy_listener(struct listener_data *ifdata)
{
- int index;
- GSList *list;
+ int index = connman_inet_ifindex("lo");
- index = connman_inet_ifindex("lo");
if (ifdata->index == index) {
__connman_resolvfile_remove(index, NULL, "127.0.0.1");
__connman_resolvfile_remove(index, NULL, "::1");
}
- for (list = request_list; list; list = list->next) {
+ for (GSList *list = request_list; list; list = list->next) {
struct request_data *req = list->data;
debug("Dropping request (id 0x%04x -> 0x%04x)",
@@ -3914,7 +4016,6 @@ int __connman_dnsproxy_add_listener(int index)
void __connman_dnsproxy_remove_listener(int index)
{
struct listener_data *ifdata;
-
DBG("index %d", index);
if (!listener_table)
@@ -3967,17 +4068,15 @@ int __connman_dnsproxy_init(void)
return err;
err = connman_notifier_register(&dnsproxy_notifier);
- if (err < 0)
- goto destroy;
-
- return 0;
+ if (err < 0) {
+ __connman_dnsproxy_remove_listener(index);
+ g_hash_table_destroy(listener_table);
+ g_hash_table_destroy(partial_tcp_req_table);
-destroy:
- __connman_dnsproxy_remove_listener(index);
- g_hash_table_destroy(listener_table);
- g_hash_table_destroy(partial_tcp_req_table);
+ return err;
+ }
- return err;
+ return 0;
}
int __connman_dnsproxy_set_mdns(int index, bool enabled)
@@ -4012,3 +4111,8 @@ void __connman_dnsproxy_cleanup(void)
if (ipv6_resolve)
g_resolv_unref(ipv6_resolve);
}
+
+void __connman_dnsproxy_set_listen_port(unsigned int port)
+{
+ dns_listen_port = port;
+}
diff --git a/src/inet.c b/src/inet.c
index 4c341438..4039a73c 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -79,7 +79,8 @@ int __connman_inet_modify_address(int cmd, int flags,
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast)
+ const char *broadcast,
+ bool is_p2p)
{
uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
@@ -94,8 +95,9 @@ int __connman_inet_modify_address(int cmd, int flags,
int sk, err;
DBG("cmd %#x flags %#x index %d family %d address %s peer %s "
- "prefixlen %hhu broadcast %s", cmd, flags, index, family,
- address, peer, prefixlen, broadcast);
+ "prefixlen %hhu broadcast %s p2p %s", cmd, flags, index,
+ family, address, peer, prefixlen, broadcast,
+ is_p2p ? "true" : "false");
if (!address)
return -EINVAL;
@@ -119,17 +121,11 @@ int __connman_inet_modify_address(int cmd, int flags,
ifaddrmsg->ifa_index = index;
if (family == AF_INET) {
- if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+ if (inet_pton(AF_INET, address, &ipv4_addr) != 1)
return -1;
- if (broadcast)
- inet_pton(AF_INET, broadcast, &ipv4_bcast);
- else
- ipv4_bcast.s_addr = ipv4_addr.s_addr |
- htonl(0xfffffffflu >> prefixlen);
-
if (peer) {
- if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
+ if (inet_pton(AF_INET, peer, &ipv4_dest) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
@@ -149,16 +145,27 @@ int __connman_inet_modify_address(int cmd, int flags,
if (err < 0)
return err;
- err = __connman_inet_rtnl_addattr_l(header,
- sizeof(request),
- IFA_BROADCAST,
- &ipv4_bcast,
- sizeof(ipv4_bcast));
- if (err < 0)
- return err;
+ /*
+ * Broadcast address must not be added for P2P / VPN as
+ * getifaddrs() cannot interpret destination address.
+ */
+ if (!is_p2p) {
+ if (broadcast)
+ inet_pton(AF_INET, broadcast, &ipv4_bcast);
+ else
+ ipv4_bcast.s_addr = ipv4_addr.s_addr |
+ htonl(0xfffffffflu >> prefixlen);
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_BROADCAST,
+ &ipv4_bcast,
+ sizeof(ipv4_bcast));
+ if (err < 0)
+ return err;
+ }
} else if (family == AF_INET6) {
- if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
+ if (inet_pton(AF_INET6, address, &ipv6_addr) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
@@ -189,13 +196,46 @@ done:
return err;
}
+static bool is_addr_unspec(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return in4->sin_addr.s_addr == INADDR_ANY;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
+static bool is_addr_ll(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return (in4->sin_addr.s_addr & IN_CLASSB_NET) ==
+ ((in_addr_t) htonl(0xa9fe0000));
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
bool __connman_inet_is_any_addr(const char *address, int family)
{
bool ret = false;
struct addrinfo hints;
struct addrinfo *result = NULL;
- struct sockaddr_in6 *in6 = NULL;
- struct sockaddr_in *in4 = NULL;
if (!address || !*address)
goto out;
@@ -208,14 +248,7 @@ bool __connman_inet_is_any_addr(const char *address, int family)
goto out;
if (result) {
- if (result->ai_family == AF_INET6) {
- in6 = (struct sockaddr_in6*)result->ai_addr;
- ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
- } else if (result->ai_family == AF_INET) {
- in4 = (struct sockaddr_in*)result->ai_addr;
- ret = in4->sin_addr.s_addr == INADDR_ANY;
- }
-
+ ret = is_addr_unspec(result->ai_family, result->ai_addr);
freeaddrinfo(result);
}
@@ -409,18 +442,20 @@ int connman_inet_set_ipv6_address(int index,
int err;
unsigned char prefix_len;
const char *address;
+ bool is_p2p;
if (!ipaddress->local)
return 0;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -434,6 +469,7 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
if (!ipaddress->local)
return -1;
@@ -442,12 +478,13 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -456,10 +493,17 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
return 0;
}
-int connman_inet_clear_ipv6_address(int index, const char *address,
- int prefix_len)
+int connman_inet_clear_ipv6_address(int index,
+ struct connman_ipaddress *ipaddress)
{
int err;
+ int prefix_len;
+ const char *address;
+ bool is_p2p;
+
+ address = ipaddress->local;
+ prefix_len = ipaddress->prefixlen;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
@@ -467,7 +511,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address,
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -481,11 +525,13 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
address, prefix_len, peer, broadcast);
@@ -494,7 +540,7 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -661,7 +707,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
@@ -711,7 +757,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
@@ -727,7 +773,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
*/
if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) &&
- inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0)
+ inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) == 1)
rt.rtmsg_flags |= RTF_GATEWAY;
rt.rtmsg_metric = 1;
@@ -770,7 +816,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
memset(&rt, 0, sizeof(rt));
- if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+ if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) != 1) {
err = -errno;
goto out;
}
@@ -1066,54 +1112,161 @@ out:
return err;
}
-bool connman_inet_compare_subnet(int index, const char *host)
+#define ADDR_TYPE_MAX 4
+
+struct interface_address {
+ int index;
+ int family;
+ bool allow_unspec;
+ /* Applies only to ADDR_TYPE_IPADDR in ipaddrs */
+ bool require_ll;
+ /* Real types must be in_addr for AF_INET and in6_addr for AF_INET6 */
+ void *ipaddrs[ADDR_TYPE_MAX];
+};
+
+enum ipaddr_type {
+ ADDR_TYPE_IPADDR = 0,
+ ADDR_TYPE_NETMASK,
+ ADDR_TYPE_BRDADDR,
+ ADDR_TYPE_DSTADDR
+};
+
+static int get_interface_addresses(struct interface_address *if_addr)
{
- struct ifreq ifr;
- struct in_addr _host_addr;
- in_addr_t host_addr, netmask_addr, if_addr;
- struct sockaddr_in *netmask, *addr;
- int sk;
+ struct ifaddrs *ifaddr;
+ struct ifaddrs *ifa;
+ struct sockaddr *addrs[ADDR_TYPE_MAX] = { 0 };
+ struct sockaddr_in *addr_in;
+ struct sockaddr_in6 *addr_in6;
+ char name[IF_NAMESIZE] = { 0 };
+ size_t len;
+ int err = -ENOENT;
+ int i;
- DBG("host %s", host);
+ if (!if_addr)
+ return -EINVAL;
- if (!host)
- return false;
+ if (!if_indextoname(if_addr->index, name))
+ return -EINVAL;
- if (inet_aton(host, &_host_addr) == 0)
- return false;
- host_addr = _host_addr.s_addr;
+ DBG("index %d interface %s", if_addr->index, name);
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return false;
+ if (getifaddrs(&ifaddr) < 0) {
+ connman_error("Cannot get addresses err %d/%s", errno,
+ strerror(errno));
+ return -errno;
+ }
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return false;
+ if (g_strcmp0(ifa->ifa_name, name) ||
+ ifa->ifa_addr->sa_family !=
+ if_addr->family)
+ continue;
+
+
+ if (if_addr->ipaddrs[ADDR_TYPE_IPADDR]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ if (if_addr->require_ll && !is_addr_ll(if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ addrs[ADDR_TYPE_IPADDR] = ifa->ifa_addr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_NETMASK]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_netmask))
+ continue;
+
+ addrs[ADDR_TYPE_NETMASK] = ifa->ifa_netmask;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_BRDADDR] &&
+ (ifa->ifa_flags & IFF_BROADCAST)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_broadaddr))
+ continue;
+
+ addrs[ADDR_TYPE_BRDADDR] = ifa->ifa_ifu.ifu_broadaddr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_DSTADDR] &&
+ (ifa->ifa_flags & IFF_POINTOPOINT)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_dstaddr))
+ continue;
+
+ addrs[ADDR_TYPE_DSTADDR] = ifa->ifa_ifu.ifu_dstaddr;
+ }
+
+ err = 0;
+
+ break;
}
- if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
- close(sk);
- return false;
+ if (err)
+ goto out;
+
+ for (i = 0; i < ADDR_TYPE_MAX; i++) {
+ if (!addrs[i])
+ continue;
+
+ switch (if_addr->family) {
+ case AF_INET:
+ len = sizeof(struct in_addr);
+ addr_in = (struct sockaddr_in*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in->sin_addr, len);
+ break;
+ case AF_INET6:
+ len = sizeof(struct in6_addr);
+ addr_in6 = (struct sockaddr_in6*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in6->sin6_addr, len);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
}
- netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
- netmask_addr = netmask->sin_addr.s_addr;
+out:
+ freeifaddrs(ifaddr);
+ return err;
+}
- if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
- close(sk);
+bool connman_inet_compare_subnet(int index, const char *host)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr iaddr = { 0 };
+ struct in_addr imask = { 0 };
+ struct in_addr haddr = { 0 };
+
+ DBG("host %s", host);
+
+ if (!host)
return false;
- }
- close(sk);
+ if (inet_pton(AF_INET, host, &haddr) != 1)
+ return false;
+
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- addr = (struct sockaddr_in *)&ifr.ifr_addr;
- if_addr = addr->sin_addr.s_addr;
+ if (get_interface_addresses(&if_addr))
+ return false;
- return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
+ return (iaddr.s_addr & imask.s_addr) == (haddr.s_addr & imask.s_addr);
}
static bool mem_mask_equal(const void *a, const void *b,
@@ -1134,47 +1287,23 @@ static bool mem_mask_equal(const void *a, const void *b,
bool connman_inet_compare_ipv6_subnet(int index, const char *host)
{
- struct ifaddrs *ifaddr, *ifa;
- bool rv = false;
- char name[IF_NAMESIZE];
- struct in6_addr haddr;
+ struct interface_address addr = { 0 };
+ struct in6_addr iaddr = { 0 };
+ struct in6_addr imask = { 0 };
+ struct in6_addr haddr = { 0 };
- if (inet_pton(AF_INET6, host, &haddr) <= 0)
+ if (inet_pton(AF_INET6, host, &haddr) != 1)
return false;
- if (!if_indextoname(index, name))
- return false;
-
- DBG("index %d interface %s", index, name);
+ addr.index = index;
+ addr.family = AF_INET6;
+ addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- if (getifaddrs(&ifaddr) < 0) {
- DBG("Cannot get addresses err %d/%s", errno, strerror(errno));
+ if (get_interface_addresses(&addr))
return false;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- struct sockaddr_in6 *iaddr;
- struct sockaddr_in6 *imask;
-
- if (!ifa->ifa_addr)
- continue;
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 ||
- ifa->ifa_addr->sa_family != AF_INET6)
- continue;
-
- iaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
- imask = (struct sockaddr_in6 *)ifa->ifa_netmask;
-
- rv = mem_mask_equal(&iaddr->sin6_addr, &haddr,
- &imask->sin6_addr,
- sizeof(haddr));
- goto out;
- }
-
-out:
- freeifaddrs(ifaddr);
- return rv;
+ return mem_mask_equal(&iaddr, &haddr, &imask, sizeof(haddr));
}
int connman_inet_remove_from_bridge(int index, const char *bridge)
@@ -1707,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)
@@ -2147,98 +2269,156 @@ GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
return prefixes;
}
-static int get_dest_addr(int family, int index, char *buf, int len)
+int connman_inet_get_dest_addr(int index, char **dest)
{
- struct ifreq ifr;
- void *addr;
- int sk;
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -errno;
+ if (!dest)
+ return -EINVAL;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
- DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ if (inet_ntop(AF_INET, &dstaddr, buf, INET_ADDRSTRLEN))
+ *dest = g_strdup(buf);
- if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
- close(sk);
- errno = EINVAL;
- return -errno;
- }
+ DBG("destination %s", *dest);
- DBG("index %d %s", index, ifr.ifr_name);
+ return *dest && **dest ? 0 : -ENOENT;
+}
- if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
- connman_error("Get destination address failed (%s)",
- strerror(errno));
- close(sk);
- return -errno;
- }
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- close(sk);
+ if (!dest)
+ return -EINVAL;
- switch (family) {
- case AF_INET:
- addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
- break;
- case AF_INET6:
- addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
- break;
- default:
- errno = EINVAL;
- return -errno;
- }
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (!inet_ntop(family, addr, buf, len)) {
- DBG("error %d/%s", errno, strerror(errno));
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ if (inet_ntop(AF_INET6, &dstaddr, buf, INET6_ADDRSTRLEN))
+ *dest = g_strdup(buf);
+
+ DBG("destination %s", *dest);
+
+ return *dest && **dest ? 0 : -ENOENT;
}
-int connman_inet_get_dest_addr(int index, char **dest)
+/* destination is optional */
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+ char **destination)
{
- char addr[INET_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in_addr addr = { 0 };
+ struct in_addr mask = { 0 };
+ struct in_addr dest = { 0 };
+ struct in_addr nw_addr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network || !netmask)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ nw_addr.s_addr = (addr.s_addr & mask.s_addr);
+
+ if (inet_ntop(AF_INET, &nw_addr, buf, INET_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &mask, buf, INET_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &dest, buf, INET_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
-int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+ char **netmask, char **destination)
{
- char addr[INET6_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in6_addr addr = { 0 };
+ struct in6_addr mask = { 0 };
+ struct in6_addr dest = { 0 };
+ struct in6_addr nw_addr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ ipv6_addr_set(&nw_addr, addr.s6_addr32[0] & mask.s6_addr32[0],
+ addr.s6_addr32[1] & mask.s6_addr32[1],
+ addr.s6_addr32[2] & mask.s6_addr32[2],
+ addr.s6_addr32[3] & mask.s6_addr32[3]);
+
+ if (inet_ntop(AF_INET6, &nw_addr, buf, INET6_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &mask, buf, INET6_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &dest, buf, INET6_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
@@ -2835,58 +3015,30 @@ bool connman_inet_is_ipv6_supported()
return true;
}
-int __connman_inet_get_interface_address(int index, int family, void *address)
+/*
+ * Omits checking of the gateway matching the actual gateway IP since both
+ * connmand and vpnd use inet.c, getting the route is via ipconfig and ipconfig
+ * is different for both. Gateway is left here for possible future use.
+ *
+ * Gateway can be NULL and connection.c then assigns 0.0.0.0 address or ::,
+ * depending on IP family.
+ */
+bool connman_inet_is_default_route(int family, const char *host,
+ const char *gateway, const char *netmask)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
-
- DBG("index %d interface %s", index, name);
-
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
+ return __connman_inet_is_any_addr(host, family) &&
+ __connman_inet_is_any_addr(netmask, family);
+}
- } else {
- err = -EINVAL;
- goto out;
- }
+int __connman_inet_get_interface_address(int index, int family, void *address)
+{
+ struct interface_address if_addr = { 0 };
- err = 0;
- break;
- }
- }
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
@@ -3020,7 +3172,7 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
ret = inet_pton(family, dst ? dst : gateway, buf);
g_free(dst);
- if (ret <= 0)
+ if (ret != 1)
return -EINVAL;
memset(&rth, 0, sizeof(rth));
@@ -3095,61 +3247,14 @@ int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
int __connman_inet_get_interface_ll_address(int index, int family,
void *address)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
+ struct interface_address if_addr = { 0 };
- DBG("index %d interface %s", index, name);
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.require_ll = true;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- if ((in4->sin_addr.s_addr & IN_CLASSB_NET) !=
- ((in_addr_t) 0xa9fe0000))
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
- continue;
-
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
- } else {
- err = -EINVAL;
- goto out;
- }
-
- err = 0;
- break;
- }
- }
-
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_address_netmask(int ifindex,
@@ -3303,7 +3408,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
addrstr[len] = '\0';
err = inet_pton(AF_INET, addrstr, addr);
- if (err <= 0) {
+ if (err != 1) {
connman_error("%s: Cannot convert to numeric addr \"%s\"\n",
__func__, addrstr);
err = -1;
diff --git a/src/ipaddress.c b/src/ipaddress.c
index d63d95c3..201d8345 100644
--- a/src/ipaddress.c
+++ b/src/ipaddress.c
@@ -70,10 +70,19 @@ struct connman_ipaddress *connman_ipaddress_alloc(int family)
ipaddress->peer = NULL;
ipaddress->broadcast = NULL;
ipaddress->gateway = NULL;
+ ipaddress->is_p2p = false;
return ipaddress;
}
+void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress, bool value)
+{
+ if (!ipaddress)
+ return;
+
+ ipaddress->is_p2p = value;
+}
+
void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
{
if (!ipaddress)
@@ -95,7 +104,7 @@ static bool check_ipv6_address(const char *address)
return false;
err = inet_pton(AF_INET6, address, buf);
- if (err > 0)
+ if (err == 1)
return true;
return false;
@@ -223,6 +232,7 @@ connman_ipaddress_copy(struct connman_ipaddress *ipaddress)
copy->peer = g_strdup(ipaddress->peer);
copy->broadcast = g_strdup(ipaddress->broadcast);
copy->gateway = g_strdup(ipaddress->gateway);
+ copy->is_p2p = ipaddress->is_p2p;
return copy;
}
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 915c0823..34b1724a 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -258,153 +258,165 @@ static const char *scope2str(unsigned char scope)
return "";
}
-static bool get_ipv6_state(gchar *ifname)
+#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf"
+#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf"
+
+static int read_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int *value)
{
- int disabled;
gchar *path;
FILE *f;
- bool enabled = false;
-
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+ int err;
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return enabled;
+ return -ENOMEM;
+ errno = 0;
f = fopen(path, "r");
+ if (!f) {
+ err = -errno;
+ } else {
+ errno = 0; /* Avoid stale errno values with fscanf */
- g_free(path);
+ err = fscanf(f, "%d", value);
+ if (err <= 0 && errno)
+ err = -errno;
- if (f) {
- if (fscanf(f, "%d", &disabled) > 0)
- enabled = !disabled;
fclose(f);
}
- return enabled;
+ if (err <= 0)
+ connman_error("failed to read %s", path);
+
+ g_free(path);
+
+ return err;
+}
+
+static int read_ipv4_conf_value(const char *ifname, const char *suffix,
+ int *value)
+{
+ return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
}
-static void set_ipv6_state(gchar *ifname, bool enable)
+static int read_ipv6_conf_value(const char *ifname, const char *suffix,
+ int *value)
{
+ return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
+}
+
+static int write_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int value) {
gchar *path;
FILE *f;
+ int rval;
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
-
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return;
+ return -ENOMEM;
f = fopen(path, "r+");
+ if (!f) {
+ rval = -errno;
+ } else {
+ rval = fprintf(f, "%d", value);
+ fclose(f);
+ }
+
+ if (rval <= 0)
+ connman_error("failed to set %s value %d", path, value);
g_free(path);
- if (!f)
- return;
+ return rval;
+}
- if (!enable)
- fprintf(f, "1");
- else
- fprintf(f, "0");
+static int write_ipv4_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
+}
- fclose(f);
+static int write_ipv6_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
}
-static int get_ipv6_privacy(gchar *ifname)
+static bool get_ipv6_state(gchar *ifname)
{
- gchar *path;
- FILE *f;
- int value;
+ int disabled;
+ bool enabled = false;
- if (!ifname)
- return 0;
+ if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0)
+ enabled = !disabled;
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
+ return enabled;
+}
- if (!path)
- return 0;
+static int set_ipv6_state(gchar *ifname, bool enable)
+{
+ int disabled = enable ? 0 : 1;
- f = fopen(path, "r");
+ DBG("%s %d", ifname, disabled);
- g_free(path);
+ return write_ipv6_conf_value(ifname, "disable_ipv6", disabled);
+}
- if (!f)
+static int get_ipv6_privacy(gchar *ifname)
+{
+ int value;
+
+ if (!ifname)
return 0;
- if (fscanf(f, "%d", &value) <= 0)
+ if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0)
value = 0;
- fclose(f);
-
return value;
}
/* Enable the IPv6 privacy extension for stateless address autoconfiguration.
* The privacy extension is described in RFC 3041 and RFC 4941
*/
-static void set_ipv6_privacy(gchar *ifname, int value)
+static int set_ipv6_privacy(gchar *ifname, int value)
{
- gchar *path;
- FILE *f;
-
if (!ifname)
- return;
-
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
-
- if (!path)
- return;
+ return -EINVAL;
if (value < 0)
value = 0;
- f = fopen(path, "r+");
-
- g_free(path);
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
- fclose(f);
+ return write_ipv6_conf_value(ifname, "use_tempaddr", value);
}
static int get_rp_filter(void)
{
- FILE *f;
- int value = -EINVAL, tmp;
-
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
+ int value;
- if (f) {
- if (fscanf(f, "%d", &tmp) == 1)
- value = tmp;
- fclose(f);
- }
+ if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0)
+ value = -EINVAL;
return value;
}
-static void set_rp_filter(int value)
+static int set_rp_filter(int value)
{
- FILE *f;
-
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
+ /* 0 = no validation, 1 = strict mode, 2 = loose mode */
+ switch (value) {
+ case -1:
+ value = 0;
+ /* fall through */
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
- fclose(f);
+ return write_ipv4_conf_value(NULL, "rp_filter", value);
}
int __connman_ipconfig_set_rp_filter()
@@ -696,6 +708,25 @@ static inline gint check_duplicate_address(gconstpointer a, gconstpointer b)
return g_strcmp0(addr1->local, addr2->local);
}
+static bool is_index_p2p_service(int index)
+{
+ struct connman_service *service;
+ enum connman_service_type type;
+
+ service = __connman_service_lookup_from_index(index);
+ if (!service)
+ return false;
+
+ type = connman_service_get_type(service);
+ switch (type) {
+ case CONNMAN_SERVICE_TYPE_P2P:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ return true;
+ default:
+ return false;
+ }
+}
+
int __connman_ipconfig_newaddr(int index, int family, const char *label,
unsigned char prefixlen, const char *address)
{
@@ -718,6 +749,9 @@ int __connman_ipconfig_newaddr(int index, int family, const char *label,
ipaddress->prefixlen = prefixlen;
ipaddress->local = g_strdup(address);
+ if (is_index_p2p_service(index))
+ connman_ipaddress_set_p2p(ipaddress, true);
+
if (g_slist_find_custom(ipdevice->address_list, ipaddress,
check_duplicate_address)) {
connman_ipaddress_free(ipaddress);
@@ -1186,6 +1220,15 @@ void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig,
ipconfig->address->prefixlen = prefixlen;
}
+static void ipconfig_set_p2p(int index, struct connman_ipconfig *ipconfig)
+{
+ if (!is_index_p2p_service(index))
+ return;
+
+ connman_ipaddress_set_p2p(ipconfig->address, true);
+ connman_ipaddress_set_p2p(ipconfig->system, true);
+}
+
static struct connman_ipconfig *create_ipv6config(int index)
{
struct connman_ipconfig *ipv6config;
@@ -1217,6 +1260,8 @@ static struct connman_ipconfig *create_ipv6config(int index)
ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+ ipconfig_set_p2p(index, ipv6config);
+
DBG("ipconfig %p index %d method %s", ipv6config, index,
__connman_ipconfig_method2string(ipv6config->method));
@@ -1255,6 +1300,8 @@ struct connman_ipconfig *__connman_ipconfig_create(int index,
ipconfig->system = connman_ipaddress_alloc(AF_INET);
+ ipconfig_set_p2p(index, ipconfig);
+
DBG("ipconfig %p index %d", ipconfig, index);
return ipconfig;
@@ -1451,10 +1498,8 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig)
err = connman_inet_clear_address(ipconfig->index,
ipconfig->address);
else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
- err = connman_inet_clear_ipv6_address(
- ipconfig->index,
- ipconfig->address->local,
- ipconfig->address->prefixlen);
+ err = connman_inet_clear_ipv6_address(ipconfig->index,
+ ipconfig->address);
else
err = -EINVAL;
@@ -1548,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);
@@ -1567,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);
@@ -1647,6 +1698,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
@@ -1657,6 +1711,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
@@ -1719,6 +1776,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
ipdevice->config_ipv4 = NULL;
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
+
return 0;
}
@@ -1728,6 +1789,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
ipdevice->config_ipv6 = NULL;
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
+
return 0;
}
diff --git a/src/iptables.c b/src/iptables.c
index 47ea1c2d..664b27f1 100644
--- a/src/iptables.c
+++ b/src/iptables.c
@@ -2931,7 +2931,7 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip,
if (!tokens)
return -1;
- if (!inet_pton(AF_INET, tokens[0], ip)) {
+ if (inet_pton(AF_INET, tokens[0], ip) != 1) {
err = -1;
goto out;
}
@@ -2972,7 +2972,7 @@ static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip,
if (!tokens)
return -1;
- if (!inet_pton(AF_INET6, tokens[0], ip)) {
+ if (inet_pton(AF_INET6, tokens[0], ip) != 1) {
err = -1;
goto out;
}
@@ -3383,7 +3383,7 @@ static int parse_rule_spec(struct connman_iptables *table,
break;
if (invert)
- ctx->ip->invflags |= IP6T_INV_DSTIP;
+ ctx->ipv6->invflags |= IP6T_INV_DSTIP;
}
break;
diff --git a/src/main.c b/src/main.c
index 2371771f..ae4a450d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -44,6 +44,18 @@
#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
+ * DNS data etc.
+ */
+#define DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL 1
+#define DEFAULT_ONLINE_CHECK_MAX_INTERVAL 12
+#define DEFAULT_LOCALTIME "/etc/localtime"
+
#define MAINFILE "main.conf"
#define CONFIGMAINFILE CONFIGDIR "/" MAINFILE
@@ -66,6 +78,7 @@ static char *default_blacklist[] = {
"ifb",
"ve-",
"vb-",
+ "ham",
NULL
};
@@ -88,9 +101,17 @@ static struct {
bool enable_6to4;
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;
bool acd;
bool use_gateways_as_timeservers;
+ char *localtime;
+ bool regdom_follows_timezone;
+ char *resolv_conf;
} connman_settings = {
.bg_scan = true,
.pref_timeservers = NULL,
@@ -110,9 +131,16 @@ static struct {
.enable_6to4 = false,
.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,
.acd = false,
.use_gateways_as_timeservers = false,
+ .localtime = NULL,
+ .resolv_conf = NULL,
};
#define CONF_BG_SCAN "BackgroundScanning"
@@ -133,14 +161,23 @@ static struct {
#define CONF_ENABLE_6TO4 "Enable6to4"
#define CONF_VENDOR_CLASS_ID "VendorClassID"
#define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck"
+#define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_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"
#define CONF_ACD "AddressConflictDetection"
#define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers"
+#define CONF_LOCALTIME "Localtime"
+#define CONF_REGDOM_FOLLOWS_TIMEZONE "RegdomFollowsTimezone"
+#define CONF_RESOLV_CONF "ResolvConf"
static const char *supported_options[] = {
CONF_BG_SCAN,
CONF_PREF_TIMESERVERS,
CONF_AUTO_CONNECT_TECHS,
+ CONF_FAVORITE_TECHS,
CONF_ALWAYS_CONNECTED_TECHS,
CONF_PREFERRED_TECHS,
CONF_FALLBACK_NAMESERVERS,
@@ -155,9 +192,17 @@ static const char *supported_options[] = {
CONF_ENABLE_6TO4,
CONF_VENDOR_CLASS_ID,
CONF_ENABLE_ONLINE_CHECK,
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+ CONF_ONLINE_CHECK_IPV4_URL,
+ CONF_ONLINE_CHECK_IPV6_URL,
+ CONF_ONLINE_CHECK_INITIAL_INTERVAL,
+ CONF_ONLINE_CHECK_MAX_INTERVAL,
CONF_AUTO_CONNECT_ROAMING_SERVICES,
CONF_ACD,
CONF_USE_GATEWAYS_AS_TIMESERVERS,
+ CONF_LOCALTIME,
+ CONF_REGDOM_FOLLOWS_TIMEZONE,
+ CONF_RESOLV_CONF,
NULL
};
@@ -280,17 +325,23 @@ static void parse_config(GKeyFile *config)
char **interfaces;
char **str_list;
char **tethering;
- char *vendor_class_id;
+ char *string;
gsize len;
- int timeout;
+ int integer;
if (!config) {
connman_settings.auto_connect =
- parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+ parse_service_types(default_auto_connect,
+ CONF_ARRAY_SIZE(default_auto_connect));
connman_settings.favorite_techs =
- parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs));
+ parse_service_types(default_favorite_techs,
+ CONF_ARRAY_SIZE(default_favorite_techs));
connman_settings.blacklisted_interfaces =
g_strdupv(default_blacklist);
+ connman_settings.online_check_ipv4_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+ connman_settings.online_check_ipv6_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
return;
}
@@ -320,6 +371,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",
@@ -369,17 +422,17 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_INPUTREQ, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_inputreq = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_inputreq = integer * 1000;
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_BROWSERLAUNCH, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_browserlaunch = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_browserlaunch = integer * 1000;
g_clear_error(&error);
@@ -440,10 +493,10 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
- vendor_class_id = __connman_config_get_string(config, "General",
+ string = __connman_config_get_string(config, "General",
CONF_VENDOR_CLASS_ID, &error);
if (!error)
- connman_settings.vendor_class_id = vendor_class_id;
+ connman_settings.vendor_class_id = string;
g_clear_error(&error);
@@ -458,6 +511,61 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
boolean = __connman_config_get_bool(config, "General",
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION, &error);
+ if (!error) {
+ connman_settings.enable_online_to_ready_transition = boolean;
+ }
+
+ g_clear_error(&error);
+
+ 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)
+ connman_settings.online_check_initial_interval = integer;
+
+ g_clear_error(&error);
+
+ integer = g_key_file_get_integer(config, "General",
+ CONF_ONLINE_CHECK_MAX_INTERVAL, &error);
+ if (!error && integer >= 0)
+ connman_settings.online_check_max_interval = integer;
+
+ g_clear_error(&error);
+
+ if (connman_settings.online_check_initial_interval < 1 ||
+ connman_settings.online_check_initial_interval >
+ connman_settings.online_check_max_interval) {
+ connman_warn("Incorrect online check intervals [%u, %u]",
+ connman_settings.online_check_initial_interval,
+ connman_settings.online_check_max_interval);
+ connman_settings.online_check_initial_interval =
+ DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL;
+ connman_settings.online_check_max_interval =
+ DEFAULT_ONLINE_CHECK_MAX_INTERVAL;
+ }
+
+ boolean = __connman_config_get_bool(config, "General",
CONF_AUTO_CONNECT_ROAMING_SERVICES, &error);
if (!error)
connman_settings.auto_connect_roaming_services = boolean;
@@ -476,6 +584,29 @@ static void parse_config(GKeyFile *config)
connman_settings.use_gateways_as_timeservers = boolean;
g_clear_error(&error);
+
+ string = __connman_config_get_string(config, "General",
+ CONF_LOCALTIME, &error);
+ if (!error)
+ connman_settings.localtime = string;
+ else
+ g_free(string);
+
+ g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General",
+ CONF_REGDOM_FOLLOWS_TIMEZONE, &error);
+ if (!error)
+ connman_settings.regdom_follows_timezone = boolean;
+
+ string = __connman_config_get_string(config, "General",
+ CONF_RESOLV_CONF, &error);
+ if (!error)
+ connman_settings.resolv_conf = string;
+ else
+ g_free(string);
+
+ g_clear_error(&error);
}
static int config_init(const char *file)
@@ -648,11 +779,17 @@ static GOptionEntry options[] = {
{ NULL },
};
-const char *connman_option_get_string(const char *key)
+char *connman_setting_get_string(const char *key)
{
if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
return connman_settings.vendor_class_id;
+ 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";
@@ -660,6 +797,10 @@ const char *connman_option_get_string(const char *key)
return option_wifi;
}
+ if (g_str_equal(key, CONF_LOCALTIME))
+ return connman_settings.localtime ?
+ connman_settings.localtime : DEFAULT_LOCALTIME;
+
return NULL;
}
@@ -686,6 +827,9 @@ bool connman_setting_get_bool(const char *key)
if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK))
return connman_settings.enable_online_check;
+ if (g_str_equal(key, CONF_ENABLE_ONLINE_TO_READY_TRANSITION))
+ return connman_settings.enable_online_to_ready_transition;
+
if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES))
return connman_settings.auto_connect_roaming_services;
@@ -695,9 +839,26 @@ bool connman_setting_get_bool(const char *key)
if (g_str_equal(key, CONF_USE_GATEWAYS_AS_TIMESERVERS))
return connman_settings.use_gateways_as_timeservers;
+ if (g_str_equal(key, CONF_REGDOM_FOLLOWS_TIMEZONE))
+ return connman_settings.regdom_follows_timezone;
+
+ if (g_str_equal(key, CONF_RESOLV_CONF))
+ return connman_settings.resolv_conf;
+
return false;
}
+unsigned int connman_setting_get_uint(const char *key)
+{
+ if (g_str_equal(key, CONF_ONLINE_CHECK_INITIAL_INTERVAL))
+ return connman_settings.online_check_initial_interval;
+
+ if (g_str_equal(key, CONF_ONLINE_CHECK_MAX_INTERVAL))
+ return connman_settings.online_check_max_interval;
+
+ return 0;
+}
+
char **connman_setting_get_string_list(const char *key)
{
if (g_str_equal(key, CONF_PREF_TIMESERVERS))
@@ -919,6 +1080,10 @@ int main(int argc, char *argv[])
g_strfreev(connman_settings.fallback_nameservers);
g_strfreev(connman_settings.blacklisted_interfaces);
g_strfreev(connman_settings.tethering_technologies);
+ g_free(connman_settings.vendor_class_id);
+ g_free(connman_settings.online_check_ipv4_url);
+ g_free(connman_settings.online_check_ipv6_url);
+ g_free(connman_settings.localtime);
g_free(option_debug);
g_free(option_wifi);
diff --git a/src/main.conf b/src/main.conf
index 14965e12..ddd57996 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -125,6 +125,31 @@
# 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.
+# Please refer to the README for more detailed information.
+# Default values are 1 and 12 respectively.
+# OnlineCheckInitialInterval = 1
+# OnlineCheckMaxInterval = 12
+
+# WARNING: Experimental feature!!!
+# In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET
+# to detect the loss of end-to-end connectivity.
+# If this setting is false, when the default service transitions to ONLINE
+# state, the HTTP GET request is no more called until next cycle, initiated
+# by a transition of the default service to DISCONNECT state.
+# If this setting is true, the HTTP GET request keeps beeing called to guarantee
+# that end-to-end connectivity is still successful. If not, the default service
+# will transition to READY state, enabling another service to become the
+# default one, in replacement.
+# EnableOnlineToReadyTransition = false
+
# List of technologies with AutoConnect = true which are always connected
# regardless of PreferredTechnologies setting. Default value is empty and
# will connect a technology only if it is at a higher preference than any
diff --git a/src/manager.c b/src/manager.c
index 3bf8f4e4..892d3a42 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -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" })) },
{ },
};
diff --git a/src/network.c b/src/network.c
index f2ab16bd..e3e02d16 100644
--- a/src/network.c
+++ b/src/network.c
@@ -940,7 +940,7 @@ static void set_disconnected(struct connman_network *network)
{
struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
enum connman_ipconfig_method ipv4_method, ipv6_method;
- enum connman_service_state state;
+ enum connman_service_state state_ipv4, state_ipv6;
struct connman_service *service;
service = connman_service_lookup_from_network(network);
@@ -1006,18 +1006,18 @@ static void set_disconnected(struct connman_network *network)
* or in failure. It does not make sense to go to disconnect
* state if we were not connected.
*/
- state = __connman_service_ipconfig_get_state(service,
+ state_ipv4 = __connman_service_ipconfig_get_state(service,
CONNMAN_IPCONFIG_TYPE_IPV4);
- if (state != CONNMAN_SERVICE_STATE_IDLE &&
- state != CONNMAN_SERVICE_STATE_FAILURE)
+ if (state_ipv4 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_DISCONNECT,
CONNMAN_IPCONFIG_TYPE_IPV4);
- state = __connman_service_ipconfig_get_state(service,
+ state_ipv6 = __connman_service_ipconfig_get_state(service,
CONNMAN_IPCONFIG_TYPE_IPV6);
- if (state != CONNMAN_SERVICE_STATE_IDLE &&
- state != CONNMAN_SERVICE_STATE_FAILURE)
+ if (state_ipv6 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv6 != CONNMAN_SERVICE_STATE_FAILURE)
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_DISCONNECT,
CONNMAN_IPCONFIG_TYPE_IPV6);
@@ -1041,11 +1041,15 @@ static void set_disconnected(struct connman_network *network)
}
}
- __connman_service_ipconfig_indicate_state(service,
+ if (state_ipv4 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV4);
- __connman_service_ipconfig_indicate_state(service,
+ if (state_ipv6 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv6 != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV6);
@@ -1797,8 +1801,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);
@@ -1850,6 +1852,19 @@ int __connman_network_disconnect(struct connman_network *network)
return err;
}
+int __connman_network_forget(struct connman_network *network)
+{
+ DBG("network %p", network);
+
+ if (!network->driver)
+ return -EUNATCH;
+
+ if (network->driver->forget)
+ return network->driver->forget(network);
+
+ return 0;
+}
+
int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig)
{
@@ -2112,6 +2127,21 @@ int connman_network_set_wifi_channel(struct connman_network *network,
return 0;
}
+int connman_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return 0;
+ return network->driver->set_autoconnect(network, autoconnect);
+}
+
+bool __connman_network_native_autoconnect(struct connman_network *network)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return false;
+ return true;
+}
+
uint16_t connman_network_get_wifi_channel(struct connman_network *network)
{
return network->wifi.channel;
diff --git a/src/ntp.c b/src/ntp.c
index e7fee22a..5d0df9e5 100644
--- a/src/ntp.c
+++ b/src/ntp.c
@@ -577,7 +577,7 @@ int __connman_ntp_start(char *server, __connman_ntp_cb_t callback,
return -EINVAL;
if (ntp_data) {
- connman_warn("ntp_data is not NULL (timerserver %s)",
+ connman_warn("ntp_data is not NULL (timeserver %s)",
ntp_data->timeserver);
free_ntp_data(ntp_data);
}
diff --git a/src/peer.c b/src/peer.c
index 2102f119..bad5c841 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -154,7 +154,7 @@ static int start_dhcp_server(struct connman_peer *peer)
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- gateway, NULL, prefixlen, broadcast);
+ gateway, NULL, prefixlen, broadcast, true);
if (err < 0)
goto error;
@@ -983,7 +983,10 @@ void connman_peer_add_service(struct connman_peer *peer,
service = g_malloc0(sizeof(struct _peer_service));
service->type = type;
- service->data = g_memdup(data, data_length * sizeof(unsigned char));
+ if (data_length > 0) {
+ service->data = g_malloc(data_length * sizeof(unsigned char));
+ memcpy(service->data, data, data_length * sizeof(unsigned char));
+ }
service->length = data_length;
peer->services = g_slist_prepend(peer->services, service);
diff --git a/src/provider.c b/src/provider.c
index 7d663e0c..e2091846 100644
--- a/src/provider.c
+++ b/src/provider.c
@@ -53,6 +53,7 @@ void __connman_provider_append_properties(struct connman_provider *provider,
DBusMessageIter *iter)
{
const char *host, *domain, *type;
+ dbus_bool_t split_routing;
if (!provider->driver || !provider->driver->get_property)
return;
@@ -72,6 +73,12 @@ void __connman_provider_append_properties(struct connman_provider *provider,
if (type)
connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
&type);
+
+ if (provider->vpn_service) {
+ split_routing = connman_provider_is_split_routing(provider);
+ connman_dbus_dict_append_basic(iter, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing);
+ }
}
struct connman_provider *
@@ -435,6 +442,15 @@ const char *__connman_provider_get_ident(struct connman_provider *provider)
return provider->identifier;
}
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider)
+{
+ if (provider && provider && provider->driver && provider->driver->get_property)
+ return provider->driver->get_property(provider, "Transport");
+
+ return NULL;
+}
+
int connman_provider_set_string(struct connman_provider *provider,
const char *key, const char *value)
{
@@ -597,6 +613,100 @@ void connman_provider_set_autoconnect(struct connman_provider *provider,
__connman_service_save(provider->vpn_service);
}
+bool connman_provider_is_split_routing(struct connman_provider *provider)
+{
+ if (!provider || !provider->vpn_service)
+ return false;
+
+ return __connman_service_is_split_routing(provider->vpn_service);
+}
+
+int connman_provider_set_split_routing(struct connman_provider *provider,
+ bool split_routing)
+{
+ struct connman_service *service;
+ enum connman_ipconfig_type type;
+ int service_index;
+ int vpn_index;
+ bool service_split_routing;
+ int err = 0;
+
+ DBG("");
+
+ if (!provider || !provider->vpn_service)
+ return -EINVAL;
+
+ service_split_routing = __connman_service_is_split_routing(
+ provider->vpn_service);
+
+ if (service_split_routing == split_routing) {
+ DBG("split_routing already set %s",
+ split_routing ? "true" : "false");
+ return -EALREADY;
+ }
+
+ switch (provider->family) {
+ case AF_INET:
+ type = CONNMAN_IPCONFIG_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ type = CONNMAN_IPCONFIG_TYPE_IPV6;
+ break;
+ case AF_UNSPEC:
+ type = CONNMAN_IPCONFIG_TYPE_ALL;
+ break;
+ default:
+ type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+ }
+
+ if (!__connman_service_is_connected_state(provider->vpn_service,
+ type)) {
+ DBG("%p VPN not connected", provider->vpn_service);
+ goto save;
+ }
+
+ vpn_index = __connman_service_get_index(provider->vpn_service);
+ service_index = __connman_connection_get_vpn_phy_index(vpn_index);
+ service = __connman_service_lookup_from_index(service_index);
+ if (!service)
+ goto save;
+
+ if (split_routing)
+ err = __connman_service_move(service, provider->vpn_service,
+ true);
+ else
+ err = __connman_service_move(provider->vpn_service, service,
+ true);
+
+ if (err) {
+ connman_warn("cannot move service %p and VPN %p error %d",
+ service, provider->vpn_service, err);
+
+ /*
+ * In case of error notify vpnd about the current split routing
+ * state.
+ */
+ __connman_service_split_routing_changed(provider->vpn_service);
+ goto out;
+ }
+
+save:
+ __connman_service_set_split_routing(provider->vpn_service,
+ split_routing);
+ __connman_service_save(provider->vpn_service);
+
+out:
+ return err;
+}
+
+int connman_provider_get_family(struct connman_provider *provider)
+{
+ if (!provider)
+ return AF_UNSPEC;
+
+ return provider->family;
+}
+
static void unregister_provider(gpointer data)
{
struct connman_provider *provider = data;
diff --git a/src/resolver.c b/src/resolver.c
index 618353fd..4ab51d6b 100644
--- a/src/resolver.c
+++ b/src/resolver.c
@@ -103,6 +103,7 @@ static int resolvfile_export(void)
int fd, err;
unsigned int count;
mode_t old_umask;
+ const char *resolv_conf;
content = g_string_new("# Generated by Connection Manager\n");
@@ -161,15 +162,33 @@ static int resolvfile_export(void)
old_umask = umask(022);
- fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd < 0) {
- connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" "
- "falling back to "RESOLV_CONF_ETC);
+ resolv_conf = connman_setting_get_string("ResolvConf");
+ /*
+ * TODO: This is mainly for backward compatibility. In some future version,
+ * "ResolvConf" setting should default to RESOLV_CONF_STATEDIR or
+ * RESOLV_CONF_ETC and this branch can be removed.
+ */
+ if (resolv_conf == NULL) {
+ fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" "
+ "falling back to "RESOLV_CONF_ETC);
- fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ err = -errno;
+ goto done;
+ }
+ }
+ } else if (resolv_conf[0] == '\0' || strcmp(resolv_conf, "/dev/null") == 0) {
+ err = 0;
+ goto done;
+ } else {
+ fd = open(resolv_conf, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
err = -errno;
goto done;
diff --git a/src/rtnl.c b/src/rtnl.c
index dfe6bb69..e8a8325e 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -177,6 +177,9 @@ static void read_uevent(struct interface_data *interface)
} else if (strcmp(line + 8, "bond") == 0) {
interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+ } else if (strcmp(line + 8, "dsa") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
} else {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
@@ -883,7 +886,7 @@ static inline void print_attr(struct rtattr *attr, const char *name)
print(" attr %d (len %d)\n", attr->rta_type, len);
}
-static void rtnl_link(struct nlmsghdr *hdr)
+static void rtnl_link(struct nlmsghdr *hdr, bool *has_master)
{
struct ifinfomsg *msg;
struct rtattr *attr;
@@ -925,6 +928,7 @@ static void rtnl_link(struct nlmsghdr *hdr)
print_attr(attr, "priority");
break;
case IFLA_MASTER:
+ *has_master = true;
print_attr(attr, "master");
break;
case IFLA_WIRELESS:
@@ -957,22 +961,32 @@ static void rtnl_link(struct nlmsghdr *hdr)
static void rtnl_newlink(struct nlmsghdr *hdr)
{
+ bool has_master = false;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- rtnl_link(hdr);
+ rtnl_link(hdr, &has_master);
if (hdr->nlmsg_type == IFLA_WIRELESS)
connman_warn_once("Obsolete WEXT WiFi driver detected");
+ /* ignore RTM_NEWLINK when adding interface to bridge */
+ if (has_master)
+ return;
+
process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
msg->ifi_change, msg, IFA_PAYLOAD(hdr));
}
static void rtnl_dellink(struct nlmsghdr *hdr)
{
+ bool has_master = false;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- rtnl_link(hdr);
+ rtnl_link(hdr, &has_master);
+
+ /* ignore RTM_DELLINK when removing interface from bridge */
+ if (has_master)
+ return;
process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
msg->ifi_change, msg, IFA_PAYLOAD(hdr));
@@ -1305,75 +1319,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)
@@ -1471,62 +1481,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)
@@ -1670,14 +1683,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;
}
diff --git a/src/service.c b/src/service.c
index 2f497d10..06d02322 100644
--- a/src/service.c
+++ b/src/service.c
@@ -49,11 +49,15 @@ 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;
static struct connman_service *current_default = NULL;
static bool services_dirty = false;
+static bool enable_online_to_ready_transition = false;
+static unsigned int online_check_initial_interval = 0;
+static unsigned int online_check_max_interval = 0;
struct connman_stats {
bool valid;
@@ -133,9 +137,10 @@ struct connman_service {
char *pac;
bool wps;
bool wps_advertizing;
- guint online_timeout;
- int online_check_interval_ipv4;
- int online_check_interval_ipv6;
+ guint online_timeout_ipv4;
+ guint online_timeout_ipv6;
+ unsigned int online_check_interval_ipv4;
+ unsigned int online_check_interval_ipv6;
bool do_split_routing;
bool new_service;
bool hidden_service;
@@ -151,6 +156,7 @@ static struct connman_ipconfig *create_ip6config(struct connman_service *service
int index);
static void dns_changed(struct connman_service *service);
static void vpn_auto_connect(void);
+static void trigger_autoconnect(struct connman_service *service);
struct find_data {
const char *path;
@@ -192,6 +198,8 @@ static const char *reason2string(enum connman_service_connect_reason reason)
return "auto";
case CONNMAN_SERVICE_CONNECT_REASON_SESSION:
return "session";
+ case CONNMAN_SERVICE_CONNECT_REASON_NATIVE:
+ return "native";
}
return "unknown";
@@ -367,7 +375,26 @@ static enum connman_service_proxy_method string2proxymethod(const char *method)
return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
}
-static void set_split_routing(struct connman_service *service, bool value)
+void __connman_service_split_routing_changed(struct connman_service *service)
+{
+ dbus_bool_t split_routing;
+
+ if (!service->path)
+ return;
+
+ if (!allow_property_changed(service))
+ return;
+
+ split_routing = service->do_split_routing;
+ if (!connman_dbus_property_changed_basic(service->path,
+ CONNMAN_SERVICE_INTERFACE, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing))
+ connman_warn("cannot send SplitRouting property change on %s",
+ service->identifier);
+}
+
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool value)
{
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
return;
@@ -378,6 +405,12 @@ static void set_split_routing(struct connman_service *service, bool value)
service->order = 0;
else
service->order = 10;
+
+ /*
+ * In order to make sure the value is propagated also when loading the
+ * VPN service signal the value regardless of the value change.
+ */
+ __connman_service_split_routing_changed(service);
}
int __connman_service_load_modifiable(struct connman_service *service)
@@ -400,9 +433,10 @@ int __connman_service_load_modifiable(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
/* fall through */
case CONNMAN_SERVICE_TYPE_WIFI:
@@ -456,9 +490,10 @@ static int service_load(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
autoconnect = g_key_file_get_boolean(keyfile,
service->identifier, "AutoConnect", &error);
@@ -545,8 +580,10 @@ static int service_load(struct connman_service *service)
str = g_key_file_get_string(keyfile,
service->identifier, "Passphrase", NULL);
if (str) {
+ char *dec = g_strcompress(str);
+ g_free(str);
g_free(service->passphrase);
- service->passphrase = str;
+ service->passphrase = dec;
}
if (service->ipconfig_ipv4)
@@ -709,9 +746,12 @@ static int service_save(struct connman_service *service)
g_free(str);
}
- if (service->passphrase && strlen(service->passphrase) > 0)
+ if (service->passphrase && strlen(service->passphrase) > 0) {
+ char *enc = g_strescape(service->passphrase, NULL);
g_key_file_set_string(keyfile, service->identifier,
- "Passphrase", service->passphrase);
+ "Passphrase", enc);
+ g_free(enc);
+ }
if (service->ipconfig_ipv4)
__connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
@@ -1400,12 +1440,16 @@ static bool check_proxy_setup(struct connman_service *service)
static void cancel_online_check(struct connman_service *service)
{
- if (service->online_timeout == 0)
- return;
-
- g_source_remove(service->online_timeout);
- service->online_timeout = 0;
- connman_service_unref(service);
+ if (service->online_timeout_ipv4) {
+ g_source_remove(service->online_timeout_ipv4);
+ service->online_timeout_ipv4 = 0;
+ connman_service_unref(service);
+ }
+ if (service->online_timeout_ipv6) {
+ g_source_remove(service->online_timeout_ipv6);
+ service->online_timeout_ipv6 = 0;
+ connman_service_unref(service);
+ }
}
static void start_online_check(struct connman_service *service,
@@ -1416,8 +1460,14 @@ static void start_online_check(struct connman_service *service,
"Default service remains in READY state.");
return;
}
+ enable_online_to_ready_transition =
+ connman_setting_get_bool("EnableOnlineToReadyTransition");
+ online_check_initial_interval =
+ connman_setting_get_uint("OnlineCheckInitialInterval");
+ online_check_max_interval =
+ connman_setting_get_uint("OnlineCheckMaxInterval");
- if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) {
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV6 || check_proxy_setup(service)) {
cancel_online_check(service);
__connman_service_wispr_start(service, type);
}
@@ -1432,7 +1482,8 @@ static void address_updated(struct connman_service *service,
nameserver_add_all(service, type);
start_online_check(service, type);
- __connman_timeserver_sync(service);
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_ADDRESS_UPDATE);
}
}
@@ -1551,6 +1602,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();
@@ -1575,6 +1645,8 @@ static void default_changed(void)
connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
+ start_wispr_when_connected(service);
+
/*
* Connect VPN automatically when new default service
* is set and connected, unless new default is VPN
@@ -1693,6 +1765,8 @@ bool connman_service_set_autoconnect(struct connman_service *service,
service->autoconnect = autoconnect;
autoconnect_changed(service);
+ connman_network_set_autoconnect(service->network, autoconnect);
+
return true;
}
@@ -2525,7 +2599,6 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
case CONNMAN_SERVICE_TYPE_UNKNOWN:
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_CELLULAR:
@@ -2536,6 +2609,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
connman_dbus_dict_append_dict(dict, "Ethernet",
append_ethernet, service);
break;
+ case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_BLUETOOTH:
@@ -3167,6 +3241,9 @@ int __connman_service_check_passphrase(enum connman_service_security security,
return 0;
}
+static void set_error(struct connman_service *service,
+ enum connman_service_error error);
+
int __connman_service_set_passphrase(struct connman_service *service,
const char *passphrase)
{
@@ -3191,6 +3268,10 @@ int __connman_service_set_passphrase(struct connman_service *service,
connman_network_set_string(service->network, "WiFi.Passphrase",
service->passphrase);
+ if (service->hidden_service &&
+ service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY)
+ set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+
return 0;
}
@@ -3428,15 +3509,28 @@ static void do_auto_connect(struct connman_service *service,
return;
/*
+ * Only user interaction should get VPN or WIFI connected in failure
+ * state.
+ */
+ if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
+ reason != CONNMAN_SERVICE_CONNECT_REASON_USER &&
+ (service->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service->type == CONNMAN_SERVICE_TYPE_WIFI))
+ return;
+
+ /*
+ * Do not use the builtin auto connect, instead rely on the
+ * native auto connect feature of the service.
+ */
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE)
+ return;
+
+ /*
* Run service auto connect for other than VPN services. Afterwards
* start also VPN auto connect process.
*/
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
__connman_service_auto_connect(reason);
- /* Only user interaction should get VPN connected in failure state. */
- else if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
- reason != CONNMAN_SERVICE_CONNECT_REASON_USER)
- return;
vpn_auto_connect();
}
@@ -3515,14 +3609,6 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
return err;
}
-/*
- * We set the timeout to 1 sec so that we have a chance to get
- * necessary IPv6 router advertisement messages that might have
- * DNS data etc.
- */
-#define ONLINE_CHECK_INITIAL_INTERVAL 1
-#define ONLINE_CHECK_MAX_INTERVAL 12
-
void __connman_service_wispr_start(struct connman_service *service,
enum connman_ipconfig_type type)
{
@@ -3530,17 +3616,14 @@ void __connman_service_wispr_start(struct connman_service *service,
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
service->online_check_interval_ipv4 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
else
service->online_check_interval_ipv6 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
__connman_wispr_start(service, type);
}
-static void set_error(struct connman_service *service,
- enum connman_service_error error);
-
static DBusMessage *set_property(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -3665,13 +3748,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")) {
@@ -3717,9 +3794,7 @@ static DBusMessage *set_property(DBusConnection *conn,
service_save(service);
timeservers_configuration_changed(service);
-
- if (service == connman_service_get_default())
- __connman_timeserver_sync(service);
+ __connman_timeserver_conf_update(service);
} else if (g_str_equal(name, "Domains.Configuration")) {
DBusMessageIter entry;
@@ -4151,6 +4226,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));
@@ -4166,6 +4242,17 @@ static bool auto_connect_service(GList *services,
continue;
}
+ if (service->connect_reason ==
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
+ DBG("service %p uses native autonnect, skip", service);
+ 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)) {
@@ -4486,7 +4573,10 @@ static DBusMessage *connect_service(DBusConnection *conn,
DBG("service %p", service);
- if (service->pending)
+ /* Hidden services do not keep the pending msg, check it from agent */
+ if (service->pending || (service->hidden &&
+ __connman_agent_is_request_pending(service,
+ dbus_message_get_sender(msg))))
return __connman_error_in_progress(msg);
index = __connman_service_get_index(service);
@@ -4495,7 +4585,7 @@ static DBusMessage *connect_service(DBusConnection *conn,
struct connman_service *temp = list->data;
if (!is_connecting(temp->state) && !is_connected(temp->state))
- break;
+ continue;
if (service == temp)
continue;
@@ -4555,6 +4645,8 @@ bool __connman_service_remove(struct connman_service *service)
return false;
__connman_service_disconnect(service);
+ if (service->network)
+ __connman_network_forget(service->network);
g_free(service->passphrase);
service->passphrase = NULL;
@@ -4647,15 +4739,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,
@@ -4762,27 +4850,22 @@ static void service_schedule_changed(void)
services_notify->id = g_timeout_add(100, service_send_changed, NULL);
}
-static DBusMessage *move_service(DBusConnection *conn,
- DBusMessage *msg, void *user_data,
- bool before)
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before)
{
- struct connman_service *service = user_data;
- struct connman_service *target;
- const char *path;
enum connman_ipconfig_method target4, target6;
enum connman_ipconfig_method service4, service6;
DBG("service %p", service);
- dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
+ if (!service)
+ return -EINVAL;
if (!service->favorite)
- return __connman_error_not_supported(msg);
+ return -EOPNOTSUPP;
- target = find_service(path);
if (!target || !target->favorite || target == service)
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
if (target->type == CONNMAN_SERVICE_TYPE_VPN) {
/*
@@ -4793,14 +4876,14 @@ static DBusMessage *move_service(DBusConnection *conn,
connman_info("Cannot move service. "
"No routes defined for provider %s",
__connman_provider_get_ident(target->provider));
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
- set_split_routing(target, true);
+ __connman_service_set_split_routing(target, true);
} else
- set_split_routing(target, false);
+ __connman_service_set_split_routing(target, false);
- set_split_routing(service, false);
+ __connman_service_set_split_routing(service, false);
target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
@@ -4823,7 +4906,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4831,7 +4914,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4839,7 +4922,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4847,7 +4930,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4870,6 +4953,39 @@ static DBusMessage *move_service(DBusConnection *conn,
service_schedule_changed();
+ return 0;
+}
+
+static DBusMessage *move_service(DBusConnection *conn,
+ DBusMessage *msg, void *user_data,
+ bool before)
+{
+ struct connman_service *service = user_data;
+ struct connman_service *target;
+ const char *path;
+ int err;
+
+ DBG("service %p", service);
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ target = find_service(path);
+
+ err = __connman_service_move(service, target, before);
+ switch (err) {
+ case 0:
+ break;
+ case -EINVAL:
+ return __connman_error_invalid_service(msg);
+ case -EOPNOTSUPP:
+ return __connman_error_not_supported(msg);
+ default:
+ connman_warn("unsupported error code %d in move_service()",
+ err);
+ break;
+ }
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
@@ -5190,6 +5306,59 @@ void connman_service_unref_debug(struct connman_service *service,
g_hash_table_remove(service_hash, service->identifier);
}
+static gint service_compare(gconstpointer a, gconstpointer b);
+
+static gint service_compare_vpn(struct connman_service *a,
+ struct connman_service *b)
+{
+ struct connman_provider *provider;
+ struct connman_service *service;
+ struct connman_service *transport;
+ const char *ident;
+ bool reverse;
+
+ if (a->provider) {
+ provider = a->provider;
+ service = b;
+ reverse = false;
+ } else if (b->provider) {
+ provider = b->provider;
+ service = a;
+ reverse = true;
+ } else {
+ return 0;
+ }
+
+ ident = __connman_provider_get_transport_ident(provider);
+ transport = connman_service_lookup_from_identifier(ident);
+ if (!transport)
+ return 0;
+
+ if (reverse)
+ return service_compare(service, transport);
+
+ return service_compare(transport, service);
+}
+
+static gint service_compare_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;
@@ -5204,11 +5373,26 @@ static gint service_compare(gconstpointer a, gconstpointer b)
b_connected = is_connected(state_b);
if (a_connected && b_connected) {
+ int rval;
+
+ /* Compare the VPN transport and the service */
+ if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service_b->type == CONNMAN_SERVICE_TYPE_VPN) &&
+ service_b->type != service_a->type) {
+ rval = service_compare_vpn(service_a, service_b);
+ if (rval)
+ return rval;
+ }
+
if (service_a->order > service_b->order)
return -1;
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) {
@@ -5239,20 +5423,11 @@ static gint service_compare(gconstpointer a, gconstpointer b)
return 1;
if (service_a->type != service_b->type) {
- unsigned int *tech_array;
- int i;
+ int rval;
- 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;
- }
- }
+ rval = service_compare_preferred(service_a, service_b);
+ if (rval)
+ return rval;
if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET)
return -1;
@@ -5454,6 +5629,9 @@ int __connman_service_set_favorite_delayed(struct connman_service *service,
service->favorite = favorite;
favorite_changed(service);
+ /* If native autoconnect is in use, the favorite state may affect the
+ * autoconnect state, so it needs to be rerun. */
+ trigger_autoconnect(service);
if (!delay_ordering) {
@@ -5596,6 +5774,7 @@ static void report_error_cb(void *user_context, bool retry,
__connman_service_clear_error(service);
service_complete(service);
+ service_list_sort();
__connman_connection_update_gateway();
}
}
@@ -5642,6 +5821,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);
@@ -5667,14 +5847,18 @@ static void request_input_cb(struct connman_service *service,
goto done;
}
- if (service->hidden && name_len > 0 && name_len <= 32) {
- device = connman_network_get_device(service->network);
- security = connman_network_get_string(service->network,
- "WiFi.Security");
- err = __connman_device_request_hidden_scan(device,
- name, name_len,
- identity, passphrase,
- security, user_data);
+ if (service->hidden) {
+ if (name_len > 0 && name_len <= 32) {
+ device = connman_network_get_device(service->network);
+ security = connman_network_get_string(service->network,
+ "WiFi.Security");
+ err = __connman_device_request_hidden_scan(device,
+ name, name_len,
+ identity, passphrase,
+ security, user_data);
+ } else {
+ err = -EINVAL;
+ }
if (err < 0)
__connman_service_return_error(service, -err,
user_data);
@@ -5700,6 +5884,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);
@@ -5753,27 +5941,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;
@@ -5867,6 +6042,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);
@@ -5909,12 +6093,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);
@@ -5983,12 +6167,14 @@ 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,
get_dbus_sender(service),
NULL);
+ goto notifier;
}
service_complete(service);
break;
@@ -5998,6 +6184,7 @@ static int service_indicate_state(struct connman_service *service)
__connman_connection_update_gateway();
+notifier:
if ((old_state == CONNMAN_SERVICE_STATE_ONLINE &&
new_state != CONNMAN_SERVICE_STATE_READY) ||
(old_state == CONNMAN_SERVICE_STATE_READY &&
@@ -6156,20 +6343,20 @@ static void service_rp_filter(struct connman_service *service,
static void redo_wispr(struct connman_service *service,
enum connman_ipconfig_type type)
{
- service->online_timeout = 0;
- connman_service_unref(service);
-
DBG("Retrying %s WISPr for %p %s",
__connman_ipconfig_type2string(type),
service, service->name);
__connman_wispr_start(service, type);
+ connman_service_unref(service);
}
static gboolean redo_wispr_ipv4(gpointer user_data)
{
struct connman_service *service = user_data;
+ service->online_timeout_ipv4 = 0;
+
redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV4);
return FALSE;
@@ -6179,16 +6366,24 @@ static gboolean redo_wispr_ipv6(gpointer user_data)
{
struct connman_service *service = user_data;
+ service->online_timeout_ipv6 = 0;
+
redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV6);
return FALSE;
}
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type)
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success)
{
GSourceFunc redo_func;
- int *interval;
+ unsigned int *interval;
+ enum connman_service_state current_state;
+ int timeout;
+
+ DBG("service %p type %s success %d\n",
+ service, __connman_ipconfig_type2string(type), success);
if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
interval = &service->online_check_interval_ipv4;
@@ -6198,19 +6393,37 @@ int __connman_service_online_check_failed(struct connman_service *service,
redo_func = redo_wispr_ipv6;
}
+ if(!enable_online_to_ready_transition)
+ goto redo_func;
+
+ if (success) {
+ *interval = online_check_max_interval;
+ } else {
+ current_state = service->state;
+ downgrade_state(service);
+ if (current_state != service->state)
+ *interval = online_check_initial_interval;
+ if (service != connman_service_get_default()) {
+ return;
+ }
+ }
+
+redo_func:
DBG("service %p type %s interval %d", service,
__connman_ipconfig_type2string(type), *interval);
- service->online_timeout = g_timeout_add_seconds(*interval * *interval,
+ timeout = g_timeout_add_seconds(*interval * *interval,
redo_func, connman_service_ref(service));
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+ service->online_timeout_ipv4 = timeout;
+ else
+ service->online_timeout_ipv6 = timeout;
/* Increment the interval for the next time, set a maximum timeout of
- * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds.
+ * online_check_max_interval seconds * online_check_max_interval seconds.
*/
- if (*interval < ONLINE_CHECK_MAX_INTERVAL)
+ if (*interval < online_check_max_interval)
(*interval)++;
-
- return EAGAIN;
}
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
@@ -6317,7 +6530,8 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
if (!is_connected(old_state) && is_connected(new_state))
nameserver_add_all(service, type);
- __connman_timeserver_sync(service);
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_STATE_UPDATE);
return service_indicate_state(service);
}
@@ -6533,6 +6747,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",
@@ -6567,6 +6782,12 @@ int __connman_service_connect(struct connman_service *service,
__connman_service_clear_error(service);
+ if (service->network && service->autoconnect &&
+ __connman_network_native_autoconnect(service->network)) {
+ DBG("service %p switch connecting reason to native", service);
+ reason = CONNMAN_SERVICE_CONNECT_REASON_NATIVE;
+ }
+
err = service_connect(service);
DBG("service %p err %d", service, err);
@@ -6590,7 +6811,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);
@@ -6606,6 +6828,13 @@ int __connman_service_connect(struct connman_service *service,
service->pending = NULL;
}
+ if (service->hidden_service &&
+ service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+ __connman_service_indicate_error(service,
+ CONNMAN_SERVICE_ERROR_INVALID_KEY);
+ return err;
+ }
+
err = __connman_agent_request_passphrase_input(service,
request_input_cb,
dbus_sender,
@@ -6613,6 +6842,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;
}
}
@@ -6665,36 +6901,6 @@ int __connman_service_disconnect(struct connman_service *service)
return err;
}
-int __connman_service_disconnect_all(void)
-{
- struct connman_service *service;
- GSList *services = NULL, *list;
- GList *iter;
-
- DBG("");
-
- for (iter = service_list; iter; iter = iter->next) {
- service = iter->data;
-
- if (!is_connected(service->state))
- break;
-
- services = g_slist_prepend(services, service);
- }
-
- for (list = services; list; list = list->next) {
- struct connman_service *service = list->data;
-
- service->ignore = true;
-
- __connman_service_disconnect(service);
- }
-
- g_slist_free(services);
-
- return 0;
-}
-
/**
* lookup_by_identifier:
* @identifier: service identifier
@@ -6810,14 +7016,14 @@ static int service_register(struct connman_service *service)
DBG("path %s", service->path);
- if (__connman_config_provision_service(service) < 0)
- service_load(service);
-
g_dbus_register_interface(connection, service->path,
CONNMAN_SERVICE_INTERFACE,
service_methods, service_signals,
NULL, service, NULL);
+ if (__connman_config_provision_service(service) < 0)
+ service_load(service);
+
service_list_sort();
__connman_connection_update_gateway();
@@ -7222,6 +7428,50 @@ static void update_from_network(struct connman_service *service,
service_list_sort();
}
+static void trigger_autoconnect(struct connman_service *service)
+{
+ struct connman_device *device;
+ bool native;
+
+ if (!service->favorite)
+ return;
+
+ native = __connman_network_native_autoconnect(service->network);
+ if (native && service->autoconnect) {
+ DBG("trigger native autoconnect");
+ connman_network_set_autoconnect(service->network, true);
+ return;
+ }
+
+ device = connman_network_get_device(service->network);
+ if (device && connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_UNKNOWN))
+ return;
+
+ switch (service->type) {
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_P2P:
+ break;
+
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ if (service->autoconnect) {
+ __connman_service_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+
+ /* fall through */
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+}
+
/**
* __connman_service_create_from_network:
* @network: network structure
@@ -7231,7 +7481,6 @@ static void update_from_network(struct connman_service *service,
struct connman_service * __connman_service_create_from_network(struct connman_network *network)
{
struct connman_service *service;
- struct connman_device *device;
const char *ident, *group;
char *name;
unsigned int *auto_connect_types, *favorite_types;
@@ -7261,8 +7510,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;
}
@@ -7293,49 +7553,21 @@ 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);
- if (service->favorite) {
- device = connman_network_get_device(service->network);
- if (device && !connman_device_get_scanning(device,
- CONNMAN_SERVICE_TYPE_UNKNOWN)) {
-
- switch (service->type) {
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_P2P:
- break;
-
- case CONNMAN_SERVICE_TYPE_GADGET:
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- if (service->autoconnect) {
- __connman_service_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
-
- /* fall through */
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- do_auto_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
- }
- }
+ trigger_autoconnect(service);
__connman_notifier_service_add(service, service->name);
@@ -7601,6 +7833,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);
@@ -7633,6 +7867,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;
diff --git a/src/session.c b/src/session.c
index 2a1dd9aa..eeefe3f2 100644
--- a/src/session.c
+++ b/src/session.c
@@ -1804,7 +1804,7 @@ static void session_activate(struct connman_session *session)
struct connman_service *service;
struct connman_service_info *info;
GSList *service_list = NULL;
- enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
+ enum connman_service_state state = CONNMAN_SERVICE_STATE_DISCONNECT;
g_hash_table_iter_init(&iter, service_hash);
diff --git a/src/shared/util.c b/src/shared/util.c
index 73c24aef..bda2d2b3 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
+#include <string.h>
#include "src/shared/util.h"
diff --git a/src/technology.c b/src/technology.c
index 4e053fc9..5c469111 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -66,6 +66,7 @@ struct connman_technology {
*/
char *tethering_ident;
char *tethering_passphrase;
+ int tethering_freq;
bool enable_persistent; /* Save the tech state */
@@ -185,10 +186,19 @@ static void technology_save(struct connman_technology *technology)
"Tethering.Identifier",
technology->tethering_ident);
- if (technology->tethering_passphrase)
+ if (technology->tethering_passphrase) {
+ char *enc = g_strescape(technology->tethering_passphrase, NULL);
g_key_file_set_string(keyfile, identifier,
- "Tethering.Passphrase",
- technology->tethering_passphrase);
+ "Tethering.Passphrase", enc);
+ g_free(enc);
+ }
+
+ 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);
@@ -262,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;
@@ -354,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;
}
@@ -390,6 +406,7 @@ static void technology_load(struct connman_technology *technology)
gchar *identifier;
GError *error = NULL;
bool enable, need_saving = false;
+ char *enc;
DBG("technology %p", technology);
@@ -436,8 +453,14 @@ static void technology_load(struct connman_technology *technology)
technology->tethering_ident = g_key_file_get_string(keyfile,
identifier, "Tethering.Identifier", NULL);
- technology->tethering_passphrase = g_key_file_get_string(keyfile,
+ enc = g_key_file_get_string(keyfile,
identifier, "Tethering.Passphrase", NULL);
+ if (enc)
+ technology->tethering_passphrase = g_strcompress(enc);
+
+ technology->tethering_freq = g_key_file_get_integer(keyfile,
+ identifier, "Tethering.Freq", NULL);
+
done:
g_free(identifier);
@@ -549,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);
}
@@ -963,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;
diff --git a/src/tethering.c b/src/tethering.c
index e2687b6e..f930a26b 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -386,7 +386,8 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
if ((__connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
- server_ip, peer_ip, prefixlen, NULL)) < 0) {
+ server_ip, peer_ip, prefixlen, NULL, true))
+ < 0) {
DBG("address setting failed");
return;
}
diff --git a/src/timeserver.c b/src/timeserver.c
index decca153..d23776fa 100644
--- a/src/timeserver.c
+++ b/src/timeserver.c
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <gweb/gresolv.h>
#include <netdb.h>
+#include <sys/time.h>
#include "connman.h"
@@ -40,11 +41,13 @@ static GSList *ts_list = NULL;
static char *ts_current = NULL;
static int ts_recheck_id = 0;
static int ts_backoff_id = 0;
+static bool ts_is_synced = false;
static GResolv *resolv = NULL;
static int resolv_id = 0;
static void sync_next(void);
+static void ts_set_nameservers(struct connman_service *service);
static void resolv_debug(const char *str, void *data)
{
@@ -53,10 +56,26 @@ static void resolv_debug(const char *str, void *data)
static void ntp_callback(bool success, void *user_data)
{
+ dbus_uint64_t timestamp;
+ struct timeval tv;
+
DBG("success %d", success);
- if (!success)
+ __connman_timeserver_set_synced(success);
+ if (!success) {
sync_next();
+ return;
+ }
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ connman_warn("Failed to get current time");
+ }
+
+ timestamp = tv.tv_sec;
+ connman_dbus_property_changed_basic(
+ CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "Time",
+ DBUS_TYPE_UINT64, &timestamp);
}
static void save_timeservers(char **servers)
@@ -165,6 +184,7 @@ static void sync_next(void)
}
__connman_ntp_stop();
+ ts_set_nameservers(ts_service);
while (ts_list) {
ts_current = ts_list->data;
@@ -272,6 +292,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service)
static gboolean ts_recheck(gpointer user_data)
{
+ struct connman_service *service;
GSList *ts;
ts = __connman_timeserver_get_all(connman_service_get_default());
@@ -287,7 +308,9 @@ static gboolean ts_recheck(gpointer user_data)
g_slist_free_full(ts, g_free);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_TS_CHANGE);
return FALSE;
}
@@ -327,37 +350,34 @@ static void ts_recheck_enable(void)
NULL);
}
-/*
- * This function must be called every time the default service changes, the
- * service timeserver(s) or gateway changes or the global timeserver(s) changes.
- */
-int __connman_timeserver_sync(struct connman_service *default_service)
+static int ts_setup_resolv(struct connman_service *service)
{
- struct connman_service *service;
- char **nameservers;
int i;
- if (default_service)
- service = default_service;
- else
- service = connman_service_get_default();
-
- if (!service)
+ i = __connman_service_get_index(service);
+ if (i < 0)
return -EINVAL;
- if (service == ts_service)
- return -EALREADY;
+ if (resolv) {
+ g_resolv_unref(resolv);
+ resolv = NULL;
+ }
+ resolv = g_resolv_new(i);
if (!resolv)
- return 0;
- /*
- * Before we start creating the new timeserver list we must stop
- * any ongoing ntp query and server resolution.
- */
+ return -ENOMEM;
- __connman_ntp_stop();
+ if (getenv("CONNMAN_RESOLV_DEBUG"))
+ g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+ return 0;
+}
- ts_recheck_disable();
+
+static void ts_set_nameservers(struct connman_service *service)
+{
+ char **nameservers;
+ int i;
if (resolv_id > 0)
g_resolv_cancel_lookup(resolv, resolv_id);
@@ -365,73 +385,121 @@ int __connman_timeserver_sync(struct connman_service *default_service)
g_resolv_flush_nameservers(resolv);
nameservers = connman_service_get_nameservers(service);
- if (!nameservers)
- return -EINVAL;
+ if (nameservers) {
+ for (i = 0; nameservers[i]; i++)
+ g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ g_strfreev(nameservers);
+ }
+}
+
+static void ts_reset(struct connman_service *service)
+{
+ if (!resolv)
+ return;
+
+ __connman_timeserver_set_synced(false);
- g_strfreev(nameservers);
+ /*
+ * Before we start creating the new timeserver list we must stop
+ * any ongoing ntp query and server resolution.
+ */
+
+ __connman_ntp_stop();
+
+ ts_recheck_disable();
+
+ ts_set_nameservers(service);
g_slist_free_full(timeservers_list, g_free);
+ g_slist_free_full(ts_list, g_free);
+ ts_list = NULL;
+
timeservers_list = __connman_timeserver_get_all(service);
__connman_service_timeserver_changed(service, timeservers_list);
if (!timeservers_list) {
DBG("No timeservers set.");
- return 0;
+ return;
}
ts_recheck_enable();
ts_service = service;
timeserver_sync_start();
+}
- return 0;
+void __connman_timeserver_sync(struct connman_service *service,
+ enum connman_timeserver_sync_reason reason)
+{
+ if (!service)
+ return;
+
+ switch (reason) {
+ case CONNMAN_TIMESERVER_SYNC_REASON_START:
+ case CONNMAN_TIMESERVER_SYNC_REASON_STATE_UPDATE:
+ if (ts_service == service)
+ return;
+ break;
+ case CONNMAN_TIMESERVER_SYNC_REASON_ADDRESS_UPDATE:
+ case CONNMAN_TIMESERVER_SYNC_REASON_TS_CHANGE:
+ if (ts_service != service)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ ts_reset(service);
}
-static int timeserver_start(struct connman_service *service)
+void __connman_timeserver_conf_update(struct connman_service *service)
{
- char **nameservers;
- int i;
+ if (!service || (ts_service && ts_service != service))
+ return;
- DBG("service %p", service);
+ ts_reset(service);
+}
- i = __connman_service_get_index(service);
- if (i < 0)
- return -EINVAL;
- nameservers = connman_service_get_nameservers(service);
+bool __connman_timeserver_is_synced(void)
+{
+ return ts_is_synced;
+}
- /* Stop an already ongoing resolution, if there is one */
- if (resolv && resolv_id > 0)
- g_resolv_cancel_lookup(resolv, resolv_id);
+void __connman_timeserver_set_synced(bool status)
+{
+ dbus_bool_t is_synced;
- /* get rid of the old resolver */
- if (resolv) {
- g_resolv_unref(resolv);
- resolv = NULL;
- }
+ if (ts_is_synced == status)
+ return;
- resolv = g_resolv_new(i);
- if (!resolv) {
- g_strfreev(nameservers);
- return -ENOMEM;
- }
+ ts_is_synced = status;
+ is_synced = status;
+ connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
+}
- if (getenv("CONNMAN_RESOLV_DEBUG"))
- g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+static int timeserver_start(struct connman_service *service)
+{
+ int rv;
- if (nameservers) {
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ DBG("service %p", service);
- g_strfreev(nameservers);
- }
+ /* get rid of the old resolver */
+ rv = ts_setup_resolv(service);
+ if (rv)
+ return rv;
+
+ ts_set_nameservers(service);
+
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_START);
- return __connman_timeserver_sync(service);
+ return 0;
}
static void timeserver_stop(void)
@@ -458,9 +526,13 @@ static void timeserver_stop(void)
int __connman_timeserver_system_set(char **servers)
{
+ struct connman_service *service;
+
save_timeservers(servers);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ if (service)
+ ts_reset(service);
return 0;
}
diff --git a/src/timezone.c b/src/timezone.c
index cc499097..f8d192df 100644
--- a/src/timezone.c
+++ b/src/timezone.c
@@ -38,9 +38,9 @@
#include "connman.h"
-#define ETC_LOCALTIME "/etc/localtime"
#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
#define USR_SHARE_ZONEINFO "/usr/share/zoneinfo"
+#define USR_SHARE_ZONEINFO_MAP USR_SHARE_ZONEINFO "/zone1970.tab"
static char *read_key_file(const char *pathname, const char *key)
{
@@ -228,18 +228,104 @@ static char *find_origin(void *src_map, struct stat *src_st,
return NULL;
}
+static char *get_timezone_alpha2(const char *zone)
+{
+ GIOChannel *channel;
+ struct stat st;
+ char **tokens;
+ char *line;
+ char *alpha2 = NULL;
+ gsize len;
+ int fd;
+
+ if (!zone)
+ return NULL;
+
+ fd = open(USR_SHARE_ZONEINFO_MAP, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ connman_warn("failed to open zoneinfo map %s",
+ USR_SHARE_ZONEINFO_MAP);
+ return NULL;
+ }
+
+ if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) {
+ connman_warn("zoneinfo map does not exist/not regular file");
+ close(fd);
+ return NULL;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+ if (!channel) {
+ connman_warn("failed to create io channel for %s",
+ USR_SHARE_ZONEINFO_MAP);
+ close(fd);
+ return NULL;
+ }
+
+ DBG("read %s for %s", USR_SHARE_ZONEINFO_MAP, zone);
+ g_io_channel_set_encoding(channel, "UTF-8", NULL);
+
+ while (g_io_channel_read_line(channel, &line, &len, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
+ if (!line || !*line || *line == '#' || *line == '\n') {
+ g_free(line);
+ continue;
+ }
+
+ /* File format: Countrycodes Coordinates TZ Comments */
+ tokens = g_strsplit_set(line, " \t", 4);
+ if (!tokens) {
+ connman_warn("line %s failed to parse", line);
+ g_free(line);
+ continue;
+ }
+
+ if (g_strv_length(tokens) >= 3 && !g_strcmp0(
+ g_strstrip(tokens[2]), zone)) {
+ /*
+ * Multiple country codes can be listed, use the first
+ * 2 chars.
+ */
+ alpha2 = g_strndup(g_strstrip(tokens[0]), 2);
+ }
+
+ g_strfreev(tokens);
+ g_free(line);
+
+ if (alpha2) {
+ if (strlen(alpha2) != 2) {
+ connman_warn("Invalid ISO3166 code %s", alpha2);
+ g_free(alpha2);
+ alpha2 = NULL;
+ } else {
+ DBG("Zone %s ISO3166 country code %s", zone,
+ alpha2);
+ }
+
+ break;
+ }
+ }
+
+ g_io_channel_unref(channel);
+ close(fd);
+
+ return alpha2;
+}
+
char *__connman_timezone_lookup(void)
{
struct stat st;
void *map;
int fd;
char *zone;
+ char *alpha2;
zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE");
DBG("sysconfig zone %s", zone);
- fd = open(ETC_LOCALTIME, O_RDONLY | O_CLOEXEC);
+ fd = open(connman_setting_get_string("Localtime"),
+ O_RDONLY | O_CLOEXEC);
if (fd < 0) {
g_free(zone);
return NULL;
@@ -283,6 +369,15 @@ done:
DBG("localtime zone %s", zone);
+ if (connman_setting_get_bool("RegdomFollowsTimezone")) {
+ alpha2 = get_timezone_alpha2(zone);
+ if (alpha2) {
+ DBG("change regdom to %s", alpha2);
+ connman_technology_set_regdom(alpha2);
+ g_free(alpha2);
+ }
+ }
+
return zone;
}
@@ -338,7 +433,7 @@ int __connman_timezone_change(const char *zone)
return -EIO;
}
- err = write_file(map, &st, ETC_LOCALTIME);
+ err = write_file(map, &st, connman_setting_get_string("Localtime"));
munmap(map, st.st_size);
@@ -432,9 +527,9 @@ int __connman_timezone_init(void)
g_io_channel_unref(channel);
- dirname = g_path_get_dirname(ETC_LOCALTIME);
+ dirname = g_path_get_dirname(connman_setting_get_string("Localtime"));
- wd = inotify_add_watch(fd, dirname, IN_DONT_FOLLOW |
+ wd = inotify_add_watch(fd, dirname, IN_CREATE | IN_DONT_FOLLOW |
IN_CLOSE_WRITE | IN_MOVED_TO);
g_free(dirname);
diff --git a/src/wispr.c b/src/wispr.c
index 41157580..a4372018 100644
--- a/src/wispr.c
+++ b/src/wispr.c
@@ -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;
@@ -59,6 +56,7 @@ struct wispr_route {
};
struct connman_wispr_portal_context {
+ int refcount;
struct connman_service *service;
enum connman_ipconfig_type type;
struct connman_wispr_portal *wispr_portal;
@@ -94,12 +92,19 @@ struct connman_wispr_portal {
static bool wispr_portal_web_result(GWebResult *result, gpointer user_data);
-static GHashTable *wispr_portal_list = NULL;
+static GHashTable *wispr_portal_hash = NULL;
+
+static char *online_check_ipv4_url = NULL;
+static char *online_check_ipv6_url = NULL;
+static bool enable_online_to_ready_transition = false;
+
+#define wispr_portal_context_ref(wp_context) \
+ wispr_portal_context_ref_debug(wp_context, __FILE__, __LINE__, __func__)
+#define wispr_portal_context_unref(wp_context) \
+ wispr_portal_context_unref_debug(wp_context, __FILE__, __LINE__, __func__)
static void connman_wispr_message_init(struct connman_wispr_message *msg)
{
- DBG("");
-
msg->has_error = false;
msg->current_element = NULL;
@@ -159,11 +164,6 @@ static void free_wispr_routes(struct connman_wispr_portal_context *wp_context)
static void free_connman_wispr_portal_context(
struct connman_wispr_portal_context *wp_context)
{
- DBG("context %p", wp_context);
-
- if (!wp_context)
- return;
-
if (wp_context->wispr_portal) {
if (wp_context->wispr_portal->ipv4_context == wp_context)
wp_context->wispr_portal->ipv4_context = NULL;
@@ -200,9 +200,38 @@ static void free_connman_wispr_portal_context(
g_free(wp_context);
}
+static struct connman_wispr_portal_context *
+wispr_portal_context_ref_debug(struct connman_wispr_portal_context *wp_context,
+ const char *file, int line, const char *caller)
+{
+ DBG("%p ref %d by %s:%d:%s()", wp_context,
+ wp_context->refcount + 1, file, line, caller);
+
+ __sync_fetch_and_add(&wp_context->refcount, 1);
+
+ return wp_context;
+}
+
+static void wispr_portal_context_unref_debug(
+ struct connman_wispr_portal_context *wp_context,
+ const char *file, int line, const char *caller)
+{
+ if (!wp_context)
+ return;
+
+ DBG("%p ref %d by %s:%d:%s()", wp_context,
+ wp_context->refcount - 1, file, line, caller);
+
+ if (__sync_fetch_and_sub(&wp_context->refcount, 1) != 1)
+ return;
+
+ free_connman_wispr_portal_context(wp_context);
+}
+
static struct connman_wispr_portal_context *create_wispr_portal_context(void)
{
- return g_try_new0(struct connman_wispr_portal_context, 1);
+ return wispr_portal_context_ref(
+ g_new0(struct connman_wispr_portal_context, 1));
}
static void free_connman_wispr_portal(gpointer data)
@@ -214,8 +243,8 @@ static void free_connman_wispr_portal(gpointer data)
if (!wispr_portal)
return;
- free_connman_wispr_portal_context(wispr_portal->ipv4_context);
- free_connman_wispr_portal_context(wispr_portal->ipv6_context);
+ wispr_portal_context_unref(wispr_portal->ipv4_context);
+ wispr_portal_context_unref(wispr_portal->ipv6_context);
g_free(wispr_portal);
}
@@ -450,10 +479,11 @@ static void portal_manage_status(GWebResult *result,
&str))
connman_info("Client-Timezone: %s", str);
- free_connman_wispr_portal_context(wp_context);
-
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_ONLINE, type);
+
+ if (enable_online_to_ready_transition)
+ __connman_service_online_check(service, type, true);
}
static bool wispr_route_request(const char *address, int ai_family,
@@ -507,16 +537,20 @@ static bool wispr_route_request(const char *address, int ai_family,
static void wispr_portal_request_portal(
struct connman_wispr_portal_context *wp_context)
{
- DBG("");
+ DBG("wp_context %p %s", wp_context,
+ __connman_ipconfig_type2string(wp_context->type));
+ wispr_portal_context_ref(wp_context);
wp_context->request_id = g_web_request_get(wp_context->web,
wp_context->status_url,
wispr_portal_web_result,
wispr_route_request,
wp_context);
- if (wp_context->request_id == 0)
+ if (wp_context->request_id == 0) {
wispr_portal_error(wp_context);
+ wispr_portal_context_unref(wp_context);
+ }
}
static bool wispr_input(const guint8 **data, gsize *length,
@@ -571,7 +605,7 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service,
if (index < 0)
return;
- wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
GINT_TO_POINTER(index));
if (!wispr_portal)
return;
@@ -581,13 +615,15 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service,
return;
if (!authentication_done) {
- wispr_portal_error(wp_context);
free_wispr_routes(wp_context);
+ wispr_portal_error(wp_context);
+ wispr_portal_context_unref(wp_context);
return;
}
/* Restarting the test */
__connman_service_wispr_start(service, wp_context->type);
+ wispr_portal_context_unref(wp_context);
}
static void wispr_portal_request_wispr_login(struct connman_service *service,
@@ -611,7 +647,7 @@ static void wispr_portal_request_wispr_login(struct connman_service *service,
return;
}
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
return;
}
@@ -663,11 +699,13 @@ static bool wispr_manage_message(GWebResult *result,
wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
+ wispr_portal_context_ref(wp_context);
if (__connman_agent_request_login_input(wp_context->service,
wispr_portal_request_wispr_login,
- wp_context) != -EINPROGRESS)
+ wp_context) != -EINPROGRESS) {
wispr_portal_error(wp_context);
- else
+ wispr_portal_context_unref(wp_context);
+ } else
return true;
break;
@@ -716,6 +754,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
if (length > 0) {
g_web_parser_feed_data(wp_context->wispr_parser,
chunk, length);
+ /* read more data */
return true;
}
@@ -733,6 +772,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
switch (status) {
case 000:
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -744,18 +784,25 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
if (g_web_result_get_header(result, "X-ConnMan-Status",
&str)) {
portal_manage_status(result, wp_context);
- return false;
- } else
+ } else {
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->redirect_url, wp_context);
+ }
break;
+ case 300:
+ case 301:
case 302:
+ case 303:
+ case 307:
+ case 308:
if (!g_web_supports_tls() ||
!g_web_result_get_header(result, "Location",
&redirect)) {
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -766,6 +813,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
wp_context->redirect_url = g_strdup(redirect);
+ wispr_portal_context_ref(wp_context);
wp_context->request_id = g_web_request_get(wp_context->web,
redirect, wispr_portal_web_result,
wispr_route_request, wp_context);
@@ -773,15 +821,12 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
goto done;
case 400:
case 404:
- if (__connman_service_online_check_failed(wp_context->service,
- wp_context->type) == 0) {
- wispr_portal_error(wp_context);
- free_connman_wispr_portal_context(wp_context);
- return false;
- }
+ __connman_service_online_check(wp_context->service,
+ wp_context->type, false);
break;
case 505:
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -794,26 +839,51 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
wp_context->request_id = 0;
done:
wp_context->wispr_msg.message_type = -1;
+ wispr_portal_context_unref(wp_context);
return false;
}
+static char *parse_proxy(const char *proxy)
+{
+ char *proxy_server;
+
+ if (!g_strcmp0(proxy, "DIRECT"))
+ return NULL;
+
+ if (!g_str_has_prefix(proxy, "PROXY "))
+ return NULL;
+
+ proxy_server = g_strdup(proxy + 6);
+
+ /* Use first proxy server */
+ for (char *c = proxy_server; *c != '\0'; ++c) {
+ if (*c == ';') {
+ *c = '\0';
+ break;
+ }
+ }
+
+ g_strstrip(proxy_server);
+
+ return proxy_server;
+}
+
static void proxy_callback(const char *proxy, void *user_data)
{
struct connman_wispr_portal_context *wp_context = user_data;
+ char *proxy_server;
DBG("proxy %s", proxy);
- if (!wp_context)
+ if (!wp_context || !proxy)
return;
wp_context->token = 0;
- if (proxy && g_strcmp0(proxy, "DIRECT") != 0) {
- if (g_str_has_prefix(proxy, "PROXY")) {
- proxy += 5;
- for (; *proxy == ' ' && *proxy != '\0'; proxy++);
- }
- g_web_set_proxy(wp_context->web, proxy);
+ proxy_server = parse_proxy(proxy);
+ if (proxy_server) {
+ g_web_set_proxy(wp_context->web, proxy_server);
+ g_free(proxy_server);
}
g_web_set_accept(wp_context->web, NULL);
@@ -828,6 +898,7 @@ static void proxy_callback(const char *proxy, void *user_data)
xml_wispr_parser_callback, wp_context);
wispr_portal_request_portal(wp_context);
+ wispr_portal_context_unref(wp_context);
}
static gboolean no_proxy_callback(gpointer user_data)
@@ -844,7 +915,6 @@ static gboolean no_proxy_callback(gpointer user_data)
static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
{
enum connman_service_proxy_method proxy_method;
- enum connman_service_type service_type;
char *interface = NULL;
char **nameservers = NULL;
int if_index;
@@ -854,23 +924,6 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
DBG("wispr/portal context %p service %p", wp_context,
wp_context->service);
- service_type = connman_service_get_type(wp_context->service);
-
- switch (service_type) {
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- case CONNMAN_SERVICE_TYPE_GADGET:
- break;
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_P2P:
- return -EOPNOTSUPP;
- }
-
interface = connman_service_get_interface(wp_context->service);
if (!interface)
return -EINVAL;
@@ -903,10 +956,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++)
@@ -922,7 +975,7 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
if (wp_context->token == 0) {
err = -EINVAL;
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
}
} else if (wp_context->timeout == 0) {
wp_context->timeout = g_idle_add(no_proxy_callback, wp_context);
@@ -940,42 +993,58 @@ int __connman_wispr_start(struct connman_service *service,
{
struct connman_wispr_portal_context *wp_context = NULL;
struct connman_wispr_portal *wispr_portal = NULL;
- int index;
+ int index, err;
- DBG("service %p", service);
+ DBG("service %p %s", service,
+ __connman_ipconfig_type2string(type));
- if (!wispr_portal_list)
+ if (!wispr_portal_hash)
return -EINVAL;
+ switch (connman_service_get_type(service)) {
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ break;
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_P2P:
+ return -EOPNOTSUPP;
+ }
+
index = __connman_service_get_index(service);
if (index < 0)
return -EINVAL;
- wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
GINT_TO_POINTER(index));
if (!wispr_portal) {
wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
if (!wispr_portal)
return -ENOMEM;
- g_hash_table_replace(wispr_portal_list,
+ g_hash_table_replace(wispr_portal_hash,
GINT_TO_POINTER(index), wispr_portal);
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
wp_context = wispr_portal->ipv4_context;
- else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
- wp_context = wispr_portal->ipv6_context;
else
- return -EINVAL;
+ wp_context = wispr_portal->ipv6_context;
/* If there is already an existing context, we wipe it */
if (wp_context)
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
wp_context = create_wispr_portal_context();
- if (!wp_context)
- return -ENOMEM;
+ if (!wp_context) {
+ err = -ENOMEM;
+ goto free_wp;
+ }
wp_context->service = service;
wp_context->type = type;
@@ -986,33 +1055,58 @@ int __connman_wispr_start(struct connman_service *service,
else
wispr_portal->ipv6_context = wp_context;
- return wispr_portal_detect(wp_context);
+ err = wispr_portal_detect(wp_context);
+ if (err)
+ goto free_wp;
+ return 0;
+
+free_wp:
+ g_hash_table_remove(wispr_portal_hash, GINT_TO_POINTER(index));
+ return err;
}
void __connman_wispr_stop(struct connman_service *service)
{
+ struct connman_wispr_portal *wispr_portal;
int index;
DBG("service %p", service);
- if (!wispr_portal_list)
+ if (!wispr_portal_hash)
return;
index = __connman_service_get_index(service);
if (index < 0)
return;
- g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
+ GINT_TO_POINTER(index));
+ if (!wispr_portal)
+ return;
+
+ if ((wispr_portal->ipv4_context &&
+ service == wispr_portal->ipv4_context->service) ||
+ (wispr_portal->ipv6_context &&
+ service == wispr_portal->ipv6_context->service))
+ g_hash_table_remove(wispr_portal_hash, GINT_TO_POINTER(index));
}
int __connman_wispr_init(void)
{
DBG("");
- wispr_portal_list = g_hash_table_new_full(g_direct_hash,
+ wispr_portal_hash = g_hash_table_new_full(g_direct_hash,
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");
+
return 0;
}
@@ -1020,6 +1114,6 @@ void __connman_wispr_cleanup(void)
{
DBG("");
- g_hash_table_destroy(wispr_portal_list);
- wispr_portal_list = NULL;
+ g_hash_table_destroy(wispr_portal_hash);
+ wispr_portal_hash = NULL;
}
diff --git a/test/monitor-connman b/test/monitor-connman
index 8403f913..c6edd760 100755
--- a/test/monitor-connman
+++ b/test/monitor-connman
@@ -1,6 +1,6 @@
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
@@ -82,6 +82,6 @@ if __name__ == '__main__':
bus.add_match_string("member=Update,interface=net.connman.Notification")
bus.add_message_filter(message_filter)
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
diff --git a/test/monitor-services b/test/monitor-services
index d570e5f5..c520a8cd 100755
--- a/test/monitor-services
+++ b/test/monitor-services
@@ -1,6 +1,6 @@
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
@@ -102,5 +102,5 @@ if __name__ == '__main__':
signal_name="PropertyChanged",
path_keyword="path")
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
diff --git a/test/p2p-on-supplicant b/test/p2p-on-supplicant
index 339d5eba..22501fc3 100755
--- a/test/p2p-on-supplicant
+++ b/test/p2p-on-supplicant
@@ -3,10 +3,9 @@
from os import O_NONBLOCK
from sys import stdin, stdout, exit, version_info, argv
from fcntl import fcntl, F_GETFL, F_SETFL
-import glib
+from gi.repository import GLib
import dbus
import dbus.mainloop.glib
-import gobject
import argparse
WPA_NAME='fi.w1.wpa_supplicant1'
@@ -32,7 +31,7 @@ class InputLine:
flags = fcntl(stdin.fileno(), F_GETFL)
flags |= O_NONBLOCK
fcntl(stdin.fileno(), F_SETFL, flags)
- glib.io_add_watch(stdin, glib.IO_IN, self.input_cb)
+ GLib.io_add_watch(stdin, GLib.IO_IN, self.input_cb)
self.prompt()
@@ -42,7 +41,7 @@ class InputLine:
stdout.flush()
def input_cb(self, fd, event):
- if event != glib.IO_IN:
+ if event != GLib.IO_IN:
return
self.line += fd.read();
@@ -610,10 +609,6 @@ def build_args(parser):
return command
def main():
- if version_info.major != 2:
- print('You need to run this under Python 2.x')
- exit(1)
-
parser = argparse.ArgumentParser(description='Connman P2P Test')
command_list = build_args(parser)
@@ -634,7 +629,7 @@ def main():
bus = dbus.SystemBus()
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
wpa_s = Wpa_s(bus, args.ifname, args.command + params)
diff --git a/test/simple-agent b/test/simple-agent
index 282785e2..04de3f60 100755
--- a/test/simple-agent
+++ b/test/simple-agent
@@ -1,6 +1,6 @@
#!/usr/bin/python
-import gobject
+from gi.repository import GLib
import dbus
import dbus.service
@@ -351,7 +351,7 @@ if __name__ == '__main__':
except:
"Cannot register vpn agent"
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
#manager.UnregisterAgent(path)
diff --git a/test/test-counter b/test/test-counter
index c09aabc1..4c551620 100755
--- a/test/test-counter
+++ b/test/test-counter
@@ -1,7 +1,8 @@
#!/usr/bin/python
+from gi.repository import GLib
+
import sys
-import gobject
import dbus
import dbus.service
@@ -73,7 +74,7 @@ if __name__ == '__main__':
manager.RegisterCounter(path, dbus.UInt32(10), dbus.UInt32(period))
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
mainloop.run()
#manager.UnregisterCounter(path)
diff --git a/test/test-session b/test/test-session
index e45d22b8..112074f1 100755
--- a/test/test-session
+++ b/test/test-session
@@ -1,15 +1,13 @@
#!/usr/bin/python
+from gi.repository import GLib
+
import sys
-import gobject
-import string
import dbus
import dbus.service
import dbus.mainloop.glib
-import glib
-
import traceback
def extract_list(list):
@@ -293,11 +291,11 @@ def main():
app_path = sys.argv[2]
bus = dbus.SessionBus()
- app_name = "com.example.SessionApplication.%s" % (string.strip(app_path, "/"))
+ app_name = "com.example.SessionApplication.%s" % (str.strip(app_path, "/"))
if sys.argv[1] == "run":
name = dbus.service.BusName(app_name, bus)
- mainloop = gobject.MainLoop()
+ mainloop = GLib.MainLoop()
app = SessionApplication(bus, app_path, mainloop)
diff --git a/tools/dnsproxy-simple-test b/tools/dnsproxy-simple-test
new file mode 100755
index 00000000..5c2f7292
--- /dev/null
+++ b/tools/dnsproxy-simple-test
@@ -0,0 +1,195 @@
+#!/bin/bash
+
+# this script runs the dnsproxy-standalone test program and runs a couple of
+# standard DNS queries against it, using the currently configured DNS server
+# in the system as dnsproxy configuration.
+
+echoerr() {
+ echo $@ 1>&2
+ echo -e "\n >>> ERROR OCCURED <<< \n" 1>&2
+ exit 1
+}
+
+
+showlog() {
+ if [ -z "$SHOW_LOG" -o -z "$logfile" ]; then
+ return
+ fi
+
+ echo
+ echo "======== debug log ==========="
+ cat "$logfile"
+ echo "===== end debug log =========="
+ echo
+}
+
+TRANSPORTS="-U -T"
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ "--valgrind")
+ VALGRIND=`which valgrind`
+ if [ -z "$VALGRIND" ]; then
+ echoerr "no valgrind executable found"
+ fi
+ # log valgrind output to stdout, since stderr is used for
+ # debug output from dnsproxy.c already and we want to parse
+ # that.
+ # also cause an error exit it valgrind error occur so that
+ # they're easily noticed.
+ VALGRIND="$VALGRIND --log-fd=1 --error-exitcode=10"
+ ;;
+ "--gdb")
+ WAIT_GDB=1
+ # wait forever to avoid timeout conditions during debugging
+ HOST_OPTS="-w"
+ ;;
+ "--show-log")
+ SHOW_LOG=1
+ ;;
+ "--testdomain="*)
+ TESTDOMAIN=`echo $1 | cut -d '=' -f2-`
+ CUSTOM_TESTDOMAIN=1
+ ;;
+ "--only-tcp")
+ TRANSPORTS="-T"
+ ;;
+ "--only-udp")
+ TRANSPORTS="-U"
+ ;;
+ "-h")
+ echo "$0 [--valgrind] [--gdb] [--show-log] [--only-tcp] [--only-udp] [--testdomain=<mydomain>]"
+ echo "--valgrind: run dnsproxy-standalone in valgrind"
+ echo "--gdb: allows you to attach via GDB before tests are started"
+ echo "--show-log: dump debug log from dnsproxy at end of test"
+ echo "--only-tcp: only perform TCP protocol based tests"
+ echo "--only-udp: only perform UDP protocol based tests"
+ echo "--testdomain=<mydomain>: the domain name to resolve"
+ exit 2
+ ;;
+ *)
+ echoerr "Unknown argument $1"
+ ;;
+ esac
+ shift
+done
+
+if [ -n "$VALGRIND" -a -n "$WAIT_GDB" ]; then
+ echo "Cannot mix valgrind frontend and GDB attachment" 1>&2
+ exit 2
+fi
+
+if [ -e "Makefile" ]; then
+ BUILDROOT="$PWD"
+else
+ if [ ! -n "$BUILDROOT" ]; then
+ echoerr "You need to set the BUILDROOT environment variable or run this script from the connman build tree root"
+ fi
+
+ pushd "$BUILDROOT" >/dev/null || echoerr "couldn't enter $BUILDROOT"
+fi
+make tools/dnsproxy-standalone || echoerr "failed to build dnsproxy-standalone"
+
+HOST=`which host`
+if [ -z "$HOST" ]; then
+ echoerr "Couldn't find 'host' DNS utility"
+fi
+
+DNSPROXY="$BUILDROOT/tools/dnsproxy-standalone"
+
+if [ ! -f "$DNSPROXY" ]; then
+ echoerr "standalone dnsproxy does not exist at $DNSPROXY"
+fi
+
+NS1=`grep -w nameserver -m 1 /etc/resolv.conf | cut -d ' ' -f 2`
+if [ -z "$NS1" ]; then
+ echoerr "Failed to determine system's nameserver from /etc/resolv.conf"
+fi
+
+DOMAIN1=`grep -w search -m 1 /etc/resolv.conf | cut -d ' ' -f 2`
+if [ -z "$DOMAIN1" ]; then
+ echoerr "Failed to determine default DNS domain from /etc/resolv.conf"
+fi
+
+# use an unprivileged port for the proxy so we don't need special permissions
+# to run this test
+PORT=8053
+
+# run the proxy in the background
+logfile=`mktemp`
+$VALGRIND $DNSPROXY $PORT "$DOMAIN1" "$NS1" 2>"$logfile" &
+proxy_pid=$!
+
+cleanup() {
+ if [ $proxy_pid -eq -1 ]; then
+ return 0
+ fi
+ kill $proxy_pid
+ wait $proxy_pid
+ ret=$?
+ proxy_pid=-1
+ if [ -n "$logfile" ]; then
+ if [ -n "$SHOW_LOG" ]; then
+ showlog
+ fi
+ rm -f "$logfile"
+ unset logfile
+ fi
+ return $ret
+}
+
+trap cleanup err exit
+
+sleep 1
+echo -e "\n\n"
+
+if [ -n "$WAIT_GDB" ]; then
+ echo "You can now attach to the dnsproxy process at PID $proxy_pid."
+ echo "Press ENTER to continue test execution"
+ read _
+fi
+
+if [ -z "$TESTDOMAIN" ]; then
+ TESTDOMAIN="www.example.com"
+fi
+
+# perform each test twice to actually get cached responses served for each
+# combination
+for I in `seq 2`; do
+ # test both UDP and TCP mode
+ for TRANSPORT in $TRANSPORTS; do
+ # test both IPv4 and IPv6
+ for IP in -4 -6; do
+ echo "Testing resolution using transport $TRANSPORT and IP${IP}"
+ set -x
+ $HOST $HOST_OPTS $TRANSPORT $IP -p$PORT $TESTDOMAIN 127.0.0.1
+ RES=$?
+ set +x
+ if [ $RES -ne 0 ]; then
+ echoerr "resolution failed"
+ fi
+
+ echo -e "\n\n"
+ done
+ done
+done
+
+NUM_HITS=`grep "cache hit.*$TESTDOMAIN" "$logfile" | wc -l`
+
+echo -e "\n\nDNS resolution succeeded for all test combinations"
+echo -e "\nNumber of cache hits: $NUM_HITS\n"
+# assert we have seen the expected number of cache hits in the log
+# this is the amount of cache hits for the default domain tests as seen before
+# refactoring of dnsproxy started.
+if [ -z "$CUSTOM_TESTDOMAIN" -a "$NUM_HITS" -ne 15 ]; then
+ echoerr "Unexpected number of cache hits encountered"
+elif [ "$NUM_HITS" -lt 8 ]; then
+ echoerr "Too low number of cache hits encountered"
+fi
+cleanup
+if [ $? -eq 0 ]; then
+ exit 0
+else
+ echoerr "dnsproxy returned non-zero exit status $?"
+fi
+
diff --git a/tools/dnsproxy-standalone.c b/tools/dnsproxy-standalone.c
new file mode 100644
index 00000000..1b9698bd
--- /dev/null
+++ b/tools/dnsproxy-standalone.c
@@ -0,0 +1,155 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+/*
+ * This is a minimal connman setup that only runs the internal dnsproxy
+ * component for testing. The advantage is that we can do a full integration
+ * test of the dnsproxy logic without requiring root privileges or setting up
+ * other complexities like D-Bus access etc.
+ */
+
+static GMainLoop *main_loop = NULL;
+
+static void usage(const char *prog)
+{
+ fprintf(stderr, "%s: <listen-port> <dns-domain> <dns-server>\n", prog);
+ exit(1);
+}
+
+static unsigned int to_uint(const char *s)
+{
+ char *end = NULL;
+ unsigned int ret;
+
+ ret = strtoul(s, &end, 10);
+
+ if (*end != '\0') {
+ fprintf(stderr, "invalid argument: %s", s);
+ exit(1);
+ }
+
+ return ret;
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct signalfd_siginfo si;
+ ssize_t result;
+ int fd;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return FALSE;
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ printf("Terminating due to signal\n");
+ g_main_loop_quit(main_loop);
+ break;
+ }
+
+ return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+ GIOChannel *channel;
+ guint source;
+ sigset_t mask;
+ int fd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+ perror("Failed to set signal mask");
+ return 0;
+ }
+
+ fd = signalfd(-1, &mask, 0);
+ if (fd < 0) {
+ perror("Failed to create signal descriptor");
+ return 0;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ signal_handler, NULL);
+
+ g_io_channel_unref(channel);
+
+ return source;
+}
+
+int main(int argc, const char **argv)
+{
+ unsigned int port = 0;
+ const char *domain = argv[2];
+ const char *server = argv[3];
+ guint signal = 0;
+
+ if (argc != 4)
+ {
+ usage(argv[0]);
+ }
+
+ port = to_uint(argv[1]);
+
+ __connman_util_init();
+ printf("Listening on local port %u\n", port);
+ __connman_dnsproxy_set_listen_port(port);
+
+ if (__connman_dnsproxy_init() < 0) {
+ fprintf(stderr, "failed to initialize dnsproxy\n");
+ return 1;
+ }
+
+ printf("Using DNS server %s on domain %s\n", server, domain);
+
+ if (__connman_dnsproxy_append(-1, domain, server) < 0) {
+ fprintf(stderr, "failed to add DNS server\n");
+ return 1;
+ }
+
+ /* we need to trick a bit to make the server entry enter "enabled"
+ * state in dnsproxy. Appending and removing an arbitrary entry causes
+ * "enable_fallback()" to be called which does what we want. Doesn't
+ * make much sense but it is good enough for the standalone server at
+ * the moment.
+ */
+ __connman_dnsproxy_append(15, domain, server);
+ __connman_dnsproxy_remove(15, domain, server);
+
+ signal = setup_signalfd();
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ g_main_loop_run(main_loop);
+
+ __connman_dnsproxy_cleanup();
+ __connman_util_cleanup();
+ g_source_remove(signal);
+
+ return 0;
+}
diff --git a/tools/dnsproxy-test.c b/tools/dnsproxy-test.c
index 371e2e23..01dcc515 100644
--- a/tools/dnsproxy-test.c
+++ b/tools/dnsproxy-test.c
@@ -99,6 +99,8 @@ static unsigned char msg_invalid[] = {
0x31, 0xC0, /* tran id */
};
+static const char *dns_port = "53";
+
static int create_tcp_socket(int family)
{
int sk, err;
@@ -139,7 +141,7 @@ static int connect_tcp_socket(char *server)
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
- getaddrinfo(server, "53", &hints, &rp);
+ getaddrinfo(server, dns_port, &hints, &rp);
sk = create_tcp_socket(rp->ai_family);
err = sk;
@@ -201,7 +203,7 @@ static int connect_udp_socket(char *server, struct sockaddr *sa,
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
- getaddrinfo(server, "53", &hints, &rp);
+ getaddrinfo(server, dns_port, &hints, &rp);
sk = create_udp_socket(rp->ai_family);
err = sk;
@@ -430,6 +432,11 @@ int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
+ if (argc == 2) {
+ /* alternative dns port */
+ dns_port = argv[1];
+ }
+
g_test_add_func("/dnsproxy/ipv4 udp msg",
test_ipv4_udp_msg);
diff --git a/tools/ip6tables-test.c b/tools/ip6tables-test.c
index 41e842dd..a52f4af0 100644
--- a/tools/ip6tables-test.c
+++ b/tools/ip6tables-test.c
@@ -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;
diff --git a/tools/iptables-test.c b/tools/iptables-test.c
index e9b7cb22..f9d091eb 100644
--- a/tools/iptables-test.c
+++ b/tools/iptables-test.c
@@ -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;
diff --git a/unit/test-iptables.c b/unit/test-iptables.c
index cd261d05..f08736ea 100644
--- a/unit/test-iptables.c
+++ b/unit/test-iptables.c
@@ -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;
diff --git a/vpn/connman-vpn.service.in b/vpn/connman-vpn.service.in
index dd15bab4..3bb52e12 100644
--- a/vpn/connman-vpn.service.in
+++ b/vpn/connman-vpn.service.in
@@ -6,7 +6,7 @@ Type=dbus
BusName=net.connman.vpn
ExecStart=@sbindir@/connman-vpnd -n
StandardOutput=null
-CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID
+CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_CHOWN CAP_FOWNER
ProtectHome=read-only
ProtectSystem=full
diff --git a/vpn/main.c b/vpn/main.c
index 133acd27..92c63e20 100644
--- a/vpn/main.c
+++ b/vpn/main.c
@@ -129,7 +129,6 @@ static gchar *option_plugin = NULL;
static gchar *option_noplugin = NULL;
static bool option_detach = true;
static bool option_version = false;
-static bool option_routes = false;
static bool parse_debug(const char *key, const char *value,
gpointer user_data, GError **error)
@@ -156,8 +155,6 @@ static GOptionEntry options[] = {
{ "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &option_detach,
"Don't fork daemon to background" },
- { "routes", 'r', 0, G_OPTION_ARG_NONE, &option_routes,
- "Create/delete VPN routes" },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
"Show version information and exit" },
{ NULL },
@@ -259,7 +256,7 @@ int main(int argc, char *argv[])
__connman_inotify_init();
__connman_agent_init();
- __vpn_provider_init(option_routes);
+ __vpn_provider_init();
__vpn_manager_init();
__vpn_ipconfig_init();
__vpn_rtnl_init();
diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c
index 48894aa5..ee40dd72 100644
--- a/vpn/plugins/l2tp.c
+++ b/vpn/plugins/l2tp.c
@@ -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);
@@ -246,6 +289,8 @@ static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
+
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_nameservers(provider, nameservers);
@@ -255,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;
}
@@ -474,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);
@@ -485,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 {
@@ -644,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;
@@ -672,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;
@@ -689,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,
@@ -734,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,
@@ -745,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;
@@ -760,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;
}
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index d600e61e..fb63a1a3 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -42,6 +42,8 @@
#include <connman/setting.h>
#include <connman/vpn-dbus.h>
+#include <openconnect.h>
+
#include "../vpn-provider.h"
#include "../vpn-agent.h"
@@ -89,7 +91,6 @@ enum oc_connect_type {
static const char *connect_types[] = {"cookie", "cookie_with_userpass",
"userpass", "publickey", "pkcs", NULL};
-static const char *protocols[] = { "anyconnect", "nc", "gp", NULL};
struct oc_private_data {
struct vpn_provider *provider;
@@ -98,21 +99,47 @@ struct oc_private_data {
char *dbus_sender;
vpn_provider_connect_cb_t cb;
void *user_data;
+
+ GThread *cookie_thread;
+ struct openconnect_info *vpninfo;
+ int fd_cmd;
+ int err;
+
int fd_in;
- int out_ch_id;
int err_ch_id;
- GIOChannel *out_ch;
GIOChannel *err_ch;
enum oc_connect_type connect_type;
- bool interactive;
+ bool tried_passphrase;
+ bool group_set;
};
+typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
+ void *user_data);
+
+static int run_connect(struct oc_private_data *data, const char *cookie);
+static int request_input_credentials_full(
+ struct oc_private_data *data,
+ request_input_reply_cb_t cb,
+ void *user_data);
+
static bool is_valid_protocol(const char* protocol)
{
+ int num_protocols;
+ int i;
+ struct oc_vpn_proto *protos;
+
if (!protocol || !*protocol)
return false;
- return g_strv_contains(protocols, protocol);
+ num_protocols = openconnect_get_supported_protocols(&protos);
+
+ for (i = 0; i < num_protocols; i++)
+ if (!strcmp(protos[i].name, protocol))
+ break;
+
+ openconnect_free_supported_protocols(protos);
+
+ return i < num_protocols;
}
static void oc_connect_done(struct oc_private_data *data, int err)
@@ -139,11 +166,7 @@ static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
if (!data || !channel)
return;
- if (data->out_ch == channel) {
- id = data->out_ch_id;
- data->out_ch = NULL;
- data->out_ch_id = 0;
- } else if (data->err_ch == channel) {
+ if (data->err_ch == channel) {
id = data->err_ch_id;
data->err_ch = NULL;
data->err_ch_id = 0;
@@ -167,6 +190,9 @@ static void free_private_data(struct oc_private_data *data)
connman_info("provider %p", data->provider);
+ if (data->vpninfo)
+ openconnect_vpninfo_free(data->vpninfo);
+
if (vpn_provider_get_plugin_data(data->provider) == data)
vpn_provider_set_plugin_data(data->provider, NULL);
@@ -175,7 +201,6 @@ static void free_private_data(struct oc_private_data *data)
if (data->fd_in > 0)
close(data->fd_in);
data->fd_in = -1;
- close_io_channel(data, data->out_ch);
close_io_channel(data, data->err_ch);
g_free(data->dbus_sender);
@@ -357,6 +382,8 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
else
connman_ipaddress_set_ipv6(ipaddress, addressv6,
prefix_len, gateway);
+
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_domain(provider, domain);
@@ -371,7 +398,7 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
return VPN_STATE_CONNECT;
}
-static ssize_t full_write(int fd, const void *buf, size_t len)
+static ssize_t full_write(int fd, const char *buf, size_t len)
{
ssize_t byte_write;
@@ -426,37 +453,6 @@ static void oc_died(struct connman_task *task, int exit_code, void *user_data)
free_private_data(data);
}
-static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition,
- gpointer user_data)
-{
- struct oc_private_data *data;
- char *str;
-
- data = user_data;
-
- if (data->out_ch != source)
- return G_SOURCE_REMOVE;
-
- if ((condition & G_IO_IN) &&
- g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
- G_IO_STATUS_NORMAL) {
-
- g_strchomp(str);
-
- /* Only cookie is printed to stdout */
- vpn_provider_set_string_hide_value(data->provider,
- "OpenConnect.Cookie", str);
-
- g_free(str);
- } else if (condition & (G_IO_ERR | G_IO_HUP)) {
- connman_info("Out channel termination");
- close_io_channel(data, source);
- return G_SOURCE_REMOVE;
- }
-
- return G_SOURCE_CONTINUE;
-}
-
static bool strv_contains_prefix(const char *strv[], const char *str)
{
int i;
@@ -472,56 +468,171 @@ static bool strv_contains_prefix(const char *strv[], const char *str)
return false;
}
-static void clear_provider_credentials(struct vpn_provider *provider)
+static void clear_provider_credentials(struct vpn_provider *provider,
+ bool clear_pkcs_pass)
{
- const char *keys[] = { "OpenConnect.Username",
+ const char *keys[] = { "OpenConnect.PKCSPassword",
+ "OpenConnect.Username",
"OpenConnect.Password",
- "OpenConnect.PKCSPassword",
+ "OpenConnect.SecondPassword",
"OpenConnect.Cookie",
NULL
};
- int i;
+ size_t i;
connman_info("provider %p", provider);
- for (i = 0; keys[i]; i++) {
+ for (i = !clear_pkcs_pass; keys[i]; i++) {
if (!vpn_provider_get_string_immutable(provider, keys[i]))
vpn_provider_set_string_hide_value(provider, keys[i],
"-");
}
}
-typedef void (* request_input_reply_cb_t) (DBusMessage *reply,
- void *user_data);
+static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
-static int request_input_credentials(struct oc_private_data *data,
- request_input_reply_cb_t cb);
+ va_start(ap, fmt);
+ msg = g_strdup_vprintf(fmt, ap);
+
+ connman_debug("%s", msg);
+ g_free(msg);
+
+ va_end(ap);
+}
+
+/*
+ * There is no enum / defines for these in openconnect.h, but these values
+ * are based on the comment for openconnect_validate_peer_cert_vfn.
+ */
+enum oc_cert_status {
+ OC_CERT_ACCEPT = 0,
+ OC_CERT_REJECT = 1
+};
+
+struct validate_cert_data {
+ GMutex mutex;
+ GCond cond;
+ const char *reason;
+ struct oc_private_data *data;
+ bool processed;
+ enum oc_cert_status status;
+};
+
+static gboolean validate_cert(void *user_data)
+{
+ struct validate_cert_data *cert_data = user_data;
+ struct oc_private_data *data;
+ const char *server_cert;
+ bool allow_self_signed;
+
+ DBG("");
+
+ g_mutex_lock(&cert_data->mutex);
+
+ data = cert_data->data;
+ server_cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.ServerCert");
+ allow_self_signed = vpn_provider_get_boolean(data->provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false);
+
+ if (!allow_self_signed) {
+ cert_data->status = OC_CERT_REJECT;
+ } else if (server_cert) {
+ /*
+ * Check peer cert hash may return negative values on errors,
+ * but anything non-zero is acceptable.
+ */
+ cert_data->status = openconnect_check_peer_cert_hash(
+ data->vpninfo,
+ server_cert);
+ } else {
+ /*
+ * We could verify this from the agent at this point, and
+ * release the thread upon reply.
+ */
+ DBG("Server cert hash: %s",
+ openconnect_get_peer_cert_hash(data->vpninfo));
+ vpn_provider_set_string(data->provider,
+ "OpenConnect.ServerCert",
+ openconnect_get_peer_cert_hash(data->vpninfo));
+ cert_data->status = OC_CERT_ACCEPT;
+ }
+
+ cert_data->processed = true;
+ g_cond_signal(&cert_data->cond);
+ g_mutex_unlock(&cert_data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+static int oc_validate_peer_cert(void *user_data, const char *reason)
+{
+ struct validate_cert_data data = { .reason = reason,
+ .data = user_data,
+ .processed = false };
+
+ g_cond_init(&data.cond);
+ g_mutex_init(&data.mutex);
+
+ g_mutex_lock(&data.mutex);
+
+ g_idle_add(validate_cert, &data);
+ while (!data.processed)
+ g_cond_wait(&data.cond, &data.mutex);
+
+ g_mutex_unlock(&data.mutex);
+
+ g_mutex_clear(&data.mutex);
+ g_cond_clear(&data.cond);
+
+ return data.status;
+}
+
+struct process_form_data {
+ GMutex mutex;
+ GCond cond;
+ struct oc_auth_form *form;
+ struct oc_private_data *data;
+ bool processed;
+ int status;
+};
static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
{
- struct oc_private_data *data = user_data;
- const char *password = NULL;
+ struct process_form_data *form_data = user_data;
+ struct oc_private_data *data = form_data->data;
+ struct oc_form_opt *opt;
const char *key;
+ const char *password = NULL;
DBusMessageIter iter, dict;
- int err;
connman_info("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ data->err = ENOENT;
goto err;
+ }
- err = vpn_agent_check_and_process_reply_error(reply, data->provider,
- data->task, data->cb, data->user_data);
- if (err) {
- /* Ensure cb is called only once */
+ if ((data->err = vpn_agent_check_and_process_reply_error(reply,
+ data->provider,
+ data->task,
+ data->cb,
+ data->user_data))) {
data->cb = NULL;
data->user_data = NULL;
goto err;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ data->err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
@@ -551,39 +662,44 @@ static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
dbus_message_iter_next(&dict);
}
- if (data->connect_type != OC_CONNECT_PKCS || !password)
+ if (!password)
goto err;
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take PKCS pass phrase on"
- " stdin");
- goto err;
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
+
+ if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name,
+ "openconnect_pkcs")) {
+ opt->_value = strdup(password);
+ form_data->status = OC_FORM_RESULT_OK;
+ data->tried_passphrase = true;
+ break;
+ }
}
- clear_provider_credentials(data->provider);
+ goto out;
- return;
err:
- oc_connect_done(data, EACCES);
+ form_data->status = OC_FORM_RESULT_ERR;
+
+out:
+ form_data->processed = true;
+ g_cond_signal(&form_data->cond);
+ g_mutex_unlock(&form_data->mutex);
}
static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
- gpointer user_data)
+ gpointer user_data)
{
struct oc_private_data *data;
const char *auth_failures[] = {
- /* Login failed */
- "Got HTTP response: HTTP/1.1 401 Unauthorized",
- "Failed to obtain WebVPN cookie",
/* Cookie not valid */
"Got inappropriate HTTP CONNECT response: "
"HTTP/1.1 401 Unauthorized",
/* Invalid cookie */
"VPN service unavailable",
- /* Problem with certificates */
- "SSL connection failure",
- "Creating SSL connection failed",
- "SSL connection cancelled",
NULL
};
const char *conn_failures[] = {
@@ -591,21 +707,8 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
"Failed to open HTTPS connection to",
NULL
};
- /* Handle both PKCS#12 and PKCS#8 failures */
- const char *pkcs_failures[] = {
- "Failed to decrypt PKCS#12 certificate file",
- "Failed to decrypt PKCS#8 certificate file",
- NULL
- };
- /* Handle both PKCS#12 and PKCS#8 requests */
- const char *pkcs_requests[] = {
- "Enter PKCS#12 pass phrase",
- "Enter PKCS#8 pass phrase",
- NULL
- };
- const char *server_key_hash = " --servercert";
+ const char *server_key_hash = " --servercert ";
char *str;
- bool close = false;
int err = 0;
data = user_data;
@@ -618,51 +721,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
if ((condition & G_IO_IN)) {
gsize len;
- int pos;
-
- if (!data->interactive) {
- if (g_io_channel_read_line(source, &str, &len, NULL,
- NULL) != G_IO_STATUS_NORMAL)
- err = EIO;
- else
- str[len - 1] = '\0';
- } else {
- GIOStatus status;
- str = g_try_new0(char, OC_MAX_READBUF_LEN);
- if (!str)
- return G_SOURCE_REMOVE;
-
- for (pos = 0; pos < OC_MAX_READBUF_LEN - 1 ; ++pos) {
- status = g_io_channel_read_chars(source,
- str+pos, 1, &len, NULL);
-
- if (status == G_IO_STATUS_EOF) {
- break;
- } else if (status != G_IO_STATUS_NORMAL) {
- err = EIO;
- break;
- }
-
- /* Ignore control chars and digits at start */
- if (!pos && (g_ascii_iscntrl(str[pos]) ||
- g_ascii_isdigit(str[pos])))
- --pos;
-
- /* Read zero length or no more to read */
- if (!len || g_io_channel_get_buffer_condition(
- source) != G_IO_IN ||
- str[pos] == '\n')
- break;
- }
- /*
- * When self signed certificates are allowed and server
- * SHA1 fingerprint is printed to stderr there is a
- * newline char at the end of SHA1 fingerprint.
- */
- if (str[pos] == '\n')
- str[pos] = '\0';
- }
+ if (g_io_channel_read_line(source, &str, &len, NULL,
+ NULL) != G_IO_STATUS_NORMAL)
+ err = EIO;
+ else
+ g_strchomp(str);
connman_info("openconnect: %s", str);
@@ -687,44 +751,12 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
vpn_provider_set_string(data->provider,
"OpenConnect.ServerCert",
- fingerprint);
-
- /*
- * OpenConnect waits for "yes" or "no" as
- * response to certificate acceptance request.
- */
- if (write_data(data->fd_in, "yes") != 0)
- connman_error("openconnect: cannot "
- "write answer to certificate "
- "accept request");
-
+ str + strlen(server_key_hash));
} else {
connman_warn("Self signed certificate is not "
- " allowed");
-
- /*
- * Close IO channel to avoid deadlock as an
- * answer is expected for the certificate
- * accept request.
- */
- close = true;
+ "allowed");
err = ECONNREFUSED;
}
- } else if (strv_contains_prefix(pkcs_failures, str)) {
- connman_warn("PKCS failure: %s", str);
- close = true;
- err = EACCES;
- } else if (strv_contains_prefix(pkcs_requests, str)) {
- connman_info("PKCS file pass phrase request: %s", str);
- err = request_input_credentials(data,
- request_input_pkcs_reply);
-
- if (err != -EINPROGRESS) {
- err = EACCES;
- close = true;
- } else {
- err = 0;
- }
} else if (strv_contains_prefix(auth_failures, str)) {
connman_warn("authentication failed: %s", str);
err = EACCES;
@@ -736,13 +768,14 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
g_free(str);
} else if (condition & (G_IO_ERR | G_IO_HUP)) {
connman_info("Err channel termination");
- close = true;
+ close_io_channel(data, source);
+ return G_SOURCE_REMOVE;
}
if (err) {
switch (err) {
case EACCES:
- clear_provider_credentials(data->provider);
+ clear_provider_credentials(data->provider, true);
break;
case ECONNREFUSED:
/*
@@ -756,168 +789,320 @@ static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
oc_connect_done(data, err);
}
- if (close) {
- close_io_channel(data, source);
- return G_SOURCE_REMOVE;
- }
-
return G_SOURCE_CONTINUE;
}
-static int run_connect(struct oc_private_data *data)
+static gboolean process_auth_form(void *user_data)
{
- struct vpn_provider *provider;
- struct connman_task *task;
- const char *vpnhost;
- const char *vpncookie = NULL;
- const char *username;
- const char *password = NULL;
- const char *certificate = NULL;
- const char *private_key;
- const char *setting_str;
- bool setting;
- bool use_stdout = false;
- int fd_out = -1;
- int fd_err;
- int err = 0;
+ 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;
- if (!data)
- return -EINVAL;
+ g_mutex_lock(&form_data->mutex);
- provider = data->provider;
- task = data->task;
+ DBG("");
- connman_info("provider %p task %p", provider, task);
+ /*
+ * 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;
+ }
+ }
- switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- vpncookie = vpn_provider_get_string(provider,
- "OpenConnect.Cookie");
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- err = -EACCES;
- goto done;
+ connman_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;
}
+ }
- connman_task_add_argument(task, "--cookie-on-stdin", NULL);
- break;
+ switch (data->connect_type) {
+ case OC_CONNECT_USERPASS:
case OC_CONNECT_COOKIE_WITH_USERPASS:
- vpncookie = vpn_provider_get_string(provider,
- "OpenConnect.Cookie");
- /* No cookie set yet, username and password used first */
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- username = vpn_provider_get_string(provider,
- "OpenConnect.Username");
- password = vpn_provider_get_string(provider,
- "OpenConnect.Password");
- if (!username || !password ||
- !g_strcmp0(username, "-") ||
- !g_strcmp0(password, "-")) {
- err = -EACCES;
- goto done;
- }
+ break;
+
+ case OC_CONNECT_PKCS:
+ password = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSPassword");
- connman_task_add_argument(task, "--cookieonly", NULL);
- connman_task_add_argument(task, "--user", username);
- connman_task_add_argument(task, "--passwd-on-stdin",
- NULL);
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
- /* Use stdout only when cookie is to be read. */
- use_stdout = true;
- } else {
- connman_task_add_argument(task, "--cookie-on-stdin",
- NULL);
+ if (opt->type == OC_FORM_OPT_PASSWORD &&
+ g_str_has_prefix(opt->name,
+ "openconnect_pkcs"))
+ break;
}
- break;
- case OC_CONNECT_USERPASS:
- username = vpn_provider_get_string(provider,
- "OpenConnect.Username");
- password = vpn_provider_get_string(provider,
- "OpenConnect.Password");
- if (!username || !password || !g_strcmp0(username, "-") ||
- !g_strcmp0(password, "-")) {
- err = -EACCES;
- goto done;
+ if (opt) {
+ if (password && g_strcmp0(password, "-")) {
+ opt->_value = strdup(password);
+ data->tried_passphrase = true;
+ form_data->status = OC_FORM_RESULT_OK;
+ goto out;
+ } else {
+ if (data->tried_passphrase) {
+ vpn_provider_add_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ clear_provider_credentials(
+ data->provider,
+ true);
+ }
+ request_input_credentials_full(data,
+ request_input_pkcs_reply,
+ form_data);
+ return G_SOURCE_REMOVE;
+ }
}
- connman_task_add_argument(task, "--user", username);
- connman_task_add_argument(task, "--passwd-on-stdin", NULL);
- break;
+ /* fall-through */
+
+ /*
+ * In case of public key, reaching here means that the
+ * passphrase previously provided was incorrect.
+ */
case OC_CONNECT_PUBLICKEY:
- certificate = vpn_provider_get_string(provider,
- "OpenConnect.ClientCert");
- private_key = vpn_provider_get_string(provider,
- "OpenConnect.UserPrivateKey");
+ data->err = -EACCES;
+ clear_provider_credentials(data->provider, true);
- if (!certificate || !private_key) {
- err = -EACCES;
- goto done;
- }
+ /* fall-through */
+ default:
+ form_data->status = OC_FORM_RESULT_ERR;
+ goto out;
+ }
- connman_task_add_argument(task, "--certificate", certificate);
- connman_task_add_argument(task, "--sslkey", private_key);
- break;
- case OC_CONNECT_PKCS:
- certificate = vpn_provider_get_string(provider,
- "OpenConnect.PKCSClientCert");
- if (!certificate) {
- err = -EACCES;
- goto done;
+ /*
+ * Form values are released with free(), so always use strdup()
+ * instead of g_strdup()
+ */
+ for (opt = form_data->form->opts; opt; opt = opt->next) {
+ if (opt->flags & OC_FORM_OPT_IGNORE)
+ continue;
+
+ if (opt->type == OC_FORM_OPT_TEXT &&
+ g_str_has_prefix(opt->name, "user")) {
+ const char *user = vpn_provider_get_string(
+ data->provider,
+ "OpenConnect.Username");
+ if (user)
+ opt->_value = strdup(user);
+ } else if (opt->type == OC_FORM_OPT_PASSWORD &&
+ 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);
}
+ }
- connman_task_add_argument(task, "--certificate", certificate);
+ form_data->status = OC_FORM_RESULT_OK;
- password = vpn_provider_get_string(data->provider,
- "OpenConnect.PKCSPassword");
- /* Add password only if it is has been set */
- if (!password || !g_strcmp0(password, "-"))
- break;
+out:
+ form_data->processed = true;
+ g_cond_signal(&form_data->cond);
+ g_mutex_unlock(&form_data->mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+static int oc_process_auth_form(void *user_data, struct oc_auth_form *form)
+{
+ struct process_form_data data = { .form = form,
+ .data = user_data,
+ .processed = false };
+
+ DBG("");
+
+ g_cond_init(&data.cond);
+ g_mutex_init(&data.mutex);
+
+ g_mutex_lock(&data.mutex);
+ g_idle_add(process_auth_form, &data);
+
+ while (!data.processed)
+ g_cond_wait(&data.cond, &data.mutex);
+
+ g_mutex_unlock(&data.mutex);
+
+ g_mutex_clear(&data.mutex);
+ g_cond_clear(&data.cond);
+
+ return data.status;
+}
+
+static gboolean authenticated(void *user_data)
+{
+ struct oc_private_data *data = user_data;
+ int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread));
+
+ DBG("");
+
+ data->cookie_thread = NULL;
+
+ if (rv == 0)
+ rv = run_connect(data, openconnect_get_cookie(data->vpninfo));
+ else if (rv < 0)
+ clear_provider_credentials(data->provider, true);
+
+ openconnect_vpninfo_free(data->vpninfo);
+ data->vpninfo = NULL;
+
+ if (rv != -EINPROGRESS) {
+ oc_connect_done(data, data->err ? data->err : rv);
+ free_private_data(data);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void *obtain_cookie_thread(void *user_data)
+{
+ struct oc_private_data *data = user_data;
+ int ret;
+
+ DBG("%p", data->vpninfo);
+
+ ret = openconnect_obtain_cookie(data->vpninfo);
+
+ g_idle_add(authenticated, data);
+
+ return GINT_TO_POINTER(ret);
+}
+
+static int authenticate(struct oc_private_data *data)
+{
+ const char *cert = NULL;
+ const char *key = NULL;
+ const char *urlpath;
+ const char *vpnhost;
+
+ DBG("");
+
+ switch (data->connect_type) {
+ case OC_CONNECT_PKCS:
+ cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSClientCert");
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ cert = vpn_provider_get_string(data->provider,
+ "OpenConnect.ClientCert");
+ key = vpn_provider_get_string(data->provider,
+ "OpenConnect.UserPrivateKey");
+ break;
- connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ case OC_CONNECT_USERPASS:
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
break;
+
+ default:
+ return -EINVAL;
}
- vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ openconnect_init_ssl();
+ data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent",
+ oc_validate_peer_cert,
+ NULL,
+ oc_process_auth_form,
+ oc_progress,
+ data);
+
+ /* Replicating how openconnect's --usergroup argument works */
+ urlpath = vpn_provider_get_string(data->provider,
+ "OpenConnect.Usergroup");
+ if (urlpath)
+ openconnect_set_urlpath(data->vpninfo, urlpath);
+
+ if (vpn_provider_get_boolean(data->provider,
+ "OpenConnect.DisableIPv6", false))
+ openconnect_disable_ipv6(data->vpninfo);
+
+ vpnhost = vpn_provider_get_string(data->provider,
+ "OpenConnect.VPNHost");
if (!vpnhost || !*vpnhost)
- vpnhost = vpn_provider_get_string(provider, "Host");
+ vpnhost = vpn_provider_get_string(data->provider, "Host");
- task_append_config_data(provider, task);
+ openconnect_set_hostname(data->vpninfo, vpnhost);
+
+ if (cert)
+ openconnect_set_client_cert(data->vpninfo, cert, key);
+
+ data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo);
/*
- * To clarify complex situation, if cookie is expected to be printed
- * to stdout all other output must go to syslog. But with PKCS all
- * output must be caught in order to get message about file decryption
- * error. For this reason, the mode has to be interactive as well.
+ * openconnect_obtain_cookie blocks, so run it in background thread
+ * instead
*/
- switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- /* fall through */
- case OC_CONNECT_COOKIE_WITH_USERPASS:
- /* fall through */
- case OC_CONNECT_USERPASS:
- /* fall through */
- case OC_CONNECT_PUBLICKEY:
- connman_task_add_argument(task, "--syslog", NULL);
+ data->cookie_thread = g_thread_try_new("obtain_cookie",
+ obtain_cookie_thread,
+ data, NULL);
+
+ if (!data->cookie_thread)
+ return -EIO;
+
+ return -EINPROGRESS;
+}
+
+static int run_connect(struct oc_private_data *data, const char *cookie)
+{
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ const char *vpnhost;
+ int fd_err;
+ int err = 0;
+ bool allow_self_signed;
+ const char *server_cert;
+
+ if (!data || !cookie)
+ return -EINVAL;
+
+ provider = data->provider;
+ task = data->task;
- setting = vpn_provider_get_boolean(provider,
+ server_cert = vpn_provider_get_string(provider,
+ "OpenConnect.ServerCert");
+ allow_self_signed = vpn_provider_get_boolean(provider,
"OpenConnect.AllowSelfSignedCert",
false);
- setting_str = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- /*
- * Run in interactive mode if self signed certificates are
- * allowed and there is no set server SHA1 fingerprint.
- */
- if (setting_str || !setting)
- connman_task_add_argument(task, "--non-inter", NULL);
- else
- data->interactive = true;
- break;
- case OC_CONNECT_PKCS:
- data->interactive = true;
- break;
- }
+ DBG("provider %p task %p", provider, task);
+
+ connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+
+ vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ if (!vpnhost || !*vpnhost)
+ vpnhost = vpn_provider_get_string(provider, "Host");
+
+ task_append_config_data(provider, task);
connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
@@ -925,66 +1110,30 @@ static int run_connect(struct oc_private_data *data)
connman_task_add_argument(task, (char *)vpnhost, NULL);
- err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ?
- &fd_out : NULL, &fd_err);
+ err = connman_task_run(task, oc_died, data, &data->fd_in,
+ NULL, &fd_err);
if (err < 0) {
err = -EIO;
goto done;
}
- switch (data->connect_type) {
- case OC_CONNECT_COOKIE:
- if (write_data(data->fd_in, vpncookie) != 0) {
- connman_error("openconnect failed to take cookie on "
- "stdin");
- err = -EIO;
- }
-
- break;
- case OC_CONNECT_USERPASS:
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take password on "
- "stdin");
- err = -EIO;
- }
-
- break;
- case OC_CONNECT_COOKIE_WITH_USERPASS:
- if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take "
- "password on stdin");
- err = -EIO;
- }
- } else {
- if (write_data(data->fd_in, vpncookie) != 0) {
- connman_error("openconnect failed to take "
- "cookie on stdin");
- err = -EIO;
- }
- }
-
- break;
- case OC_CONNECT_PUBLICKEY:
- break;
- case OC_CONNECT_PKCS:
- if (!password || !g_strcmp0(password, "-"))
- break;
+ if (write_data(data->fd_in, cookie) != 0) {
+ connman_error("openconnect failed to take cookie on "
+ "stdin");
+ err = -EIO;
+ }
- if (write_data(data->fd_in, password) != 0) {
- connman_error("openconnect failed to take PKCS "
- "pass phrase on stdin");
+ if (!server_cert || !allow_self_signed) {
+ if (write_data(data->fd_in,
+ (allow_self_signed ? "yes" : "no"))) {
+ connman_error("openconnect failed to take certificate "
+ "acknowledgement on stdin");
err = -EIO;
}
-
- break;
}
if (err) {
- if (fd_out > 0)
- close(fd_out);
-
- if (fd_err > 0)
+ if (fd_err >= 0)
close(fd_err);
goto done;
@@ -992,22 +1141,6 @@ static int run_connect(struct oc_private_data *data)
err = -EINPROGRESS;
- if (use_stdout) {
- data->out_ch = g_io_channel_unix_new(fd_out);
-
- /* Use ASCII encoding only */
- if (g_io_channel_set_encoding(data->out_ch, NULL, NULL) !=
- G_IO_STATUS_NORMAL) {
- close_io_channel(data, data->out_ch);
- err = -EIO;
- } else {
- data->out_ch_id = g_io_add_watch(data->out_ch,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- (GIOFunc)io_channel_out_cb,
- data);
- }
- }
-
data->err_ch = g_io_channel_unix_new(fd_err);
/* Use ASCII encoding only */
@@ -1022,7 +1155,7 @@ static int run_connect(struct oc_private_data *data)
}
done:
- clear_provider_credentials(data->provider);
+ clear_provider_credentials(data->provider, err != -EINPROGRESS);
return err;
}
@@ -1112,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;
@@ -1119,8 +1253,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
connman_info("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ err = ENOENT;
goto err;
+ }
err = vpn_agent_check_and_process_reply_error(reply, data->provider,
data->task, data->cb, data->user_data);
@@ -1131,8 +1267,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
goto out;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
@@ -1205,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)
@@ -1224,41 +1375,53 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
switch (data->connect_type) {
case OC_CONNECT_COOKIE:
- if (!cookie)
+ if (!cookie) {
+ err = EACCES;
goto err;
+ }
break;
case OC_CONNECT_USERPASS:
/* fall through */
case OC_CONNECT_COOKIE_WITH_USERPASS:
- if (!username || !password)
+ if (!username || !password) {
+ err = EACCES;
goto err;
+ }
break;
case OC_CONNECT_PUBLICKEY:
break; // This should not be reached.
case OC_CONNECT_PKCS:
- if (!pkcspassword)
+ if (!pkcspassword) {
+ err = EACCES;
goto err;
+ }
break;
}
- err = run_connect(data);
+ if (cookie)
+ err = run_connect(data, cookie);
+ else
+ err = authenticate(data);
+
if (err != -EINPROGRESS)
goto err;
return;
err:
- oc_connect_done(data, EACCES);
+ oc_connect_done(data, err);
out:
free_private_data(data);
}
-static int request_input_credentials(struct oc_private_data *data,
- request_input_reply_cb_t cb)
+static int request_input_credentials_full(
+ struct oc_private_data *data,
+ request_input_reply_cb_t cb,
+ void *user_data)
{
DBusMessage *message;
const char *path;
@@ -1269,6 +1432,7 @@ static int request_input_credentials(struct oc_private_data *data,
DBusMessageIter dict;
int err;
void *agent;
+ bool use_second_password = false;
if (!data || !cb)
return -ESRCH;
@@ -1299,7 +1463,7 @@ static int request_input_credentials(struct oc_private_data *data,
/*
* For backwards compatibility add OpenConnect.ServerCert and
- * OpenConnect.VPNHost as madnatory only in the default authentication
+ * OpenConnect.VPNHost as mandatory only in the default authentication
* mode. Otherwise. add the fields as informational. These should be
* set in provider settings and not to be queried with every connection
* attempt.
@@ -1335,6 +1499,16 @@ static int request_input_credentials(struct oc_private_data *data,
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;
@@ -1343,6 +1517,16 @@ static int request_input_credentials(struct oc_private_data *data,
request_input_append_informational,
"OpenConnect.PKCSClientCert");
+ /* Do not allow to store or retrieve the encrypted PKCS pass */
+ vpn_agent_append_allow_credential_storage(&dict, false);
+ vpn_agent_append_allow_credential_retrieval(&dict, false);
+
+ /*
+ * Indicate to keep credentials, the PKCS password should not
+ * affect the credential storing.
+ */
+ vpn_agent_append_keep_credentials(&dict, true);
+
request_input_append_to_dict(data->provider, &dict,
request_input_append_password,
"OpenConnect.PKCSPassword");
@@ -1354,7 +1538,7 @@ static int request_input_credentials(struct oc_private_data *data,
connman_dbus_dict_close(&iter, &dict);
err = connman_agent_queue_message(data->provider, message,
- connman_timeout_input_request(), cb, data, agent);
+ connman_timeout_input_request(), cb, user_data, agent);
dbus_message_unref(message);
@@ -1366,6 +1550,12 @@ static int request_input_credentials(struct oc_private_data *data,
return -EINPROGRESS;
}
+static int request_input_credentials(struct oc_private_data *data,
+ request_input_reply_cb_t cb)
+{
+ return request_input_credentials_full(data, cb, data);
+}
+
static enum oc_connect_type get_authentication_type(
struct vpn_provider *provider)
{
@@ -1395,12 +1585,14 @@ static int oc_connect(struct vpn_provider *provider,
const char *dbus_sender, void *user_data)
{
struct oc_private_data *data;
- const char *vpncookie;
+ const char *vpncookie = NULL;
const char *certificate;
const char *username;
const char *password;
+ const char *second_password = NULL;
const char *private_key;
int err;
+ bool use_second_password = false;
connman_info("provider %p task %p", provider, task);
@@ -1430,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;
@@ -1481,7 +1683,9 @@ static int oc_connect(struct vpn_provider *provider,
break;
}
- return run_connect(data);
+ if (vpncookie && g_strcmp0(vpncookie, "-"))
+ return run_connect(data, vpncookie);
+ return authenticate(data);
request_input:
err = request_input_credentials(data, request_input_credentials_reply);
@@ -1497,6 +1701,8 @@ request_input:
static void oc_disconnect(struct vpn_provider *provider)
{
+ struct oc_private_data *data;
+
connman_info("provider %p", provider);
if (!provider)
@@ -1508,6 +1714,19 @@ static void oc_disconnect(struct vpn_provider *provider)
* agent request to avoid having multiple ones visible.
*/
connman_agent_cancel(provider);
+
+ data = vpn_provider_get_plugin_data(provider);
+
+ if (!data)
+ return;
+
+ if (data->cookie_thread) {
+ char cmd = OC_CMD_CANCEL;
+ int w = write(data->fd_cmd, &cmd, 1);
+ if (w != 1)
+ DBG("Write failed, might be leaking a thread");
+ }
+
}
static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
@@ -1547,7 +1766,7 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
switch (exit_code) {
case 2:
/* Cookie has failed */
- clear_provider_credentials(provider);
+ clear_provider_credentials(provider, false);
return VPN_PROVIDER_ERROR_LOGIN_FAILED;
case 1:
/* fall through */
@@ -1557,7 +1776,8 @@ static int oc_error_code(struct vpn_provider *provider, int exit_code)
}
static int oc_route_env_parse(struct vpn_provider *provider, const char *key,
- int *family, unsigned long *idx, enum vpn_provider_route_type *type)
+ int *family, unsigned long *idx,
+ enum vpn_provider_route_type *type)
{
char *end;
const char *start;
diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c
index bc0303c2..6e288efe 100644
--- a/vpn/plugins/openvpn.c
+++ b/vpn/plugins/openvpn.c
@@ -51,39 +51,53 @@
#include "../vpn-agent.h"
#include "vpn.h"
-#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
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 },
+ { "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 {
@@ -290,6 +304,7 @@ static int ov_notify(DBusMessage *msg, struct vpn_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
connman_ipaddress_set_peer(ipaddress, peer);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
if (nameserver_list) {
@@ -352,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;
+
+ 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;
- if (connman_task_add_argument(task,
- ov_options[i].ov_opt,
- ov_options[i].has_value ? option : NULL) < 0)
+ /* 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;
}
@@ -497,16 +532,13 @@ static int run_connect(struct ov_private_data *data,
connman_task_add_argument(task, "--ifconfig-noexec", NULL);
/*
- * Disable client restarts because we can't handle this at the
- * moment. The problem is that when OpenVPN decides to switch
+ * Disable client restarts with TCP because we can't handle this at
+ * the moment. The problem is that when OpenVPN decides to switch
* from CONNECTED state to RECONNECTING and then to RESOLVE,
* it is not possible to do a DNS lookup. The DNS server is
* not accessible through the tunnel anymore and so we end up
* trying to resolve the OpenVPN servers address.
- */
- connman_task_add_argument(task, "--ping-restart", "0");
-
- /*
+ *
* Disable connetion retrying when OpenVPN is connected over TCP.
* With TCP OpenVPN attempts to handle reconnection silently without
* reporting the error back when establishing a connection or
@@ -516,8 +548,24 @@ static int run_connect(struct ov_private_data *data,
* including DNS.
*/
option = vpn_provider_get_string(provider, "OpenVPN.Proto");
- if (option && g_str_has_prefix(option, "tcp"))
+ if (option && g_str_has_prefix(option, "tcp")) {
+ option = vpn_provider_get_string(provider, "OpenVPN.PingExit");
+ if (!option)
+ connman_task_add_argument(task, "--ping-restart", "0");
+
connman_task_add_argument(task, "--connect-retry-max", "1");
+ /* Apply defaults for --ping and --ping-exit only with UDP protocol. */
+ } else {
+ /* Apply default of 10 second interval for ping if omitted. */
+ option = vpn_provider_get_string(provider, "OpenVPN.Ping");
+ if (!option)
+ connman_task_add_argument(task, "--ping", "10");
+
+ /* Apply default of 60 seconds for ping exit if omitted. */
+ option = vpn_provider_get_string(provider, "OpenVPN.PingExit");
+ if (!option)
+ connman_task_add_argument(task, "--ping-exit", "60");
+ }
err = connman_task_run(task, ov_died, data, NULL, NULL, NULL);
if (err < 0) {
@@ -1083,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;
diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c
index 5fc861e4..7274376f 100644
--- a/vpn/plugins/pptp.c
+++ b/vpn/plugins/pptp.c
@@ -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
@@ -54,15 +55,18 @@
enum {
OPT_STRING = 1,
OPT_BOOL = 2,
+ OPT_PPTP_ONLY = 3,
};
struct {
const char *cm_opt;
const char *pptp_opt;
- const char *vpnc_default;
+ const char *pptp_default;
int type;
} pptp_options[] = {
{ "PPTP.User", "user", NULL, OPT_STRING },
+ { "PPTP.IdleWait", "--idle-wait", NULL, OPT_PPTP_ONLY},
+ { "PPTP.MaxEchoWait", "--max-echo-wait", NULL, OPT_PPTP_ONLY},
{ "PPPD.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
{ "PPPD.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
{ "PPPD.Debug", "debug", NULL, OPT_STRING },
@@ -83,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)
{
@@ -122,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);
@@ -140,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);
@@ -204,6 +250,7 @@ static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
vpn_provider_set_nameservers(provider, nameservers);
@@ -213,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;
}
@@ -274,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;
@@ -431,16 +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)
{
- const char *opt_s, *host;
+ 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;
@@ -450,16 +510,11 @@ static int run_connect(struct vpn_provider *provider,
DBG("username %s password %p", username, password);
host = vpn_provider_get_string(provider, "Host");
- str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
- PPTP, host);
- if (!str) {
- connman_error("can not allocate memory");
- err = -ENOMEM;
- goto done;
- }
- connman_task_add_argument(task, "pty", str);
- g_free(str);
+ /* Create PPTP options for pppd "pty" */
+ pptp_opt_s = g_string_new(NULL);
+ g_string_append_printf(pptp_opt_s, "%s %s --nolaunchpppd --loglevel 2",
+ PPTP, host);
connman_task_add_argument(task, "nodetach", NULL);
connman_task_add_argument(task, "lock", NULL);
@@ -474,7 +529,7 @@ static int run_connect(struct vpn_provider *provider,
opt_s = vpn_provider_get_string(provider,
pptp_options[i].cm_opt);
if (!opt_s)
- opt_s = pptp_options[i].vpnc_default;
+ opt_s = pptp_options[i].pptp_default;
if (!opt_s)
continue;
@@ -485,32 +540,31 @@ static int run_connect(struct vpn_provider *provider,
else if (pptp_options[i].type == OPT_BOOL)
pptp_write_bool_option(task,
pptp_options[i].pptp_opt, opt_s);
+ else if (pptp_options[i].type == OPT_PPTP_ONLY)
+ g_string_append_printf(pptp_opt_s, " %s %s",
+ pptp_options[i].pptp_opt, opt_s);
}
+ str = g_string_free(pptp_opt_s, FALSE);
+ connman_task_add_argument(task, "pty", str);
+ g_free(str);
+
connman_task_add_argument(task, "plugin",
SCRIPTDIR "/libppp-plugin.so");
- 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,
@@ -518,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)
@@ -528,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,
@@ -539,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",
@@ -555,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;
}
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
index e04670c8..cb0d304b 100644
--- a/vpn/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -65,7 +65,7 @@ struct vpn_data {
struct vpn_driver_data {
const char *name;
const char *program;
- struct vpn_driver *vpn_driver;
+ const struct vpn_driver *vpn_driver;
struct vpn_provider_driver provider_driver;
};
@@ -421,61 +421,26 @@ exist_err:
return ret;
}
-static gboolean is_numeric(const char *str)
+static gid_t get_gid(const char *group_name)
{
- gint i;
-
- if(!str || !(*str))
- return false;
-
- for(i = 0; str[i] ; i++) {
- if(!g_ascii_isdigit(str[i]))
- return false;
- }
-
- return true;
-}
-
-static gint get_gid(const char *group_name)
-{
- gint gid = -1;
struct group *grp;
- if(!group_name || !(*group_name))
- return gid;
-
- if (is_numeric(group_name)) {
- gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10);
- grp = getgrgid(group_id);
- } else {
- grp = getgrnam(group_name);
- }
-
+ grp = vpn_util_get_group(group_name);
if (grp)
- gid = grp->gr_gid;
+ return grp->gr_gid;
- return gid;
+ return -1;
}
-static gint get_uid(const char *user_name)
+static uid_t get_uid(const char *user_name)
{
- gint uid = -1;
struct passwd *pw;
- if(!user_name || !(*user_name))
- return uid;
-
- if (is_numeric(user_name)) {
- uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10);
- pw = getpwuid(user_id);
- } else {
- pw = getpwnam(user_name);
- }
-
+ pw = vpn_util_get_passwd(user_name);
if (pw)
- uid = pw->pw_uid;
+ return pw->pw_uid;
- return uid;
+ return -1;
}
static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
@@ -508,8 +473,8 @@ static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
static void vpn_task_setup(gpointer user_data)
{
struct vpn_plugin_data *data;
- gint uid;
- gint gid;
+ uid_t uid;
+ gid_t gid;
gid_t *gid_list = NULL;
size_t gid_list_size;
const gchar *user;
@@ -632,7 +597,7 @@ static int vpn_connect(struct vpn_provider *provider,
vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) {
ret = vpn_driver_data->vpn_driver->connect(provider,
- NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, cb, dbus_sender, user_data);
if (ret) {
stop_vpn(provider);
goto exist_err;
@@ -812,7 +777,7 @@ static int vpn_route_env_parse(struct vpn_provider *provider, const char *key,
return 0;
}
-int vpn_register(const char *name, struct vpn_driver *vpn_driver,
+int vpn_register(const char *name, const struct vpn_driver *vpn_driver,
const char *program)
{
struct vpn_driver_data *data;
diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h
index 71e04f61..fd10addf 100644
--- a/vpn/plugins/vpn.h
+++ b/vpn/plugins/vpn.h
@@ -58,7 +58,7 @@ struct vpn_driver {
enum vpn_provider_route_type *type);
};
-int vpn_register(const char *name, struct vpn_driver *driver,
+int vpn_register(const char *name, const struct vpn_driver *driver,
const char *program);
void vpn_unregister(const char *provider_name);
void vpn_died(struct connman_task *task, int exit_code, void *user_data);
diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 8350fc3c..73012c5b 100644
--- a/vpn/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -30,6 +30,10 @@
#include <stdio.h>
#include <net/if.h>
#include <linux/if_tun.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
#include <glib.h>
@@ -50,6 +54,7 @@
#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define PID_PATH_ROOT RUNSTATEDIR "/user"
enum {
OPT_STRING = 1,
@@ -240,6 +245,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
}
connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
+ connman_ipaddress_set_p2p(ipaddress, true);
vpn_provider_set_ipaddress(provider, ipaddress);
g_free(address);
@@ -430,14 +436,49 @@ static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
return G_SOURCE_CONTINUE;
}
+static char *create_pid_path(const char *user, const char *group)
+{
+ struct passwd *pwd;
+ struct group *grp;
+ char *uid_str;
+ char *pid_path = NULL;
+ int mode = S_IRWXU|S_IRWXG;
+ gid_t gid;
+
+ if (!user || !*user)
+ return NULL;
+
+ if (vpn_settings_is_system_user(user))
+ return NULL;
+
+ pwd = vpn_util_get_passwd(user);
+ uid_str = g_strdup_printf("%d", pwd->pw_uid);
+
+ grp = vpn_util_get_group(group);
+ gid = grp ? grp->gr_gid : pwd->pw_gid;
+
+ pid_path = g_build_filename(PID_PATH_ROOT, uid_str, "vpnc", "pid",
+ NULL);
+ if (vpn_util_create_path(pid_path, pwd->pw_uid, gid, mode)) {
+ g_free(pid_path);
+ pid_path = NULL;
+ }
+
+ g_free(uid_str);
+
+ return pid_path;
+}
+
static int run_connect(struct vc_private_data *data)
{
struct vpn_provider *provider;
struct connman_task *task;
+ struct vpn_plugin_data *plugin_data;
const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username",
"VPNC.Xauth.Password", NULL};
const char *if_name;
const char *option;
+ char *pid_path;
int err;
int fd_in;
int fd_err;
@@ -473,6 +514,20 @@ static int run_connect(struct vc_private_data *data)
connman_task_add_argument(task, "--ifmode", "tun");
}
+ plugin_data = vpn_settings_get_vpn_plugin_config("vpnc");
+
+ option = vpn_settings_get_binary_user(plugin_data);
+ if (option) {
+ pid_path = create_pid_path(option,
+ vpn_settings_get_binary_group(
+ plugin_data));
+ if (pid_path)
+ connman_task_add_argument(task, "--pid-file",
+ pid_path);
+
+ g_free(pid_path);
+ }
+
connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
option = vpn_provider_get_string(provider, "VPNC.Debug");
@@ -619,8 +674,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
DBG("provider %p", data->provider);
- if (!reply)
+ if (!reply) {
+ err = ENOENT;
goto err;
+ }
err = vpn_agent_check_and_process_reply_error(reply, data->provider,
data->task, data->cb, data->user_data);
@@ -631,8 +688,10 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
return;
}
- if (!vpn_agent_check_reply_has_dict(reply))
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
goto err;
+ }
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &dict);
@@ -687,17 +746,22 @@ static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
dbus_message_iter_next(&dict);
}
- if (!secret || !username || !password)
+ if (!secret || !username || !password) {
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ err = EACCES;
goto err;
+ }
- err = run_connect(data);
- if (err != -EINPROGRESS)
+ /* vpn_provider.c:connect_cb() expects positive errors */
+ err = -run_connect(data);
+ if (err != EINPROGRESS)
goto err;
return;
err:
- vc_connect_done(data, EACCES);
+ vc_connect_done(data, err);
}
static int request_input_credentials(struct vc_private_data *data,
diff --git a/vpn/plugins/wireguard.c b/vpn/plugins/wireguard.c
index de2dbda3..03658943 100644
--- a/vpn/plugins/wireguard.c
+++ b/vpn/plugins/wireguard.c
@@ -49,6 +49,24 @@
#include "vpn.h"
#include "wireguard.h"
+#define DNS_RERESOLVE_TIMEOUT 20
+
+struct wireguard_info {
+ struct wg_device device;
+ struct wg_peer peer;
+ char *endpoint_fqdn;
+ char *port;
+ int reresolve_id;
+};
+
+struct sockaddr_u {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ };
+};
+
static int parse_key(const char *str, wg_key key)
{
unsigned char *buf;
@@ -116,7 +134,7 @@ static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
return 0;
}
-static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
+static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
@@ -151,7 +169,7 @@ static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
return -EINVAL;
}
- memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+ memcpy(addr, rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(result);
return 0;
@@ -194,6 +212,8 @@ static int parse_address(const char *address, const char *gateway,
err = -EINVAL;
}
+ connman_ipaddress_set_p2p(*ipaddress, true);
+
g_strfreev(tokens);
if (err)
connman_ipaddress_free(*ipaddress);
@@ -225,7 +245,7 @@ static char *get_ifname(void)
for (i = 0; i < 256; i++) {
data.ifname = g_strdup_printf("wg%d", i);
data.found = false;
- __vpn_ipconfig_foreach(ifname_check_cb, &data);
+ vpn_ipconfig_foreach(ifname_check_cb, &data);
if (!data.found)
return data.ifname;
@@ -236,10 +256,53 @@ static char *get_ifname(void)
return NULL;
}
-struct wireguard_info {
- struct wg_device device;
- struct wg_peer peer;
-};
+static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family)
+ return false;
+
+ if (a->sa.sa_family == AF_INET)
+ return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
+ else if (a->sa.sa_family == AF_INET6)
+ return !memcmp(a->sin6.sin6_addr.s6_addr,
+ b->sin6.sin6_addr.s6_addr,
+ sizeof(a->sin6.sin6_addr.s6_addr));
+
+ return false;
+}
+
+static gboolean wg_dns_reresolve_cb(gpointer user_data)
+{
+ struct wireguard_info *info = user_data;
+ struct sockaddr_u addr;
+ int err;
+
+ DBG("");
+
+ err = parse_endpoint(info->endpoint_fqdn,
+ info->port, &addr);
+ if (err)
+ return TRUE;
+
+ if (sockaddr_cmp_addr(&addr,
+ (struct sockaddr_u *)&info->peer.endpoint.addr))
+ return TRUE;
+
+ if (addr.sa.sa_family == AF_INET)
+ memcpy(&info->peer.endpoint.addr, &addr.sin,
+ sizeof(info->peer.endpoint.addr4));
+ else
+ memcpy(&info->peer.endpoint.addr, &addr.sin6,
+ sizeof(info->peer.endpoint.addr6));
+
+ DBG("Endpoint address has changed, udpate WireGuard device");
+ err = wg_set_device(&info->device);
+ if (err)
+ DBG("Failed to update Endpoint address for WireGuard device %s",
+ info->device.name);
+
+ return TRUE;
+}
static int wg_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name,
@@ -323,10 +386,14 @@ static int wg_connect(struct vpn_provider *provider,
option = "51820";
gateway = vpn_provider_get_string(provider, "Host");
- err = parse_endpoint(gateway, option, &info->peer);
+ err = parse_endpoint(gateway, option,
+ (struct sockaddr_u *)&info->peer.endpoint.addr);
if (err)
goto done;
+ info->endpoint_fqdn = g_strdup(gateway);
+ info->port = g_strdup(option);
+
option = vpn_provider_get_string(provider, "WireGuard.Address");
if (!option) {
DBG("Missing WireGuard.Address configuration");
@@ -342,7 +409,7 @@ static int wg_connect(struct vpn_provider *provider,
err = -ENOENT;
goto done;
}
- stpncpy(info->device.name, ifname, sizeof(info->device.name));
+ stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
g_free(ifname);
err = wg_add_device(info->device.name);
@@ -367,6 +434,11 @@ done:
connman_ipaddress_free(ipaddress);
+ if (!err)
+ info->reresolve_id =
+ g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
+ wg_dns_reresolve_cb, info);
+
return err;
}
@@ -377,10 +449,16 @@ static void wg_disconnect(struct vpn_provider *provider)
info = vpn_provider_get_plugin_data(provider);
if (!info)
return;
+
+ if (info->reresolve_id > 0)
+ g_source_remove(info->reresolve_id);
+
vpn_provider_set_plugin_data(provider, NULL);
wg_del_device(info->device.name);
+ g_free(info->endpoint_fqdn);
+ g_free(info->port);
g_free(info);
}
diff --git a/vpn/vpn-config.c b/vpn/vpn-config.c
index f56e51ee..8c6b0681 100644
--- a/vpn/vpn-config.c
+++ b/vpn/vpn-config.c
@@ -229,7 +229,7 @@ static int load_provider(GKeyFile *keyfile, const char *group,
host = get_string(config_provider, "Host");
domain = get_string(config_provider, "Domain");
- if (host && domain) {
+ if (host) {
char *id = __vpn_provider_create_identifier(host, domain);
struct vpn_provider *provider;
@@ -252,7 +252,7 @@ static int load_provider(GKeyFile *keyfile, const char *group,
DBG("provider identifier %s", id);
} else {
- DBG("invalid values host %s domain %s", host, domain);
+ DBG("invalid configuration: no host specified");
err = -EINVAL;
goto err;
}
@@ -580,3 +580,18 @@ char **__vpn_config_get_string_list(GKeyFile *key_file,
return strlist;
}
+
+bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name,
+ const char *key, bool default_value)
+{
+ GError *error = NULL;
+ bool val;
+
+ val = g_key_file_get_boolean(key_file, group_name, key, &error);
+ if (error) {
+ g_error_free(error);
+ return default_value;
+ }
+
+ return val;
+}
diff --git a/vpn/vpn-ipconfig.c b/vpn/vpn-ipconfig.c
index c096fa37..825b43c4 100644
--- a/vpn/vpn-ipconfig.c
+++ b/vpn/vpn-ipconfig.c
@@ -108,7 +108,7 @@ unsigned int __vpn_ipconfig_get_flags_from_index(int index)
return ipdevice->flags;
}
-void __vpn_ipconfig_foreach(void (*function) (int index,
+void vpn_ipconfig_foreach(void (*function) (int index,
void *user_data), void *user_data)
{
GList *list, *keys;
@@ -211,7 +211,7 @@ int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family)
if (family == AF_INET)
return connman_inet_set_address(ipconfig->index,
- ipconfig->address);
+ ipconfig->address);
else if (family == AF_INET6)
return connman_inet_set_ipv6_address(ipconfig->index,
ipconfig->address);
@@ -282,7 +282,10 @@ static struct vpn_ipconfig *create_ipv6config(int index)
return NULL;
}
+ connman_ipaddress_set_p2p(ipv6config->address, true);
+
ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+ connman_ipaddress_set_p2p(ipv6config->system, true);
DBG("ipconfig %p", ipv6config);
@@ -314,7 +317,10 @@ struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family)
return NULL;
}
+ connman_ipaddress_set_p2p(ipconfig->address, true);
+
ipconfig->system = connman_ipaddress_alloc(AF_INET);
+ connman_ipaddress_set_p2p(ipconfig->system, true);
DBG("ipconfig %p", ipconfig);
diff --git a/vpn/vpn-polkit.policy b/vpn/vpn-polkit.policy
index 0c427220..b187b377 100644
--- a/vpn/vpn-polkit.policy
+++ b/vpn/vpn-polkit.policy
@@ -13,7 +13,7 @@
<message>Policy prevents modification of settings</message>
<defaults>
<allow_inactive>no</allow_inactive>
- <allow_active>auth_self_keep_session</allow_active>
+ <allow_active>auth_self_keep</allow_active>
</defaults>
</action>
@@ -22,7 +22,7 @@
<message>Policy prevents modification of secrets</message>
<defaults>
<allow_inactive>no</allow_inactive>
- <allow_active>auth_admin_keep_session</allow_active>
+ <allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index 5ce93287..cc325967 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -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
@@ -24,6 +24,21 @@
#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"
@@ -44,7 +60,6 @@ static DBusConnection *connection;
static GHashTable *provider_hash;
static GSList *driver_list;
static int configuration_count;
-static bool handle_routes;
struct vpn_route {
int family;
@@ -71,6 +86,7 @@ struct vpn_provider {
char *host;
char *domain;
int family;
+ bool do_split_routing;
GHashTable *routes;
struct vpn_provider_driver *driver;
void *driver_data;
@@ -89,14 +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;
@@ -366,22 +425,8 @@ static void set_user_networks(struct vpn_provider *provider, GSList *networks)
static void del_routes(struct vpn_provider *provider)
{
GHashTableIter hash;
- gpointer value, key;
g_hash_table_iter_init(&hash, provider->user_routes);
- while (handle_routes && g_hash_table_iter_next(&hash,
- &key, &value)) {
- struct vpn_route *route = value;
- if (route->family == AF_INET6) {
- unsigned char prefixlen = atoi(route->netmask);
- connman_inet_del_ipv6_network_route(provider->index,
- route->network,
- prefixlen);
- } else
- connman_inet_del_host_route(provider->index,
- route->network);
- }
-
g_hash_table_remove_all(provider->user_routes);
g_slist_free_full(provider->user_networks, free_route);
provider->user_networks = NULL;
@@ -404,6 +449,16 @@ static void send_value(const char *path, const char *key, const char *value)
&str);
}
+static void send_value_boolean(const char *path, const char *key,
+ dbus_bool_t value)
+{
+ connman_dbus_property_changed_basic(path,
+ VPN_CONNECTION_INTERFACE,
+ key,
+ DBUS_TYPE_BOOLEAN,
+ &value);
+}
+
static gboolean provider_send_changed(gpointer data)
{
struct vpn_provider *provider = data;
@@ -474,6 +529,11 @@ static bool compare_network_lists(GSList *a, GSList *b)
return true;
}
+static const char *bool2str(bool value)
+{
+ return value ? "true" : "false";
+}
+
static int set_provider_property(struct vpn_provider *provider,
const char *name, DBusMessageIter *value, int type)
{
@@ -500,10 +560,17 @@ static int set_provider_property(struct vpn_provider *provider,
del_routes(provider);
provider->user_networks = networks;
set_user_networks(provider, provider->user_networks);
+ send_routes(provider, provider->user_routes, "UserRoutes");
+ } else if (g_str_equal(name, "SplitRouting")) {
+ dbus_bool_t split_routing;
+
+ if (type != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
- if (!handle_routes)
- send_routes(provider, provider->user_routes,
- "UserRoutes");
+ dbus_message_iter_get_basic(value, &split_routing);
+
+ DBG("property %s value %s ", name, bool2str(split_routing));
+ vpn_provider_set_boolean(provider, name, split_routing, false);
} else {
const char *str;
@@ -576,8 +643,13 @@ static DBusMessage *set_properties(DBusMessageIter *iter, DBusMessage *msg,
dbus_message_iter_recurse(&entry, &value);
type = dbus_message_iter_get_arg_type(&value);
- /* Ignore and report back all invalid property types */
- if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_ARRAY) {
+ switch (type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_BOOLEAN:
+ break;
+ default:
+ /* Ignore and report back all invalid property types */
invalid = append_to_gstring(invalid, key);
continue;
}
@@ -702,8 +774,7 @@ static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
del_routes(provider);
- if (!handle_routes)
- send_routes(provider, provider->user_routes, name);
+ send_routes(provider, provider->user_routes, name);
} else if (vpn_provider_get_string(provider, name)) {
err = vpn_provider_set_string(provider, name, NULL);
switch (err) {
@@ -727,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)
{
@@ -735,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);
@@ -841,6 +986,8 @@ static void provider_resolv_host_addr(struct vpn_provider *provider)
void __vpn_provider_append_properties(struct vpn_provider *provider,
DBusMessageIter *iter)
{
+ dbus_bool_t split_routing;
+
if (provider->host)
connman_dbus_dict_append_basic(iter, "Host",
DBUS_TYPE_STRING, &provider->host);
@@ -852,6 +999,10 @@ void __vpn_provider_append_properties(struct vpn_provider *provider,
if (provider->type)
connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
&provider->type);
+
+ split_routing = provider->do_split_routing;
+ connman_dbus_dict_append_basic(iter, "SplitRouting", DBUS_TYPE_BOOLEAN,
+ &split_routing);
}
int __vpn_provider_append_user_route(struct vpn_provider *provider,
@@ -984,7 +1135,7 @@ static GSList *get_routes(gchar **networks)
static int provider_load_from_keyfile(struct vpn_provider *provider,
GKeyFile *keyfile)
{
- gsize idx = 0;
+ gsize idx;
gchar **settings;
gchar *key, *value;
gsize length, num_user_networks;
@@ -997,28 +1148,26 @@ static int provider_load_from_keyfile(struct vpn_provider *provider,
return -ENOENT;
}
- while (idx < length) {
+ for (idx = 0; idx < length; idx++) {
key = settings[idx];
- if (key) {
- if (g_str_equal(key, "Networks")) {
- networks = __vpn_config_get_string_list(keyfile,
- provider->identifier,
- key,
- &num_user_networks,
+ if (!key)
+ continue;
+
+ if (g_str_equal(key, "Networks")) {
+ networks = __vpn_config_get_string_list(keyfile,
+ provider->identifier,key,
+ &num_user_networks, NULL);
+ provider->user_networks = get_routes(networks);
+ } else {
+ value = __vpn_config_get_string(keyfile,
+ provider->identifier, key,
NULL);
- provider->user_networks = get_routes(networks);
-
- } else {
- value = __vpn_config_get_string(keyfile,
- provider->identifier,
- key, NULL);
- vpn_provider_set_string(provider, key,
- value);
- g_free(value);
- }
+
+ vpn_provider_set_string(provider, key, value);
+ g_free(value);
}
- idx += 1;
}
+
g_strfreev(settings);
g_strfreev(networks);
@@ -1100,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");
@@ -1135,6 +1287,12 @@ static int vpn_provider_save(struct vpn_provider *provider)
"Host", provider->host);
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;
@@ -1251,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);
@@ -1640,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)
{
@@ -1655,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);
@@ -1725,6 +1900,7 @@ static void append_properties(DBusMessageIter *iter,
GHashTableIter hash;
gpointer value, key;
dbus_bool_t immutable;
+ dbus_bool_t split_routing;
connman_dbus_dict_open(iter, &dict);
@@ -1752,6 +1928,10 @@ static void append_properties(DBusMessageIter *iter,
connman_dbus_dict_append_basic(&dict, "Immutable", DBUS_TYPE_BOOLEAN,
&immutable);
+ split_routing = provider->do_split_routing;
+ connman_dbus_dict_append_basic(&dict, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing);
+
if (provider->family == AF_INET)
connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4,
provider);
@@ -1776,8 +1956,7 @@ static void append_properties(DBusMessageIter *iter,
while (g_hash_table_iter_next(&hash, &key, &value)) {
struct vpn_setting *setting = value;
- if (!setting->hide_value &&
- setting->value)
+ if (!setting->hide_value && setting->value)
connman_dbus_dict_append_basic(&dict, key,
DBUS_TYPE_STRING,
&setting->value);
@@ -1806,55 +1985,6 @@ static void connection_added_signal(struct vpn_provider *provider)
dbus_message_unref(signal);
}
-static bool check_host(char **hosts, char *host)
-{
- int i;
-
- if (!hosts)
- return false;
-
- for (i = 0; hosts[i]; i++) {
- if (g_strcmp0(hosts[i], host) == 0)
- return true;
- }
-
- return false;
-}
-
-static void provider_append_routes(gpointer key, gpointer value,
- gpointer user_data)
-{
- struct vpn_route *route = value;
- struct vpn_provider *provider = user_data;
- int index = provider->index;
-
- if (!handle_routes)
- return;
-
- /*
- * If the VPN administrator/user has given a route to
- * VPN server, then we must discard that because the
- * server cannot be contacted via VPN tunnel.
- */
- if (check_host(provider->host_ip, route->network)) {
- DBG("Discarding VPN route to %s via %s at index %d",
- route->network, route->gateway, index);
- return;
- }
-
- if (route->family == AF_INET6) {
- unsigned char prefix_len = atoi(route->netmask);
-
- connman_inet_add_ipv6_network_route(index, route->network,
- route->gateway,
- prefix_len);
- } else {
- connman_inet_add_network_route(index, route->network,
- route->gateway,
- route->netmask);
- }
-}
-
static int set_connected(struct vpn_provider *provider,
bool connected)
{
@@ -1871,18 +2001,8 @@ static int set_connected(struct vpn_provider *provider,
__vpn_ipconfig_address_add(ipconfig, provider->family);
- if (handle_routes)
- __vpn_ipconfig_gateway_add(ipconfig, provider->family);
-
provider_indicate_state(provider,
VPN_PROVIDER_STATE_READY);
-
- g_hash_table_foreach(provider->routes, provider_append_routes,
- provider);
-
- g_hash_table_foreach(provider->user_routes,
- provider_append_routes, provider);
-
} else {
provider_indicate_state(provider,
VPN_PROVIDER_STATE_DISCONNECT);
@@ -1922,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:
@@ -1931,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,
@@ -1939,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);
@@ -1949,10 +2090,61 @@ int vpn_provider_indicate_error(struct vpn_provider *provider,
return 0;
}
+static gboolean provider_property_changed(DBusConnection *conn,
+ DBusMessage *message, void *user_data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter value;
+ struct vpn_provider *provider = user_data;
+ const char *key;
+
+ if (!dbus_message_iter_init(message, &iter))
+ return TRUE;
+
+ dbus_message_iter_get_basic(&iter, &key);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value);
+
+ DBG("provider %p key %s", provider, key);
+
+ if (g_str_equal(key, "SplitRouting")) {
+ dbus_bool_t split_routing;
+
+ if (dbus_message_iter_get_arg_type(&value) !=
+ DBUS_TYPE_BOOLEAN)
+ goto out;
+
+ dbus_message_iter_get_basic(&value, &split_routing);
+
+ DBG("property %s value %s", key, bool2str(split_routing));
+
+ /*
+ * Even though this is coming from connmand, signal the value
+ * for other components listening to the changes via VPN API
+ * only. provider.c will skip setting the same value in order
+ * to avoid signaling loop. This is needed for ensuring that
+ * all components using VPN API will be informed about the
+ * correct status of SplitRouting. Especially when loading the
+ * services after a crash, for instance.
+ */
+ vpn_provider_set_boolean(provider, "SplitRouting",
+ split_routing, true);
+ }
+
+out:
+ return TRUE;
+}
+
static int connection_unregister(struct vpn_provider *provider)
{
DBG("provider %p path %s", provider, provider->path);
+ if (provider->signal_watch) {
+ g_dbus_remove_watch(connection, provider->signal_watch);
+ provider->signal_watch = 0;
+ }
+
if (!provider->path)
return -EALREADY;
@@ -1967,6 +2159,8 @@ static int connection_unregister(struct vpn_provider *provider)
static int connection_register(struct vpn_provider *provider)
{
+ char *connmand_vpn_path;
+
DBG("provider %p path %s", provider, provider->path);
if (provider->path)
@@ -1980,6 +2174,18 @@ static int connection_register(struct vpn_provider *provider)
connection_methods, connection_signals,
NULL, provider, NULL);
+ connmand_vpn_path = g_strdup_printf("%s/service/vpn_%s", CONNMAN_PATH,
+ provider->identifier);
+
+ provider->signal_watch = g_dbus_add_signal_watch(connection,
+ CONNMAN_SERVICE, connmand_vpn_path,
+ CONNMAN_SERVICE_INTERFACE,
+ PROPERTY_CHANGED,
+ provider_property_changed,
+ provider, NULL);
+
+ g_free(connmand_vpn_path);
+
return 0;
}
@@ -2017,6 +2223,7 @@ static void provider_initialize(struct vpn_provider *provider)
provider->domain = NULL;
provider->identifier = NULL;
provider->immutable = false;
+ provider->do_split_routing = false;
provider->user_networks = NULL;
provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, free_route);
@@ -2024,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)
@@ -2112,9 +2320,12 @@ static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile,
return NULL;
}
- if (provider_register(provider) == 0)
+ if (!provider_register(provider)) {
connection_register(provider);
+ connection_added_signal(provider);
+ }
}
+
return provider;
}
@@ -2166,9 +2377,10 @@ char *__vpn_provider_create_identifier(const char *host, const char *domain)
{
char *ident;
- ident = g_strdup_printf("%s_%s", host, domain);
- if (!ident)
- return NULL;
+ if (domain)
+ ident = g_strdup_printf("%s_%s", host, domain);
+ else
+ ident = g_strdup_printf("%s", host);
provider_dbus_ident(ident);
@@ -2184,6 +2396,7 @@ int __vpn_provider_create(DBusMessage *msg)
GSList *networks = NULL;
char *ident;
int err;
+ dbus_bool_t split_routing = false;
dbus_message_iter_init(msg, &iter);
dbus_message_iter_recurse(&iter, &array);
@@ -2210,6 +2423,11 @@ int __vpn_provider_create(DBusMessage *msg)
g_str_equal(key, "Domain"))
dbus_message_iter_get_basic(&value, &domain);
break;
+ case DBUS_TYPE_BOOLEAN:
+ if (g_str_equal(key, "SplitRouting"))
+ dbus_message_iter_get_basic(&value,
+ &split_routing);
+ break;
case DBUS_TYPE_ARRAY:
if (g_str_equal(key, "UserRoutes"))
networks = get_user_networks(&value);
@@ -2219,7 +2437,7 @@ int __vpn_provider_create(DBusMessage *msg)
dbus_message_iter_next(&array);
}
- if (!host || !domain)
+ if (!host)
return -EINVAL;
DBG("Type %s name %s networks %p", type, name, networks);
@@ -2243,6 +2461,7 @@ int __vpn_provider_create(DBusMessage *msg)
provider->domain = g_strdup(domain);
provider->name = g_strdup(name);
provider->type = g_strdup(type);
+ provider->do_split_routing = split_routing;
if (provider_register(provider) == 0)
vpn_provider_load(provider);
@@ -2404,7 +2623,7 @@ int __vpn_provider_create_from_config(GHashTable *settings,
networks_str = get_string(settings, "Networks");
networks = parse_user_networks(networks_str);
- if (!host || !domain) {
+ if (!host) {
err = -EINVAL;
goto fail;
}
@@ -2563,6 +2782,10 @@ static int set_string(struct vpn_provider *provider,
g_free(provider->domain);
provider->domain = g_strdup(value);
send_value(provider->path, "Domain", provider->domain);
+ } else if (g_str_equal(key, "SplitRouting")) {
+ connman_warn("VPN SplitRouting value attempted to set as "
+ "string, is boolean");
+ return -EINVAL;
} else {
struct vpn_setting *setting;
bool replace = true;
@@ -2649,6 +2872,25 @@ const char *vpn_provider_get_string(struct vpn_provider *provider,
return setting->value;
}
+int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
+ bool value, bool force_change)
+{
+ DBG("provider %p key %s", provider, key);
+
+ if (g_str_equal(key, "SplitRouting")) {
+ if (provider->do_split_routing == value && !force_change)
+ return -EALREADY;
+
+ DBG("SplitRouting set to %s", bool2str(value));
+
+ provider->do_split_routing = value;
+ send_value_boolean(provider->path, key,
+ provider->do_split_routing);
+ }
+
+ return 0;
+}
+
bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
bool default_value)
{
@@ -2695,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)
@@ -2917,11 +3174,8 @@ int vpn_provider_append_route(struct vpn_provider *provider,
break;
}
- if (!handle_routes) {
- if (route->netmask && route->gateway &&
- route->network)
- provider_schedule_changed(provider);
- }
+ if (route->netmask && route->gateway && route->network)
+ provider_schedule_changed(provider);
return 0;
}
@@ -2980,6 +3234,12 @@ void vpn_provider_driver_unregister(struct vpn_provider_driver *driver)
if (provider && provider->driver &&
g_strcmp0(provider->driver->name,
driver->name) == 0) {
+ /*
+ * Cancel VPN agent request to avoid segfault at
+ * shutdown as the callback, if set can point to a
+ * function in the plugin that is to be removed.
+ */
+ connman_agent_cancel(provider);
provider->driver = NULL;
}
}
@@ -3000,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;
}
@@ -3058,7 +3379,7 @@ void vpn_provider_clear_address(struct vpn_provider *provider, int family)
DBG("ipv6 %s/%d", address, len);
connman_inet_clear_ipv6_address(provider->index,
- address, len);
+ provider->prev_ipv6_addr);
connman_ipaddress_free(provider->prev_ipv6_addr);
provider->prev_ipv6_addr = NULL;
@@ -3148,13 +3469,220 @@ static void remove_unprovisioned_providers(void)
g_strfreev(providers);
}
-int __vpn_provider_init(bool do_routes)
+static gboolean connman_property_changed(DBusConnection *conn,
+ DBusMessage *message,
+ void *user_data)
{
- int err;
+ 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("");
- handle_routes = do_routes;
+ 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;
+
+ DBG("");
err = connman_agent_driver_register(&agent_driver);
if (err < 0) {
@@ -3169,6 +3697,20 @@ int __vpn_provider_init(bool do_routes)
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;
}
@@ -3183,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);
}
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index 0275d51a..5d1455da 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -83,8 +83,15 @@ const char *vpn_provider_get_string(struct vpn_provider *provider,
const char *key);
bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
const char *key);
+int vpn_provider_set_boolean(struct vpn_provider *provider, const char *key,
+ bool value,
+ bool force_change);
bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
bool default_value);
+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);
@@ -118,6 +125,7 @@ const char *vpn_provider_get_save_group(struct vpn_provider *provider);
const char *vpn_provider_get_name(struct vpn_provider *provider);
const char *vpn_provider_get_host(struct vpn_provider *provider);
const char *vpn_provider_get_path(struct vpn_provider *provider);
+const char *vpn_provider_get_ident(struct vpn_provider *provider);
unsigned int vpn_provider_get_authentication_errors(
struct vpn_provider *provider);
diff --git a/vpn/vpn-rtnl.c b/vpn/vpn-rtnl.c
index 6ddfd832..5a02d779 100644
--- a/vpn/vpn-rtnl.c
+++ b/vpn/vpn-rtnl.c
@@ -184,7 +184,7 @@ int vpn_rtnl_register(struct vpn_rtnl *rtnl)
rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
compare_priority);
- __vpn_ipconfig_foreach(trigger_rtnl, rtnl);
+ vpn_ipconfig_foreach(trigger_rtnl, rtnl);
return 0;
}
@@ -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;
}
diff --git a/vpn/vpn-settings.c b/vpn/vpn-settings.c
index 0eca2bc8..e78e5019 100644
--- a/vpn/vpn-settings.c
+++ b/vpn/vpn-settings.c
@@ -2,7 +2,7 @@
* ConnMan VPN daemon settings
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
- * Copyright (C) 2018-2019 Jolla Ltd. All rights reserved.
+ * Copyright (C) 2018-2020 Jolla Ltd. All rights reserved.
* Contact: jussi.laakkonen@jolla.com
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,9 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
#include <connman/log.h>
@@ -37,11 +40,13 @@ static struct {
char *binary_user;
char *binary_group;
char **binary_supplementary_groups;
+ char **system_binary_users;
} connman_vpn_settings = {
.timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
.binary_user = NULL,
.binary_group = NULL,
.binary_supplementary_groups = NULL,
+ .system_binary_users = NULL,
};
struct vpn_plugin_data {
@@ -52,6 +57,58 @@ struct vpn_plugin_data {
GHashTable *plugin_hash = NULL;
+bool vpn_settings_is_system_user(const char *user)
+{
+ struct passwd *pwd;
+ struct passwd *system_pwd;
+ int i;
+
+ /*
+ * The username is not set = override should not be used. This is the
+ * case after the override is reset.
+ */
+ if (!user)
+ return true;
+
+ DBG("check user \"%s\"", user);
+
+ /*
+ * Ignore errors if no entry was found. Treat as system user to
+ * prevent using an invalid override.
+ */
+ pwd = vpn_util_get_passwd(user);
+ if (!pwd)
+ return true;
+
+ if (!connman_vpn_settings.system_binary_users) {
+ DBG("no binary users set");
+
+ /*
+ * Check if the user is root, or the uid equals to process
+ * effective uid.
+ */
+ return !pwd->pw_uid || pwd->pw_uid == geteuid();
+ }
+
+ /* Root set as user or the effective user id */
+ if (!pwd->pw_uid || pwd->pw_uid == geteuid())
+ return true;
+
+ for (i = 0; connman_vpn_settings.system_binary_users[i]; i++) {
+ const char *system_user =
+ connman_vpn_settings.system_binary_users[i];
+
+ system_pwd = vpn_util_get_passwd(system_user);
+ if (!system_pwd)
+ continue;
+
+ if (pwd->pw_uid == system_pwd->pw_uid)
+ return true;
+ }
+
+ return false;
+}
+
const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
{
if (data && data->binary_user)
@@ -129,6 +186,9 @@ static void parse_config(GKeyFile *config, const char *file)
connman_vpn_settings.binary_supplementary_groups = get_string_list(
config, VPN_GROUP,
"SupplementaryGroups");
+ connman_vpn_settings.system_binary_users = get_string_list(
+ config, VPN_GROUP,
+ "SystemBinaryUsers");
}
struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
@@ -245,6 +305,7 @@ void __vpn_settings_cleanup()
g_free(connman_vpn_settings.binary_user);
g_free(connman_vpn_settings.binary_group);
g_strfreev(connman_vpn_settings.binary_supplementary_groups);
+ g_strfreev(connman_vpn_settings.system_binary_users);
if (plugin_hash) {
g_hash_table_destroy(plugin_hash);
diff --git a/vpn/vpn-util.c b/vpn/vpn-util.c
new file mode 100644
index 00000000..bc3b01dd
--- /dev/null
+++ b/vpn/vpn-util.c
@@ -0,0 +1,224 @@
+/*
+ * ConnMan VPN daemon utils
+ *
+ * Copyright (C) 2020 Jolla Ltd. All rights reserved.
+ * Copyright (C) 2020 Open Mobile Platform LLC.
+ * Contact: jussi.laakkonen@jolla.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib/gstdio.h>
+
+#include <connman/log.h>
+
+#include "vpn.h"
+
+static bool is_string_digits(const char *str)
+{
+ int i;
+
+ if (!str || !*str)
+ return false;
+
+ for (i = 0; str[i]; i++) {
+ if (!g_ascii_isdigit(str[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static uid_t get_str_id(const char *username)
+{
+ if (!username || !*username)
+ return 0;
+
+ return (uid_t)g_ascii_strtoull(username, NULL, 10);
+}
+
+struct passwd *vpn_util_get_passwd(const char *username)
+{
+ struct passwd *pwd;
+ uid_t uid;
+
+ if (!username || !*username)
+ return NULL;
+
+ if (is_string_digits(username)) {
+ uid = get_str_id(username);
+ pwd = getpwuid(uid);
+ } else {
+ pwd = getpwnam(username);
+ }
+
+ return pwd;
+}
+
+struct group *vpn_util_get_group(const char *groupname)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!groupname || !*groupname)
+ return NULL;
+
+ if (is_string_digits(groupname)) {
+ gid = get_str_id(groupname);
+ grp = getgrgid(gid);
+ } else {
+ grp = getgrnam(groupname);
+ }
+
+ return grp;
+}
+
+/*
+ * These prefixes are used for checking if the requested path for
+ * vpn_util_create_path() is acceptable. Allow only prefixes meant for run-time
+ * or temporary use to limit the access to any system resources.
+ *
+ * VPN core and plugins would need to create only temporary dirs for the
+ * run-time use. The requested dirs can be created for a specific user when
+ * running a VPN plugin as a different user and thus, user specific run dir is
+ * allowed and limitation to access any other system dir is restricted.
+ */
+static const char *allowed_prefixes[] = { RUNSTATEDIR "/connman-vpn/",
+ RUNSTATEDIR "/user/", "/tmp/", NULL };
+
+static int is_path_allowed(const char *path)
+{
+ int err = -EPERM;
+ int i;
+
+ if (!path || !*path || !g_path_is_absolute(path))
+ return -EINVAL;
+
+ if (g_strrstr(path, "..") || g_strrstr(path, "./"))
+ return -EPERM;
+
+ for (i = 0; allowed_prefixes[i]; i++) {
+ if (g_str_has_prefix(path, allowed_prefixes[i])) {
+ const char *suffix = path +
+ strlen(allowed_prefixes[i]);
+
+ /*
+ * Don't allow plain prefixes, an additional dir must
+ * be included after the prexix in the requested path.
+ */
+ if (suffix && *suffix != G_DIR_SEPARATOR &&
+ g_strrstr(suffix,
+ G_DIR_SEPARATOR_S)) {
+ DBG("allowed %s, has suffix %s", path, suffix);
+ err = 0;
+ }
+
+ break;
+ }
+ }
+
+ return err;
+}
+
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode)
+{
+ mode_t old_umask;
+ char *dir_p;
+ int err;
+
+ err = is_path_allowed(path);
+ if (err)
+ return err;
+
+ dir_p = g_path_get_dirname(path);
+ if (!dir_p)
+ return -ENOMEM;
+
+ err = g_unlink(dir_p);
+ if (err)
+ err = -errno;
+
+ switch (err) {
+ case 0:
+ /* Removed */
+ case -ENOENT:
+ /* Did not exist */
+ break;
+ case -EACCES:
+ /*
+ * Cannot get write access to the containing directory, check
+ * if the path exists.
+ */
+ if (!g_file_test(dir_p, G_FILE_TEST_EXISTS))
+ goto out;
+
+ /* If the dir does not exist new one cannot be created */
+ if (!g_file_test(dir_p, G_FILE_TEST_IS_DIR))
+ goto out;
+
+ /* Do a chmod as the dir exists */
+ /* fallthrough */
+ case -EISDIR:
+ /* Exists as dir, just chmod and change owner */
+ err = g_chmod(dir_p, mode);
+ if (err) {
+ connman_warn("chmod %s failed, err %d", dir_p, err);
+ err = -errno;
+ }
+
+ goto chown;
+ default:
+ /* Any other error that is not handled here */
+ connman_warn("remove %s failed, err %d", dir_p, err);
+ goto out;
+ }
+
+ /* Set dir creation mask to correspond to the mode */
+ old_umask = umask(~mode & 0777);
+
+ DBG("mkdir %s", dir_p);
+ err = g_mkdir_with_parents(dir_p, mode);
+
+ umask(old_umask);
+
+ if (err) {
+ connman_warn("mkdir %s failed, err %d", dir_p, err);
+ err = -errno;
+ goto out;
+ }
+
+chown:
+ if (uid && grp) {
+ err = chown(dir_p, uid, grp);
+ if (err) {
+ err = -errno;
+ connman_warn("chown %s failed for %d/%d, err %d",
+ dir_p, uid, grp, err);
+ }
+ }
+
+out:
+ g_free(dir_p);
+
+ return err;
+}
+
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 45cf46dc..477cb227 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -35,7 +35,7 @@ struct vpn_ipconfig;
struct connman_ipaddress *__vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig);
unsigned short __vpn_ipconfig_get_type_from_index(int index);
unsigned int __vpn_ipconfig_get_flags_from_index(int index);
-void __vpn_ipconfig_foreach(void (*function) (int index,
+void vpn_ipconfig_foreach(void (*function) (int index,
void *user_data), void *user_data);
void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig,
const char *address);
@@ -85,7 +85,6 @@ int __vpn_provider_create_from_config(GHashTable *settings,
int __vpn_provider_set_string_immutable(struct vpn_provider *provider,
const char *key, const char *value);
DBusMessage *__vpn_provider_get_connections(DBusMessage *msg);
-const char *vpn_provider_get_ident(struct vpn_provider *provider);
struct vpn_provider *__vpn_provider_lookup(const char *identifier);
int __vpn_provider_indicate_state(struct vpn_provider *provider,
enum vpn_provider_state state);
@@ -97,7 +96,7 @@ int __vpn_provider_disconnect(struct vpn_provider *provider);
int __vpn_provider_remove(const char *path);
int __vpn_provider_delete(struct vpn_provider *provider);
void __vpn_provider_cleanup(void);
-int __vpn_provider_init(bool handle_routes);
+int __vpn_provider_init();
#include "vpn-rtnl.h"
@@ -112,10 +111,12 @@ int __vpn_rtnl_send(const void *buf, size_t len);
int __vpn_config_init(void);
void __vpn_config_cleanup(void);
-char *__vpn_config_get_string(GKeyFile *key_file,
- const char *group_name, const char *key, GError **error);
-char **__vpn_config_get_string_list(GKeyFile *key_file,
- const char *group_name, const char *key, gsize *length, GError **error);
+char *__vpn_config_get_string(GKeyFile *key_file, const char *group_name,
+ const char *key, GError **error);
+char **__vpn_config_get_string_list(GKeyFile *key_file, const char *group_name,
+ const char *key, gsize *length, GError **error);
+bool __vpn_config_get_boolean(GKeyFile *key_file, const char *group_name,
+ const char *key, bool default_value);
int __vpn_settings_init(const char *file);
void __vpn_settings_cleanup(void);
@@ -132,3 +133,8 @@ const char * vpn_settings_get_binary_user(struct vpn_plugin_data *data);
const char * vpn_settings_get_binary_group(struct vpn_plugin_data *data);
char ** vpn_settings_get_binary_supplementary_groups(
struct vpn_plugin_data *data);
+bool vpn_settings_is_system_user(const char *user);
+
+struct passwd *vpn_util_get_passwd(const char *username);
+struct group *vpn_util_get_group(const char *groupname);
+int vpn_util_create_path(const char *path, uid_t uid, gid_t grp, int mode);