test-driver
m4/
!m4/configmake.m4
+TAGS
+cscope.*
connman.pc
include/connman
src/connmand-wait-online
plugins/connman.policy
scripts/connman
-scripts/openconnect-script
+scripts/vpn-script
scripts/openvpn-script
scripts/connman_resolvconf.conf
client/connmanctl
Artem Yamshanov <me@anticode.ninja>
Matthias Berndt <matthias_berndt@gmx.de>
Henrik Persson <Henrik.Persson@verisure.com>
+Nicola Lunghi <nick83ola@gmail.com>
+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>
+ver 1.38:
+ Fix issue with online check on IP address update.
+ Fix issue with OpenVPN and encrypted private keys.
+ Fix issue with finishing of VPN connections.
+ Add support for updated stable iwd APIs.
+ Add support for WireGuard networks.
+
ver 1.37:
Fix issue with handling invalid gateway addresses.
Fix issue with handling updates of default gateway.
endif
shared_sources = src/shared/util.h src/shared/util.c \
- src/shared/netlink.h src/shared/netlink.c \
src/shared/arp.h src/shared/arp.c
if DATAFILES
if NFTABLES
src_connmand_SOURCES += src/firewall-nftables.c
-src_connmand_LDADD += @NFTABLES_LIBS@
+src_connmand_LDADD += @NFTABLES_LIBS@ @LIBMNL_LIBS@
endif
if VPN
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-config.c vpn/vpn-settings.c
vpn_connman_vpnd_LDADD = gdbus/libgdbus-internal.la $(builtin_vpn_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ @GNUTLS_LIBS@ \
endif
if NFTABLES
-AM_CFLAGS += @NFTABLES_CFLAGS@
-src_connmand_CFLAGS += @NFTABLES_CFLAGS@
+AM_CFLAGS += @NFTABLES_CFLAGS@ @LIBMNL_CFLAGS@
+src_connmand_CFLAGS += @NFTABLES_CFLAGS@ @LIBMNL_CFLAGS@
endif
EXTRA_DIST += vpn/vpn-dbus.conf vpn/vpn-polkit.conf
tools/tap-test tools/wpad-test \
tools/stats-tool tools/private-network-test \
tools/session-test \
- tools/dnsproxy-test tools/netlink-test
+ tools/dnsproxy-test
tools_supplicant_test_SOURCES = tools/supplicant-test.c \
tools/supplicant-dbus.h tools/supplicant-dbus.c \
tools_dnsproxy_test_SOURCES = tools/dnsproxy-test.c
tools_dnsproxy_test_LDADD = @GLIB_LIBS@
-tools_netlink_test_SOURCES = src/shared/util.c src/shared/netlink.c \
- tools/netlink-test.c
-tools_netlink_test_LDADD = @GLIB_LIBS@
-
endif
test_scripts = test/get-state test/list-services \
builtin_modules += vpn
builtin_sources += plugins/vpn.c
+if WIREGUARD
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+if WIREGUARD_BUILTIN
+builtin_vpn_modules += wireguard
+builtin_vpn_sources += src/shared/mnlg.h src/shared/mnlg.c \
+ vpn/plugins/wireguard.h vpn/plugins/libwireguard.c \
+ vpn/plugins/wireguard.c
+builtin_vpn_cflags += @LIBMNL_CFLAGS@ -DWIREGUARD=\"@WIREGUARD@\"
+builtin_vpn_libadd += @LIBMNL_LIBS@
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/wireguard.la
+vpn_plugin_objects += $(plugins_wireguard_la_OBJECTS)
+vpn_plugins_wireguard_la_SOURCES = src/shared/mnlg.h src/shared/mnlg.c \
+ vpn/plugins/wireguard.h \
+ vpn/plugins/libwireguard.c \
+ vpn/plugins/wireguard.c
+vpn_plugins_wireguard_la_CFLAGS = $(plugin_cflags) @LIBMNL_CFLAGS@ \
+ -DWIREGUARD=\"@WIREGUARD@\" \
+ -DVPN_STATEDIR=\""$(vpn_statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_wireguard_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_wireguard_la_LIBADD = @LIBMNL_LIBS@
+endif
+endif
+
if OPENCONNECT
+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_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
else
vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la
vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS)
-vpn_plugins_openconnect_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
- vpn/plugins/openconnect.c
+vpn_plugins_openconnect_la_SOURCES = vpn/plugins/openconnect.c
vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \
-DOPENCONNECT=\"@OPENCONNECT@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
endif
if OPENVPN
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
if OPENVPN_BUILTIN
builtin_vpn_modules += openvpn
builtin_vpn_sources += vpn/plugins/openvpn.c
-builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
builtin_vpn_cflags += -DOPENVPN=\"@OPENVPN@\"
else
vpn_plugin_LTLIBRARIES += vpn/plugins/openvpn.la
vpn_plugin_objects += $(plugins_openvpn_la_OBJECTS)
-vpn_plugins_openvpn_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
- vpn/plugins/openvpn.c
+vpn_plugins_openvpn_la_SOURCES = vpn/plugins/openvpn.c
vpn_plugins_openvpn_la_CFLAGS = $(plugin_cflags) -DOPENVPN=\"@OPENVPN@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
-DSCRIPTDIR=\""$(build_scriptdir)"\"
endif
if VPNC
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
if VPNC_BUILTIN
builtin_vpn_modules += vpnc
builtin_vpn_sources += vpn/plugins/vpnc.c
-builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
builtin_vpn_cflags += -DVPNC=\"@VPNC@\"
else
vpn_plugin_LTLIBRARIES += vpn/plugins/vpnc.la
vpn_plugin_objects += $(plugins_vpnc_la_OBJECTS)
-vpn_plugins_vpnc_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
- vpn/plugins/vpnc.c
+vpn_plugins_vpnc_la_SOURCES = vpn/plugins/vpnc.c
vpn_plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
-DSCRIPTDIR=\""$(build_scriptdir)"\"
endif
if L2TP
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
if L2TP_BUILTIN
builtin_vpn_modules += l2tp
builtin_vpn_sources += vpn/plugins/l2tp.c
-builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
builtin_vpn_cflags += -DL2TP=\"@L2TP@\"
else
vpn_plugin_LTLIBRARIES += vpn/plugins/l2tp.la
vpn_plugin_objects += $(plugins_l2tp_la_OBJECTS)
-vpn_plugins_l2tp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
- vpn/plugins/l2tp.c
+vpn_plugins_l2tp_la_SOURCES = vpn/plugins/l2tp.c
vpn_plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
-DSCRIPTDIR=\""$(build_scriptdir)"\"
endif
if PPTP
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
if PPTP_BUILTIN
builtin_vpn_modules += pptp
builtin_vpn_sources += vpn/plugins/pptp.c
-builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
builtin_vpn_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
else
vpn_plugin_LTLIBRARIES += vpn/plugins/pptp.la
vpn_plugin_objects += $(plugins_pptp_la_OBJECTS)
-vpn_plugins_pptp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
- vpn/plugins/pptp.c
+vpn_plugins_pptp_la_SOURCES = vpn/plugins/pptp.c
vpn_plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
-DPPTP=\"@PPTP@\" \
-DVPN_STATEDIR=\""$(vpn_statedir)"\" \
endif
if OPENCONNECT
-script_PROGRAMS += scripts/openconnect-script
+script_PROGRAMS += scripts/vpn-script
-scripts_openconnect_script_LDADD = @DBUS_LIBS@
+scripts_vpn_script_LDADD = @DBUS_LIBS@
else
if VPNC
-script_PROGRAMS += scripts/openconnect-script
+script_PROGRAMS += scripts/vpn-script
-scripts_openconnect_script_LDADD = @DBUS_LIBS@
+scripts_vpn_script_LDADD = @DBUS_LIBS@
endif
endif
get log traces as follows:
connmand -d 2>&1 | ts '[%H:%M:%.S]' | tee connman.log
-The 'ts' program is normaly avialable in the moreutils package.
+The 'ts' program is normally available in the moreutils package.
Kernel configuration
network. While the same setup works well for a WiFi or ethernet
uplink.
+Up to (at least) version 2.4.5 of OpenVPN getting information about
+private key decryption failures via management channel is missing. This
+will result in attempting with the invalid key over and over as the
+information about failed decryprion is not delivered to OpenVPN plugin.
+The following patch to OpenVPN is required for the private key
+decryption failures to be sent:
+https://git.sailfishos.org/mer-core/openvpn/blob/
+4f4b4af116292a207416c8a990392e35a6fc41af/rpm/privatekey-passphrase-
+handling.diff
GnuTLS
======
Connection: close
Currently following information is returned from connman.net if
-the connection is successfull (200 OK http response code is returned):
+the connection is successful (200 OK http response code is returned):
Server: nginx
Date: Mon, 09 Jun 2014 09:25:42 GMT
Content-Type: text/html
ids and passphrases.
+- Change OpenConnect plugin to use libopenconnect
+
+ Priority: Medium
+ Complexity: C4
+
+ Current implementation of OpenConnect uses screenscraping and interactive
+ mode for accepting self signed certificates and reacting to PKCS pass
+ phrase requests. This should be replaced with libopenconnect use. It may be
+ worthwhile to attempt to replace the whole authentication with the use of
+ openconnect_obtain_cookie() whatever authentication type is used. This
+ would lead to using only the cookie when connecting (--cookie-on-stdin)
+ and would cleanup the code at run_connect().
+
+ The usage of stdout can be removed as unnecessary. Cookie should be
+ retrieved with openconnect_obtain_cookie(). Remove this also from
+ connman_task_run().
+
+ Function is_valid_protocol() must use openconnect_get_supported_protocols.
+ Also the static const char *protocols[] would be unnecessary.
+
+ Reading the stderr with byte-by-byte approach is to be removed, as well as
+ are the PKCS failures and requests in stderr IO channel processing.
+
+ The use of interactive mode toggle is to be removed. Non-interactive mode
+ must be used, which leads to using --syslog with each authentication type
+ as task arg.
+
+ If the peer certificate cannot be verified with normal means it is because
+ the peer certificate is self signed and the user setting
+ "AllowSelfSignedCert" has to be used for the verify certificate callback
+ reply. The callback for certificate validation must return zero if user has
+ allowed self signed certificates. In such case save the SHA1 fingerprint of
+ server certificate as it is done now, otherwise indicate error to
+ libopenconnect.
+
Tools
=====
{ },
};
-static int agent_register_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int agent_register_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
DBusConnection *connection = user_data;
return result;
}
-static int agent_unregister_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int agent_unregister_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error unregistering Agent: %s\n", error);
{ },
};
-static int vpn_agent_register_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int vpn_agent_register_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
DBusConnection *connection = user_data;
return result;
}
-static int vpn_agent_unregister_return(DBusMessageIter *iter,
+static int vpn_agent_unregister_return(DBusMessageIter *iter, int errnum,
const char *error, void *user_data)
{
if (error) {
return '?';
}
-static int enable_return(DBusMessageIter *iter, const char *error,
+static int enable_return(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
char *tech = user_data;
else
str = tech;
- if (!error)
+ switch (errnum) {
+ case 0:
fprintf(stdout, "Enabled %s\n", str);
- else
+ break;
+ case -ENODEV:
+ fprintf(stderr, "%s is not available\n", str);
+ break;
+ case -EALREADY:
+ fprintf(stderr, "%s is already enabled\n", str);
+ break;
+ default:
fprintf(stderr, "Error %s: %s\n", str, error);
+ }
g_free(user_data);
"Powered", DBUS_TYPE_BOOLEAN, &b);
}
-static int disable_return(DBusMessageIter *iter, const char *error,
+static int disable_return(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
char *tech = user_data;
else
str = tech;
- if (!error)
- fprintf(stdout, "Disabled %s\n", str);
- else
+ switch (errnum) {
+ case 0:
+ fprintf(stdout, "Disable %s\n", str);
+ break;
+ case -ENODEV:
+ fprintf(stderr, "%s is not available\n", str);
+ break;
+ case -EALREADY:
+ fprintf(stderr, "%s is already disabled\n", str);
+ break;
+ default:
fprintf(stderr, "Error %s: %s\n", str, error);
+ }
g_free(user_data);
"Powered", DBUS_TYPE_BOOLEAN, &b);
}
-static int state_print(DBusMessageIter *iter, const char *error,
+static int state_print(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
DBusMessageIter entry;
state_print, NULL, NULL, NULL);
}
-static int clock_print(DBusMessageIter *iter, const char *error,
+static int clock_print(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
DBusMessageIter entry;
clock_print, NULL, NULL, NULL);
}
-static int services_list(DBusMessageIter *iter, const char *error,
+static int services_list(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
if (!error) {
return 0;
}
-static int peers_list(DBusMessageIter *iter,
+static int peers_list(DBusMessageIter *iter, int errnum,
const char *error, void *user_data)
{
if (!error) {
return 0;
}
-static int tethering_clients_list(DBusMessageIter *iter,
+static int tethering_clients_list(DBusMessageIter *iter, int errnum,
const char *error, void *user_data)
{
if (!error) {
return 0;
}
-static int object_properties(DBusMessageIter *iter,
+static int object_properties(DBusMessageIter *iter, int errnum,
const char *error, void *user_data)
{
char *path = user_data;
object_properties, path, NULL, NULL);
}
-static int technology_print(DBusMessageIter *iter, const char *error,
+static int technology_print(DBusMessageIter *iter, int errnum, const char *error,
void *user_data)
{
DBusMessageIter array;
dbus_bool_t enable;
};
-static int tether_set_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int tether_set_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
struct tether_enable *tether = user_data;
char *str;
return -EINPROGRESS;
}
-static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int tether_set_ssid_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
struct tether_properties *tether = user_data;
return tether_update(tether);
}
-static int tether_set_passphrase_return(DBusMessageIter *iter,
- const char *error, void *user_data)
+static int tether_set_passphrase_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
struct tether_properties *tether = user_data;
tethering_clients_list, NULL, NULL, NULL);
}
-static int scan_return(DBusMessageIter *iter, const char *error,
+static int scan_return(DBusMessageIter *iter, int ernnum, const char *error,
void *user_data)
{
char *path = user_data;
scan_return, path, NULL, NULL);
}
-static int connect_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int connect_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
char *path = user_data;
iface, "Connect", connect_return, path, NULL, NULL);
}
-static int disconnect_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int disconnect_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
char *path = user_data;
char *target;
};
-static int move_before_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int move_before_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
struct move_service *services = user_data;
char *service;
services->target);
}
-static int move_after_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int move_after_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
struct move_service *services = user_data;
char *service;
services->target);
}
-static int config_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int config_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
char *service_name = user_data;
return 0;
}
-static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int vpnconnections_properties(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
char *path = user_data;
char *str;
return 0;
}
-static int vpnconnections_list(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int vpnconnections_list(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (!error)
__connmanctl_vpnconnections_list(iter);
session_notify_path = NULL;
}
-static int session_connect_cb(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int session_connect_cb(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error: %s\n", error);
session_connect_cb, NULL, NULL, NULL);
}
-static int session_disconnect_cb(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int session_disconnect_cb(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error)
fprintf(stderr, "Error: %s\n", error);
session_disconnect_cb, NULL, NULL, NULL);
}
-static int session_create_cb(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int session_create_cb(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
gboolean connect = GPOINTER_TO_INT(user_data);
char *str;
return res;
}
-static int session_destroy_cb(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int session_destroy_cb(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error destroying session: %s", error);
session_destroy_append, session_path);
}
-static int session_config_return(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int session_config_return(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
char *property_name = user_data;
return lookup_options(session_options, text, state);
}
-static int peer_service_cb(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int peer_service_cb(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
bool registration = GPOINTER_TO_INT(user_data);
}
}
-static int populate_service_hash(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int populate_service_hash(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error getting services: %s", error);
}
}
-static int populate_vpnconnection_hash(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int populate_vpnconnection_hash(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
DBusMessageIter array;
}
}
-static int populate_peer_hash(DBusMessageIter *iter,
- const char *error, void *user_data)
+static int populate_peer_hash(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error getting peers: %s", error);
}
}
-static int populate_technology_hash(DBusMessageIter *iter, const char *error,
- void *user_data)
+static int populate_technology_hash(DBusMessageIter *iter, int errnum,
+ const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error getting technologies: %s\n", error);
#define TIMEOUT 120000
+#ifndef DBUS_ERROR_UNKNOWN_METHOD
+#define DBUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod"
+#endif
+
+#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
+#endif
+
+#ifndef DBUS_ERROR_UNKNOWN_OBJECT
+#define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject"
+#endif
+
+static int string2errno(const char *error)
+{
+ if (!g_strcmp0(error, DBUS_ERROR_UNKNOWN_METHOD))
+ return -ENOTSUP;
+ if (!g_strcmp0(error, DBUS_ERROR_UNKNOWN_PROPERTY))
+ return -ENOENT;
+ if (!g_strcmp0(error, DBUS_ERROR_UNKNOWN_OBJECT))
+ return -ENODEV;
+ if (!g_strcmp0(error, "net.connman.Error.AlreadyEnabled"))
+ return -EALREADY;
+ if (!g_strcmp0(error, "net.connman.Error.AlreadyDisabled"))
+ return -EALREADY;
+
+ return -EINVAL;
+}
+
void __connmanctl_dbus_print(DBusMessageIter *iter, const char *pre,
const char *dict, const char *sep)
{
dbus_error_init(&err);
dbus_set_error_from_message(&err, reply);
- callback->cb(NULL, err.message, callback->user_data);
+ callback->cb(NULL, string2errno(err.name), err.message,
+ callback->user_data);
dbus_error_free(&err);
goto end;
}
dbus_message_iter_init(reply, &iter);
- res = callback->cb(&iter, NULL, callback->user_data);
+ res = callback->cb(&iter, 0, NULL, callback->user_data);
end:
__connmanctl_redraw_rl();
const char *dict, const char *sep);
typedef int (*connmanctl_dbus_method_return_func_t)(DBusMessageIter *iter,
- const char *error, void *user_data);
+ int errnum, const char *error, void *user_data);
typedef void (*connmanctl_dbus_append_func_t)(DBusMessageIter *iter,
void *user_data);
else
str = path;
- fprintf(stdout, " %c %-20s %s", state, name, str);
+ fprintf(stdout, " %c %-20s vpn_%s", state, name, str);
}
void __connmanctl_vpnconnections_list(DBusMessageIter *iter)
AC_PREREQ(2.60)
-AC_INIT(connman, 1.37)
+AC_INIT(connman, 1.38)
AC_CONFIG_MACRO_DIR([m4])
fi
AM_CONDITIONAL(XTABLES, test "${found_iptables}" != "no")
+found_libmnl="no"
+if (test "${firewall_type}" = "nftables" -o \
+ "${enable_wireguard}" != "no"); then
+ PKG_CHECK_MODULES(LIBMNL, [libmnl >= 1.0.0], [found_libmnl="yes"],
+ AC_MSG_ERROR([libmnl >= 1.0.0 not found]))
+ AC_SUBST(LIBMNL_CFLAGS)
+ AC_SUBST(LIBMNL_LIBS)
+fi
+AM_CONDITIONAL(LIBMNL, test "${found_libmnl}" != "no")
+
found_nftables="no"
if (test "${firewall_type}" = "nftables"); then
- PKG_CHECK_MODULES(NFTABLES, [libnftnl >= 1.0.4 libmnl >= 1.0.0], [found_nftables="yes"],
- AC_MSG_ERROR([libnftnl >= 1.0.4 or libmnl >= 1.0.0 not found]))
+ PKG_CHECK_MODULES(NFTABLES, [libnftnl >= 1.0.4], [found_nftables="yes"],
+ AC_MSG_ERROR([libnftnl >= 1.0.4]))
AC_SUBST(NFTABLES_CFLAGS)
AC_SUBST(NFTABLES_LIBS)
fi
[enable_ethernet=${enableval}])
AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
+AC_ARG_ENABLE(wireguard, AC_HELP_STRING([--disable-wireguard],
+ [disable Wireguard support]),
+ [enable_wireguard=${enableval}])
+AM_CONDITIONAL(WIREGUARD, test "${enable_wireguard}" != "no")
+AM_CONDITIONAL(WIREGUARD_BUILTIN, test "${enable_wireguard}" = "builtin")
+
AC_ARG_ENABLE(gadget, AC_HELP_STRING([--disable-gadget],
[disable USB Gadget support]),
[enable_gadget=${enableval}])
"${enable_openvpn}" != "no" -o \
"${enable_vpnc}" != "no" -o \
"${enable_l2tp}" != "no" -o \
- "${enable_pptp}" != "no")
+ "${enable_pptp}" != "no" -o \
+ "${enable_wireguard}" != "no")
AC_MSG_CHECKING(which DNS backend to use)
AC_ARG_WITH(dns-backend, AC_HELP_STRING([--with-dns-backend=TYPE],
void RequestBrowser(object service, string url)
This method gets called when it is required
- to ask the user to open a website to procceed
+ to ask the user to open a website to proceed
with login handling.
This can happen if connected to a hotspot portal
keys are the field names and the values are the
actual fields. Alternatively an error indicating that
the request got canceled can be returned.
- OperationAborted will be return on a successfull
+ OperationAborted will be return on a successful
cancel request.
Most common return field names are "Name" and of
string PreviousPassphrase
The previous passphrase successfully saved, i.e.
- which led to a successfull connection. This field is
+ which led to a successful connection. This field is
provided as an informational argument when connecting
with it does not work anymore, for instance when it
has been changed on the AP. Such argument appears when
}
-M8: Use g_try_malloc instead of g_malloc
-========================================
-When g_malloc fails, the whole program would exit. Most of time, this is not
-the expected behavior, and you may want to use g_try_malloc instead.
+M8: Abort if small allocation fail
+==================================
+When g_malloc fails, the whole program would exit. Small allocations
+are very unlikely to fail and if an allocations is not possible, it is
+very likely the error code can't recover from this
+situation. Furthermore, many of the error paths are not tested at
+all. Instead use g_malloc() when the allocation fails and rely on an
+external watchdog to restart the program.
+
+Furthermore, Glib's functions such as g_strdup use g_malloc which
+obviously terminates the program anyway.
+
+For large allocation using g_try_malloc is still the right choice.
Example:
additional = g_try_malloc(len - 1); // correct
interface is used. The byte values must have prefix 0 added,
the bytes must be separated by ":" char and its length must be
exactly 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 = 17 characters.
+- DeviceName: The interface name where this setting should be applied, e.g.
+ eth0. The MAC address will take preference over DeviceName in matching.
- Nameservers: Comma separated list of nameservers
- SearchDomains: Comma separated list of DNS search domains
- Timeservers: Comma separated list of timeservers
MAC address of the interface to be used. If not specified, the first
found interface is used. Must be in format ab:cd:ef:01:23:45.
.TP
+.BI DeviceName= ifname
+Device name the interface to be used, e.g. eth0. MAC takes preference
+over DeviceName.
+.TP
.BI Nameservers= servers
Comma separated list of nameservers.
.TP
Passphrase = password
IPv4 = 192.168.2.2/255.255.255.0/192.168.2.1
MAC = 06:05:04:03:02:01
+
+[service_vlan]
+Type = ethernet
+DeviceName = enp4s0.1
+IPv4 = 192.168.1.42/255.255.255.0/192.168.1.1
.fi
.SH "SEE ALSO"
.BR connman (8)
\fIConnMan\fP's vpn connections are configured with so called
"\fBprovisioning files\fP" which reside under \fI@vpn_storagedir@/\fP.
The files can be named anything, as long as they contain only printable
-ascii characers, for example letters, numbers and underscores. The file
+ascii characters, for example letters, numbers and underscores. The file
must end with \fB.config\fP. Each VPN connection requires a provisioning
file, but multiple connections can be specified in the same file.
.SH "FILE FORMAT"
optional but it can be used to set up various aspects of ConnMan-VPN's
behavior. The location of the file may be changed through use of
the \fB\-\-config= \fRargument for \fBconnman-vpn\fP(8).
+.P
+DAC privileges (user, group and supplementary groups) of a VPN binary
+ran by \fBconnman-vpn\fP(8) can be controlled by this configuration.
+Configuration in
+.B connman-vpn.conf
+is for all VPN types and can be overridden by defining separate configs into
+.B @sysconfdir@/connman/vpn-plugin/
+using the plugin name + .conf suffix using the same syntax. For example,
+for OpenVPN the path to config is
+.B @sysconfdir@/connman/vpn-plugin/openvpn.conf
+which will override any value in the main configuration.
+
.SH "FILE FORMAT"
.P
The configuration file consists of sections (groups) of key-value pairs.
Set input request timeout. Default is 300 seconds. The request for inputs
like passphrase will timeout after certain amount of time. Use this setting
to increase the value in case of different user interface designs.
-.SH "EXAMPLE"
-The following example configuration sets InputRequestTimeout to 10 minutes.
+.SS [DACPrivileges]
+This section controls the DAC privileges to use for a VPN binary used by a VPN
+plugin. DAC privileges that can be set are user, group and supplementary groups.
+.TP
+.BI User= username/uid
+User on the system to use for running VPN binary. Username or uid can be used.
+.TP
+.BI Group= groupname/gid
+The main group to use for running VPN binary. Group name or gid can be used.
+.TP
+.BI SupplementaryGroups= groupnames/gids
+Comma separated list of supplementary groups to set for the VPN binary. Groups
+can be defined with their names or gid's.
+.SH "EXAMPLES"
+The following example configuration sets InputRequestTimeout to 10 minutes,
+runs VPNs as user "vpn_user" of group "vpn" with additional supplementary
+groups "inet" and "net_admin".
.PP
.nf
[General]
InputRequestTimeout = 600
+
+[DACPrivileges]
+User = vpn_user
+Group = vpn
+SupplementaryGroups = inet, net_admin
.fi
.SH "SEE ALSO"
.BR connman (8), \ connman-vpn (8)
List of technoolgies which are always connected regardless
of PreferredTechnologies setting (AutoConnect = true). The
default value is empty and this feature is disabled unless
-explicitely enabled in the config file.
+explicitly enabled in the config file.
.TP
.BI PreferredTechnologies= technology\fR[,...]
List of preferred technologies from the most preferred
.PP
.TP
.B state
-Shows the system properties. Includes ths online state of the
+Shows the system properties. Includes the online state of the
system, offline mode, and session mode.
.PP
.TP
RX.Errors
- Total number of erronous packets
+ Total number of erroneous packets
received.
TX.Errors
- Total number of erronous packets
+ Total number of erroneous packets
sent.
RX.Dropped
creation of a tun/tap interface, and IP
configuration, NAT and IP forwarding on that
interface.
- An object path, a dictionnary and a file descriptor
+ An object path, a dictionary and a file descriptor
with IP settings are returned.
Possible Errors: [service].Error.InvalidArguments
In addition to WiFi naming, WiFi networks are subject to a grouping policy
performed around SSID and security type. This means that one service will be
-seen for N WiFi networks providing the same SSID and the same security metod.
+seen for N WiFi networks providing the same SSID and the same security method.
For instance, if 5 APs are servicing an SSID called "TEST" with WPA2
authentication and 3 APs are servicing the same SSID with open authentication
method, the user will see only two services listed with the name "TEST"
array{byte} WiFiDisplayIEs [readonly]
- The TLV formated byte array representing the
- WiFi Display Informations Elements.
+ The TLV formatted byte array representing the
+ WiFi Display Information Elements.
Adapter).
Then in the vtable's connect method all the needed pieces to perform a
-connection shall be perfomed.
+connection shall be performed.
To learn how to use the connman_network_*() functions such as
connman_network_set_index() and connman_network_set_connected() see
The services are sorted in the order of the bearer
entries in this list.
- Also "*" matches any bearer. This is usefull to prefer
+ Also "*" matches any bearer. This is useful to prefer
certain bearers such as 'wifi' with a fallback to any
other available bearer.
instead waits until another session actively requires to go online.
This is comparable to piggy-backing.
-Connnect()
+Connect()
+------+
| v
+------------+
string OpenConnect.ClientCert
Informational field containing a pkcs11 URL or a path
- name for the client certificate.
+ name for the client certificate.
string OpenConnect.Cookie
Return the OpenConnect cookie value that is used for
authenticating the VPN session.
+ string OpenConnect.PKCSClientCert
+
+ Informational field containing a PKCS#1/PKCS#8/PKCS#12
+ URL or a path name for the PKCS#1/PKCS#8/PKCS#12 client
+ certificate.
+
+ string OpenConnect.PKCSPassword
+
+ Password for decrypting PKCS#8/PKCS#12 client
+ certificate.
+
string OpenConnect.ServerCert
Return the OpenConnect server hash used to identify
Return the final VPN server to use after possible
web authentication logins, selections and redirections.
+ string OpenVPN.PrivateKeyPassword
+
+ Return the private key password used to decrypt the
+ encrypted OpenVPN private key file.
+
+ boolean AllowStoreCredentials
+
+ Indicates to the receiving UI whether the values
+ entered by the user can be stored for future use.
+ "Requirement" should be set to "control". A "Value"
+ of true indicates that the option to store the
+ credentials can be offered to the user, false
+ indicates that no such option should be presented.
+
+ boolean AllowRetrieveCredentials
+
+ Tells the receiving UI whether to attempt to retrieve
+ previously stored values. "Requirement" should be set
+ to "control". "Value" should be set to true if
+ previously stored values can be used, false otherwise.
+
+ boolean KeepCredentials
+
+ Indicates to the receiving UI whether to keep ("Value"
+ is set "true") or clear ("Value" is set "false") the
+ credentials or not. "Requirement" should be set to
+ "control". By default this is not required to be set
+ and is handled only when explicitly defined as "true".
+ This is useful in case of having both the
+ AllowStoreCredentials and the AllowRetrieveCredentials
+ set as "false", but clearing credentials is not
+ required. In such case the value can be explicitly set
+ to "true". An example case is when the password for
+ encrypted Private Key is requested.
+
+ string VpnAgent.AuthFailure
+
+ Informational field that can be used to indicate VPN
+ agent that previous authentication has failed and new
+ credentials should be requested from user. Additional
+ information about the failure can be added as "Value".
+
Arguments string Type
Contains the type of a field. For example "password",
string Requirement
Contains the requirement option. Valid values are
- "mandatory", "optional", "alternate" or
- "informational".
+ "mandatory", "optional", "alternate", "informational"
+ and "control".
The "alternate" value specifies that this field can be
returned as an alternative to another one.
is here only to provide an information so a value is
attached to it.
+ A "control" argument is used to specify behaviour. The
+ effect will depend on the field name and value, but
+ control fields will not usually be presented directly
+ to the user, and are not expected to be returned.
+
array{string} Alternates
Contains the list of alternate field names this
"Requirement" : "informational"
} }
==> { "OpenConnect.Cookie" : "0123456@adfsf@asasdf" }
+
+ Requesting a username and password but without allowing
+ the values entered by the user to be stored.
+
+ RequestInput("/vpn3",
+ { "Username" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ } }
+ { "Password" : { "Type" : "password",
+ "Requirement" : "mandatory"
+ } }
+ { "AllowStoreCredentials" : { "Type" : "boolean",
+ "Requirement" : "control",
+ "Value" : false
+ } }
+ ==> { "Username" : "foo", "Password" : "secret123" }
Replace * with an identifier unique to the config file.
Allowed fields:
-- Type: Provider type. Value of OpenConnect, OpenVPN, VPNC, L2TP or PPTP
+- Type: Provider type. Value of OpenConnect, OpenVPN, VPNC, L2TP, PPTP or
+ WireGuard
VPN related parameters (M = mandatory, O = optional):
- Name: A user defined name for the VPN (M)
OpenConnect.CACert --cafile File containing other Certificate
Authorities in addition to the ones
in the system trust database (O)
- OpenConnect.ClientCert --certificate Client certificate file, if needed
- by web authentication (O)
+ OpenConnect.ClientCert --certificate Client certificate file, needed
+ by web authentication when AuthType
+ is set as "publickey" (O)
VPN.MTU --mtu Request MTU from server as the MTU
of the tunnel (O)
OpenConnect.Cookie --cookie-on-stdin Cookie received as a result of the
Only usable for extremely simple VPN
configurations and should normally
be set only via the VPN Agent API.
-If OpenConnect.Cookie or OpenConnect.ServerCert are missing, the VPN Agent will
-be contacted to supply the information.
+ OpenConnect.AllowSelfSignedCert none Additional option to define if self
+ signed server certificates are
+ allowed. Boolean string and defaults
+ to false, value "true" enables the
+ option. Affects to the OpenConnect
+ internal function only: --servercert
+ is not added to startup parameters
+ and receiving self signed cert from
+ server terminates the connection if
+ set as false (or omitted) (O)
+ OpenConnect.AuthType Type of authentication used with
+ OpenConnect. Applicable values are
+ "cookie", "cookie_with_userpass",
+ "userpass", "publickey" and
+ "pkcs". Value "cookie" is basic
+ cookie based authentication. Value
+ "cookie_with_userpass" means that
+ credentials are used to retrieve the
+ connection cookie, which hides the
+ username from commandline. With
+ value "userpass" username and
+ password are used. Value "publickey"
+ requires CACert and UserPrivateKey
+ to be set. Value "pkcs" uses the
+ PKCSClientCert and requests password
+ input. Defaults to "cookie" (O)
+ cookie --cookie-on-stdin Default cookie based authentication
+ cookie_with_userpass Two phased connection, first
+ authentication: --cookieonly authenticate with credentials then
+ --passwd-on-stdin use cookie for connection. Username
+ --user is hidden from commandline during
+ connection: --cookie-on-stdin connection.
+ userpass --passwd-on-stdin Credential based authentication,
+ --user username is visible on commandline.
+ publickey --clientcert Non-encrypted client certificate and
+ --sslkey private key file is used for auth.
+ pkcs --cliencert Authenticate with PKCS#1/PKCS#8/
+ PKCS#12 client certificate.
+ OpenConnect.DisableIPv6 --disable-ipv6 Do not ask for IPv6 connectivity.
+ Boolean string and defaults to
+ false, value "true" enables the
+ option (O)
+ OpenConnect.NoDTLS --no-dtls Disable DTLS and ESP (O)
+ OpenConnect.NoHTTPKeepalive --no-http-keepalive Disable HTTP connection
+ re-use to workaround issues with
+ some servers. Boolean string and
+ defaults to false, value "true"
+ enables the option (O)
+ OpenConnect.PKCSClientCert --certificate Certificate and private key in
+ a PKCS#1/PKCS#8/PKCS#12 structure.
+ Needed when AuthType is "pkcs" (O)
+ OpenConnect.Usergroup --usergroup Set login usergroup on remote server
+ (O)
+ OpenConnect.UserPrivateKey --sslkey SSL private key file needed by web
+ authentication when AuthType is set
+ as "publickey" (O)
+
+The VPN agent will be contacted to supply the information based on the
+authentication type as follows:
+ Authentication type Information requested Saved with name
+ cookie OpenConnect.Cookie OpenConnect.Cookie
+ cookie_with_userpass Username OpenConnect.Username
+ Password OpenConnect.Password
+ userpass Username OpenConnect.Username
+ Password OpenConnect.Password
+ publickey <none>
+ pkcs OpenConnect.PKCSPassword OpenConnect.PKCSPassword
OpenVPN VPN supports following options (see openvpn(8) for details):
Option name OpenVPN option Description
OpenVPN 2.3+.
OpenVPN.TLSAuth sub-option of --tls-remote (O)
OpenVPN.TLSAuthDir sub-option of --tls-remote (O)
+ OpenVPN.TLSCipher --tls-cipher Add an additional layer of HMAC
+ authentication on top of the TLS
+ control channel to mitigate DoS attacks
+ and attacks on the TLS stack. Static
+ key file given as parameter (0)
OpenVPN.Cipher --cipher Encrypt packets with cipher algorithm
given as parameter (O)
OpenVPN.Auth --auth Authenticate packets with HMAC using
PPPD.ReqMPPEStateful mppe-stateful Allow MPPE to use stateful mode (O)
PPPD.NoVJ novj No Van Jacobson compression (O)
-
PPTP VPN supports following options (see pptp(8) and pppd(8) for details)
Option name pptp config value Description
PPTP.User - PPTP user name, asked from the user
PPPD.RequirMPPEStateful mppe-stateful Allow MPPE to use stateful mode (O)
PPPD.NoVJ novj No Van Jacobson compression (O)
+WireGuard VPN supports following options
+ Option name Description
+ WireGuard.Address Internal IP address (local/netmask/peer)
+ WireGuard.ListPort Local listen port (optional)
+ WireGuard.DNS List of nameservers separated
+ by comma (optional)
+ WireGuard.PrivateKey Private key of interface
+ WireGuard.PublicKey Public key of peer
+ WireGuard.PresharedKey Preshared key of peer (optional)
+ WireGuard.AllowedIPs See Cryptokey Routing
+ WireGuard.EndpointPort Endpoint listen port (optional)
+ WireGuard.PersistentKeepalive Keep alive in seconds (optional)
+
Example
=======
[provider_openconnect]
Type = OpenConnect
+AuthType = pkcs
Name = Connection to corporate network using Cisco VPN
Host = 7.6.5.4
Domain = corporate.com
OpenVPN.CACert = /etc/certs/cacert.pem
OpenVPN.Cert = /etc/certs/cert.pem
OpenVPN.Key = /etc/certs/cert.key
+
+[provider_wireguard]
+Type = WireGuard
+Name = Wireguard VPN Tunnel
+Host = 3.2.5.6
+Domain = my.home.network
+WireGuard.Address = 10.2.0.2/24
+WireGuard.ListenPort = 47824
+WireGuard.DNS = 10.2.0.1
+WireGuard.PrivateKey = qKIj010hDdWSjQQyVCnEgthLXusBgm3I6HWrJUaJymc=
+WireGuard.PublicKey = zzqUfWGIil6QxrAGz77HE5BGUEdD2PgHYnCg3CDKagE=
+WireGuard.AllowedIPs = 0.0.0.0/0, ::/0
+WireGuard.EndpointPort = 51820
void SetProperty(string name, variant value) [experimental]
- Changes the value of the specified property. Only
- properties that are listed as read-write are
- changeable. On success a PropertyChanged signal
- will be emitted.
+ Changes the value of the specified property or the
+ properties defined as a dict passed as variant, where
+ the format is equal to the dict returned by
+ GetProperties(). Only properties that are listed as
+ read-write are changeable. Property name "Properties"
+ indicates a dict of properties. On success a
+ PropertyChanged signal will be emitted for the
+ specified property or for all changed properties
+ individually. If there is no change in property value
+ no PropertyChanged signal is sent. Configuration is
+ written to disk when one or more values are changed.
+ In case a dict of properties are given, configuration
+ write is done after all properties are processed.
+ Specifics in dict use in contrast to setting a single
+ property:
+ - Dict can contain values set as empty strings
+ or arrays. This causes the values to be
+ cleared as if using ClearProperty().
+ - If there are errors with the properties,
+ InvalidProperty or PermissionDenied error is
+ returned. InvalidProperty is sent when there
+ is at least one invalid property, in this
+ case there can be also properties that
+ cannot be changed (immutable properties).
+ If there are only immutable properties
+ PermissionDenied error is returned.
+ - The properties that are invalid or immutable
+ are reported back at the end of the error
+ message as a comma separated property name
+ list.
+ - One invalid/immutable property does not
+ cause the rest of the properties to be
+ ignored. If there are valid and invalid
+ properties, the valid properties emit
+ PropertyChanged signal and invalid are
+ reported back with an InvalidProperty
+ message.
Possible Errors: [connection].Error.InvalidArguments
[connection].Error.InvalidProperty
+ [connection].Error.PermissionDenied
+ [connection].Error.NotSupported
void ClearProperty(string name) [experimental]
Possible Errors: [connection].Error.InvalidArguments
[connection].Error.InvalidProperty
+ [connection].Error.PermissionDenied
void Connect() [experimental]
int ProtocolFamily
Protocol family of the route. Set to 4
- if IPv4 and 6 if IPv6 route.
+ if IPv4 and 6 if IPv6 route. Set to 0
+ (PF_UNSPEC) or omit, to have it assigned
+ automatically.
string Network
to VPN server), then State property is set to "ready" and PropertyChanged
signal is sent. If the connection cannot be established, then
State property is set to "failure".
-After successfull connection, the relevant connection properties are sent
+After successful connection, the relevant connection properties are sent
by PropertyChanged signal; like IPv[4|6] information, the index of the
VPN tunneling interface (if there is any), nameserver information,
server specified routes etc.
+
+VPN agent interface
+===================
+
+VPN agent interface described in vpn-agent-api.txt is used for
+interaction between the connectivity UI and ConnMan. A VPN agent
+registered via Management interface gets requests from the VPN plugins
+to input credentials or other authentication information for the VPN
+connection and offers information about the VPN to be connected.
+
+In addition to basic credentials, there are additional types of optional
+and control parameters. The user can dictate whether to store the
+credentials with the optional SaveCredentials value. The VPN plugins can
+also define with the control values AllowStoreCredentials,
+AllowRetrieveCredentials and KeepCredentials how the VPN agent must
+handle the credentials. AllowStoreCredentials as false indicates that
+client cannot use SaveCredentials option. AllowRetrieveCredentials set
+as false, without AllowStoreCredentials set as false should not have
+that same effect and in that case user is allowed to save credentials.
+
+These three control values become useful when a VPN has two or more
+sets of authentication credentials, second of which can be requested
+when the VPN detects a need for them. The first, main credentials,
+would be requested without these control values, so user is able to
+select whether the credentials are saved or not with SaveCredentials
+value. After the VPN initializes the connection and, e.g., needs to
+decrypt a private key file, a new request is sent to VPN agent. In this
+new request both AllowStoreCredentials and AllowRetrieveCredentials are
+set as false indicating that in no circumstances existing credentials
+stored are to be used and neither there should be option visible for
+the user to select saving of the credentials. Depending on VPN agent
+implementation these values can be interpreted as clearing of all the
+existing credentials related to the VPN connection from the credential
+storage. By including the KeepCredentials as true value the VPN can,
+however, tell the VPN agent not to clear the credentials for this VPN
+connection. The KeepCredentials is used to inform the VPN agent that
+these new, second/third/etc. credentials are only to be queried from
+the user and forgotten after that, when used in conjunction with the
+AllowStoreCredentials and AllowRetrieveCredentials set as false.
Internals
=========
-Through such API, everything is made to hide irrelevant informations for the
+Through such API, everything is made to hide irrelevant information for the
applications, which are:
- Everything related to the P2P group and the Group Owner (GO)
if (dhcp_client->state == RENEWING)
return dhcp_send_kernel_packet(&packet,
dhcp_client->requested_ip, CLIENT_PORT,
- dhcp_client->server_ip, SERVER_PORT);
+ dhcp_client->server_ip, SERVER_PORT,
+ dhcp_client->interface);
return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
INADDR_BROADCAST, SERVER_PORT,
dhcp_add_option_uint32(&packet, DHCP_SERVER_ID, server);
return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
- server, SERVER_PORT);
+ server, SERVER_PORT,
+ dhcp_client->interface);
}
static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
- uint32_t dest_ip, int dest_port)
+ uint32_t dest_ip, int dest_port,
+ const char *interface)
{
struct sockaddr_in client;
int fd, n, opt = 1;
if (fd < 0)
return -errno;
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface, strlen(interface) + 1) < 0) {
+ int err = errno;
+ close(fd);
+ return -err;
+ }
+
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
int err = errno;
close(fd);
int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len);
int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
uint32_t source_ip, int source_port,
- uint32_t dest_ip, int dest_port);
+ uint32_t dest_ip, int dest_port,
+ const char *interface);
int dhcp_l3_socket(int port, const char *interface, int family);
int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd);
int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
GSupplicantInterfaceCallback callback,
void *user_data);
+int g_supplicant_interface_set_bss_expiration_age(GSupplicantInterface *interface,
+ unsigned int bss_expiration_age);
+
int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
unsigned int ap_scan);
key, dbus_message_iter_get_arg_type(iter));
}
+static void set_bss_expiration_age(DBusMessageIter *iter, void *user_data)
+{
+ unsigned int bss_expiration_age = GPOINTER_TO_UINT(user_data);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+ &bss_expiration_age);
+}
+
+int g_supplicant_interface_set_bss_expiration_age(GSupplicantInterface *interface,
+ unsigned int bss_expiration_age)
+{
+ return supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface",
+ "BSSExpireAge", DBUS_TYPE_UINT32_AS_STRING,
+ set_bss_expiration_age, NULL,
+ GUINT_TO_POINTER(bss_expiration_age), NULL);
+}
+
struct set_apscan_data
{
unsigned int ap_scan;
}
}
-#define DQUAD(_a,_b,_c,_d) ( ((_a)<<24) | ((_b)<<16) | ((_c)<<8) | (_d) )
+#define DQUAD(_a,_b,_c,_d) ( (((uint32_t)_a)<<24) | (((uint32_t)_b)<<16) | \
+ (((uint32_t)_c)<<8) | ((uint32_t)_d) )
#define V4MATCH(addr, a,b,c,d, m) ( ((addr) ^ DQUAD(a,b,c,d)) >> (32 - (m)) )
#define RFC3484_SCOPE_LINK 2
{
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (void *)sa;
- guint32 addr = ntohl(sin->sin_addr.s_addr);
+ uint32_t addr = ntohl(sin->sin_addr.s_addr);
if (V4MATCH(addr, 169,254,0,0, 16) ||
V4MATCH(addr, 127,0,0,0, 8))
addr = NULL;
result = getaddrinfo(host, NULL, &hints, &addr);
- freeaddrinfo(addr);
+ if(!result)
+ freeaddrinfo(addr);
return result == 0;
}
int connman_inet_set_gateway_interface(int index);
int connman_inet_clear_gateway_interface(int index);
bool connman_inet_compare_subnet(int index, const char *host);
+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 *domain);
int connman_provider_set_nameservers(struct connman_provider *provider,
char * const *nameservers);
-int connman_provider_append_route(struct connman_provider *provider,
- const char *key, const char *value);
+void connman_provider_set_autoconnect(struct connman_provider *provider,
+ bool flag);
const char *connman_provider_get_driver_name(struct connman_provider *provider);
const char *connman_provider_get_save_group(struct connman_provider *provider);
const char *connman_service_get_proxy_autoconfig(struct connman_service *service);
bool connman_service_get_favorite(struct connman_service *service);
bool connman_service_get_autoconnect(struct connman_service *service);
+bool connman_service_set_autoconnect(struct connman_service *service,
+ bool autoconnect);
/* Return non-zero value to terminate the loop, zero to continue */
typedef int (* connman_service_iterate_cb) (struct connman_service *service,
typedef DBusMessage * (* connman_task_notify_t) (struct connman_task *task,
DBusMessage *message, void *user_data);
-struct connman_task *connman_task_create(const char *program);
+typedef void (* connman_task_setup_t) (void *setup_data);
+
+struct connman_task *connman_task_create(const char *program,
+ connman_task_setup_t task_setup,
+ void *setup_data);
void connman_task_destroy(struct connman_task *task);
const char *connman_task_get_path(struct connman_task *task);
return -errno;
vifr.cmd = GET_VLAN_VID_CMD;
- strncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
vid = vifr.u.VID;
return -errno;
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ stpncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
/* check if it is a vlan and get physical interface name*/
vifr.cmd = GET_VLAN_REALDEV_NAME_CMD;
- strncpy(vifr.device1, ifname, sizeof(vifr.device1));
+ stpncpy(vifr.device1, ifname, sizeof(vifr.device1));
if(ioctl(sk, SIOCSIFVLAN, &vifr) >= 0)
- strncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
+ stpncpy(ifr.ifr_name, vifr.u.device2, sizeof(ifr.ifr_name));
/* get driver info */
drvinfocmd.cmd = ETHTOOL_GDRVINFO;
static GHashTable *adapters;
static GHashTable *devices;
static GHashTable *networks;
+static GHashTable *known_networks;
+static GHashTable *stations;
+static GHashTable *access_points;
static bool agent_registered;
#define IWD_SERVICE "net.connman.iwd"
#define IWD_ADAPTER_INTERFACE "net.connman.iwd.Adapter"
#define IWD_DEVICE_INTERFACE "net.connman.iwd.Device"
#define IWD_NETWORK_INTERFACE "net.connman.iwd.Network"
+#define IWD_KNOWN_NETWORK_INTERFACE "net.connman.iwd.KnownNetwork"
+#define IWD_STATION_INTERFACE "net.connman.iwd.Station"
+#define IWD_AP_INTERFACE "net.connman.iwd.AccessPoint"
#define IWD_AGENT_INTERFACE "net.connman.iwd.Agent"
#define IWD_AGENT_ERROR_INTERFACE "net.connman.iwd.Agent.Error"
char *vendor;
char *model;
bool powered;
+ bool ad_hoc;
+ bool station;
+ bool ap;
};
struct iwd_device {
char *name;
char *address;
bool powered;
- bool scanning;
+ char *mode;
struct connman_device *device;
};
char *name;
char *type;
bool connected;
+ char *known_network;
struct iwd_device *iwdd;
struct connman_network *network;
};
+struct iwd_known_network {
+ GDBusProxy *proxy;
+ char *path;
+ char *name;
+ char *type;
+ bool hidden;
+ char *last_connected_time;
+ bool auto_connect;
+ int auto_connect_id;
+};
+
+struct iwd_station {
+ GDBusProxy *proxy;
+ char *path;
+ char *state;
+ char *connected_network;
+ bool scanning;
+};
+
+struct iwd_ap {
+ GDBusProxy *proxy;
+ char *path;
+ bool started;
+
+ int index;
+ char *bridge;
+ struct connman_technology *tech;
+};
+
static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
{
DBusMessageIter iter;
return str;
}
+static GSList *proxy_get_strings(GDBusProxy *proxy, const char *property)
+{
+ DBusMessageIter array, entry;
+ GSList *list = NULL;
+
+ if (!g_dbus_proxy_get_property(proxy, property, &array))
+ return NULL;
+
+ dbus_message_iter_recurse(&array, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING){
+ const char *val;
+
+ dbus_message_iter_get_basic(&entry, &val);
+ list = g_slist_prepend(list, g_strdup(val));
+ dbus_message_iter_next(&entry);
+ }
+
+ return list;
+}
+
static bool proxy_get_bool(GDBusProxy *proxy, const char *property)
{
DBusMessageIter iter;
return;
DBG("%s connect failed: %s", path, dbus_error);
- connman_network_set_error(iwdn->network,
+ if (!strcmp(dbus_error, "net.connman.iwd.Failed"))
+ connman_network_set_error(iwdn->network,
+ CONNMAN_NETWORK_ERROR_INVALID_KEY);
+ else
+ connman_network_set_error(iwdn->network,
CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
return;
}
static int cm_network_disconnect(struct connman_network *network)
{
struct iwd_network *iwdn = connman_network_get_data(network);
- struct iwd_device *iwdd;
+ struct iwd_station *iwds;
- if (!iwdn)
+ if (!iwdn && !iwdn->iwdd)
return -EINVAL;
- iwdd = g_hash_table_lookup(devices, iwdn->device);
- if (!iwdd)
+ iwds = g_hash_table_lookup(stations, iwdn->iwdd->path);
+ if (!iwds)
return -EIO;
- if (!g_dbus_proxy_method_call(iwdd->proxy, "Disconnect",
+ if (!g_dbus_proxy_method_call(iwds->proxy, "Disconnect",
NULL, cm_network_disconnect_cb, g_strdup(iwdn->path), g_free))
return -EIO;
return set_device_powered(device, false);
}
+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);
+ if (!iwds)
+ return;
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *dbus_error = dbus_message_get_error_name(message);
+
+ DBG("%s scan failed: %s", path, dbus_error);
+ }
+}
+
+static int cm_device_scan(struct connman_device *device,
+ struct connman_device_scan_params *params)
+{
+ struct iwd_device *iwdd = connman_device_get_data(device);
+ struct iwd_station *iwds;
+
+ if (strcmp(iwdd->mode, "station"))
+ return -EINVAL;
+
+ iwds = g_hash_table_lookup(stations, iwdd->path);
+ if (!iwds)
+ return -EIO;
+
+ if (!g_dbus_proxy_method_call(iwds->proxy, "Scan",
+ NULL, cm_device_scan_cb, g_strdup(iwds->path), g_free))
+ return -EIO;
+
+ return -EINPROGRESS;
+}
+
static struct connman_device_driver device_driver = {
.name = "iwd",
.type = CONNMAN_DEVICE_TYPE_WIFI,
.remove = cm_device_remove,
.enable = cm_device_enable,
.disable = cm_device_disable,
+ .scan = cm_device_scan,
};
static int cm_tech_probe(struct connman_technology *technology)
{
}
-static struct connman_technology_driver tech_driver = {
- .name = "iwd",
- .type = CONNMAN_SERVICE_TYPE_WIFI,
- .probe = cm_tech_probe,
- .remove = cm_tech_remove,
+struct tech_cb_data {
+ struct iwd_device *iwdd;
+ char *path;
+ char *ssid;
+ char *passphrase;
+ char *bridge;
+ int index;
+ struct connman_technology *tech;
};
-static unsigned char calculate_strength(int strength)
+static void tech_cb_free(struct tech_cb_data *cbd)
{
- unsigned char res;
+ g_free(cbd->path);
+ g_free(cbd->ssid);
+ g_free(cbd->passphrase);
+ g_free(cbd->bridge);
+ g_free(cbd);
+}
- /*
- * Network's maximum signal strength expressed in 100 * dBm.
- * The value is the range of 0 (strongest signal) to -10000
- * (weakest signal)
- *
- * ConnMan expects it in the range from 100 (strongest) to 0
- * (weakest).
- */
- res = (unsigned char)((strength * -10000) / 100);
+static int cm_change_tethering(struct iwd_device *iwdd,
+ struct connman_technology *technology,
+ const char *identifier, const char *passphrase,
+ const char *bridge, bool enabled);
- return res;
+static void tech_ap_start_cb(DBusMessage *message, void *user_data)
+{
+
+ struct tech_cb_data *cbd = user_data;
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *dbus_error = dbus_message_get_error_name(message);
+
+ connman_warn("iwd device %s could not enable AccessPoint mode: %s",
+ cbd->path, dbus_error);
+ goto out;
+ }
+
+ /* wait for 'Started' signal */
+ return;
+out:
+ cm_change_tethering(cbd->iwdd, cbd->tech,
+ cbd->ssid, cbd->passphrase, cbd->bridge, false);
+ tech_cb_free(cbd);
}
-static void _update_signal_strength(const char *path, int16_t signal_strength)
+static void tech_ap_stop_cb(DBusMessage *message, void *user_data)
{
- struct iwd_network *iwdn;
+ struct tech_cb_data *cbd = user_data;
- iwdn = g_hash_table_lookup(networks, path);
- if (!iwdn)
- return;
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *dbus_error = dbus_message_get_error_name(message);
- if (!iwdn->network)
- return;
+ connman_warn("iwd device %s could not disable AccessPoint mode: %s",
+ cbd->path, dbus_error);
+ goto out;
+ }
- connman_network_set_strength(iwdn->network,
- calculate_strength(signal_strength));
+ return;
+out:
+ tech_cb_free(cbd);
}
-static void ordered_networks_cb(DBusMessage *message, void *user_data)
+static void ap_start_append(DBusMessageIter *iter, void *user_data)
{
- DBusMessageIter array, entry;
+ struct tech_cb_data *cbd = user_data;
+
+ DBG("ssid %s", cbd->ssid);
+ DBG("passphrase %s", cbd->passphrase);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cbd->ssid);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cbd->passphrase);
+}
+
+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;
DBG("");
- if (!dbus_message_iter_init(message, &array))
- return;
+ iwdd = g_hash_table_lookup(devices, cbd->path);
+ if (!iwdd) {
+ DBG("device already removed");
+ goto out;
+ }
- if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
- return;
+ if (dbus_error_is_set(error)) {
+ connman_warn("iwd device %s could not enable AcessPoint mode: %s",
+ cbd->path, error->message);
+ goto out;
+ }
- dbus_message_iter_recurse(&array, &entry);
- while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
- DBusMessageIter value;
- const char *path, *name, *type;
- int16_t signal_strength;
+ iwdap = g_hash_table_lookup(access_points, iwdd->path);
+ if (!iwdap) {
+ DBG("%s no ap object found", iwdd->path);
+ goto out;
+ }
+ iwdap->index = cbd->index;
+ iwdap->bridge = g_strdup(cbd->bridge);
+ iwdap->tech = cbd->tech;
- dbus_message_iter_recurse(&entry, &value);
+ if (!g_dbus_proxy_method_call(iwdap->proxy, "Start",
+ ap_start_append, tech_ap_start_cb, cbd, NULL)) {
+ connman_warn("iwd ap %s could not start AccessPoint mode: %s",
+ cbd->path, error->message);
+ goto out;
+ }
- dbus_message_iter_get_basic(&value, &path);
+ return;
+out:
+ if (iwdap) {
+ iwdap->index = -1;
+ g_free(iwdap->bridge);
+ iwdap->bridge = NULL;
+ }
+ tech_cb_free(cbd);
+}
- dbus_message_iter_next(&value);
- dbus_message_iter_get_basic(&value, &name);
+static void tech_disable_tethering_cb(const DBusError *error, void *user_data)
+{
+ struct tech_cb_data *cbd = user_data;
+ struct iwd_device *iwdd;
+ struct iwd_ap *iwdap;
- dbus_message_iter_next(&value);
- dbus_message_iter_get_basic(&value, &signal_strength);
+ DBG("");
- dbus_message_iter_next(&value);
- dbus_message_iter_get_basic(&value, &type);
+ iwdd = g_hash_table_lookup(devices, cbd->path);
+ if (!iwdd) {
+ DBG("device already removed");
+ goto out;
+ }
- _update_signal_strength(path, signal_strength);
+ if (dbus_error_is_set(error)) {
+ connman_warn("iwd device %s could not enable Station mode: %s",
+ cbd->path, error->message);
+ goto out;
+ }
- dbus_message_iter_next(&entry);
+ iwdap = g_hash_table_lookup(access_points, iwdd->path);
+ if (!iwdap) {
+ DBG("%s no ap object found", iwdd->path);
+ goto out;
+ }
+
+ g_free(iwdap->bridge);
+ iwdap->index = -1;
+ 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",
+ cbd->path, error->message);
+ goto out;
+ }
+
+ return;
+out:
+ tech_cb_free(cbd);
+}
+
+static int cm_change_tethering(struct iwd_device *iwdd,
+ struct connman_technology *technology,
+ const char *identifier, const char *passphrase,
+ const char *bridge, bool enabled)
+{
+ struct tech_cb_data *cbd;
+ int index;
+ const char *mode;
+ GDBusResultFunction cb;
+
+ index = connman_inet_ifindex(iwdd->name);
+ if (index < 0)
+ return -ENODEV;
+
+ cbd = g_new(struct tech_cb_data, 1);
+ cbd->iwdd = iwdd;
+ cbd->path = g_strdup(iwdd->path);
+ cbd->ssid = g_strdup(identifier);
+ cbd->passphrase = g_strdup(passphrase);
+ cbd->bridge = g_strdup(bridge);
+ cbd->tech = technology;
+ cbd->index = index;
+
+ if (enabled) {
+ mode = "ap";
+ cb = tech_enable_tethering_cb;
+ } else {
+ mode = "station";
+ cb = tech_disable_tethering_cb;
+ }
+
+ if (!g_dbus_proxy_set_property_basic(iwdd->proxy,
+ "Mode", DBUS_TYPE_STRING, &mode,
+ cb, cbd, NULL)) {
+ tech_cb_free(cbd);
+ return -EIO;
}
+
+ return 0;
}
-static void update_signal_strength(struct iwd_device *iwdd)
+static int cm_tech_tethering(struct connman_technology *technology,
+ const char *identifier, const char *passphrase,
+ const char *bridge, bool enabled)
{
- if (!g_dbus_proxy_method_call(iwdd->proxy,
- "GetOrderedNetworks",
- NULL, ordered_networks_cb,
- NULL, NULL))
- DBG("GetOrderedNetworks() failed");
+ GHashTableIter iter;
+ gpointer key, value;
+ int err = 0, res;
+
+ g_hash_table_iter_init(&iter, devices);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct iwd_device *iwdd = value;
+ struct iwd_adapter *iwda;
+
+ iwda = g_hash_table_lookup(adapters, iwdd->adapter);
+ if (!iwda)
+ continue;
+
+ if (!iwda->station || !iwda->ap )
+ /* No support for Station and AccessPoint mode */
+ continue;
+
+ if (!enabled && !g_strcmp0("ap", iwdd->mode)) {
+ res = cm_change_tethering(iwdd, technology, identifier,
+ passphrase, bridge, enabled);
+ if (res)
+ connman_warn("%s switching to Station mode failed",
+ iwdd->path);
+ if (!err)
+ err = res;
+ continue;
+ }
+
+ if (enabled && !g_strcmp0("station", iwdd->mode)) {
+ err = cm_change_tethering(iwdd, technology, identifier,
+ passphrase, bridge, enabled);
+ if (err)
+ connman_warn("%s switching to AccessPoint mode failed",
+ iwdd->path);
+ break;
+ }
+ }
+
+ return err;
}
+static struct connman_technology_driver tech_driver = {
+ .name = "iwd",
+ .type = CONNMAN_SERVICE_TYPE_WIFI,
+ .probe = cm_tech_probe,
+ .remove = cm_tech_remove,
+ .set_tethering = cm_tech_tethering,
+};
+
static const char *security_remap(const char *security)
{
if (!g_strcmp0(security, "open"))
connman_network_set_blob(iwdn->network, "WiFi.SSID", iwdn->name,
strlen(iwdn->name));
connman_network_set_string(iwdn->network, "WiFi.Security",
- iwdn->type);
+ security_remap(iwdn->type));
connman_network_set_string(iwdn->network, "WiFi.Mode", "managed");
if (connman_device_add_network(iwdd->device, iwdn->network) < 0) {
iwdd->powered = powered;
DBG("%s powered %d", path, iwdd->powered);
- } else if (!strcmp(name, "Scanning")) {
- dbus_bool_t scanning;
+ } else if (!strcmp(name, "Mode")) {
+ const char *mode;
- dbus_message_iter_get_basic(iter, &scanning);
- iwdd->scanning = scanning;
-
- DBG("%s scanning %d", path, iwdd->scanning);
-
- if (!iwdd->scanning)
- update_signal_strength(iwdd);
+ dbus_message_iter_get_basic(iter, &mode);
+ g_free(iwdd->mode);
+ iwdd->mode = g_strdup(mode);
+ DBG("%s mode %s", path, iwdd->mode);
}
}
}
}
+static unsigned char calculate_strength(int strength)
+{
+ unsigned char res;
+
+ /*
+ * Network's maximum signal strength expressed in 100 * dBm.
+ * The value is the range of 0 (strongest signal) to -10000
+ * (weakest signal)
+ *
+ * ConnMan expects it in the range from 100 (strongest) to 0
+ * (weakest).
+ */
+ res = (unsigned char)((strength + 10000) / 100);
+
+ return res;
+}
+
+static void _update_signal_strength(const char *path, int16_t signal_strength)
+{
+ struct iwd_network *iwdn;
+
+ iwdn = g_hash_table_lookup(networks, path);
+ if (!iwdn)
+ return;
+
+ connman_network_set_strength(iwdn->network,
+ calculate_strength(signal_strength));
+ connman_network_update(iwdn->network);
+}
+
+static void ordered_networks_cb(DBusMessage *message, void *user_data)
+{
+ DBusMessageIter array, entry;
+
+ DBG("");
+
+ if (!dbus_message_iter_init(message, &array))
+ return;
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(&array, &entry);
+ while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter value;
+ const char *path;
+ int16_t signal_strength;
+
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ dbus_message_iter_get_basic(&value, &path);
+
+ dbus_message_iter_next(&value);
+ dbus_message_iter_get_basic(&value, &signal_strength);
+
+ _update_signal_strength(path, signal_strength);
+
+ dbus_message_iter_next(&entry);
+ }
+}
+
+static void update_signal_strength(struct iwd_station *iwds)
+{
+ if (!g_dbus_proxy_method_call(iwds->proxy,
+ "GetOrderedNetworks",
+ NULL, ordered_networks_cb,
+ NULL, NULL))
+ DBG("GetOrderedNetworks() failed");
+}
+
+static void station_property_change(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct iwd_station *iwds;
+ const char *path;
+
+ path = g_dbus_proxy_get_path(proxy);
+ iwds = g_hash_table_lookup(stations, path);
+ if (!iwds)
+ return;
+
+ if (!strcmp(name, "State")) {
+ const char *state;
+
+ dbus_message_iter_get_basic(iter, &state);
+ g_free(iwds->state);
+ iwds->state = g_strdup(state);
+
+ DBG("%s state %s", path, iwds->state);
+ } else if (!strcmp(name, "ConnectedNetwork")) {
+ const char *connected_network;
+
+ g_free(iwds->connected_network);
+ if (iter) {
+ dbus_message_iter_get_basic(iter, &connected_network);
+ iwds->connected_network = g_strdup(connected_network);
+ } else {
+ iwds->connected_network = NULL;
+ }
+
+ DBG("%s connected_network %s", path, iwds->connected_network);
+ } else if (!strcmp(name, "Scanning")) {
+ dbus_bool_t scanning;
+
+ dbus_message_iter_get_basic(iter, &scanning);
+ iwds->scanning = scanning;
+
+ if (!iwds->scanning)
+ update_signal_strength(iwds);
+
+ DBG("%s scanning %d", path, iwds->scanning);
+ }
+}
+
+static void ap_property_change(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct iwd_ap *iwdap;
+ const char *path;
+ int err;
+
+ path = g_dbus_proxy_get_path(proxy);
+ iwdap = g_hash_table_lookup(access_points, path);
+ if (!iwdap)
+ return;
+
+ if (!strcmp(name, "Started")) {
+ dbus_bool_t started;
+
+ dbus_message_iter_get_basic(iter, &started);
+ iwdap->started = started;
+
+ DBG("%s started %d", path, iwdap->started);
+
+ if (iwdap->started && iwdap->index != -1) {
+ DBG("index %d bridge %s", iwdap->index, iwdap->bridge);
+ err = connman_technology_tethering_notify(
+ iwdap->tech, true);
+ if (err)
+ return;
+ err = connman_inet_add_to_bridge(
+ iwdap->index, iwdap->bridge);
+ }
+ }
+}
+
static void adapter_free(gpointer data)
{
struct iwd_adapter *iwda = data;
g_free(iwdn->device);
g_free(iwdn->name);
g_free(iwdn->type);
+ g_free(iwdn->known_network);
g_free(iwdn);
}
+static void known_network_free(gpointer data)
+{
+ struct iwd_known_network *iwdkn = data;
+
+ if (iwdkn->proxy) {
+ g_dbus_proxy_unref(iwdkn->proxy);
+ iwdkn->proxy = NULL;
+ }
+
+ if (iwdkn->auto_connect_id)
+ g_source_remove(iwdkn->auto_connect_id);
+
+ g_free(iwdkn->path);
+ g_free(iwdkn->name);
+ g_free(iwdkn->type);
+ g_free(iwdkn->last_connected_time);
+ g_free(iwdkn);
+}
+
+static void station_free(gpointer data)
+{
+ struct iwd_station *iwds = data;
+
+ if (iwds->proxy) {
+ g_dbus_proxy_unref(iwds->proxy);
+ iwds->proxy = NULL;
+ }
+ g_free(iwds->path);
+ g_free(iwds->connected_network);
+ g_free(iwds);
+}
+
+static void ap_free(gpointer data)
+{
+ struct iwd_ap *iwdap = data;
+
+ if (iwdap->proxy) {
+ g_dbus_proxy_unref(iwdap->proxy);
+ iwdap->proxy = NULL;
+ }
+ g_free(iwdap->bridge);
+ g_free(iwdap);
+}
+
static void create_adapter(GDBusProxy *proxy)
{
const char *path = g_dbus_proxy_get_path(proxy);
struct iwd_adapter *iwda;
+ GSList *modes, *list;
iwda = g_try_new0(struct iwd_adapter, 1);
iwda->model = g_strdup(proxy_get_string(proxy, "Model"));
iwda->powered = proxy_get_bool(proxy, "Powered");
- DBG("%s vendor '%s' model '%s' powered %d", path, iwda->vendor,
- iwda->model, iwda->powered);
+ modes = proxy_get_strings(proxy, "SupportedModes");
+ for (list = modes; list; list = list->next) {
+ char *m = list->data;
+
+ if (!m)
+ continue;
+
+ if (!strcmp(m, "ad-hoc"))
+ iwda->ad_hoc = true;
+ else if (!strcmp(m, "station"))
+ iwda->station = true;
+ else if (!strcmp(m, "ap"))
+ iwda->ap = true;
+ }
+ g_slist_free_full(modes, g_free);
+
+ DBG("%s vendor '%s' model '%s' powered %d ad-hoc %d station %d ap %d",
+ path, iwda->vendor, iwda->model, iwda->powered,
+ iwda->ad_hoc, iwda->station, iwda->ap);
g_dbus_proxy_set_property_watch(iwda->proxy,
adapter_property_change, NULL);
iwdd->name = g_strdup(proxy_get_string(proxy, "Name"));
iwdd->address = g_strdup(proxy_get_string(proxy, "Address"));
iwdd->powered = proxy_get_bool(proxy, "Powered");
- iwdd->scanning = proxy_get_bool(proxy, "Scanning");
+ iwdd->mode = g_strdup(proxy_get_string(proxy, "Mode"));
- DBG("adapter %s name %s address %s powered %d scanning %d",
+ DBG("adapter %s name %s address %s powered %d mode %s",
iwdd->adapter, iwdd->name, iwdd->address,
- iwdd->powered, iwdd->scanning);
+ iwdd->powered, iwdd->mode);
g_dbus_proxy_set_property_watch(iwdd->proxy,
device_property_change, NULL);
iwdn->name = g_strdup(proxy_get_string(proxy, "Name"));
iwdn->type = g_strdup(proxy_get_string(proxy, "Type"));
iwdn->connected = proxy_get_bool(proxy, "Connected");
+ iwdn->known_network = g_strdup(proxy_get_string(proxy, "KnownNetwork"));
- DBG("device %s name '%s' type %s connected %d",
- iwdn->device,
- iwdn->name,
- iwdn->type,
- iwdn->connected);
+ DBG("device %s name '%s' type %s connected %d known_network %s",
+ iwdn->device, iwdn->name, iwdn->type, iwdn->connected,
+ iwdn->known_network);
g_dbus_proxy_set_property_watch(iwdn->proxy,
network_property_change, NULL);
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)
+{
+ 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 void disable_auto_connect(struct iwd_known_network *iwdkn)
+{
+ if (iwdkn->auto_connect_id)
+ return;
+
+ iwdkn->auto_connect_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ 0,
+ disable_auto_connect_cb,
+ g_strdup(iwdkn->path),
+ g_free);
+}
+
+static void known_network_property_change(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct iwd_known_network *iwdkn;
+ const char *path;
+
+ path = g_dbus_proxy_get_path(proxy);
+ iwdkn = g_hash_table_lookup(known_networks, path);
+ if (!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)
+{
+ 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->path = g_strdup(path);
+ g_hash_table_replace(known_networks, iwdkn->path, iwdkn);
+
+ iwdkn->proxy = g_dbus_proxy_ref(proxy);
+
+ if (!iwdkn->proxy) {
+ connman_error("Cannot create IWD known network watcher %s", path);
+ g_hash_table_remove(known_networks, path);
+ return;
+ }
+
+ iwdkn->name = g_strdup(proxy_get_string(proxy, "Name"));
+ iwdkn->type = g_strdup(proxy_get_string(proxy, "Type"));
+ 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");
+
+ DBG("name '%s' type %s hidden %d, last_connection_time %s auto_connect %d",
+ iwdkn->name, iwdkn->type, iwdkn->hidden,
+ iwdkn->last_connected_time, iwdkn->auto_connect);
+
+ 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)
+{
+ 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->path = g_strdup(path);
+ g_hash_table_replace(stations, iwds->path, iwds);
+
+ iwds->proxy = g_dbus_proxy_ref(proxy);
+
+ if (!iwds->proxy) {
+ connman_error("Cannot create IWD station watcher %s", path);
+ g_hash_table_remove(stations, path);
+ return;
+ }
+
+ iwds->state = g_strdup(proxy_get_string(proxy, "State"));
+ iwds->connected_network = g_strdup(proxy_get_string(proxy, "ConnectedNetwork"));
+ iwds->scanning = proxy_get_bool(proxy, "Scanning");
+
+ DBG("state '%s' connected_network %s scanning %d",
+ iwds->state, iwds->connected_network, iwds->scanning);
+
+ g_dbus_proxy_set_property_watch(iwds->proxy,
+ station_property_change, NULL);
+}
+
+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->index = -1;
+
+ iwdap->path = g_strdup(path);
+ g_hash_table_replace(access_points, iwdap->path, iwdap);
+
+ iwdap->proxy = g_dbus_proxy_ref(proxy);
+
+ if (!iwdap->proxy) {
+ connman_error("Cannot create IWD access point watcher %s", path);
+ g_hash_table_remove(access_points, path);
+ return;
+ }
+
+ iwdap->started = proxy_get_bool(proxy, "Started");
+
+ DBG("started %d", iwdap->started);
+
+ g_dbus_proxy_set_property_watch(iwdap->proxy,
+ ap_property_change, NULL);
+}
+
static void object_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
create_device(proxy);
else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
create_network(proxy);
+ else if (!strcmp(interface, IWD_KNOWN_NETWORK_INTERFACE))
+ create_know_network(proxy);
+ else if (!strcmp(interface, IWD_STATION_INTERFACE))
+ create_station(proxy);
+ else if (!strcmp(interface, IWD_AP_INTERFACE))
+ create_ap(proxy);
}
static void object_removed(GDBusProxy *proxy, void *user_data)
g_hash_table_remove(devices, path);
else if (!strcmp(interface, IWD_NETWORK_INTERFACE))
g_hash_table_remove(networks, path);
+ else if (!strcmp(interface, IWD_KNOWN_NETWORK_INTERFACE))
+ g_hash_table_remove(known_networks, path);
+ else if (!strcmp(interface, IWD_STATION_INTERFACE))
+ g_hash_table_remove(stations, path);
+ else if (!strcmp(interface, IWD_AP_INTERFACE))
+ g_hash_table_remove(access_points, path);
}
static int iwd_init(void)
networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
network_free);
+ known_networks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ known_network_free);
+
+ stations = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ station_free);
+
+ access_points = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+ ap_free);
+
if (connman_technology_driver_register(&tech_driver) < 0) {
connman_warn("Failed to initialize technology for IWD");
goto out;
if (networks)
g_hash_table_destroy(networks);
+ if (known_networks)
+ g_hash_table_destroy(known_networks);
+
+ if (stations)
+ g_hash_table_destroy(stations);
+
+ if (access_points)
+ g_hash_table_destroy(access_points);
+
if (adapters)
g_hash_table_destroy(adapters);
g_dbus_client_unref(client);
+ g_hash_table_destroy(access_points);
+ g_hash_table_destroy(stations);
+ g_hash_table_destroy(known_networks);
g_hash_table_destroy(networks);
g_hash_table_destroy(devices);
g_hash_table_destroy(adapters);
if (success) {
/*
* Don't handle do anything on success here. oFono will send
- * the change via PropertyChanged singal.
+ * the change via PropertyChanged signal.
*/
return;
}
if (success) {
/*
* Don't handle do anything on success here. oFono will send
- * the change via PropertyChanged singal.
+ * the change via PropertyChanged signal.
*/
return;
}
if (modem_hash) {
/*
- * We should propably wait for the SetProperty() reply
+ * We should probably wait for the SetProperty() reply
* message, because ...
*/
g_hash_table_foreach(modem_hash, modem_power_down, NULL);
struct policy_file {
/*
* A valid file is a keyfile with one ore more groups. All
- * groups are keept in this list.
+ * groups are kept in this list.
*/
GSList *groups;
};
/*
* SELinux combines Role-Based Access Control (RBAC), Type
- * Enforcment (TE) and optionally Multi-Level Security (MLS).
+ * Enforcement (TE) and optionally Multi-Level Security (MLS).
*
* When SELinux is enabled all processes and files are labeled
* with a contex that contains information such as user, role
*
* For identifyng application we (ab)using the type
* information. In the above example the haifux_exec_t type
- * will be transfered to haifux_t as defined in the domain
+ * will be transferred to haifux_t as defined in the domain
* transition and thus we are able to identify the application
* as haifux_t.
*/
err = install_ldisc(install_channel, true);
if (err < 0) {
- connman_error("ldisc installtion failed");
+ connman_error("ldisc installation failed");
return err;
}
}
if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0)
return -EISCONN;
+ if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".OperationCanceled") == 0)
+ return -ECANCELED;
+
return -ECONNREFUSED;
}
if (!dbus_pending_call_get_completed(call))
return;
+ if (call != data->call) {
+ connman_error("invalid call %p to VPN connect_reply data %p "
+ " call %p ", call, data, data->call);
+ dbus_pending_call_unref(call);
+ return;
+ }
+
DBG("user_data %p path %s", user_data, cb_data ? cb_data->path : NULL);
reply = dbus_pending_call_steal_reply(call);
+ if (!reply)
+ goto out;
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, reply)) {
int err = errorstr2val(error.name);
- if (err != -EINPROGRESS) {
+ /*
+ * 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) {
+ DBG("%s connect canceled", data->path);
+ connman_provider_set_autoconnect(data->provider, false);
+ } else if (err != -EINPROGRESS) {
connman_error("Connect reply: %s (%s)", error.message,
error.name);
DBG("data %p cb_data %p", data, cb_data);
dbus_message_unref(reply);
- dbus_pending_call_unref(call);
+out:
+ dbus_pending_call_unref(data->call);
+
+ data->call = NULL;
+ data->connect_pending = false;
}
static int connect_provider(struct connection_data *data, void *user_data,
return -EINVAL;
}
- data->connect_pending = false;
+ if (data->connect_pending && data->call) {
+ connman_info("connect already pending");
+ return -EALREADY;
+ }
/* We need to pass original dbus sender to connman-vpnd,
* use a Connect2 method for that if the original dbus sender is set.
return -EINVAL;
}
+ if (data->call) {
+ dbus_pending_call_cancel(data->call);
+ dbus_pending_call_unref(data->call);
+ }
+
+ data->call = call;
+ data->connect_pending = true;
+
if (cb_data) {
g_free(cb_data->path);
cb_data->path = g_strdup(data->path);
static int provider_disconnect(struct connman_provider *provider)
{
+ int err = 0;
+
struct connection_data *data;
DBG("provider %p", provider);
return -EINVAL;
if (provider_is_connected(data))
- return disconnect_provider(data);
+ err = disconnect_provider(data);
- return 0;
+ if (data->call) {
+ dbus_pending_call_cancel(data->call);
+ dbus_pending_call_unref(data->call);
+ data->call = NULL;
+ }
+
+ data->connect_pending = false;
+
+ return err;
}
static void configuration_create_reply(DBusPendingCall *call, void *user_data)
static void vpn_disconnect_check_provider(struct connection_data *data)
{
- if (data->service_ident && provider_is_connected(data)) {
+ if (provider_is_connected(data)) {
+ /* With NULL service ident NULL is returned immediately */
struct connman_service *service =
connman_service_lookup_from_identifier
(data->service_ident);
if (!vpn_is_valid_transport(service)) {
- disconnect_provider(data);
+ connman_provider_disconnect(data->provider);
}
}
}
#include <gsupplicant/gsupplicant.h>
+#include "src/shared/util.h"
+
#define CLEANUP_TIMEOUT 8 /* in seconds */
#define INACTIVE_TIMEOUT 12 /* in seconds */
#define FAVORITE_MAXIMUM_RETRIES 2
-#define BGSCAN_DEFAULT "simple:30:-45:300"
+#define BGSCAN_DEFAULT "simple:30:-65:300"
#define AUTOSCAN_EXPONENTIAL "exponential:3:300"
#define AUTOSCAN_SINGLE "single:3"
}
struct last_connected {
- GTimeVal modified;
+ struct timeval modified;
gchar *ssid;
int freq;
};
static gint sort_entry(gconstpointer a, gconstpointer b, gpointer user_data)
{
- GTimeVal *aval = (GTimeVal *)a;
- GTimeVal *bval = (GTimeVal *)b;
+ struct timeval *aval = (struct timeval *)a;
+ struct timeval *bval = (struct timeval *)b;
/* Note that the sort order is descending */
if (aval->tv_sec < bval->tv_sec)
GSequence *latest_list;
struct last_connected *entry;
GKeyFile *keyfile;
- GTimeVal modified;
+ struct timeval modified;
gchar **services;
gchar *str;
char *ssid;
g_key_file_free(keyfile);
continue;
}
- g_time_val_from_iso8601(str, &modified);
+ util_iso8601_to_timeval(str, &modified);
g_free(str);
ssid = g_key_file_get_string(keyfile,
return;
}
- if (wifi->network != wifi->pending_network)
+ if (wifi->network && wifi->network != wifi->pending_network)
connman_network_set_connected(wifi->network, false);
wifi->network = NULL;
info->wifi->tethering = false;
connman_technology_tethering_notify(info->technology, false);
- g_free(info->ifname);
- g_free(info->ssid);
- g_free(info);
-
if (info->wifi->ap_supported == WIFI_AP_SUPPORTED) {
g_free(info->wifi->tethering_param->ssid);
g_free(info->wifi->tethering_param);
info->wifi->tethering_param = NULL;
}
+
+ g_free(info->ifname);
+ g_free(info->ssid);
+ g_free(info);
return;
}
+++ /dev/null
-/*
- *
- * 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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <libgen.h>
-
-#include <dbus/dbus.h>
-
-extern char **environ;
-
-static void print(const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- vsyslog(LOG_INFO, format, ap);
- va_end(ap);
-}
-
-static void append(DBusMessageIter *dict, const char *pattern)
-{
- DBusMessageIter entry;
- const char *key, *value;
- char *delim;
-
- delim = strchr(pattern, '=');
- *delim = '\0';
-
- key = pattern;
- value = delim + 1;
-
- /* We clean the environment before invoking openconnect, but
- might as well still filter out the few things that get
- added that we're not interested in */
- if (!strcmp(key, "PWD") || !strcmp(key, "_") ||
- !strcmp(key, "SHLVL") || !strcmp(key, "connman_busname") ||
- !strcmp(key, "connman_network"))
- return;
-
- dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
- NULL, &entry);
-
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
-
- dbus_message_iter_close_container(dict, &entry);
-}
-
-int main(int argc, char *argv[])
-{
- DBusConnection *conn;
- DBusError error;
- DBusMessage *msg;
- DBusMessageIter iter, dict;
- char **envp, *busname, *reason, *interface, *path;
- int ret = 0;
-
- openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON);
-
- busname = getenv("CONNMAN_BUSNAME");
- interface = getenv("CONNMAN_INTERFACE");
- path = getenv("CONNMAN_PATH");
-
- reason = getenv("reason");
-
- if (!busname || !interface || !path || !reason) {
- print("Required environment variables not set");
- ret = 1;
- goto out;
- }
-
- if (strcmp(reason, "pre-init") == 0)
- goto out;
-
- dbus_error_init(&error);
-
- conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
- if (!conn) {
- if (dbus_error_is_set(&error)) {
- print("%s", error.message);
- dbus_error_free(&error);
- } else
- print("Failed to get on system bus");
-
- goto out;
- }
-
- msg = dbus_message_new_method_call(busname, path,
- interface, "notify");
- if (!msg) {
- dbus_connection_unref(conn);
- print("Failed to allocate method call");
- goto out;
- }
-
- dbus_message_set_no_reply(msg, TRUE);
-
- dbus_message_append_args(msg,
- DBUS_TYPE_STRING, &reason,
- DBUS_TYPE_INVALID);
-
- dbus_message_iter_init_append(msg, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
- for (envp = environ; envp && *envp; envp++)
- append(&dict, *envp);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- if (!dbus_connection_send(conn, msg, NULL)) {
- print("Failed to send message");
- goto out;
- }
-
- dbus_connection_flush(conn);
-
- dbus_message_unref(msg);
-
- dbus_connection_unref(conn);
-
-out:
- closelog();
-
- return ret;
-}
--- /dev/null
+/*
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <libgen.h>
+
+#include <dbus/dbus.h>
+
+extern char **environ;
+
+static void print(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vsyslog(LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+static void append(DBusMessageIter *dict, const char *pattern)
+{
+ DBusMessageIter entry;
+ const char *key, *value;
+ char *delim;
+
+ delim = strchr(pattern, '=');
+ *delim = '\0';
+
+ key = pattern;
+ value = delim + 1;
+
+ /*
+ * We clean the environment before invoking openconnect/vpnc,
+ * but might as well still filter out the few things that get
+ * added that we're not interested in
+ */
+ if (!strcmp(key, "PWD") || !strcmp(key, "_") ||
+ !strcmp(key, "SHLVL") || !strcmp(key, "connman_busname") ||
+ !strcmp(key, "connman_network"))
+ return;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+int main(int argc, char *argv[])
+{
+ DBusConnection *conn;
+ DBusError error;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict;
+ char **envp, *busname, *reason, *interface, *path;
+ int ret = 0;
+
+ openlog(basename(argv[0]), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+
+ busname = getenv("CONNMAN_BUSNAME");
+ interface = getenv("CONNMAN_INTERFACE");
+ path = getenv("CONNMAN_PATH");
+
+ reason = getenv("reason");
+
+ if (!busname || !interface || !path || !reason) {
+ print("Required environment variables not set");
+ ret = 1;
+ goto out;
+ }
+
+ if (strcmp(reason, "pre-init") == 0)
+ goto out;
+
+ dbus_error_init(&error);
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+ if (!conn) {
+ if (dbus_error_is_set(&error)) {
+ print("%s", error.message);
+ dbus_error_free(&error);
+ } else
+ print("Failed to get on system bus");
+
+ goto out;
+ }
+
+ msg = dbus_message_new_method_call(busname, path,
+ interface, "notify");
+ if (!msg) {
+ dbus_connection_unref(conn);
+ print("Failed to allocate method call");
+ goto out;
+ }
+
+ dbus_message_set_no_reply(msg, TRUE);
+
+ dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ for (envp = environ; envp && *envp; envp++)
+ append(&dict, *envp);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ if (!dbus_connection_send(conn, msg, NULL)) {
+ print("Failed to send message");
+ goto out;
+ }
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(msg);
+
+ dbus_connection_unref(conn);
+
+out:
+ closelog();
+
+ return ret;
+}
driver = get_driver();
DBG("driver %p", driver);
- if (driver && driver->context_ref) {
+ if (driver && driver->context_ref)
queue_data->user_context = driver->context_ref(user_context);
- queue_data->driver = driver;
- } else
+ else
queue_data->user_context = user_context;
+ queue_data->driver = driver;
queue_data->msg = dbus_message_ref(msg);
queue_data->timeout = timeout;
queue_data->callback = callback;
char *ipv6_gateway;
char *ipv6_privacy;
char *mac;
+ char *devname;
bool mdns;
char **nameservers;
char **search_domains;
#define SERVICE_KEY_IPv6 "IPv6"
#define SERVICE_KEY_IPv6_PRIVACY "IPv6.Privacy"
#define SERVICE_KEY_MAC "MAC"
+#define SERVICE_KEY_DEVICE_NAME "DeviceName"
#define SERVICE_KEY_NAMESERVERS "Nameservers"
#define SERVICE_KEY_SEARCH_DOMAINS "SearchDomains"
#define SERVICE_KEY_TIMESERVERS "Timeservers"
SERVICE_KEY_IPv6,
SERVICE_KEY_IPv6_PRIVACY,
SERVICE_KEY_MAC,
+ SERVICE_KEY_DEVICE_NAME,
SERVICE_KEY_MDNS,
SERVICE_KEY_NAMESERVERS,
SERVICE_KEY_SEARCH_DOMAINS,
g_free(config_service->ipv6_gateway);
g_free(config_service->ipv6_privacy);
g_free(config_service->mac);
+ g_free(config_service->devname);
g_strfreev(config_service->nameservers);
g_strfreev(config_service->search_domains);
g_strfreev(config_service->timeservers);
service->mac = str;
}
+ str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DEVICE_NAME, NULL);
+ if (str) {
+ g_free(service->devname);
+ service->devname = str;
+ }
+
str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DOMAIN, NULL);
if (str) {
g_free(service->domain_name);
g_free(service->ipv6_address);
g_free(service->ipv6_gateway);
g_free(service->mac);
+ g_free(service->devname);
g_free(service);
return false;
if (g_ascii_strcasecmp(device_addr, config->mac) != 0)
return -ENOENT;
+ } else if (config->devname) {
+ struct connman_device *device;
+ const char *devname;
+
+ device = connman_network_get_device(network);
+ if (!device) {
+ connman_error("Network device is missing");
+ return -ENODEV;
+ }
+
+ devname = connman_device_get_string(device, "Interface");
+
+ DBG("wants %s has %s", config->devname, devname);
+
+ if (g_ascii_strcasecmp(devname, config->devname) != 0)
+ return -ENOENT;
}
if (!config->ipv6_address) {
if (!active_gateway->ipv4_gateway)
return;
+
+ /*
+ * If VPN server is on same subnet as we are, skip adding
+ * route.
+ */
+ if (connman_inet_compare_subnet(active_gateway->index,
+ gateway))
+ return;
+
DBG("active gw %s", active_gateway->ipv4_gateway->gateway);
if (g_strcmp0(active_gateway->ipv4_gateway->gateway,
if (!active_gateway->ipv6_gateway)
return;
+ if (connman_inet_compare_ipv6_subnet(active_gateway->index,
+ gateway))
+ return;
+
DBG("active gw %s", active_gateway->ipv6_gateway->gateway);
if (g_strcmp0(active_gateway->ipv6_gateway->gateway,
data->ipv6_gateway, do_ipv6);
/* with vpn this will be called after the network was deleted,
- * we need to call set_default here because we will not recieve any
+ * we need to call set_default here because we will not receive any
* gateway delete notification.
* We hit the same issue if remove_gateway() fails.
*/
DBusMessage *__connman_error_operation_timeout(DBusMessage *msg);
DBusMessage *__connman_error_invalid_service(DBusMessage *msg);
DBusMessage *__connman_error_invalid_property(DBusMessage *msg);
+DBusMessage *__connman_error_operation_canceled(DBusMessage *msg);
int __connman_manager_init(void);
void __connman_manager_cleanup(void);
GKeyFile *__connman_storage_load_config(const char *ident);
GKeyFile *__connman_storage_load_provider_config(const char *ident);
-GKeyFile *__connman_storage_open_service(const char *ident);
int __connman_storage_save_service(GKeyFile *keyfile, const char *ident);
GKeyFile *__connman_storage_load_provider(const char *identifier);
void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier);
void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig,
const char *file, int line, const char *caller);
-void __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig);
void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig);
void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data);
char **prefixes);
char **__connman_ipconfig_get_dhcpv6_prefixes(struct connman_ipconfig *ipconfig);
-int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
+void __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
GKeyFile *keyfile, const char *identifier, const char *prefix);
-int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
+void __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
GKeyFile *keyfile, const char *identifier, const char *prefix);
bool __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig);
int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig);
hdr->nscount = 0;
hdr->arcount = 0;
- /* if this is a negative reply, we are authorative */
+ /* if this is a negative reply, we are authoritative */
if (answers == 0)
hdr->aa = 1;
else
cache_refresh();
}
+static void dnsproxy_service_state_changed(struct connman_service *service,
+ enum connman_service_state state)
+{
+ GSList *list;
+ int index;
+
+ switch (state) {
+ case CONNMAN_SERVICE_STATE_DISCONNECT:
+ case CONNMAN_SERVICE_STATE_IDLE:
+ break;
+ case CONNMAN_SERVICE_STATE_ASSOCIATION:
+ case CONNMAN_SERVICE_STATE_CONFIGURATION:
+ case CONNMAN_SERVICE_STATE_FAILURE:
+ case CONNMAN_SERVICE_STATE_ONLINE:
+ case CONNMAN_SERVICE_STATE_READY:
+ case CONNMAN_SERVICE_STATE_UNKNOWN:
+ return;
+ }
+
+ index = __connman_service_get_index(service);
+ list = server_list;
+
+ while (list) {
+ struct server_data *data = list->data;
+
+ /* Get next before the list is changed by destroy_server() */
+ list = list->next;
+
+ if (data->index == index) {
+ DBG("removing server data of index %d", index);
+ destroy_server(data);
+ }
+ }
+}
+
static const struct connman_notifier dnsproxy_notifier = {
.name = "dnsproxy",
.default_changed = dnsproxy_default_changed,
.offline_mode = dnsproxy_offline_mode,
+ .service_state_changed = dnsproxy_service_state_changed,
};
static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
return __connman_error_not_registered(msg);
case ENXIO:
return __connman_error_not_found(msg);
+ case EPERM:
case EACCES:
return __connman_error_permission_denied(msg);
case EEXIST:
return __connman_error_in_progress(msg);
case ENOKEY:
return __connman_error_passphrase_required(msg);
+ case ECANCELED:
+ return __connman_error_operation_canceled(msg);
}
return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
".InvalidProperty", "Invalid property");
}
+
+DBusMessage *__connman_error_operation_canceled(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+ ".OperationCanceled", "Operation canceled");
+}
/*
* This file is based on the libnftnl examples:
* https://git.netfilter.org/libnftnl/tree/examples
- * by Pablo Neira Ayuso. and inspiration from systemd nft implemention
+ * by Pablo Neira Ayuso. and inspiration from systemd nft implementation
* https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c
* by Daniel Mack.
*/
if (!rule)
return -ENOMEM;
- nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
- nftnl_rule_set(rule, NFTNL_RULE_CHAIN, handle->chain);
+ nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, handle->chain);
nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle);
err = socket_open_and_bind(&nl);
if (!rule)
return -ENOMEM;
- nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
- nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+ nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
/* family ipv4 */
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
if (!rule)
return -ENOMEM;
- nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
- nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
+ nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST);
/* OIF */
expr = nftnl_expr_alloc("meta");
if (!rule)
return -ENOMEM;
- nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
- nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
+ nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
expr = nftnl_expr_alloc("meta");
if (!expr)
if (!rule)
return -ENOMEM;
- nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
- nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
+ nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE);
+ nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT);
/* family ipv4 */
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
if (!chain)
return NULL;
- nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table);
- nftnl_chain_set(chain, NFTNL_CHAIN_NAME, name);
+ nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, name);
if (type)
nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type);
return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
}
+static bool mem_mask_equal(const void *a, const void *b,
+ const void *mask, size_t n)
+{
+ const unsigned char *addr1 = a;
+ const unsigned char *addr2 = b;
+ const unsigned char *bitmask = mask;
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ if ((addr1[i] ^ addr2[i]) & bitmask[i])
+ return false;
+ }
+
+ return true;
+}
+
+bool connman_inet_compare_ipv6_subnet(int index, const char *host)
+{
+ struct ifaddrs *ifaddr, *ifa;
+ bool rv = false;
+ char name[IF_NAMESIZE];
+ struct in6_addr haddr;
+
+ if (inet_pton(AF_INET6, host, &haddr) <= 0)
+ return false;
+
+ if (!if_indextoname(index, name))
+ return false;
+
+ DBG("index %d interface %s", index, name);
+
+ if (getifaddrs(&ifaddr) < 0) {
+ DBG("Cannot get addresses err %d/%s", errno, strerror(errno));
+ 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;
+}
+
int connman_inet_remove_from_bridge(int index, const char *bridge)
{
struct ifreq ifr;
rth->req.u.r.rt.rtm_scope = 0;
rth->req.u.r.rt.rtm_type = 0;
rth->req.u.r.rt.rtm_src_len = 0;
- rth->req.u.r.rt.rtm_dst_len = rp->ai_addrlen << 3;
rth->req.u.r.rt.rtm_tos = 0;
- __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req), RTA_DST,
- &rp->ai_addr, rp->ai_addrlen);
+ if (rp->ai_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)rp->ai_addr;
+
+ rth->req.u.r.rt.rtm_dst_len = 32;
+ __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req),
+ RTA_DST, &sin->sin_addr, sizeof(sin->sin_addr));
+ } else if (rp->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rp->ai_addr;
+
+ rth->req.u.r.rt.rtm_dst_len = 128;
+ __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req),
+ RTA_DST, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+ }
freeaddrinfo(rp);
if (cmdline[len - 1] == '\n')
cmdline[--len] = '\0';
- /* split in arguments (seperated by space) */
+ /* split in arguments (separated by space) */
args = g_strsplit(cmdline, " ", 0);
if (!args) {
connman_error("%s: Cannot split cmdline \"%s\"\n", __func__,
}
/*
- * Perform two passes to retreive a char ** array of
+ * Perform two passes to retrieve a char ** array of
* nameservers that are not 0.0.0.0
*
* The first pass counts them, the second fills in the
int ipv6_privacy;
};
+struct ipconfig_store {
+ GKeyFile *file;
+ const char *group;
+ const char *prefix;
+};
+
static GHashTable *ipdevice_hash = NULL;
static GList *ipconfig_list = NULL;
static bool is_ipv6_supported = false;
-void __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig)
+static void store_set_str(struct ipconfig_store *store,
+ const char *key, const char *val)
+
{
- if (!ipconfig)
+ char *pk;
+
+ if (!val || strlen(val) == 0)
+ return;
+
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ g_key_file_set_string(store->file, store->group, pk, val);
+ g_free(pk);
+}
+
+static char *store_get_str(struct ipconfig_store *store, const char *key)
+{
+ char *pk, *val;
+
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ val = g_key_file_get_string(store->file, store->group, pk, NULL);
+ g_free(pk);
+
+ return val;
+}
+
+static void store_set_strs(struct ipconfig_store *store,
+ const char *key, char **val)
+{
+ guint len;
+ char *pk;
+
+ if (!val)
+ return;
+
+ len = g_strv_length(val);
+ if (len == 0)
return;
- connman_ipaddress_clear(ipconfig->address);
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ g_key_file_set_string_list(store->file, store->group,
+ pk, (const gchar **)val, len);
+ g_free(pk);
+}
+
+static char **store_get_strs(struct ipconfig_store *store, const char *key)
+{
+ gsize len;
+ char *pk, **val;
+
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ val = g_key_file_get_string_list(store->file, store->group,
+ pk, &len, NULL);
+ g_free(pk);
+
+ if (val && len == 0) {
+ g_free(val);
+ return NULL;
+ }
+
+ return val;
+}
+
+static void store_set_int(struct ipconfig_store *store,
+ const char *key, int val)
+{
+ char *pk;
+
+ if (val == 0)
+ return;
+
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ g_key_file_set_integer(store->file, store->group, pk, val);
+ g_free(pk);
+}
+
+static int store_get_int(struct ipconfig_store *store, const char *key)
+{
+ int val;
+ char *pk;
+
+ pk = g_strdup_printf("%s%s", store->prefix, key);
+ val = g_key_file_get_integer(store->file, store->group, pk, 0);
+ g_free(pk);
+
+ return val;
}
static void free_address_list(struct connman_ipdevice *ipdevice)
case CONNMAN_IPCONFIG_METHOD_OFF:
break;
case CONNMAN_IPCONFIG_METHOD_AUTO:
- case CONNMAN_IPCONFIG_METHOD_FIXED:
case CONNMAN_IPCONFIG_METHOD_DHCP:
- case CONNMAN_IPCONFIG_METHOD_MANUAL:
err = __connman_ipconfig_address_unset(ipconfig);
connman_ipaddress_clear(ipconfig->address);
return err;
+ case CONNMAN_IPCONFIG_METHOD_FIXED:
+ case CONNMAN_IPCONFIG_METHOD_MANUAL:
+ return __connman_ipconfig_address_unset(ipconfig);
}
return 0;
DBUS_TYPE_UINT16, &ipdevice->mtu);
}
-int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
+void __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
GKeyFile *keyfile, const char *identifier, const char *prefix)
{
char *method;
- char *key;
char *str;
+ struct ipconfig_store is = { .file = keyfile,
+ .group = identifier,
+ .prefix = prefix };
DBG("ipconfig %p identifier %s", ipconfig, identifier);
- key = g_strdup_printf("%smethod", prefix);
- method = g_key_file_get_string(keyfile, identifier, key, NULL);
+ method = store_get_str(&is, "method");
if (!method) {
switch (ipconfig->type) {
case CONNMAN_IPCONFIG_TYPE_IPV4:
ipconfig->method = CONNMAN_IPCONFIG_METHOD_DHCP;
break;
+
case CONNMAN_IPCONFIG_TYPE_IPV6:
ipconfig->method = CONNMAN_IPCONFIG_METHOD_AUTO;
break;
+
case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
case CONNMAN_IPCONFIG_TYPE_ALL:
ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF;
break;
}
- } else
+ } else {
ipconfig->method = __connman_ipconfig_string2method(method);
+ g_free(method);
+ }
if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_UNKNOWN)
ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF;
if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) {
- gsize length;
- char *pprefix;
-
if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO ||
- ipconfig->method == CONNMAN_IPCONFIG_METHOD_MANUAL) {
+ ipconfig->method == CONNMAN_IPCONFIG_METHOD_MANUAL) {
char *privacy;
- pprefix = g_strdup_printf("%sprivacy", prefix);
- privacy = g_key_file_get_string(keyfile, identifier,
- pprefix, NULL);
+ privacy = store_get_str(&is, "privacy");
ipconfig->ipv6_privacy_config = string2privacy(privacy);
- g_free(pprefix);
g_free(privacy);
}
- pprefix = g_strdup_printf("%sDHCP.LastPrefixes", prefix);
+ g_strfreev(ipconfig->last_dhcpv6_prefixes);
ipconfig->last_dhcpv6_prefixes =
- g_key_file_get_string_list(keyfile, identifier, pprefix,
- &length, NULL);
- if (ipconfig->last_dhcpv6_prefixes && length == 0) {
- g_free(ipconfig->last_dhcpv6_prefixes);
- ipconfig->last_dhcpv6_prefixes = NULL;
- }
- g_free(pprefix);
+ store_get_strs(&is, "DHCP.LastPrefixes");
}
- g_free(method);
- g_free(key);
switch (ipconfig->method) {
case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
case CONNMAN_IPCONFIG_METHOD_FIXED:
case CONNMAN_IPCONFIG_METHOD_MANUAL:
+ ipconfig->address->prefixlen =
+ store_get_int(&is, "netmask_prefixlen");
- key = g_strdup_printf("%snetmask_prefixlen", prefix);
- ipconfig->address->prefixlen = g_key_file_get_integer(
- keyfile, identifier, key, NULL);
- g_free(key);
-
- key = g_strdup_printf("%slocal_address", prefix);
g_free(ipconfig->address->local);
- ipconfig->address->local = g_key_file_get_string(
- keyfile, identifier, key, NULL);
- g_free(key);
+ ipconfig->address->local =
+ store_get_str(&is, "local_address");
- key = g_strdup_printf("%speer_address", prefix);
g_free(ipconfig->address->peer);
- ipconfig->address->peer = g_key_file_get_string(
- keyfile, identifier, key, NULL);
- g_free(key);
+ ipconfig->address->peer =
+ store_get_str(&is, "peer_address");
- key = g_strdup_printf("%sbroadcast_address", prefix);
g_free(ipconfig->address->broadcast);
- ipconfig->address->broadcast = g_key_file_get_string(
- keyfile, identifier, key, NULL);
- g_free(key);
+ ipconfig->address->broadcast =
+ store_get_str(&is, "broadcast_address");
- key = g_strdup_printf("%sgateway", prefix);
g_free(ipconfig->address->gateway);
- ipconfig->address->gateway = g_key_file_get_string(
- keyfile, identifier, key, NULL);
- g_free(key);
+ ipconfig->address->gateway =
+ store_get_str(&is, "gateway");
break;
case CONNMAN_IPCONFIG_METHOD_AUTO:
-
if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4)
break;
/* fall through */
case CONNMAN_IPCONFIG_METHOD_DHCP:
-
- key = g_strdup_printf("%sDHCP.LastAddress", prefix);
- str = g_key_file_get_string(keyfile, identifier, key, NULL);
+ str = store_get_str(&is, "DHCP.LastAddress");
if (str) {
g_free(ipconfig->last_dhcp_address);
ipconfig->last_dhcp_address = str;
}
- g_free(key);
break;
}
-
- return 0;
}
-int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
+void __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
GKeyFile *keyfile, const char *identifier, const char *prefix)
{
const char *method;
- char *key;
+ struct ipconfig_store is = { .file = keyfile,
+ .group = identifier,
+ .prefix = prefix };
method = __connman_ipconfig_method2string(ipconfig->method);
-
DBG("ipconfig %p identifier %s method %s", ipconfig, identifier,
method);
- if (method) {
- key = g_strdup_printf("%smethod", prefix);
- g_key_file_set_string(keyfile, identifier, key, method);
- g_free(key);
- }
+ store_set_str(&is, "method", method);
if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) {
- const char *privacy;
- privacy = privacy2string(ipconfig->ipv6_privacy_config);
- key = g_strdup_printf("%sprivacy", prefix);
- g_key_file_set_string(keyfile, identifier, key, privacy);
- g_free(key);
-
- key = g_strdup_printf("%sDHCP.LastAddress", prefix);
- if (ipconfig->last_dhcp_address &&
- strlen(ipconfig->last_dhcp_address) > 0)
- g_key_file_set_string(keyfile, identifier, key,
- ipconfig->last_dhcp_address);
- else
- g_key_file_remove_key(keyfile, identifier, key, NULL);
- g_free(key);
-
- key = g_strdup_printf("%sDHCP.LastPrefixes", prefix);
- if (ipconfig->last_dhcpv6_prefixes &&
- ipconfig->last_dhcpv6_prefixes[0]) {
- guint len =
- g_strv_length(ipconfig->last_dhcpv6_prefixes);
-
- g_key_file_set_string_list(keyfile, identifier, key,
- (const gchar **)ipconfig->last_dhcpv6_prefixes,
- len);
- } else
- g_key_file_remove_key(keyfile, identifier, key, NULL);
- g_free(key);
+ store_set_str(&is, "privacy",
+ privacy2string(ipconfig->ipv6_privacy_config));
+
+ store_set_str(&is, "DHCP.LastAddress",
+ ipconfig->last_dhcp_address);
+
+ store_set_strs(&is, "DHCP.LastPrefixes",
+ ipconfig->last_dhcpv6_prefixes);
}
switch (ipconfig->method) {
case CONNMAN_IPCONFIG_METHOD_FIXED:
case CONNMAN_IPCONFIG_METHOD_MANUAL:
break;
+
case CONNMAN_IPCONFIG_METHOD_DHCP:
- key = g_strdup_printf("%sDHCP.LastAddress", prefix);
- if (ipconfig->last_dhcp_address &&
- strlen(ipconfig->last_dhcp_address) > 0)
- g_key_file_set_string(keyfile, identifier, key,
- ipconfig->last_dhcp_address);
- else
- g_key_file_remove_key(keyfile, identifier, key, NULL);
- g_free(key);
+ store_set_str(&is, "DHCP.LastAddress",
+ ipconfig->last_dhcp_address);
/* fall through */
+
case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
case CONNMAN_IPCONFIG_METHOD_OFF:
case CONNMAN_IPCONFIG_METHOD_AUTO:
- return 0;
+ return;
}
- key = g_strdup_printf("%snetmask_prefixlen", prefix);
- if (ipconfig->address->prefixlen != 0)
- g_key_file_set_integer(keyfile, identifier,
- key, ipconfig->address->prefixlen);
- g_free(key);
-
- key = g_strdup_printf("%slocal_address", prefix);
- if (ipconfig->address->local)
- g_key_file_set_string(keyfile, identifier,
- key, ipconfig->address->local);
- g_free(key);
-
- key = g_strdup_printf("%speer_address", prefix);
- if (ipconfig->address->peer)
- g_key_file_set_string(keyfile, identifier,
- key, ipconfig->address->peer);
- g_free(key);
-
- key = g_strdup_printf("%sbroadcast_address", prefix);
- if (ipconfig->address->broadcast)
- g_key_file_set_string(keyfile, identifier,
- key, ipconfig->address->broadcast);
- g_free(key);
-
- key = g_strdup_printf("%sgateway", prefix);
- if (ipconfig->address->gateway)
- g_key_file_set_string(keyfile, identifier,
- key, ipconfig->address->gateway);
- else
- g_key_file_remove_key(keyfile, identifier, key, NULL);
- g_free(key);
-
- return 0;
+ store_set_int(&is, "netmask_prefixlen", ipconfig->address->prefixlen);
+ store_set_str(&is, "local_address", ipconfig->address->local);
+ store_set_str(&is, "peer_address", ipconfig->address->peer);
+ store_set_str(&is, "broadcast_address", ipconfig->address->broadcast);
+ store_set_str(&is, "gateway", ipconfig->address->gateway);
}
int __connman_ipconfig_init(void)
* - ipt_entry->target_offset = Size of ipt_entry + matches
* - ipt_entry->next_offset = Size of ipt_entry + matches + target
* - IPT_SO_SET_REPLACE is used to write a table (contains the complete
- * - hook_entry and overflow mark the begining and the end of a chain, e.g
+ * - hook_entry and overflow mark the beginning and the end of a chain, e.g
* entry hook: pre/in/fwd/out/post -1/0/352/504/-1
* underflow: pre/in/fwd/out/post -1/200/352/904/-1
* means that INPUT starts at offset 0 and ends at 200 (the start offset to
entry_before = before->data;
/*
- * We've just appended/insterted a new entry. All references
+ * We've just appended/inserted a new entry. All references
* should be bumped accordingly.
*/
update_targets_reference(table, entry_before, e, false);
* - if '!' is found, set the invert flag to true and
* removes the '!' from the optarg string and jumps
* back to getopt to reparse the current optarg string.
- * After reparsing the invert flag is reseted to false.
+ * After reparsing the invert flag is reset to false.
* - If 'm' or 'j' is found then call either
* prepare_matches() or prepare_target(). Those function
* will modify (extend) the longopts for getopt_long.
optarg[0] = '\0';
/*
- * And recall getopt_long without reseting
+ * And recall getopt_long without resetting
* invert.
*/
continue;
/**
* connman_network_create:
- * @identifier: network identifier (for example an unqiue name)
+ * @identifier: network identifier (for example an unique name)
*
* Allocate a new network and assign the #identifier to it.
*
if (g_hash_table_lookup(service_hash, service)) {
/*
- * This is a tempory check for consistency. It can be
+ * This is a temporary check for consistency. It can be
* removed when there are no reports for the following
* error message.
*/
else
return -EOPNOTSUPP;
- if (err < 0) {
- if (err != -EINPROGRESS)
- return err;
+ switch (err) {
+ case 0:
+ return 0;
+ case -EINPROGRESS:
provider_indicate_state(provider,
CONNMAN_SERVICE_STATE_ASSOCIATION);
-
+ /* fall through */
+ /*
+ * Return EINPROGRESS also for when there is an existing pending call.
+ * The state should not be indicated again but the real state is
+ * still in progress for the provider.
+ */
+ case -EALREADY:
return -EINPROGRESS;
}
- return 0;
+ return err;
}
int __connman_provider_remove_by_path(const char *path)
ipconfig = __connman_service_get_ip4config(service);
if (!ipconfig) {
- DBG("Couldnt create ipconfig");
+ DBG("Couldn't create ipconfig");
goto done;
}
}
ipconfig = __connman_service_get_ip6config(service);
if (!ipconfig) {
- DBG("Couldnt create ipconfig for IPv6");
+ DBG("Couldn't create ipconfig for IPv6");
goto done;
}
}
return 0;
}
+void connman_provider_set_autoconnect(struct connman_provider *provider,
+ bool flag)
+{
+ if (!provider || !provider->vpn_service)
+ return;
+
+ /* Save VPN service if autoconnect value changes */
+ if (connman_service_set_autoconnect(provider->vpn_service, flag))
+ __connman_service_save(provider->vpn_service);
+}
+
static void unregister_provider(gpointer data)
{
struct connman_provider *provider = data;
g_list_free(entries);
}
-static int resolvfile_export(void)
+static bool already_exported(GList *export_list, const char *str)
{
GList *list;
+
+ for (list = export_list; list; list = g_list_next(list)) {
+ const char *str0 = list->data;
+ if (g_strcmp0(str0, str) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int resolvfile_export(void)
+{
+ GList *list, *export_list;
GString *content;
int fd, err;
unsigned int count;
* MAXDNSRCH/MAXNS entries are used.
*/
+ export_list = NULL;
for (count = 0, list = g_list_first(resolvfile_list);
list && (count < MAXDNSRCH);
list = g_list_next(list)) {
if (!entry->domain)
continue;
+ if (already_exported(export_list, entry->domain))
+ continue;
+
if (count == 0)
g_string_append_printf(content, "search ");
g_string_append_printf(content, "%s ", entry->domain);
+
+ export_list = g_list_append(export_list, entry->domain);
+
count++;
}
+ g_list_free(export_list);
+
if (count)
g_string_append_printf(content, "\n");
+ export_list = NULL;
for (count = 0, list = g_list_first(resolvfile_list);
list && (count < MAXNS);
list = g_list_next(list)) {
if (!entry->server)
continue;
- g_string_append_printf(content, "nameserver %s\n",
- entry->server);
+ if (already_exported(export_list, entry->server))
+ continue;
+
+ g_string_append_printf(content, "nameserver %s\n", entry->server);
+
+ export_list = g_list_append(export_list, entry->server);
+
count++;
}
+ g_list_free(export_list);
old_umask = umask(022);
{
struct resolvfile_entry *entry;
- DBG("index %d server %s", index, server);
+ DBG("index %d domain %s server %s", index, domain, server);
if (index < 0)
return -ENOENT;
{
GList *list, *matches = NULL;
- DBG("index %d server %s", index, server);
+ DBG("index %d domain %s server %s", index, domain, server);
for (list = resolvfile_list; list; list = g_list_next(list)) {
struct resolvfile_entry *entry = list->data;
__connman_technology_add_interface(interface->service_type,
interface->index, interface->ident);
- for (list = watch_list; list; list = list->next) {
+ list = watch_list;
+ while (list) {
+ GSList *next = list->next;
struct watch_data *watch = list->data;
- if (watch->index != index)
- continue;
-
- if (watch->newlink)
+ if (watch->index == index && watch->newlink)
watch->newlink(flags, change, watch->user_data);
+
+ list = next;
}
}
#include <connman/setting.h>
#include <connman/agent.h>
+#include "src/shared/util.h"
+
#include "connman.h"
#define CONNECT_TIMEOUT 120
+#define VPN_AUTOCONNECT_TIMEOUT_DEFAULT 1
+#define VPN_AUTOCONNECT_TIMEOUT_STEP 30
+#define VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD 270
+
static DBusConnection *connection = NULL;
static GList *service_list = NULL;
bool hidden;
bool ignore;
bool autoconnect;
- GTimeVal modified;
+ struct timeval modified;
unsigned int order;
char *name;
char *passphrase;
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);
struct find_data {
const char *path;
str = g_key_file_get_string(keyfile,
service->identifier, "Modified", NULL);
if (str) {
- g_time_val_from_iso8601(str, &service->modified);
+ util_iso8601_to_timeval(str, &service->modified);
g_free(str);
}
str = g_key_file_get_string(keyfile,
service->identifier, "Modified", NULL);
if (str) {
- g_time_val_from_iso8601(str, &service->modified);
+ util_iso8601_to_timeval(str, &service->modified);
g_free(str);
}
if (service->new_service)
return -ESRCH;
- keyfile = __connman_storage_open_service(service->identifier);
+ keyfile = g_key_file_new();
if (!keyfile)
return -EIO;
g_key_file_set_boolean(keyfile, service->identifier,
"Favorite", service->favorite);
- g_key_file_remove_key(keyfile, service->identifier,
- "Failure", NULL);
-
/* fall through */
case CONNMAN_SERVICE_TYPE_ETHERNET:
break;
}
- str = g_time_val_to_iso8601(&service->modified);
+ str = util_timeval_to_iso8601(&service->modified);
if (str) {
g_key_file_set_string(keyfile, service->identifier,
- "Modified", str);
+ "Modified", str);
g_free(str);
}
if (service->passphrase && strlen(service->passphrase) > 0)
g_key_file_set_string(keyfile, service->identifier,
- "Passphrase", service->passphrase);
- else
- g_key_file_remove_key(keyfile, service->identifier,
- "Passphrase", NULL);
+ "Passphrase", service->passphrase);
if (service->ipconfig_ipv4)
__connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
- service->identifier, "IPv4.");
+ service->identifier, "IPv4.");
if (service->ipconfig_ipv6)
__connman_ipconfig_save(service->ipconfig_ipv6, keyfile,
- service->identifier, "IPv6.");
+ service->identifier, "IPv6.");
if (service->nameservers_config) {
guint len = g_strv_length(service->nameservers_config);
g_key_file_set_string_list(keyfile, service->identifier,
- "Nameservers",
+ "Nameservers",
(const gchar **) service->nameservers_config, len);
- } else
- g_key_file_remove_key(keyfile, service->identifier,
- "Nameservers", NULL);
+ }
if (service->timeservers_config) {
guint len = g_strv_length(service->timeservers_config);
g_key_file_set_string_list(keyfile, service->identifier,
- "Timeservers",
+ "Timeservers",
(const gchar **) service->timeservers_config, len);
- } else
- g_key_file_remove_key(keyfile, service->identifier,
- "Timeservers", NULL);
+ }
if (service->domains) {
guint len = g_strv_length(service->domains);
g_key_file_set_string_list(keyfile, service->identifier,
- "Domains",
+ "Domains",
(const gchar **) service->domains, len);
- } else
- g_key_file_remove_key(keyfile, service->identifier,
- "Domains", NULL);
+ }
cst_str = proxymethod2string(service->proxy_config);
if (cst_str)
g_key_file_set_string_list(keyfile, service->identifier,
"Proxy.Servers",
(const gchar **) service->proxies, len);
- } else
- g_key_file_remove_key(keyfile, service->identifier,
- "Proxy.Servers", NULL);
+ }
if (service->excludes) {
guint len = g_strv_length(service->excludes);
g_key_file_set_string_list(keyfile, service->identifier,
"Proxy.Excludes",
(const gchar **) service->excludes, len);
- } else
- g_key_file_remove_key(keyfile, service->identifier,
- "Proxy.Excludes", NULL);
+ }
if (service->pac && strlen(service->pac) > 0)
g_key_file_set_string(keyfile, service->identifier,
- "Proxy.URL", service->pac);
- else
- g_key_file_remove_key(keyfile, service->identifier,
- "Proxy.URL", NULL);
+ "Proxy.URL", service->pac);
if (service->mdns_config)
g_key_file_set_boolean(keyfile, service->identifier,
- "mDNS", TRUE);
- else
- g_key_file_remove_key(keyfile, service->identifier,
- "mDNS", NULL);
+ "mDNS", TRUE);
if (service->hidden_service)
- g_key_file_set_boolean(keyfile, service->identifier, "Hidden",
- TRUE);
+ g_key_file_set_boolean(keyfile, service->identifier,
+ "Hidden", TRUE);
if (service->config_file && strlen(service->config_file) > 0)
g_key_file_set_string(keyfile, service->identifier,
"Config.file", service->config_file);
- if (service->config_entry &&
- strlen(service->config_entry) > 0)
+ if (service->config_entry && strlen(service->config_entry) > 0)
g_key_file_set_string(keyfile, service->identifier,
"Config.ident", service->config_entry);
else
nameservers = service->nameservers;
- for (i = 0; nameservers && nameservers[i]; i++)
- if (g_strcmp0(nameservers[i], nameserver) == 0)
- return -EEXIST;
-
if (nameservers) {
+ for (i = 0; nameservers[i]; i++) {
+ if (g_strcmp0(nameservers[i], nameserver) == 0)
+ return -EEXIST;
+ }
+
len = g_strv_length(nameservers);
nameservers = g_try_renew(char *, nameservers, len + 2);
} else {
nameserver_del_routes(index, service->nameservers, type);
}
+static bool check_proxy_setup(struct connman_service *service)
+{
+ /*
+ * We start WPAD if we haven't got a PAC URL from DHCP and
+ * if our proxy manual configuration is either empty or set
+ * to AUTO with an empty URL.
+ */
+
+ if (service->proxy != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN)
+ return true;
+
+ if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN &&
+ (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_AUTO ||
+ service->pac))
+ return true;
+
+ if (__connman_wpad_start(service) < 0) {
+ service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+ __connman_notifier_proxy_changed(service);
+ return true;
+ }
+
+ return false;
+}
+
+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);
+}
+
+static void start_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type)
+{
+ if (!connman_setting_get_bool("EnableOnlineCheck")) {
+ connman_info("Online check disabled. "
+ "Default service remains in READY state.");
+ return;
+ }
+
+ if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) {
+ cancel_online_check(service);
+ __connman_service_wispr_start(service, type);
+ }
+}
+
static void address_updated(struct connman_service *service,
enum connman_ipconfig_type type)
{
service == connman_service_get_default()) {
nameserver_remove_all(service, type);
nameserver_add_all(service, type);
+ start_online_check(service, type);
__connman_timeserver_sync(service);
}
if (service->domainname &&
connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
+
+ /*
+ * Connect VPN automatically when new default service
+ * is set and connected, unless new default is VPN
+ */
+ if (is_connected(service->state) &&
+ service->type != CONNMAN_SERVICE_TYPE_VPN) {
+ DBG("running vpn_auto_connect");
+ vpn_auto_connect();
+ }
}
__connman_notifier_default_changed(service);
DBUS_TYPE_BOOLEAN, &autoconnect);
}
+bool connman_service_set_autoconnect(struct connman_service *service,
+ bool autoconnect)
+{
+ if (service->autoconnect == autoconnect)
+ return false;
+
+ service->autoconnect = autoconnect;
+ autoconnect_changed(service);
+
+ return true;
+}
+
static void append_security(DBusMessageIter *iter, void *user_data)
{
struct connman_service *service = user_data;
return -EINVAL;
}
+static void do_auto_connect(struct connman_service *service,
+ enum connman_service_connect_reason reason)
+{
+ /*
+ * CONNMAN_SERVICE_CONNECT_REASON_NONE must be ignored for VPNs. VPNs
+ * always have reason CONNMAN_SERVICE_CONNECT_REASON_USER/AUTO.
+ */
+ if (!service || (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+ reason == CONNMAN_SERVICE_CONNECT_REASON_NONE))
+ 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();
+}
+
int __connman_service_reset_ipconfig(struct connman_service *service,
enum connman_ipconfig_type type, DBusMessageIter *array,
enum connman_service_state *new_state)
settings_changed(service, new_ipconfig);
address_updated(service, type);
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
}
DBG("err %d ipconfig %p type %d method %d state %s", err,
__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)
{
dbus_message_iter_get_basic(&value, &autoconnect);
- if (service->autoconnect == autoconnect)
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-
- service->autoconnect = autoconnect;
-
- autoconnect_changed(service);
+ if (autoconnect && service->type == CONNMAN_SERVICE_TYPE_VPN) {
+ /*
+ * Changing the autoconnect flag on VPN to "on" should
+ * have the same effect as user connecting the VPN =
+ * clear previous error and change state to idle.
+ */
+ set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
- if (autoconnect)
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ if (service->state == CONNMAN_SERVICE_STATE_FAILURE) {
+ service->state = CONNMAN_SERVICE_STATE_IDLE;
+ state_changed(service);
+ }
+ }
- service_save(service);
+ if (connman_service_set_autoconnect(service, autoconnect)) {
+ service_save(service);
+ if (autoconnect)
+ do_auto_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ }
} else if (g_str_equal(name, "Nameservers.Configuration")) {
DBusMessageIter entry;
GString *str;
reply_pending(service, EIO);
if (service->connect_reason != CONNMAN_SERVICE_CONNECT_REASON_USER)
- __connman_service_auto_connect(service->connect_reason);
+ do_auto_connect(service, service->connect_reason);
- g_get_current_time(&service->modified);
+ gettimeofday(&service->modified, NULL);
service_save(service);
}
return false;
}
+static int service_indicate_state(struct connman_service *service);
+
static bool auto_connect_service(GList *services,
enum connman_service_connect_reason reason,
bool preferred)
DBG("service %p %s %s", service, service->name,
(preferred) ? "preferred" : reason2string(reason));
- __connman_service_connect(service, reason);
+ if (__connman_service_connect(service, reason) == 0)
+ service_indicate_state(service);
if (autoconnect_no_session_active(service))
return true;
static gboolean run_vpn_auto_connect(gpointer data) {
GList *list;
bool need_split = false;
+ bool autoconnectable_vpns = false;
+ int attempts = 0;
+ int timeout = VPN_AUTOCONNECT_TIMEOUT_DEFAULT;
+ struct connman_service *def_service;
- vpn_autoconnect_id = 0;
+ attempts = GPOINTER_TO_INT(data);
+ def_service = connman_service_get_default();
+
+ /*
+ * Stop auto connecting VPN if there is no transport service or the
+ * transport service is not connected or if the current default service
+ * is a connected VPN (in ready state).
+ */
+ if (!def_service || !is_connected(def_service->state) ||
+ (def_service->type == CONNMAN_SERVICE_TYPE_VPN &&
+ is_connected(def_service->state))) {
+
+ DBG("stopped, default service %s connected %d",
+ def_service ? def_service->identifier : "NULL",
+ def_service ? is_connected(def_service->state) : -1);
+ goto out;
+ }
for (list = service_list; list; list = list->next) {
struct connman_service *service = list->data;
continue;
if (is_connected(service->state) ||
- is_connecting(service->state)) {
+ is_connecting(service->state)) {
if (!service->do_split_routing)
need_split = true;
+
+ /*
+ * If the service is connecting it must be accounted
+ * for to keep the autoconnection in main loop.
+ */
+ if (is_connecting(service->state))
+ autoconnectable_vpns = true;
+
continue;
}
res = __connman_service_connect(service,
CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- if (res < 0 && res != -EINPROGRESS)
+
+ switch (res) {
+ case 0:
+ service_indicate_state(service);
+ /* fall through */
+ case -EINPROGRESS:
+ autoconnectable_vpns = true;
+ break;
+ default:
continue;
+ }
if (!service->do_split_routing)
need_split = true;
}
- return FALSE;
+ /* Stop if there is no VPN to automatically connect.*/
+ if (!autoconnectable_vpns) {
+ DBG("stopping, no autoconnectable VPNs found");
+ goto out;
+ }
+
+ /* Increase the attempt count up to the threshold.*/
+ if (attempts < VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD)
+ attempts++;
+
+ /*
+ * Timeout increases with 1s after VPN_AUTOCONNECT_TIMEOUT_STEP amount
+ * of attempts made. After VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD is
+ * reached the delay does not increase.
+ */
+ timeout = timeout + (int)(attempts / VPN_AUTOCONNECT_TIMEOUT_STEP);
+
+ /* Re add this to main loop */
+ vpn_autoconnect_id =
+ g_timeout_add_seconds(timeout, run_vpn_auto_connect,
+ GINT_TO_POINTER(attempts));
+
+ DBG("re-added to main loop, next VPN autoconnect in %d seconds (#%d)",
+ timeout, attempts);
+
+ return G_SOURCE_REMOVE;
+
+out:
+ vpn_autoconnect_id = 0;
+ return G_SOURCE_REMOVE;
}
static void vpn_auto_connect(void)
{
- if (vpn_autoconnect_id)
- return;
+ /*
+ * Remove existing autoconnect from main loop to reset the attempt
+ * counter in order to get VPN connected when there is a network change.
+ */
+ if (vpn_autoconnect_id) {
+ if (!g_source_remove(vpn_autoconnect_id))
+ return;
+ }
vpn_autoconnect_id =
g_idle_add(run_vpn_auto_connect, NULL);
if (autoconnect &&
service->connect_reason !=
CONNMAN_SERVICE_CONNECT_REASON_USER)
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
return FALSE;
}
}
}
- g_get_current_time(&service->modified);
+ gettimeofday(&service->modified, NULL);
service_save(service);
service_save(target);
__connman_service_return_error(service,
ECONNABORTED,
user_data);
- goto done;
} else {
+ err = -ETIMEDOUT;
+
if (service->hidden)
__connman_service_return_error(service,
ETIMEDOUT, user_data);
}
+
+ goto done;
}
if (service->hidden && name_len > 0 && name_len <= 32) {
"WiFi.UseWPS", false);
}
- g_get_current_time(&service->modified);
+ gettimeofday(&service->modified, NULL);
service_save(service);
domain_changed(service);
*/
downgrade_connected_services();
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
break;
case CONNMAN_SERVICE_STATE_FAILURE:
return CONNMAN_SERVICE_STATE_UNKNOWN;
}
-static void check_proxy_setup(struct connman_service *service)
-{
- /*
- * We start WPAD if we haven't got a PAC URL from DHCP and
- * if our proxy manual configuration is either empty or set
- * to AUTO with an empty URL.
- */
-
- if (service->proxy != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN)
- goto done;
-
- if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN &&
- (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_AUTO ||
- service->pac))
- goto done;
-
- if (__connman_wpad_start(service) < 0) {
- service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
- __connman_notifier_proxy_changed(service);
- goto done;
- }
-
- return;
-
-done:
- __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
-}
-
/*
* How many networks are connected at the same time. If more than 1,
* then set the rp_filter setting properly (loose mode routing) so that network
return EAGAIN;
}
-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);
-}
-
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_service_state new_state,
enum connman_ipconfig_type type)
case CONNMAN_SERVICE_STATE_CONFIGURATION:
break;
case CONNMAN_SERVICE_STATE_READY:
- if (connman_setting_get_bool("EnableOnlineCheck"))
- if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
- check_proxy_setup(service);
- } else {
- __connman_service_wispr_start(service, type);
- }
- else
- connman_info("Online check disabled. "
- "Default service remains in READY state.");
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
service_rp_filter(service, true);
set_mdns(service, service->mdns_config);
case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_CELLULAR:
- __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ do_auto_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
break;
}
}
--- /dev/null
+/*
+ * mnlg.c Generic Netlink helpers for libmnl
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Jiri Pirko <jiri@mellanox.com>
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+
+#include "mnlg.h"
+
+#ifndef MNL_ARRAY_SIZE
+#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+#ifndef NETLINK_CAP_ACK
+#define NETLINK_CAP_ACK 10
+#endif
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+#endif
+
+struct mnlg_socket {
+ struct mnl_socket *nl;
+ char *buf;
+ uint32_t id;
+ uint8_t version;
+ unsigned int seq;
+ unsigned int portid;
+};
+
+static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags, uint32_t id,
+ uint8_t version)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+
+ nlh = mnl_nlmsg_put_header(nlg->buf);
+ nlh->nlmsg_type = id;
+ nlh->nlmsg_flags = flags;
+ nlg->seq = time(NULL);
+ nlh->nlmsg_seq = nlg->seq;
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = cmd;
+ genl->version = version;
+
+ return nlh;
+}
+
+struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags)
+{
+ return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
+}
+
+int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
+{
+ return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+}
+
+static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ int len = *(int *)NLMSG_DATA(nlh);
+
+ if (len < 0) {
+ errno = -len;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_STOP;
+}
+
+static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnlg_cb_noop,
+ [NLMSG_ERROR] = mnlg_cb_error,
+ [NLMSG_DONE] = mnlg_cb_stop,
+ [NLMSG_OVERRUN] = mnlg_cb_noop,
+};
+
+int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
+{
+ int err;
+
+ do {
+ err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
+ MNL_SOCKET_BUFFER_SIZE);
+ if (err <= 0)
+ break;
+ err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
+ data_cb, data, mnlg_cb_array,
+ ARRAY_SIZE(mnlg_cb_array));
+ } while (err > 0);
+
+ return err;
+}
+
+struct group_info {
+ bool found;
+ uint32_t id;
+ const char *name;
+};
+
+static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case CTRL_ATTR_MCAST_GRP_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case CTRL_ATTR_MCAST_GRP_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_genl_mc_grps(struct nlattr *nested,
+ struct group_info *group_info)
+{
+ struct nlattr *pos;
+ const char *name;
+
+ mnl_attr_for_each_nested(pos, nested) {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
+
+ mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
+ if (!tb[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !tb[CTRL_ATTR_MCAST_GRP_ID])
+ continue;
+
+ name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+ if (strcmp(name, group_info->name) != 0)
+ continue;
+
+ group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
+ group_info->found = true;
+ }
+}
+
+static int get_group_id_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ if (type == CTRL_ATTR_MCAST_GROUPS &&
+ mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int get_group_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct group_info *group_info = data;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb);
+ if (!tb[CTRL_ATTR_MCAST_GROUPS])
+ return MNL_CB_ERROR;
+ parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info);
+ return MNL_CB_OK;
+}
+
+int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
+{
+ struct nlmsghdr *nlh;
+ struct group_info group_info;
+ int err;
+
+ nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
+ mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id);
+
+ err = mnlg_socket_send(nlg, nlh);
+ if (err < 0)
+ return err;
+
+ group_info.found = false;
+ group_info.name = group_name;
+ err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info);
+ if (err < 0)
+ return err;
+
+ if (!group_info.found) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP,
+ &group_info.id, sizeof(group_info.id));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ if (type == CTRL_ATTR_FAMILY_ID &&
+ mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ uint32_t *p_id = data;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ return MNL_CB_ERROR;
+ *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+ return MNL_CB_OK;
+}
+
+struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
+{
+ struct mnlg_socket *nlg;
+ struct nlmsghdr *nlh;
+ int one = 1;
+ int err;
+
+ nlg = malloc(sizeof(*nlg));
+ if (!nlg)
+ return NULL;
+
+ nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
+ if (!nlg->buf)
+ goto err_buf_alloc;
+
+ nlg->nl = mnl_socket_open(NETLINK_GENERIC);
+ if (!nlg->nl)
+ goto err_mnl_socket_open;
+
+ /* Older kernels may no support capped/extended ACK reporting */
+ mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one));
+ mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one));
+
+ err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
+ if (err < 0)
+ goto err_mnl_socket_bind;
+
+ nlg->portid = mnl_socket_get_portid(nlg->nl);
+
+ nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
+
+ err = mnlg_socket_send(nlg, nlh);
+ if (err < 0)
+ goto err_mnlg_socket_send;
+
+ err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id);
+ if (err < 0)
+ goto err_mnlg_socket_recv_run;
+
+ nlg->version = version;
+ return nlg;
+
+err_mnlg_socket_recv_run:
+err_mnlg_socket_send:
+err_mnl_socket_bind:
+ mnl_socket_close(nlg->nl);
+err_mnl_socket_open:
+ free(nlg->buf);
+err_buf_alloc:
+ free(nlg);
+ return NULL;
+}
+
+void mnlg_socket_close(struct mnlg_socket *nlg)
+{
+ mnl_socket_close(nlg->nl);
+ free(nlg->buf);
+ free(nlg);
+}
--- /dev/null
+/*
+ * mnlg.h Generic Netlink helpers for libmnl
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Jiri Pirko <jiri@mellanox.com>
+ */
+
+#ifndef _MNLG_H_
+#define _MNLG_H_
+
+#include <libmnl/libmnl.h>
+
+struct mnlg_socket;
+
+struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags);
+int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh);
+int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
+int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
+struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
+void mnlg_socket_close(struct mnlg_socket *nlg);
+
+#endif /* _MNLG_H_ */
+++ /dev/null
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2011-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2013-2014 BMW Car IT GmbH.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser 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
- *
- */
-
-/*
- * This file is a copy from ELL which has been ported to use GLib's
- * data structures.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-
-#include <gdbus.h>
-
-#include "src/shared/util.h"
-#include "src/shared/netlink.h"
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-struct command {
- unsigned int id;
- uint32_t seq;
- uint32_t len;
- netlink_command_func_t handler;
- netlink_destroy_func_t destroy;
- void *user_data;
-};
-
-struct notify {
- uint32_t group;
- netlink_notify_func_t handler;
- netlink_destroy_func_t destroy;
- void *user_data;
-};
-
-struct netlink_info {
- uint32_t pid;
- GIOChannel *channel;
- uint32_t next_seq;
- GQueue *command_queue;
- GHashTable *command_pending;
- GHashTable *command_lookup;
- unsigned int next_command_id;
- GHashTable *notify_groups;
- GHashTable *notify_lookup;
- unsigned int next_notify_id;
- netlink_debug_func_t debug_handler;
- netlink_destroy_func_t debug_destroy;
- void *debug_data;
-};
-
-
-static void destroy_command(struct command *command)
-{
- if (command->destroy)
- command->destroy(command->user_data);
-
- g_free(command);
-}
-
-static void destroy_notify(struct notify *notify)
-{
- if (notify->destroy)
- notify->destroy(notify->user_data);
-
- g_free(notify);
-}
-
-static gboolean can_write_data(GIOChannel *chan,
- GIOCondition cond, gpointer user_data)
-{
- struct netlink_info *netlink = user_data;
- struct command *command;
- struct sockaddr_nl addr;
- const void *data;
- ssize_t written;
- int sk;
-
- command = g_queue_pop_head(netlink->command_queue);
- if (!command)
- return FALSE;
-
- sk = g_io_channel_unix_get_fd(chan);
-
- memset(&addr, 0, sizeof(addr));
- addr.nl_family = AF_NETLINK;
- addr.nl_pid = 0;
-
- data = ((void *) command) + NLMSG_ALIGN(sizeof(struct command));
-
- written = sendto(sk, data, command->len, 0,
- (struct sockaddr *) &addr, sizeof(addr));
- if (written < 0 || (uint32_t) written != command->len) {
- g_hash_table_remove(netlink->command_lookup,
- GUINT_TO_POINTER(command->id));
- destroy_command(command);
- return FALSE;
- }
-
- util_hexdump('<', data, command->len,
- netlink->debug_handler, netlink->debug_data);
-
- g_hash_table_replace(netlink->command_pending,
- GUINT_TO_POINTER(command->seq), command);
-
- return g_queue_get_length(netlink->command_queue) > 0;
-}
-
-static void do_notify(gpointer key, gpointer value, gpointer user_data)
-{
- struct nlmsghdr *nlmsg = user_data;
- struct notify *notify = value;
-
- if (notify->handler) {
- notify->handler(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg),
- nlmsg->nlmsg_len - NLMSG_HDRLEN, notify->user_data);
- }
-}
-
-static void process_broadcast(struct netlink_info *netlink, uint32_t group,
- struct nlmsghdr *nlmsg)
-{
- GHashTable *notify_list;
-
- notify_list = g_hash_table_lookup(netlink->notify_groups,
- GUINT_TO_POINTER(group));
- if (!notify_list)
- return;
-
- g_hash_table_foreach(notify_list, do_notify, nlmsg);
-}
-
-static void process_message(struct netlink_info *netlink,
- struct nlmsghdr *nlmsg)
-{
- const void *data = nlmsg;
- struct command *command;
-
- command = g_hash_table_lookup(netlink->command_pending,
- GUINT_TO_POINTER(nlmsg->nlmsg_seq));
- if (!command)
- return;
-
- g_hash_table_remove(netlink->command_pending,
- GUINT_TO_POINTER(nlmsg->nlmsg_seq));
-
- if (!command->handler)
- goto done;
-
- if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) {
- const struct nlmsgerr *err;
-
- switch (nlmsg->nlmsg_type) {
- case NLMSG_ERROR:
- err = data + NLMSG_HDRLEN;
-
- command->handler(-err->error, 0, NULL, 0,
- command->user_data);
- break;
- }
- } else {
- command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN,
- nlmsg->nlmsg_len - NLMSG_HDRLEN,
- command->user_data);
- }
-
-done:
- g_hash_table_remove(netlink->command_lookup,
- GUINT_TO_POINTER(command->id));
-
- destroy_command(command);
-}
-
-static void process_multi(struct netlink_info *netlink, struct nlmsghdr *nlmsg)
-{
- const void *data = nlmsg;
- struct command *command;
-
- command = g_hash_table_lookup(netlink->command_pending,
- GUINT_TO_POINTER(nlmsg->nlmsg_seq));
- if (!command)
- return;
-
- if (!command->handler)
- goto done;
-
- if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) {
- const struct nlmsgerr *err;
-
- switch (nlmsg->nlmsg_type) {
- case NLMSG_DONE:
- case NLMSG_ERROR:
- err = data + NLMSG_HDRLEN;
-
- command->handler(-err->error, 0, NULL, 0,
- command->user_data);
- break;
- }
- } else {
- command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN,
- nlmsg->nlmsg_len - NLMSG_HDRLEN,
- command->user_data);
- return;
- }
-
-done:
- g_hash_table_remove(netlink->command_pending,
- GUINT_TO_POINTER(nlmsg->nlmsg_seq));
-
- g_hash_table_remove(netlink->command_lookup,
- GUINT_TO_POINTER(command->id));
-
- destroy_command(command);
-}
-
-static gboolean can_read_data(GIOChannel *chan,
- GIOCondition cond, gpointer data)
-{
- struct netlink_info *netlink = data;
- struct cmsghdr *cmsg;
- struct msghdr msg;
- struct iovec iov;
- struct nlmsghdr *nlmsg;
- unsigned char buffer[4096];
- unsigned char control[32];
- uint32_t group = 0;
- ssize_t len;
- int sk;
-
- sk = g_io_channel_unix_get_fd(chan);
-
- iov.iov_base = buffer;
- iov.iov_len = sizeof(buffer);
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- len = recvmsg(sk, &msg, 0);
- if (len < 0)
- return FALSE;
-
- util_hexdump('>', buffer, len, netlink->debug_handler,
- netlink->debug_data);
-
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
- cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- struct nl_pktinfo *pktinfo;
-
- if (cmsg->cmsg_level != SOL_NETLINK)
- continue;
-
- if (cmsg->cmsg_type != NETLINK_PKTINFO)
- continue;
-
- pktinfo = (void *) CMSG_DATA(cmsg);
-
- group = pktinfo->group;
- }
-
- for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len);
- nlmsg = NLMSG_NEXT(nlmsg, len)) {
- if (group > 0 && nlmsg->nlmsg_seq == 0) {
- process_broadcast(netlink, group, nlmsg);
- continue;
- }
-
- if (nlmsg->nlmsg_pid != netlink->pid)
- continue;
-
- if (nlmsg->nlmsg_flags & NLM_F_MULTI)
- process_multi(netlink, nlmsg);
- else
- process_message(netlink, nlmsg);
- }
-
- return TRUE;
-}
-
-static gboolean netlink_event(GIOChannel *chan,
- GIOCondition cond, gpointer data)
-{
- if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
- return FALSE;
-
- return TRUE;
-}
-
-static int create_netlink_socket(int protocol, uint32_t *pid)
-{
- struct sockaddr_nl addr;
- socklen_t addrlen = sizeof(addr);
- int sk, pktinfo = 1;
-
- sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- protocol);
- if (sk < 0)
- return -1;
-
- memset(&addr, 0, sizeof(addr));
- addr.nl_family = AF_NETLINK;
-
- if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return -1;
- }
-
- if (getsockname(sk, (struct sockaddr *) &addr, &addrlen) < 0) {
- close(sk);
- return -1;
- }
-
- if (setsockopt(sk, SOL_NETLINK, NETLINK_PKTINFO,
- &pktinfo, sizeof(pktinfo)) < 0) {
- close(sk);
- return -1;
- }
-
- if (pid)
- *pid = addr.nl_pid;
-
- return sk;
-}
-
-struct netlink_info *netlink_new(int protocol)
-{
- struct netlink_info *netlink;
- int sk;
-
- netlink = g_try_new0(struct netlink_info, 1);
- if (!netlink)
- return NULL;
-
- netlink->next_seq = 1;
- netlink->next_command_id = 1;
- netlink->next_notify_id = 1;
-
- sk = create_netlink_socket(protocol, &netlink->pid);
- if (sk < 0) {
- g_free(netlink);
- return NULL;
- }
-
- netlink->channel = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(netlink->channel, TRUE);
-
- g_io_channel_set_encoding(netlink->channel, NULL, NULL);
- g_io_channel_set_buffered(netlink->channel, FALSE);
-
- g_io_add_watch(netlink->channel, G_IO_IN, can_read_data, netlink);
- g_io_add_watch(netlink->channel, G_IO_NVAL | G_IO_HUP | G_IO_ERR,
- netlink_event, netlink);
-
- netlink->command_queue = g_queue_new();
- netlink->command_pending = g_hash_table_new(g_direct_hash,
- g_direct_equal);
- netlink->command_lookup = g_hash_table_new(g_direct_hash,
- g_direct_equal);
-
- netlink->notify_groups = g_hash_table_new(g_direct_hash,
- g_direct_equal);
- netlink->notify_lookup = g_hash_table_new(g_direct_hash,
- g_direct_equal);
-
- return netlink;
-}
-
-static gboolean cleanup_notify(gpointer key, gpointer value, gpointer user_data)
-{
- struct notify *notify = value;
-
- destroy_notify(notify);
-
- return TRUE;
-
-}
-
-static gboolean cleanup_notify_group(gpointer key, gpointer value,
- gpointer user_data)
-{
- GHashTable *notify_list = value;
-
- g_hash_table_foreach_remove(notify_list, cleanup_notify, user_data);
- g_hash_table_destroy(notify_list);
-
- return TRUE;
-}
-
-static gboolean cleanup_command(gpointer key, gpointer value,
- gpointer user_data)
-{
- struct command *command = value;
-
- destroy_command(command);
-
- return TRUE;
-}
-
-void netlink_destroy(struct netlink_info *netlink)
-{
- g_hash_table_destroy(netlink->notify_lookup);
-
- g_hash_table_foreach_remove(netlink->notify_groups,
- cleanup_notify_group, NULL);
- g_hash_table_destroy(netlink->notify_groups);
-
- g_queue_free(netlink->command_queue);
-
- g_hash_table_destroy(netlink->command_pending);
-
- g_hash_table_foreach_remove(netlink->command_lookup,
- cleanup_command, NULL);
- g_hash_table_destroy(netlink->command_lookup);
-
- g_io_channel_shutdown(netlink->channel, TRUE, NULL);
- g_io_channel_unref(netlink->channel);
-
- g_free(netlink);
-}
-
-unsigned int netlink_send(struct netlink_info *netlink,
- uint16_t type, uint16_t flags, const void *data,
- uint32_t len, netlink_command_func_t function,
- void *user_data, netlink_destroy_func_t destroy)
-{
- struct command *command;
- struct nlmsghdr *nlmsg;
- size_t size;
-
- if (!netlink)
- return 0;
-
- if (!netlink->command_queue ||
- !netlink->command_pending ||
- !netlink->command_lookup)
- return 0;
-
- size = NLMSG_ALIGN(sizeof(struct command)) +
- NLMSG_HDRLEN + NLMSG_ALIGN(len);
-
- command = g_try_malloc0(size);
- if (!command)
- return 0;
-
- command->handler = function;
- command->destroy = destroy;
- command->user_data = user_data;
-
- command->id = netlink->next_command_id;
-
- g_hash_table_replace(netlink->command_lookup,
- GUINT_TO_POINTER(command->id), command);
-
- command->seq = netlink->next_seq++;
- command->len = NLMSG_HDRLEN + NLMSG_ALIGN(len);
-
- nlmsg = ((void *) command) + NLMSG_ALIGN(sizeof(struct command));
-
- nlmsg->nlmsg_len = command->len;
- nlmsg->nlmsg_type = type;
- nlmsg->nlmsg_flags = NLM_F_REQUEST | flags;
- nlmsg->nlmsg_seq = command->seq;
- nlmsg->nlmsg_pid = netlink->pid;
-
- if (data && len > 0)
- memcpy(((void *) nlmsg) + NLMSG_HDRLEN, data, len);
-
- g_queue_push_tail(netlink->command_queue, command);
-
- netlink->next_command_id++;
-
- /* Arm IOChannel to call can_write_data in case it is not armed yet. */
- if (g_queue_get_length(netlink->command_queue) == 1)
- g_io_add_watch(netlink->channel, G_IO_OUT, can_write_data,
- netlink);
-
- return command->id;
-}
-
-bool netlink_cancel(struct netlink_info *netlink, unsigned int id)
-{
- struct command *command;
-
- if (!netlink || id == 0)
- return false;
-
- if (!netlink->command_queue ||
- !netlink->command_pending ||
- !netlink->command_lookup)
- return false;
-
- command = g_hash_table_lookup(netlink->command_lookup,
- GUINT_TO_POINTER(id));
- if (!command)
- return false;
-
- g_hash_table_remove(netlink->command_lookup, GUINT_TO_POINTER(id));
-
- if (!g_queue_remove(netlink->command_queue, command)) {
- g_hash_table_remove(netlink->command_pending,
- GUINT_TO_POINTER(command->seq));
- }
-
- destroy_command(command);
-
- return true;
-}
-
-static bool add_membership(struct netlink_info *netlink, uint32_t group)
-{
- int sk, value = group;
-
- sk = g_io_channel_unix_get_fd(netlink->channel);
-
- if (setsockopt(sk, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
- &value, sizeof(value)) < 0)
- return false;
-
- return true;
-}
-
-static bool drop_membership(struct netlink_info *netlink, uint32_t group)
-{
- int sk, value = group;
-
- sk = g_io_channel_unix_get_fd(netlink->channel);
-
- if (setsockopt(sk, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
- &value, sizeof(value)) < 0)
- return false;
-
- return true;
-}
-
-unsigned int netlink_register(struct netlink_info *netlink,
- uint32_t group, netlink_notify_func_t function,
- void *user_data, netlink_destroy_func_t destroy)
-{
- GHashTable *notify_list;
- struct notify *notify;
- unsigned int id;
-
- if (!netlink)
- return 0;
-
- if (!netlink->notify_groups || !netlink->notify_lookup)
- return 0;
-
- notify_list = g_hash_table_lookup(netlink->notify_groups,
- GUINT_TO_POINTER(group));
- if (!notify_list) {
- notify_list = g_hash_table_new(g_direct_hash, g_direct_equal);
- if (!notify_list)
- return 0;
-
- g_hash_table_replace(netlink->notify_groups,
- GUINT_TO_POINTER(group), notify_list);
- }
-
- notify = g_new(struct notify, 1);
-
- notify->group = group;
- notify->handler = function;
- notify->destroy = destroy;
- notify->user_data = user_data;
-
- id = netlink->next_notify_id;
-
- g_hash_table_replace(netlink->notify_lookup,
- GUINT_TO_POINTER(id), notify_list);
- g_hash_table_replace(notify_list, GUINT_TO_POINTER(id), notify);
-
- if (g_hash_table_size(notify_list) == 1) {
- if (add_membership(netlink, notify->group) == false)
- goto remove_notify;
- }
-
- netlink->next_notify_id++;
-
- return id;
-
-remove_notify:
- g_hash_table_remove(notify_list, GUINT_TO_POINTER(id));
- g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id));
- g_free(notify);
-
- return 0;
-}
-
-bool netlink_unregister(struct netlink_info *netlink, unsigned int id)
-{
- GHashTable *notify_list;
- struct notify *notify;
-
- if (!netlink || id == 0)
- return false;
-
- if (!netlink->notify_groups || !netlink->notify_lookup)
- return false;
-
- notify_list = g_hash_table_lookup(netlink->notify_lookup,
- GUINT_TO_POINTER(id));
-
- if (!notify_list)
- return false;
-
- g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id));
-
- notify = g_hash_table_lookup(notify_list, GUINT_TO_POINTER(id));
- if (!notify)
- return false;
-
- g_hash_table_remove(notify_list, GUINT_TO_POINTER(id));
-
- if (g_hash_table_size(notify_list) == 0)
- drop_membership(netlink, notify->group);
-
- destroy_notify(notify);
-
- return true;
-}
-
-bool netlink_set_debug(struct netlink_info *netlink,
- netlink_debug_func_t function,
- void *user_data, netlink_destroy_func_t destroy)
-{
- if (!netlink)
- return false;
-
- if (netlink->debug_destroy)
- netlink->debug_destroy(netlink->debug_data);
-
- netlink->debug_handler = function;
- netlink->debug_destroy = destroy;
- netlink->debug_data = user_data;
-
- return true;
-}
+++ /dev/null
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2011-2012 Intel Corporation. All rights reserved.
- * Copyright (C) 2013 BMW Car IT GbmH.
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation.
- *
- * This library 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <stdint.h>
-#include <stdbool.h>
-
-typedef void (*netlink_debug_func_t) (const char *str, void *user_data);
-typedef void (*netlink_command_func_t) (unsigned int error,
- uint16_t type, const void *data,
- uint32_t len, void *user_data);
-typedef void (*netlink_notify_func_t) (uint16_t type, const void *data,
- uint32_t len, void *user_data);
-typedef void (*netlink_destroy_func_t) (void *user_data);
-
-struct netlink_info;
-
-struct netlink_info *netlink_new(int protocol);
-void netlink_destroy(struct netlink_info *netlink);
-
-unsigned int netlink_send(struct netlink_info *netlink,
- uint16_t type, uint16_t flags, const void *data,
- uint32_t len, netlink_command_func_t function,
- void *user_data, netlink_destroy_func_t destroy);
-bool netlink_cancel(struct netlink_info *netlink, unsigned int id);
-
-unsigned int netlink_register(struct netlink_info *netlink,
- uint32_t group, netlink_notify_func_t function,
- void *user_data, netlink_destroy_func_t destroy);
-bool netlink_unregister(struct netlink_info *netlink, unsigned int id);
-
-bool netlink_set_debug(struct netlink_info *netlink,
- netlink_debug_func_t function,
- void *user_data, netlink_destroy_func_t destroy);
function(str, user_data);
}
}
+
+void util_iso8601_to_timeval(char *str, struct timeval *time)
+{
+ struct tm tm;
+ time_t t;
+ char *p;
+
+ p = strptime(str, "%FT%T", &tm);
+ if (!p)
+ return;
+
+ if (*p != 'Z') {
+ /* backwards compatibility */
+ if (*p != '.' || p[strlen(p) - 1] != 'Z')
+ return;
+ }
+
+ t = mktime(&tm);
+ if (t < 0)
+ return;
+
+ time->tv_sec = t;
+ time->tv_usec = 0;
+}
+
+char *util_timeval_to_iso8601(struct timeval *time)
+{
+ char buf[255];
+ struct tm tm;
+ time_t t;
+
+ t = time->tv_sec;
+ if (!localtime_r(&t, &tm))
+ return NULL;
+ if (!strftime(buf, sizeof(buf), "%FT%TZ", &tm))
+ return NULL;
+
+ return g_strdup(buf);
+}
*
*/
+#include <sys/time.h>
+
#include <glib.h>
typedef void (*util_debug_func_t)(const char *str, void *user_data);
return ret;
}
+
+void util_iso8601_to_timeval(char *str, struct timeval *time);
+char *util_timeval_to_iso8601(struct timeval *time);
*
* File properties:
* The ring buffer is mmap to a file
- * Initialy only the smallest possible amount of disk space is allocated
+ * Initially only the smallest possible amount of disk space is allocated
* The files grow to the configured maximal size
* The grows by _SC_PAGESIZE step size
* For each service a file is created
*
* History file:
* Same format as the ring buffer file
- * For a period of at least 2 months dayly records are keept
- * If older, then only a monthly record is keept
+ * For a period of at least 2 months daily records are kept
+ * If older, then only a monthly record is kept
*/
STORAGEDIR);
file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644);
if (file->fd < 0) {
- connman_error("create tempory file error %s for %s",
+ connman_error("create temporary file error %s for %s",
strerror(errno), file->name);
g_free(file->name);
file->name = NULL;
return keyfile;
}
-GKeyFile *__connman_storage_open_service(const char *service_id)
-{
- gchar *pathname;
- GKeyFile *keyfile = NULL;
-
- pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, SETTINGS);
- if (!pathname)
- return NULL;
-
- keyfile = storage_load(pathname);
- if (keyfile) {
- g_free(pathname);
- return keyfile;
- }
-
- g_free(pathname);
-
- keyfile = g_key_file_new();
-
- return keyfile;
-}
-
gchar **connman_storage_get_services(void)
{
struct dirent *d;
GPtrArray *argv;
GPtrArray *envp;
connman_task_exit_t exit_func;
+ connman_task_setup_t setup_func;
void *exit_data;
GHashTable *notify;
+ void *setup_data;
};
static GHashTable *task_hash = NULL;
*
* Returns: a newly-allocated #connman_task structure
*/
-struct connman_task *connman_task_create(const char *program)
+struct connman_task *connman_task_create(const char *program,
+ connman_task_setup_t custom_task_setup,
+ void *setup_data)
{
struct connman_task *task;
gint counter;
str = g_strdup(program);
g_ptr_array_add(task->argv, str);
+ task->setup_func = custom_task_setup;
+
task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
+ task->setup_data = setup_data;
+
DBG("task %p", task);
g_hash_table_insert(task_hash, task->path, task);
* connman_task_destory:
* @task: task structure
*
- * Remove and destory #task
+ * Remove and destroy #task
*/
void connman_task_destroy(struct connman_task *task)
{
/**
* connman_task_set_notify:
* @task: task structure
- * @member: notifcation method name
+ * @member: notification method name
* @function: notification callback
* @user_data: optional notification user data
*
sigemptyset(&mask);
if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
connman_error("Failed to clean signal mask");
+
+ if (task->setup_func)
+ task->setup_func(task->setup_data);
}
/**
void __connman_tethering_client_unregister(const char *addr)
{
- g_hash_table_remove(clients_table, addr);
client_removed(addr);
+ g_hash_table_remove(clients_table, addr);
}
int __connman_tethering_init(void)
}
/*
- * This function must be called everytime the default service changes, the
+ * 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)
subpath, d->d_name);
if (compare_file(src_map, src_st, pathname) == 0) {
- str = g_strdup_printf("%s/%s",
+ if (!subpath)
+ str = g_strdup(d->d_name);
+ else
+ str = g_strdup_printf("%s/%s",
subpath, d->d_name);
closedir(dir);
return str;
const char *error, void *user_data)
{
struct connman_wispr_portal_context *wp_context = user_data;
+ struct connman_wispr_portal *wispr_portal;
+ int index;
DBG("");
if (!service || !wp_context)
return;
+ /*
+ * No way to cancel this if wp_context has been freed, so we lookup
+ * from the service and check that this is still the right context.
+ */
+ index = __connman_service_get_index(service);
+ if (index < 0)
+ return;
+
+ wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ GINT_TO_POINTER(index));
+ if (!wispr_portal)
+ return;
+
+ if (wp_context != wispr_portal->ipv4_context &&
+ wp_context != wispr_portal->ipv6_context)
+ return;
+
if (!authentication_done) {
wispr_portal_error(wp_context);
free_wispr_routes(wp_context);
import subprocess
if (len(sys.argv) < 3):
- print "Usage: %s [binary] [log]" % (sys.argv[0])
+ print("Usage: %s [binary] [log]" % (sys.argv[0]))
sys.exit(1)
binary = sys.argv[1]
frame_count = len(frames);
count = 0
-print "-------- backtrace --------"
+print("-------- backtrace --------")
while count < frame_count:
- print "[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1])
+ print("[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1]))
count = count + 2
-print "---------------------------"
+print("---------------------------")
import dbus
if (len(sys.argv) < 4):
- print "Usage: %s <type> ... " % (sys.argv[0])
- print " type: openconnect"
- print " <name> <host> <domain> <cookie> [servercert]"
- print " type: openvpn"
- print " <name> <host> <domain> [<cafile> <certfile> <keyfile>]"
- print " type: pptp"
- print " <name> <host> <domain> <user> <password>"
- print " type: l2tp"
- print " <name> <host> <domain> <user> <password>"
+ print("Usage: %s <type> ... " % (sys.argv[0]))
+ print(" type: openconnect")
+ print(" <name> <host> <domain> <cookie> [servercert]")
+ print(" type: openvpn")
+ print(" <name> <host> <domain> [<cafile> <certfile> <keyfile>]")
+ print(" type: pptp")
+ print(" <name> <host> <domain> <user> <password>")
+ print(" type: l2tp")
+ print(" <name> <host> <domain> <user> <password>")
sys.exit(1)
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object("net.connman", "/"),
"net.connman.Manager")
-print "Attempting to connect service %s" % (sys.argv[3])
+print("Attempting to connect service %s" % (sys.argv[3]))
if sys.argv[1] == "openconnect":
if (len(sys.argv) > 6):
"L2TP.Password": sys.argv[6]}))
else:
- print "Unknown VPN type"
+ print("Unknown VPN type")
sys.exit(1)
-print "VPN service path is %s" %(path)
+print("VPN service path is %s" %(path))
import dbus
if (len(sys.argv) != 2):
- print "Usage: %s type" % (sys.argv[0])
+ print("Usage: %s type" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
properties = tech.GetProperties()
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["Type"]:
if properties[key] == tech_type:
- print "Disabling %s tethering" % tech_type
+ print("Disabling %s tethering" % tech_type)
tech.SetProperty("Tethering", dbus.Boolean(0))
return tech_type
break;
if tech == None:
- print "Failed to disable %s tethering" % (sys.argv[1])
+ print("Failed to disable %s tethering" % (sys.argv[1]))
import dbus
if (len(sys.argv) >= 3 and len(sys.argv) != 4 and sys.argv[1] == "wifi"):
- print "Usage: %s wifi [SSID] [passphrase]" % (sys.argv[0])
+ print("Usage: %s wifi [SSID] [passphrase]" % (sys.argv[0]))
sys.exit(1)
elif (len(sys.argv) < 2):
- print "Usage: %s type" % (sys.argv[0])
+ print("Usage: %s type" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
properties = tech.GetProperties()
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["Type"]:
if properties[key] == tech_type:
if len(ssid) > 0:
if len(psk) > 0:
tech.SetProperty("TetheringPassphrase",
psk)
- print "Enabling %s tethering" % tech_type
+ print("Enabling %s tethering" % tech_type)
tech.SetProperty("Tethering", dbus.Boolean(1))
return tech_type
break;
if tech == None:
- print "Failed to enable %s tethering" % (sys.argv[1])
+ print("Failed to enable %s tethering" % (sys.argv[1]))
properties = clock.GetProperties()
-print "Timeserver is %s" % (properties["Timeservers"])
+print("Timeserver is %s" % (properties["Timeservers"]))
#!/usr/bin/python
import dbus
-import urllib
+import urllib.request, urllib.parse, urllib.error
def get_pac(url):
- conn = urllib.urlopen(url, proxies={})
+ conn = urllib.request.urlopen(url, proxies={})
data = conn.read()
- print data
+ print(data)
conn.close()
bus = dbus.SystemBus()
proxy = properties["Proxy"]
if "Method" in proxy:
- print "[ %s ]" % (path)
+ print("[ %s ]" % (path))
method = proxy["Method"]
- print "Method = %s" % (method)
+ print("Method = %s" % (method))
if method in ["auto"]:
url = proxy["URL"]
- print "URL = %s" % (url)
- print
+ print("URL = %s" % (url))
+ print()
get_pac(url)
else:
- print
+ print()
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["Servers", "Excludes"]:
val += extract_list(values[key])
path = entry[0]
properties = entry[1]
- print "[ %s ]" % (path)
+ print("[ %s ]" % (path))
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["IPv4", "IPv4.Configuration",
"IPv6", "IPv6.Configuration",
"Proxy", "Proxy.Configuration",
val = int(properties[key])
else:
val = str(properties[key])
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
- print
+ print()
properties = manager.GetProperties()
-print "System is %s" % (properties["State"])
+print("System is %s" % (properties["State"]))
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["PrefixLength"]:
val += "%s" % (int(values[key]))
service = dbus.Interface(bus.get_object("net.connman", path),
"net.connman.Service")
identifier = path[path.rfind("/") + 1:]
- print "[ %s ]" % (identifier)
+ print("[ %s ]" % (identifier))
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["IPv4", "IPv4.Configuration",
"IPv6", "IPv6.Configuration",
"Proxy", "Proxy.Configuration",
val = int(properties[key])
else:
val = properties[key]
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
- print
+ print()
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["PrefixLength"]:
val += "%s" % (int(values[key]))
iface = interface[interface.rfind(".") + 1:]
val = extract(name, value)
- print "{%s} [%s] %s = %s" % (iface, path, name, val)
+ print("{%s} [%s] %s = %s" % (iface, path, name, val))
def message_filter(connection, message):
if not isinstance(message, MethodCallMessage):
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["Servers", "Excludes"]:
val += extract_list(values[key])
val = int(value)
else:
val = str(value)
- print "[%s] %s = %s" % (service, name, val)
+ print("[%s] %s = %s" % (service, name, val))
def services_changed(services, removed):
for i in services:
service = i[0][i[0].rfind("/") + 1:]
- print "[%s] changed" % (service)
- for n in i[1].keys():
+ print("[%s] changed" % (service))
+ for n in list(i[1].keys()):
property_changed(n, i[1][n], i[0])
for i in removed:
service = i[i.rfind("/") + 1:]
- print "[%s] removed" % (service)
+ print("[%s] removed" % (service))
def technology_added(path, properties):
technology = path[path.rfind("/") + 1:]
- print "[%s] added" % (technology)
- for n in properties.keys():
+ print("[%s] added" % (technology))
+ for n in list(properties.keys()):
property_changed(n, properties[n], technology)
def technology_removed(path):
technology = path[path.rfind("/") + 1:]
- print "[%s] removed" % (technology)
+ print("[%s] removed" % (technology))
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["ProtocolFamily"]:
val += "%s" % (int(values[key]))
iface = interface[interface.rfind(".") + 1:]
val = extract(name, value)
- print "{%s} [%s] %s = %s" % (iface, path, name, val)
+ print("{%s} [%s] %s = %s" % (iface, path, name, val))
def message_filter(connection, message):
if not isinstance(message, MethodCallMessage):
def prompt(self):
self.line = ''
- print '> ',
+ print('> ', end=' ')
stdout.flush()
def input_cb(self, fd, event):
return True
def error_print(ex):
- print 'Command Error: %s' % ex
+ print('Command Error: %s' % ex)
def checkarg(nb_args = 0, min_args = False):
def under(function):
for k in d:
try:
if type(d[k]) is dbus.Byte:
- print 'Key %s --> 0x%x' % (k, d[k])
+ print('Key %s --> 0x%x' % (k, d[k]))
else:
- print 'Key %s --> %s' % (k, d[k])
+ print('Key %s --> %s' % (k, d[k]))
except:
- print "Error: Key %s content cannot be printed" % k
+ print("Error: Key %s content cannot be printed" % k)
pass
def print_tuple(t):
if type(e) is dbus.Dictionary:
print_dict(e)
else:
- print 'Element: %s' % e
+ print('Element: %s' % e)
class Wpa_s:
def __init__(self, bus, iface_name, command):
try:
self.create_if([iface_name])
except:
- print "Error creating interface: %s" % iface_name
+ print("Error creating interface: %s" % iface_name)
if len(command.strip(' ')):
self.__command(command)
def help(self, args):
- list = self.command_list.keys()
+ list = list(self.command_list.keys())
list.sort()
for key in list:
help = ''
- if (self.command_list[key].has_key(ArgFields.help)):
+ if (ArgFields.help in self.command_list[key]):
help = self.command_list[key][ArgFields.help]
- print "%s\t%s" % (key.rjust(25), help.ljust(50))
+ print("%s\t%s" % (key.rjust(25), help.ljust(50)))
def __command(self, cmd_line):
cmd = cmd_line.split(' ')
try:
func = getattr(self, cmd[0])
- except Exception, e:
- print 'Error: command unknown - %s' % e
+ except Exception as e:
+ print('Error: command unknown - %s' % e)
return
try:
func(cmd[1:])
- except Exception, e:
+ except Exception as e:
error_print(e)
def __wpa_property_changed(*args, **kwargs):
- print 'WPA - Signal: %s' % kwargs.get('signal')
+ print('WPA - Signal: %s' % kwargs.get('signal'))
def __if_property_changed(*args, **kwargs):
signal = kwargs.get('signal')
- print 'IF - Signal: %s' % signal
+ print('IF - Signal: %s' % signal)
if signal == 'BSSAdded':
return
print_tuple(args[1:])
def __p2p_property_changed(*args, **kwargs):
- print 'IF P2P - Signal: %s' % kwargs.get('signal')
+ print('IF P2P - Signal: %s' % kwargs.get('signal'))
if args[0].debug:
print_tuple(args[1:])
def __peer_if_p2p_property_changed(*args, **kwargs):
- print 'Peer - ',
+ print('Peer - ', end=' ')
args[0].__p2p_property_changed(*args, **kwargs)
def __DeviceFound(self, object_path):
del self.peers[object_path]
def __PeerJoined(self, object_path):
- print 'Peer %s joined' % object_path
+ print('Peer %s joined' % object_path)
def __PeerDisconnected(self, object_path):
- print 'Peer %s disconnected' % object_path
+ print('Peer %s disconnected' % object_path)
def __group_if_property_changed(*args, **kwargs):
- print 'Group - ',
+ print('Group - ', end=' ')
args[0].__if_property_changed(*args, **kwargs)
def __group_if_p2p_property_changed(*args, **kwargs):
- print 'Group - ',
+ print('Group - ', end=' ')
args[0].__p2p_property_changed(*args, **kwargs)
def __GroupFinished(self, properties):
- print 'Group running on %s is being removed' % ifname
+ print('Group running on %s is being removed' % ifname)
self.group_obj = self.group_if = self.group_iface_path = None
if self.debug:
print_dict(properties)
def __InvitationResult(self, response):
- print 'Invitation result status: %d ' % response['status']
+ print('Invitation result status: %d ' % response['status'])
- if response.has_key('bssid'):
- print 'bssid: %s' % response['bssid']
+ if 'bssid' in response:
+ print('bssid: %s' % response['bssid'])
if self.debug:
print_dict(response)
def __ServiceDiscoveryResponse(self, response):
peer = response['peer_object']
if peer in self.peers:
- print 'Peer %s has this TLVs:' % (self.peers[peer]['DeviceName'])
- print response['tlvs']
+ print('Peer %s has this TLVs:' % (self.peers[peer]['DeviceName']))
+ print(response['tlvs'])
def __InterfaceAdded(self, path, properties):
- print 'Interface %s Added (%s)' % (properties['Ifname'], path)
+ print('Interface %s Added (%s)' % (properties['Ifname'], path))
if self.debug:
print_dict(properties)
p2p = dbus.Interface(self.bus.get_object(WPA_INTF,
print_dict(p2p.GetAll(WPA_P2P_INTF))
def __InterfaceRemoved(self, path):
- print 'Interface Removed (%s)' % (path)
+ print('Interface Removed (%s)' % (path))
def __listen_if_signals(self):
self.bus.add_signal_receiver(self.__if_property_changed,
p2p_if.Set(WPA_P2P_INTF, 'P2PDeviceConfig',
dbus.Dictionary({ 'DeviceName' : 'ConnManP2P' },
signature='sv'))
- print 'Interface %s: %s' % (iface_name, self.iface_path)
+ print('Interface %s: %s' % (iface_name, self.iface_path))
self.iface_name = iface_name
self.__listen_if_signals()
return
self.wpa.RemoveInterface(self.iface_path)
- print 'Interface %s removed' % self.iface_name
+ print('Interface %s removed' % self.iface_name)
self.__reset()
@checkarg()
return
self.iface.Scan(({ 'Type': 'passive' }))
- print 'Scan started'
+ print('Scan started')
@checkarg()
def quit(self, args = None):
return
for p in self.peers:
- print 'Peer Name=%s' % (self.peers[p]['DeviceName'])
+ print('Peer Name=%s' % (self.peers[p]['DeviceName']))
def __find_peer(self, peer_name, ret_object_path = False):
if len(self.peers) == 0:
break
if not peer:
- print 'No peer found under the name: %s' % peer_name
+ print('No peer found under the name: %s' % peer_name)
p = None
if ret_object_path:
if (peer['groupcapability'] & P2P_GROUP_CAPAB_GROUP_OWNER ==
P2P_GROUP_CAPAB_GROUP_OWNER):
- print 'Joining an existing P2P group'
+ print('Joining an existing P2P group')
pin = self.p2p.Connect(({ 'peer' : peer_path,
'wps_method' : 'pbc',
'join' : True,
'go_intent' : 0 }))
else:
- print 'Associating with another P2P device'
+ print('Associating with another P2P device')
pin = self.p2p.Connect(({ 'peer' : peer_path,
'wps_method' : 'pbc',
'join' : False,
'go_intent' : 7 }))
if not pin:
- print 'WPS PIN in use: %s' % pin
+ print('WPS PIN in use: %s' % pin)
@checkarg(nb_args = 1)
def p2p_disconnect(self, args):
return
if not self.group_if:
- print 'Peer %s is not connected' % (peer['DeviceName'])
+ print('Peer %s is not connected' % (peer['DeviceName']))
return
self.group_if.Disconnect()
sd_req.append(dbus.Byte(a))
ref = self.p2p.ServiceDiscoveryRequest(({ 'tlv' : sd_req }))
- print 'Service discovery reference: %s' % ref
+ print('Service discovery reference: %s' % ref)
@checkarg(nb_args = 1)
def p2p_serv_disc_cancel_req(self, args):
service['query'] = args[1]
service['response'] = args[2]
else:
- print 'Unknown service: %s' % args[0]
+ print('Unknown service: %s' % args[0])
return
self.p2p.AddService((service))
elif args[0] == 'bonjour':
service['query'] = args[1]
else:
- print 'Unknown service: %s' % args[0]
+ print('Unknown service: %s' % args[0])
return
self.p2p.DeleteService((service))
command['p2p_service_flush'] = {}
command['p2p_invite'] = {ArgFields.help:'<p2p device name>'}
- command_list = command.keys()
+ command_list = list(command.keys())
command_list.sort()
subparsers = parser.add_subparsers(help='commands', dest='command')
subparsers.add_parser('')
for key in command_list:
help=None
metavar=None
- if command[key].has_key(ArgFields.help):
+ if ArgFields.help in command[key]:
help = command[key][ArgFields.help]
- if command[key].has_key(ArgFields.metavar):
+ if ArgFields.metavar in command[key]:
metavar = command[key][ArgFields.metavar]
command_parser = subparsers.add_parser(key, help=help)
command_parser.add_argument(key, nargs='*', metavar=metavar, help=help)
def main():
if version_info.major != 2:
- print 'You need to run this under Python 2.x'
+ print('You need to run this under Python 2.x')
exit(1)
parser = argparse.ArgumentParser(description='Connman P2P Test')
import dbus
if (len(sys.argv) < 2):
- print "Usage: %s <VPN service path> " % (sys.argv[0])
+ print("Usage: %s <VPN service path> " % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
path = "" + sys.argv[1]
-print "remove path is %s" %(path)
+print("remove path is %s" %(path))
manager.RemoveProvider(sys.argv[1])
import dbus
def print_usage():
- print "Usage: %s <service> <target service>" % (sys.argv[0])
+ print("Usage: %s <service> <target service>" % (sys.argv[0]))
if (len(sys.argv) < 2):
service2 = dbus.Interface(bus.get_object('net.connman', path2),
'net.connman.Service')
-print "Moving %s before %s" % (sys.argv[1], sys.argv[2])
+print("Moving %s before %s" % (sys.argv[1], sys.argv[2]))
service.MoveBefore(service2)
-print
+print()
import dbus
def print_usage():
- print "Usage: %s TimeUpdates|TimezoneUpdates manual|auto" % (sys.argv[0])
+ print("Usage: %s TimeUpdates|TimezoneUpdates manual|auto" % (sys.argv[0]))
sys.exit(1)
clock = dbus.Interface(bus.get_object('net.connman', '/'),
'net.connman.Clock')
-print "Setting %s to %s" % (sys.argv[1], sys.argv[2])
+print("Setting %s to %s" % (sys.argv[1], sys.argv[2]))
clock.SetProperty(sys.argv[1], make_variant(sys.argv[2]),
signature=dbus.Signature('sv'))
import dbus
if (len(sys.argv) < 2):
- print "Usage: %s <service> [domain*]" % (sys.argv[0])
+ print("Usage: %s <service> [domain*]" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
properties = service.GetProperties()
-print "Setting domains to %s" % (sys.argv[2:])
+print("Setting domains to %s" % (sys.argv[2:]))
service.SetProperty("Domains.Configuration",
dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
import dbus
if (len(sys.argv) < 1):
- print "Usage: %s [timeserver*]" % (sys.argv[0])
+ print("Usage: %s [timeserver*]" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
clock = dbus.Interface(bus.get_object('net.connman', '/'),
'net.connman.Clock')
-print "Setting timeserver to %s" % (sys.argv[1:])
+print("Setting timeserver to %s" % (sys.argv[1:]))
clock.SetProperty("Timeservers", dbus.Array(sys.argv[1:],
signature=dbus.Signature('s')))
return dbus.String(string, variant_level=1)
def print_usage():
- print "Usage: %s <service> [off|dhcp|manual <address> [netmask] [gateway]]" % (sys.argv[0])
+ print("Usage: %s <service> [off|dhcp|manual <address> [netmask] [gateway]]" % (sys.argv[0]))
if (len(sys.argv) < 3):
properties = service.GetProperties()
-print "Setting method %s for %s" % (sys.argv[2], sys.argv[1])
+print("Setting method %s for %s" % (sys.argv[2], sys.argv[1]))
ipv4_configuration = { "Method": make_variant(sys.argv[2]) }
if (len(sys.argv) > 3):
ipv4_configuration["Gateway"] = make_variant(sys.argv[5])
service.SetProperty("IPv4.Configuration", ipv4_configuration)
-print "New IPv4.Configuration: ", ipv4_configuration
+print("New IPv4.Configuration: ", ipv4_configuration)
-print
+print()
return dbus.Byte(int(string), variant_level=1)
def print_usage():
- print "Usage: %s <service> off|manual|auto [<address> [prefixlen] [gateway]] [<privacy>]" % (sys.argv[0])
+ print("Usage: %s <service> off|manual|auto [<address> [prefixlen] [gateway]] [<privacy>]" % (sys.argv[0]))
if (len(sys.argv) < 3):
print_usage()
properties = service.GetProperties()
-print "Setting method %s for %s" % (sys.argv[2], sys.argv[1])
+print("Setting method %s for %s" % (sys.argv[2], sys.argv[1]))
ipv6_configuration = { "Method": make_variant(sys.argv[2])}
if sys.argv[2] == "auto":
ipv6_configuration["Gateway"] = make_variant(sys.argv[5])
service.SetProperty("IPv6.Configuration", ipv6_configuration)
-print "New IPv6.Configuration: ", ipv6_configuration
+print("New IPv6.Configuration: ", ipv6_configuration)
-print
+print()
import dbus
if (len(sys.argv) < 2):
- print "Usage: %s <service> [nameserver*]" % (sys.argv[0])
+ print("Usage: %s <service> [nameserver*]" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
properties = service.GetProperties()
-print "Setting nameserver to %s" % (sys.argv[2:])
+print("Setting nameserver to %s" % (sys.argv[2:]))
service.SetProperty("Nameservers.Configuration",
dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
import dbus
if (len(sys.argv) < 2):
- print "Usage:"
- print "%s <service> direct" % (sys.argv[0])
- print "%s <service> manual [servers=uri1,uri2,...] [excludes=host1,host2,...]" % (sys.argv[0])
- print "%s <service> auto url=[pac-url]" % (sys.argv[0])
- print "Example: %s service0 manual servers=proxy.example.com:8080" % sys.argv[0]
- print " This would set the proxy uri and the method to manual"
+ print("Usage:")
+ print("%s <service> direct" % (sys.argv[0]))
+ print("%s <service> manual [servers=uri1,uri2,...] [excludes=host1,host2,...]" % (sys.argv[0]))
+ print("%s <service> auto url=[pac-url]" % (sys.argv[0]))
+ print("Example: %s service0 manual servers=proxy.example.com:8080" % sys.argv[0])
+ print(" This would set the proxy uri and the method to manual")
sys.exit(1)
bus = dbus.SystemBus()
try:
service.SetProperty("Proxy.Configuration", dbus.Dictionary(values, signature='sv'))
-except dbus.exceptions.DBusException, e_msg:
- print e_msg
+except dbus.exceptions.DBusException as e_msg:
+ print(e_msg)
import dbus
if (len(sys.argv) < 2):
- print "Usage: %s <service> [timeserver*]" % (sys.argv[0])
+ print("Usage: %s <service> [timeserver*]" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
properties = service.GetProperties()
-print "Setting timeserver to %s" % (sys.argv[2:])
+print("Setting timeserver to %s" % (sys.argv[2:]))
service.SetProperty("Timeservers.Configuration",
dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
import dbus
if (len(sys.argv) != 2):
- print "Usage: %s <timezone>" % (sys.argv[0])
+ print("Usage: %s <timezone>" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
clock = dbus.Interface(bus.get_object('net.connman', '/'),
'net.connman.Clock')
-print "Setting timezone to %s" % (sys.argv[1])
+print("Setting timezone to %s" % (sys.argv[1]))
try:
clock.SetProperty("Timezone", dbus.String(sys.argv[1], variant_level=1),
signature=dbus.Signature('sv'))
-except dbus.exceptions.DBusException, e_msg:
- print e_msg
+except dbus.exceptions.DBusException as e_msg:
+ print(e_msg)
object = dbus.Interface(bus.get_object("net.connman", '/'),
"org.freedesktop.DBus.Introspectable")
-print object.Introspect()
+print(object.Introspect())
manager = dbus.Interface(bus.get_object("net.connman", "/"),
"net.connman.Manager")
object = dbus.Interface(bus.get_object("net.connman", path),
"org.freedesktop.DBus.Introspectable")
- print object.Introspect()
+ print(object.Introspect())
response = {}
if not self.identity and not self.passphrase and not self.wpspin:
- print "Service credentials requested, type cancel to cancel"
- args = raw_input('Answer: ')
+ print("Service credentials requested, type cancel to cancel")
+ args = input('Answer: ')
for arg in args.split():
if arg.startswith("cancel"):
response = {}
if not self.username and not self.password:
- print "User login requested, type cancel to cancel"
- print "or browser to login through the browser by yourself."
- args = raw_input('Answer: ')
+ print("User login requested, type cancel to cancel")
+ print("or browser to login through the browser by yourself.")
+ args = input('Answer: ')
for arg in args.split():
if arg.startswith("cancel") or arg.startswith("browser"):
response = {}
if not self.name and not self.ssid:
- args = raw_input('Answer ')
+ args = input('Answer ')
for arg in args.split():
if arg.startswith("Name="):
in_signature='oa{sv}',
out_signature='a{sv}')
def RequestInput(self, path, fields):
- print "RequestInput (%s,%s)" % (path, fields)
+ print("RequestInput (%s,%s)" % (path, fields))
response = {}
- if fields.has_key("Name"):
+ if "Name" in fields:
response.update(self.input_hidden())
- if fields.has_key("Passphrase"):
+ if "Passphrase" in fields:
response.update(self.input_passphrase())
- if fields.has_key("Username"):
+ if "Username" in fields:
response.update(self.input_username())
- if response.has_key("Error"):
+ if "Error" in response:
if response["Error"] == "cancel":
raise Canceled("canceled")
return
raise LaunchBrowser("launch browser")
return
- print "returning (%s)" % (response)
+ print("returning (%s)" % (response))
return response
in_signature='os',
out_signature='')
def RequestBrowser(self, path, url):
- print "RequestBrowser (%s,%s)" % (path, url)
+ print("RequestBrowser (%s,%s)" % (path, url))
- print "Please login through the given url in a browser"
- print "Then press enter to accept or some text to cancel"
+ print("Please login through the given url in a browser")
+ print("Then press enter to accept or some text to cancel")
- args = raw_input('> ')
+ args = input('> ')
if len(args) > 0:
raise Canceled("canceled")
in_signature='os',
out_signature='')
def ReportError(self, path, error):
- print "ReportError %s, %s" % (path, error)
- retry = raw_input("Retry service (yes/no): ")
+ print("ReportError %s, %s" % (path, error))
+ retry = input("Retry service (yes/no): ")
if (retry == "yes"):
class Retry(dbus.DBusException):
_dbus_error_name = "net.connman.Agent.Error.Retry"
@dbus.service.method("net.connman.Agent",
in_signature='', out_signature='')
def Cancel(self):
- print "Cancel"
+ print("Cancel")
class VpnAgent(dbus.service.Object):
name = None
response = {}
if not self.cookie:
- print "VPN credentials requested, type cancel to cancel"
- args = raw_input('Answer: ')
+ print("VPN credentials requested, type cancel to cancel")
+ args = input('Answer: ')
for arg in args.split():
if arg.startswith("cancel"):
response = {}
if not self.username and not self.password:
- print "User login requested, type cancel to cancel"
- args = raw_input('Answer: ')
+ print("User login requested, type cancel to cancel")
+ args = input('Answer: ')
for arg in args.split():
if arg.startswith("cancel"):
in_signature='oa{sv}',
out_signature='a{sv}')
def RequestInput(self, path, fields):
- print "RequestInput (%s,%s)" % (path, fields)
+ print("RequestInput (%s,%s)" % (path, fields))
response = {}
- if fields.has_key("OpenConnect.Cookie"):
+ if "OpenConnect.Cookie" in fields:
response.update(self.input_cookie())
- if fields.has_key("Username") or fields.has_key("Password"):
+ if "Username" in fields or "Password" in fields:
response.update(self.input_username())
- if response.has_key("Error"):
+ if "Error" in response:
if response["Error"] == "cancel":
raise Canceled("canceled")
return
- print "returning (%s)" % (response)
+ print("returning (%s)" % (response))
return response
in_signature='os',
out_signature='')
def ReportError(self, path, error):
- print "ReportError %s, %s" % (path, error)
- retry = raw_input("Retry service (yes/no): ")
+ print("ReportError %s, %s" % (path, error))
+ retry = input("Retry service (yes/no): ")
if (retry == "yes"):
class Retry(dbus.DBusException):
_dbus_error_name = "net.connman.vpn.Agent.Error.Retry"
@dbus.service.method("net.connman.vpn.Agent",
in_signature='', out_signature='')
def Cancel(self):
- print "Cancel"
+ print("Cancel")
def vpnNameOwnerChanged(proxy):
if proxy:
'net.connman.vpn.Manager')
vpn_manager.RegisterAgent(path)
except:
- print "vpn agent is not registered"
+ print("vpn agent is not registered")
else:
print("vpnd is disconnected from system bus")
vpn_manager = None
def print_usage():
- print "Usage:"
- print "For hidden service:"
- print "%s Name=<hidden service name> [SSID=<hidden ssid>]" % (sys.argv[0])
- print "For EAP/WPA input:"
- print "%s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0])
- print "For WISPr login, L2TP or PPTP input:"
- print "%s Username=<username> Password=<password>" % (sys.argv[0])
- print "For OpenConnect input:"
- print "%s Cookie=<string>" % (sys.argv[0])
- print "Help: %s help" % (sys.argv[0])
+ print("Usage:")
+ print("For hidden service:")
+ print("%s Name=<hidden service name> [SSID=<hidden ssid>]" % (sys.argv[0]))
+ print("For EAP/WPA input:")
+ print("%s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0]))
+ print("For WISPr login, L2TP or PPTP input:")
+ print("%s Username=<username> Password=<password>" % (sys.argv[0]))
+ print("For OpenConnect input:")
+ print("%s Cookie=<string>" % (sys.argv[0]))
+ print("Help: %s help" % (sys.argv[0]))
sys.exit(1)
if __name__ == '__main__':
vpn_object = VpnAgent(bus, vpn_path)
except:
vpn_manager = None
- print "net.connman.vpn is not present"
+ print("net.connman.vpn is not present")
if len(sys.argv) >= 2:
for arg in sys.argv[1:]:
try:
manager.RegisterAgent(path)
except:
- print "Cannot register connman agent."
+ print("Cannot register connman agent.")
if vpn_manager != None:
try:
properties = clock.GetProperties()
-for key in properties.keys():
+for key in list(properties.keys()):
if key in ["Timeservers"]:
list = ""
for val in properties[key]:
list = list + val + " "
- print "%s = [ %s]" % (key, list)
+ print("%s = [ %s]" % (key, list))
else:
- print "%s = %s" % (key, properties[key])
+ print("%s = %s" % (key, properties[key]))
state = manager.state()
-print "System is %s" % (states[state])
+print("System is %s" % (states[state]))
"net.connman.Manager")
if len(sys.argv) < 2:
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " state"
- print " services"
- print " autoconnect <service> [autoconnect]"
- print " connect <service>"
- print " disconnect <service>"
- print " remove <service>"
- print ""
- print " scan <type>"
- print " enable <type>"
- print " disable <type>"
- print " offlinemode [on|off]"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" state")
+ print(" services")
+ print(" autoconnect <service> [autoconnect]")
+ print(" connect <service>")
+ print(" disconnect <service>")
+ print(" remove <service>")
+ print("")
+ print(" scan <type>")
+ print(" enable <type>")
+ print(" disable <type>")
+ print(" offlinemode [on|off]")
sys.exit(1)
def print_services(services):
else:
favorite = " "
- if "Name" in properties.keys():
+ if "Name" in list(properties.keys()):
name = properties["Name"]
else:
name = "{" + properties["Type"] + "}"
- print "%s%s%s %-26s { %s }" % (favorite, autoconnect, state,
- name, identifier)
+ print("%s%s%s %-26s { %s }" % (favorite, autoconnect, state,
+ name, identifier))
if sys.argv[1] == "state":
properties = manager.GetProperties()
- print "System is %s" % (properties["State"])
+ print("System is %s" % (properties["State"]))
elif sys.argv[1] in ["services", "list", "show"]:
print_services(manager.GetServices())
elif sys.argv[1] in ["autoconnect", "autoconn"]:
if (len(sys.argv) < 3):
- print "Need at least service parameter"
+ print("Need at least service parameter")
sys.exit(1)
path = "/net/connman/service/" + sys.argv[2]
service.SetProperty("AutoConnect", autoconnect);
- print "Auto connect %s for %s" % (autoconnect, sys.argv[2])
+ print("Auto connect %s for %s" % (autoconnect, sys.argv[2]))
else:
properties = service.GetProperties()
- if "Name" in properties.keys():
+ if "Name" in list(properties.keys()):
name = properties["Name"]
else:
name = "{" + properties["Type"] + "}"
- if "AutoConnect" in properties.keys():
+ if "AutoConnect" in list(properties.keys()):
autoconnect = properties["AutoConnect"]
else:
autoconnect = dbus.Boolean(0)
- print "Auto connect %s for %s" % (autoconnect, name)
+ print("Auto connect %s for %s" % (autoconnect, name))
elif sys.argv[1] in ["connect", "conn"]:
if (len(sys.argv) < 3):
- print "Need at least service parameter"
+ print("Need at least service parameter")
sys.exit(1)
path = "/net/connman/service/" + sys.argv[2]
try:
service.Connect(timeout=60000)
- except dbus.DBusException, error:
- print "%s: %s" % (error._dbus_error_name, error.message)
+ except dbus.DBusException as error:
+ print("%s: %s" % (error._dbus_error_name, error.message))
elif sys.argv[1] in ["disconnect", "disc"]:
if (len(sys.argv) < 3):
- print "Need at least service parameter"
+ print("Need at least service parameter")
sys.exit(1)
path = "/net/connman/service/" + sys.argv[2]
try:
service.Disconnect()
- except dbus.DBusException, error:
- print "%s: %s" % (error._dbus_error_name, error.message)
+ except dbus.DBusException as error:
+ print("%s: %s" % (error._dbus_error_name, error.message))
elif sys.argv[1] in ["remove"]:
if (len(sys.argv) < 3):
- print "Need at least service parameter"
+ print("Need at least service parameter")
sys.exit(1)
path = "/net/connman/service/" + sys.argv[2]
properties = service.GetProperties()
if properties["Favorite"] == dbus.Boolean(0):
- print "Only favorite services can be removed"
+ print("Only favorite services can be removed")
sys.exit(1)
try:
service.Remove()
- except dbus.DBusException, error:
- print "%s: %s" % (error._dbus_error_name, error.message)
+ except dbus.DBusException as error:
+ print("%s: %s" % (error._dbus_error_name, error.message))
elif sys.argv[1] == "scan":
if len(sys.argv) == 3:
"net.connman.Technology")
technology.Scan()
else:
- print "'%s' takes two arguments" % sys.argv[1]
+ print("'%s' takes two arguments" % sys.argv[1])
elif sys.argv[1] == "enable":
if len(sys.argv) == 3:
"net.connman.Technology")
technology.SetProperty("Powered", True)
else:
- print "'%s' takes two arguments" % sys.argv[1]
+ print("'%s' takes two arguments" % sys.argv[1])
elif sys.argv[1] == "disable":
if len(sys.argv) == 3:
"net.connman.Technology")
technology.SetProperty("Powered", False)
else:
- print "'%s' takes two arguments" % sys.argv[1]
+ print("'%s' takes two arguments" % sys.argv[1])
elif sys.argv[1] in ["offlinemode", "flightmode"]:
elif sys.argv[2] == "off" or sys.argv[2] == "0" or sys.argv[2] == "no":
active = dbus.Boolean(0)
else:
- print "Use either 'on', '1', 'yes', 'off', '0' or 'no'"
+ print("Use either 'on', '1', 'yes', 'off', '0' or 'no'")
exit()
manager.SetProperty("OfflineMode", active)
elif len(sys.argv) == 2:
properties = manager.GetProperties()
- print "Offline mode is %s" % (properties["OfflineMode"])
+ print("Offline mode is %s" % (properties["OfflineMode"]))
else:
- print "'%s' takes max. two arguments" % sys.argv[1]
+ print("'%s' takes max. two arguments" % sys.argv[1])
else:
- print "Unknown command"
+ print("Unknown command")
return ''
def print_stats(stats):
- keys = stats.keys()
+ keys = list(stats.keys())
keys.sort()
for key in keys:
if hstr:
str = "%s (%s)" % (str, hstr)
- print str
+ print(str)
class Counter(dbus.service.Object):
@dbus.service.method("net.connman.Counter",
@dbus.service.method("net.connman.Counter",
in_signature='oa{sv}a{sv}', out_signature='')
def Usage(self, path, home, roaming):
- print "%s" % (path)
+ print("%s" % (path))
if len(home) > 0:
- print " Home"
+ print(" Home")
print_stats(home)
if len(roaming) > 0:
- print " Roaming"
+ print(" Roaming")
print_stats(roaming)
if __name__ == '__main__':
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["PrefixLength"]:
val += "%s" % (int(values[key]))
properties = manager.GetProperties()
-for key in properties.keys():
+for key in list(properties.keys()):
if key in ["OfflineMode", "SessionMode"]:
- print "%s" % (key)
+ print("%s" % (key))
if properties[key] == dbus.Boolean(1):
- print " true"
+ print(" true")
else:
- print " false"
+ print(" false")
else:
- print "%s" % (key)
- print " %s" % (properties[key])
+ print("%s" % (key))
+ print(" %s" % (properties[key]))
print ("Services")
services = manager.GetServices()
for (path, properties) in services:
- print " %s" % (path)
- for key in properties.keys():
+ print(" %s" % (path))
+ for key in list(properties.keys()):
if key in ["Available", "Remember", "Default",
"Favorite", "Immutable", "AutoConnect",
"LoginRequired",
else:
val = str(properties[key])
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
print ("Technologies")
technologies = manager.GetTechnologies()
for (path, properties) in technologies:
- print " %s" % (path)
- for key in properties.keys():
+ print(" %s" % (path))
+ for key in list(properties.keys()):
if key in ["Connected", "Powered", "Tethering"]:
if properties[key] == dbus.Boolean(1):
else:
val = properties[key]
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
dummy = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH),
'org.freedesktop.DBus.Introspectable')
-print dummy.Introspect()
+print(dummy.Introspect())
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["PrefixLength"]:
val += "%s" % (int(values[key]))
@dbus.service.method("net.connman.Notification",
in_signature='', out_signature='')
def Release(self):
- print "Release %s" % (self._object_path)
+ print("Release %s" % (self._object_path))
session_name = self._object_path.split('/')[-1]
self.app.release(session_name)
@dbus.service.method("net.connman.Notification",
in_signature='a{sv}', out_signature='')
def Update(self, settings):
- print "Update called at %s" % (self._object_path)
+ print("Update called at %s" % (self._object_path))
try:
- for key in settings.keys():
+ for key in list(settings.keys()):
if key in ["IPv4", "IPv6"]:
val = extract_values(settings[key])
elif key in ["AllowedBearers"]:
val = extract_list(settings[key])
else:
val = settings[key]
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
except:
- print "Exception:"
+ print("Exception:")
traceback.print_exc()
class SessionApplication(dbus.service.Object):
def connman_name_owner_changed(self, proxy):
try:
if proxy:
- print "connman appeared on D-Bus ", str(proxy)
+ print("connman appeared on D-Bus ", str(proxy))
bus = dbus.SystemBus()
self.manager = dbus.Interface(bus.get_object("net.connman", "/"),
"net.connman.Manager")
else:
- print "connman disappeared on D-Bus"
+ print("connman disappeared on D-Bus")
self.manager = None
- for s in self.sessions.keys():
+ for s in list(self.sessions.keys()):
self.sessions[s]['notify'].remove_from_connection()
self.sessions[s]['notify'] = None
return value
def find_session(self, session_name):
- if not session_name in self.sessions.keys():
+ if not session_name in list(self.sessions.keys()):
return None
return self.sessions[session_name]
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def CreateSession(self, session_name):
- print "Create session"
+ print("Create session")
s = self.find_session(session_name)
if s and s['session'] :
- print "Session %s already created-> drop reqest" % (session_name)
+ print("Session %s already created-> drop request" % (session_name))
return
try:
s['notify_path'] = self._object_path + "/" + session_name
s['notify'] = Notification(bus, self, s['notify_path'])
s['notify'].add_to_connection(bus, s['notify_path'])
- if not 'settings' in s.keys():
+ if not 'settings' in list(s.keys()):
s['settings'] = {};
s['session_path'] = self.manager.CreateSession(s['settings'], s['notify_path'])
- print "notify path %s" % (s['notify_path'])
- print "session path %s" % (s['session_path'])
+ print("notify path %s" % (s['notify_path']))
+ print("session path %s" % (s['session_path']))
s['session'] = dbus.Interface(bus.get_object("net.connman", s['session_path']),
"net.connman.Session")
self.sessions[session_name] = s
- except dbus.DBusException, e:
+ except dbus.DBusException as e:
if e.get_dbus_name() in ['net.connman.Error.Failed']:
- print e.get_dbus_message()
+ print(e.get_dbus_message())
return
traceback.print_exc()
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def DestroySession(self, session_name):
- print "Destroy session"
+ print("Destroy session")
s = self.find_session(session_name)
if s == None or s['session'] == None:
- print "The session is not running -> drop request"
+ print("The session is not running -> drop request")
return
try:
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def Connect(self, session_name):
- print "Connect session"
+ print("Connect session")
s = self.find_session(session_name)
if s == None or s['session'] == None:
- print "The session is not running -> drop request"
+ print("The session is not running -> drop request")
return
try:
s['session'].Connect()
- except dbus.DBusException, e:
+ except dbus.DBusException as e:
if e.get_dbus_name() in ['net.connman.Error.Failed']:
- print e.get_dbus_message()
+ print(e.get_dbus_message())
return
traceback.print_exc()
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def Disconnect(self, session_name):
- print "Disconnect session"
+ print("Disconnect session")
s = self.find_session(session_name)
if s == None or s['session'] == None:
- print "The session is not running -> drop request"
+ print("The session is not running -> drop request")
return
try:
s['session'].Disconnect()
- except dbus.DBusException, e:
+ except dbus.DBusException as e:
if e.get_dbus_name() in ['net.connman.Error.Failed']:
- print e.get_dbus_message()
+ print(e.get_dbus_message())
return
traceback.print_exc()
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def Change(self, session_name, key, value):
- print "Update session settings"
+ print("Update session settings")
s = self.find_session(session_name)
if s == None or s['session'] == None:
- print "The session is not running -> drop request"
+ print("The session is not running -> drop request")
return
try:
val = self.type_convert(key, value)
s['session'].Change(key, val)
- except dbus.DBusException, e:
+ except dbus.DBusException as e:
if e.get_dbus_name() in ['net.connman.Error.Failed']:
- print e.get_dbus_message()
+ print(e.get_dbus_message())
return
traceback.print_exc()
@dbus.service.method("com.example.TestSession",
in_signature='', out_signature='')
def Configure(self, session_name, key, value):
- print "Configure session settings"
+ print("Configure session settings")
s = self.find_session(session_name)
if s == None:
s = {}
s['notify_path'] = None
s['notify'] = None
- if not 'settings' in s.keys():
+ if not 'settings' in list(s.keys()):
s['settings'] = {};
s['session_path'] = None
s['session'] = None
self.sessions[session_name] = s
if s and s['session']:
- print "The session is running, use change -> drop request"
+ print("The session is running, use change -> drop request")
return
val = self.type_convert(key, value)
s['settings'][key] = val
def main():
if len(sys.argv) < 2:
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " enable"
- print " disable"
- print " create <app_path> <session_name>"
- print " destroy <app_path> <session_name>"
- print " connect <app_path> <session_name>"
- print " disconnect <app_path> <session_name>"
- print " change <app_path> <session_name> <key> <value>"
- print " configure <app_path> <session_name> <key> <value>"
- print ""
- print " run <app_path>"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" enable")
+ print(" disable")
+ print(" create <app_path> <session_name>")
+ print(" destroy <app_path> <session_name>")
+ print(" connect <app_path> <session_name>")
+ print(" disconnect <app_path> <session_name>")
+ print(" change <app_path> <session_name> <key> <value>")
+ print(" configure <app_path> <session_name> <key> <value>")
+ print("")
+ print(" run <app_path>")
sys.exit(1)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
return
if (len(sys.argv) < 3):
- print "Need test application path"
+ print("Need test application path")
sys.exit(1)
app_path = sys.argv[2]
elif sys.argv[1] == "change":
if len(sys.argv) < 5:
- print "Arguments missing"
+ print("Arguments missing")
sys.exit(1)
app.Change(sys.argv[3], sys.argv[4], sys.argv[5:])
elif sys.argv[1] == "configure":
if len(sys.argv) < 5:
- print "Arguments missing"
+ print("Arguments missing")
sys.exit(1)
app.Configure(sys.argv[3], sys.argv[4], sys.argv[5:])
else:
- print "Unknown command '%s'" % sys.argv[1]
+ print("Unknown command '%s'" % sys.argv[1])
sys.exit(1)
if __name__ == '__main__':
import dbus
if (len(sys.argv) < 2):
- print "Usage: %s <VPN connection id>" % (sys.argv[0])
+ print("Usage: %s <VPN connection id>" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
path = "/net/connman/vpn/connection/" + sys.argv[1]
-print "Attempting to connect VPN %s" % (path)
+print("Attempting to connect VPN %s" % (path))
connection = dbus.Interface(bus.get_object("net.connman.vpn", path),
"net.connman.vpn.Connection")
import dbus
if (len(sys.argv) < 1):
- print "Usage: %s <VPN connection id>" % (sys.argv[0])
+ print("Usage: %s <VPN connection id>" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
path = "/net/connman/vpn/connection/" + sys.argv[1]
-print "Attempting to disconnect VPN %s" % (path)
+print("Attempting to disconnect VPN %s" % (path))
connection = dbus.Interface(bus.get_object("net.connman.vpn", path),
"net.connman.vpn.Connection")
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["Servers", "Excludes"]:
val += extract_list(values[key])
path = entry[0]
properties = entry[1]
- print "[ %s ]" % (path)
+ print("[ %s ]" % (path))
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["IPv4", "IPv6" ]:
val = extract_values(properties[key])
elif key in ["Nameservers","ServerRoutes","UserRoutes"]:
val = extract_list(properties[key])
else:
val = str(properties[key])
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
- print
+ print()
def extract_values(values):
val = "{"
- for key in values.keys():
+ for key in list(values.keys()):
val += " " + key + "="
if key in ["Servers", "Excludes"]:
val += extract_list(values[key])
argc = len(sys.argv)
if (argc < 2):
- print "Usage: %s <VPN connection id> [<property name>] [<property values>]" % (sys.argv[0])
+ print("Usage: %s <VPN connection id> [<property name>] [<property values>]" % (sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
path = "/net/connman/vpn/connection/" + sys.argv[1]
-print "Attempting to connect VPN %s" % (path)
+print("Attempting to connect VPN %s" % (path))
connection = dbus.Interface(bus.get_object("net.connman.vpn", path),
"net.connman.vpn.Connection")
if (argc < 3):
properties = connection.GetProperties()
- for key in properties.keys():
+ for key in list(properties.keys()):
if key in ["IPv4", "IPv6" ]:
val = extract_values(properties[key])
elif key in ["Nameservers","ServerRoutes","UserRoutes"]:
val = extract_list(properties[key])
else:
val = str(properties[key])
- print " %s = %s" % (key, val)
+ print(" %s = %s" % (key, val))
elif (argc < 4):
try:
connection.ClearProperty(sys.argv[2])
- except dbus.DBusException, error:
- print "%s: %s" % (error._dbus_error_name, error.message)
+ except dbus.DBusException as error:
+ print("%s: %s" % (error._dbus_error_name, error.message))
else:
try:
connection.SetProperty(sys.argv[2], sys.argv[3])
- except dbus.DBusException, error:
- print "%s: %s" % (error._dbus_error_name, error.message)
+ except dbus.DBusException as error:
+ print("%s: %s" % (error._dbus_error_name, error.message))
+++ /dev/null
-/*
- *
- * Connection Manager
- *
- * Copyright (C) 2013-2014 BMW Car IT GmbH.
- *
- * 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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/genetlink.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <net/if.h>
-
-#include <string.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "../src/shared/netlink.h"
-
-#define NFGEN_DATA(nlh) ((void *)((char *)(nlh) + \
- NLMSG_ALIGN(sizeof(struct nfgenmsg))))
-#define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN))
-#define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) && \
- (nla)->nla_len >= sizeof(struct nlattr) && \
- (nla)->nla_len <= (len))
-#define NLA_NEXT(nla,attrlen) ((attrlen) -= NLA_ALIGN((nla)->nla_len), \
- (struct nlattr*)(((char*)(nla)) + \
- NLA_ALIGN((nla)->nla_len)))
-
-static GMainLoop *mainloop;
-
-static void do_debug(const char *str, void *user_data)
-{
- const char *prefix = user_data;
-
- printf("%s%s\n", prefix, str);
-}
-
-static void getlink_callback(unsigned int error, uint16_t type, const void *data,
- uint32_t len, void *user_data)
-{
- const struct ifinfomsg *ifi = data;
- struct rtattr *rta;
- int bytes;
- char ifname[IF_NAMESIZE];
- uint32_t index, flags;
-
- g_assert_cmpuint(error, ==, 0);
-
- bytes = len - NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
- memset(ifname, 0, sizeof(ifname));
-
- index = ifi->ifi_index;
- flags = ifi->ifi_flags;
-
- for (rta = IFLA_RTA(ifi); RTA_OK(rta, bytes);
- rta = RTA_NEXT(rta, bytes)) {
- switch (rta->rta_type) {
- case IFLA_IFNAME:
- if (RTA_PAYLOAD(rta) <= IF_NAMESIZE)
- strcpy(ifname, RTA_DATA(rta));
- break;
- }
- }
-
- printf("index=%d flags=0x%08x name=%s\n", index, flags, ifname);
-
- g_main_loop_quit(mainloop);
-}
-
-static void test_case_1(void)
-{
- struct netlink_info *netlink;
- struct ifinfomsg msg;
-
- netlink = netlink_new(NETLINK_ROUTE);
-
- printf("\n");
- netlink_set_debug(netlink, do_debug, "[NETLINK] ", NULL);
-
- memset(&msg, 0, sizeof(msg));
-
- netlink_send(netlink, RTM_GETLINK, NLM_F_DUMP, &msg, sizeof(msg),
- getlink_callback, NULL, NULL);
-
- mainloop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(mainloop);
- g_main_loop_unref(mainloop);
-
- netlink_destroy(netlink);
-}
-
-int main(int argc, char *argv[])
-{
- g_test_init(&argc, &argv, NULL);
-
- g_test_add_func("/netlink/Test case 1", test_case_1);
-
- return g_test_run();
-}
static bool parse_start_ts(const char *key, const char *value,
gpointer user_data, GError **error)
{
- GTimeVal time_val;
+ struct tm tm;
- if (!g_time_val_from_iso8601(value, &time_val))
- return false;
-
- option_start_ts = time_val.tv_sec;
+ strptime(value, "%FT%TZ", &tm);
+ option_start_ts = mktime(&tm);
return true;
}
BusName=net.connman.vpn
ExecStart=@sbindir@/connman-vpnd -n
StandardOutput=null
-CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
+CapabilityBoundingSet=CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID
ProtectHome=read-only
ProtectSystem=full
#define CONFIGMAINFILE CONFIGDIR "/connman-vpn.conf"
-#define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
-
static GMainLoop *main_loop = NULL;
static unsigned int __terminated = 0;
-static struct {
- unsigned int timeout_inputreq;
-} connman_vpn_settings = {
- .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
-};
-
-static GKeyFile *load_config(const char *file)
-{
- GError *err = NULL;
- GKeyFile *keyfile;
-
- keyfile = g_key_file_new();
-
- g_key_file_set_list_separator(keyfile, ',');
-
- if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
- if (err->code != G_FILE_ERROR_NOENT) {
- connman_error("Parsing %s failed: %s", file,
- err->message);
- }
-
- g_error_free(err);
- g_key_file_free(keyfile);
- return NULL;
- }
-
- return keyfile;
-}
-
-static void parse_config(GKeyFile *config, const char *file)
-{
- GError *error = NULL;
- int timeout;
-
- if (!config)
- return;
-
- DBG("parsing %s", file);
-
- timeout = g_key_file_get_integer(config, "General",
- "InputRequestTimeout", &error);
- if (!error && timeout >= 0)
- connman_vpn_settings.timeout_inputreq = timeout * 1000;
-
- g_clear_error(&error);
-}
-
-static int config_init(const char *file)
-{
- GKeyFile *config;
-
- config = load_config(file);
- parse_config(config, file);
- if (config)
- g_key_file_free(config);
-
- return 0;
-}
-
static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
*/
unsigned int connman_timeout_input_request(void)
{
- return connman_vpn_settings.timeout_inputreq;
+ return __vpn_settings_get_timeout_inputreq();
}
int main(int argc, char *argv[])
__connman_dbus_init(conn);
if (!option_config)
- config_init(CONFIGMAINFILE);
+ __vpn_settings_init(CONFIGMAINFILE);
else
- config_init(option_config);
+ __vpn_settings_init(option_config);
__connman_inotify_init();
__connman_agent_init();
__connman_inotify_cleanup();
__connman_dbus_cleanup();
__connman_log_cleanup(false);
+ __vpn_settings_cleanup();
dbus_connection_unref(conn);
OPT_L2G = 2,
OPT_L2 = 3,
OPT_PPPD = 4,
+ OPT_L2LNS = 5,
};
struct {
{ "L2TP.DefaultRoute", "defaultroute", OPT_L2, NULL, OPT_STRING },
{ "L2TP.FlowBit", "flow bit", OPT_L2, NULL, OPT_STRING },
{ "L2TP.TunnelRWS", "tunnel rws", OPT_L2, NULL, OPT_STRING },
- { "L2TP.Exclusive", "exclusive", OPT_L2, NULL, OPT_STRING },
+ { "L2TP.Exclusive", "exclusive", OPT_L2LNS, NULL, OPT_STRING },
{ "L2TP.Autodial", "autodial", OPT_L2, "yes", OPT_STRING },
{ "L2TP.Redial", "redial", OPT_L2, "yes", OPT_STRING },
{ "L2TP.RedialTimeout", "redial timeout", OPT_L2, "10", OPT_STRING },
{ "L2TP.ForceUserSpace", "force userspace", OPT_L2G, NULL, OPT_STRING },
{ "L2TP.ListenAddr", "listen-addr", OPT_L2G, NULL, OPT_STRING },
{ "L2TP.Rand Source", "rand source", OPT_L2G, NULL, OPT_STRING },
- { "L2TP.IPsecSaref", "ipsec saref", OPT_L2G, NULL, OPT_STRING },
+ { "L2TP.IPsecSaref", "ipsec saref", OPT_L2G, "no", OPT_STRING },
{ "L2TP.Port", "port", OPT_L2G, NULL, OPT_STRING },
{ "PPPD.EchoFailure", "lcp-echo-failure", OPT_PPPD, "0", OPT_STRING },
{ "PPPD.EchoInterval", "lcp-echo-interval", OPT_PPPD, "0", OPT_STRING },
DBG("authentication failure");
vpn_provider_set_string(provider, "L2TP.User", NULL);
- vpn_provider_set_string(provider, "L2TP.Password", NULL);
+ vpn_provider_set_string_hide_value(provider, "L2TP.Password",
+ NULL);
return VPN_STATE_AUTH_FAILURE;
}
l2tp_write_option(fd, "[global]", NULL);
l2tp_write_fields(provider, fd, OPT_L2G);
+ l2tp_write_option(fd, "[lns default]", NULL);
+ l2tp_write_fields(provider, fd, OPT_L2LNS);
+
l2tp_write_option(fd, "[lac l2tp]", NULL);
option = vpn_provider_get_string(provider, "Host");
static void request_input_reply(DBusMessage *reply, void *user_data)
{
struct request_input_reply *l2tp_reply = user_data;
+ struct l2tp_private_data *data;
const char *error = NULL;
char *username = NULL, *password = NULL;
char *key;
DBusMessageIter iter, dict;
+ int err;
DBG("provider %p", l2tp_reply->provider);
- if (!reply || dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
- if (reply)
- error = dbus_message_get_error_name(reply);
+ if (!reply)
+ goto done;
+
+ data = l2tp_reply->user_data;
+
+ err = vpn_agent_check_and_process_reply_error(reply,
+ l2tp_reply->provider, data->task, data->cb,
+ data->user_data);
+ if (err) {
+ /* Ensure cb is called only once */
+ data->cb = NULL;
+ data->user_data = NULL;
+ error = dbus_message_get_error_name(reply);
goto done;
}
connman_dbus_dict_open(&iter, &dict);
+ if (vpn_provider_get_authentication_errors(provider))
+ vpn_agent_append_auth_failure(&dict, provider, NULL);
+
vpn_agent_append_user_info(&dict, provider, "L2TP.User");
vpn_agent_append_host_and_name(&dict, provider);
int l2tp_fd, pppd_fd;
int err;
- if (!username || !password) {
+ if (!username || !*username || !password || !*password) {
DBG("Cannot connect username %s password %p",
username, password);
err = -EINVAL;
{
struct l2tp_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)
DBG("user %s password %p", username, password);
- if (!username || !password) {
+ if (!username || !*username || !password || !*password) {
struct l2tp_private_data *data;
data = g_try_new0(struct l2tp_private_data, 1);
static void l2tp_disconnect(struct vpn_provider *provider)
{
- vpn_provider_set_string(provider, "L2TP.Password", NULL);
+ if (!provider)
+ return;
+
+ vpn_provider_set_string_hide_value(provider, "L2TP.Password", NULL);
+
+ connman_agent_cancel(provider);
}
static struct vpn_driver vpn_driver = {
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/genetlink.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <libmnl/libmnl.h>
+
+#include "src/shared/mnlg.h"
+#include "wireguard.h"
+
+/* wireguard.h netlink uapi: */
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ WGPEER_A_PROTOCOL_VERSION,
+ __WGPEER_A_LAST
+};
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+
+/* wireguard-specific parts: */
+
+struct inflatable_buffer {
+ char *buffer;
+ char *next;
+ bool good;
+ size_t len;
+ size_t pos;
+};
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
+{
+ size_t len, expand_to;
+ char *new_buffer;
+
+ if (!buffer->good || !buffer->next) {
+ free(buffer->next);
+ buffer->good = false;
+ return 0;
+ }
+
+ len = strlen(buffer->next) + 1;
+
+ if (len == 1) {
+ free(buffer->next);
+ buffer->good = false;
+ return 0;
+ }
+
+ if (buffer->len - buffer->pos <= len) {
+ expand_to = max(buffer->len * 2, buffer->len + len + 1);
+ new_buffer = realloc(buffer->buffer, expand_to);
+ if (!new_buffer) {
+ free(buffer->next);
+ buffer->good = false;
+ return -errno;
+ }
+ memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
+ buffer->buffer = new_buffer;
+ buffer->len = expand_to;
+ }
+ memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
+ free(buffer->next);
+ buffer->good = false;
+ buffer->pos += len;
+ return 0;
+}
+
+static int parse_linkinfo(const struct nlattr *attr, void *data)
+{
+ struct inflatable_buffer *buffer = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
+ buffer->good = true;
+ return MNL_CB_OK;
+}
+
+static int parse_infomsg(const struct nlattr *attr, void *data)
+{
+ struct inflatable_buffer *buffer = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
+ return mnl_attr_parse_nested(attr, parse_linkinfo, data);
+ else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
+ buffer->next = strdup(mnl_attr_get_str(attr));
+ return MNL_CB_OK;
+}
+
+static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct inflatable_buffer *buffer = data;
+ int ret;
+
+ buffer->good = false;
+ buffer->next = NULL;
+ ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
+ if (ret != MNL_CB_OK)
+ return ret;
+ ret = add_next_to_inflatable_buffer(buffer);
+ if (ret < 0)
+ return ret;
+ if (nlh->nlmsg_type != NLMSG_DONE)
+ return MNL_CB_OK + 1;
+ return MNL_CB_OK;
+}
+
+static int fetch_device_names(struct inflatable_buffer *buffer)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer = NULL;
+ size_t message_len;
+ unsigned int portid, seq;
+ ssize_t len;
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+
+ ret = -ENOMEM;
+ rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
+ if (!rtnl_buffer)
+ goto cleanup;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ seq = time(NULL);
+ portid = mnl_socket_get_portid(nl);
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq;
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ message_len = nlh->nlmsg_len;
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+another:
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, buffer)) < 0) {
+ /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
+ * during the dump. That's unfortunate, but is pretty common on busy
+ * systems that are adding and removing tunnels all the time. Rather
+ * than retrying, potentially indefinitely, we just work with the
+ * partial results. */
+ if (errno != EINTR) {
+ ret = -errno;
+ goto cleanup;
+ }
+ }
+ if (len == MNL_CB_OK + 1)
+ goto another;
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+static int add_del_iface(const char *ifname, bool add)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer;
+ ssize_t len;
+ int ret;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ struct nlattr *nest;
+
+ rtnl_buffer = calloc(MNL_SOCKET_BUFFER_SIZE, 1);
+ if (!rtnl_buffer) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = add ? RTM_NEWLINK : RTM_DELLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (add ? NLM_F_CREATE | NLM_F_EXCL : 0);
+ nlh->nlmsg_seq = time(NULL);
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname);
+ nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
+ mnl_attr_put_strz(nlh, IFLA_INFO_KIND, WG_GENL_NAME);
+ mnl_attr_nest_end(nlh, nest);
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, nlh->nlmsg_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, MNL_SOCKET_BUFFER_SIZE)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if (mnl_cb_run(rtnl_buffer, len, nlh->nlmsg_seq, mnl_socket_get_portid(nl), NULL, NULL) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+int wg_set_device(wg_device *dev)
+{
+ int ret = 0;
+ wg_peer *peer = NULL;
+ wg_allowedip *allowedip = NULL;
+ struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg)
+ return -errno;
+
+again:
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
+
+ if (!peer) {
+ uint32_t flags = 0;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ flags |= WGDEVICE_F_REPLACE_PEERS;
+ if (flags)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
+ }
+ if (!dev->first_peer)
+ goto send;
+ peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
+ peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
+ for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
+ uint32_t flags = 0;
+
+ peer_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
+ if (!peer_nest)
+ goto toobig_peers;
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
+ goto toobig_peers;
+ if (peer->flags & WGPEER_REMOVE_ME)
+ flags |= WGPEER_F_REMOVE_ME;
+ if (!allowedip) {
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
+ goto toobig_peers;
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
+ goto toobig_peers;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
+ goto toobig_peers;
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
+ goto toobig_peers;
+ }
+ }
+ if (flags) {
+ if (!mnl_attr_put_u32_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
+ goto toobig_peers;
+ }
+ if (peer->first_allowedip) {
+ if (!allowedip)
+ allowedip = peer->first_allowedip;
+ allowedips_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
+ if (!allowedips_nest)
+ goto toobig_allowedips;
+ for (; allowedip; allowedip = allowedip->next_allowedip) {
+ allowedip_nest = mnl_attr_nest_start_check(nlh, MNL_SOCKET_BUFFER_SIZE, 0);
+ if (!allowedip_nest)
+ goto toobig_allowedips;
+ if (!mnl_attr_put_u16_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
+ goto toobig_allowedips;
+ if (allowedip->family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
+ goto toobig_allowedips;
+ } else if (allowedip->family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
+ goto toobig_allowedips;
+ }
+ if (!mnl_attr_put_u8_check(nlh, MNL_SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
+ goto toobig_allowedips;
+ mnl_attr_nest_end(nlh, allowedip_nest);
+ allowedip_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ allowedips_nest = NULL;
+ }
+
+ mnl_attr_nest_end(nlh, peer_nest);
+ peer_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, peers_nest);
+ peers_nest = NULL;
+ goto send;
+toobig_allowedips:
+ if (allowedip_nest)
+ mnl_attr_nest_cancel(nlh, allowedip_nest);
+ if (allowedips_nest)
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ mnl_attr_nest_end(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+toobig_peers:
+ if (peer_nest)
+ mnl_attr_nest_cancel(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+send:
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ if (peer)
+ goto again;
+
+out:
+ mnlg_socket_close(nlg);
+ errno = -ret;
+ return ret;
+}
+
+static int parse_allowedip(const struct nlattr *attr, void *data)
+{
+ wg_allowedip *allowedip = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGALLOWEDIP_A_UNSPEC:
+ break;
+ case WGALLOWEDIP_A_FAMILY:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ allowedip->family = mnl_attr_get_u16(attr);
+ break;
+ case WGALLOWEDIP_A_IPADDR:
+ if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
+ memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
+ else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
+ memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
+ break;
+ case WGALLOWEDIP_A_CIDR_MASK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U8))
+ allowedip->cidr = mnl_attr_get_u8(attr);
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_allowedips(const struct nlattr *attr, void *data)
+{
+ wg_peer *peer = data;
+ wg_allowedip *new_allowedip = calloc(1, sizeof(wg_allowedip));
+ int ret;
+
+ if (!new_allowedip)
+ return MNL_CB_ERROR;
+ if (!peer->first_allowedip)
+ peer->first_allowedip = peer->last_allowedip = new_allowedip;
+ else {
+ peer->last_allowedip->next_allowedip = new_allowedip;
+ peer->last_allowedip = new_allowedip;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
+ if (!ret)
+ return ret;
+ if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128))) {
+ errno = EAFNOSUPPORT;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
+bool wg_key_is_zero(const wg_key key)
+{
+ volatile uint8_t acc = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(wg_key); ++i) {
+ acc |= key[i];
+ __asm__ ("" : "=r" (acc) : "0" (acc));
+ }
+ return 1 & ((acc - 1) >> 8);
+}
+
+static int parse_peer(const struct nlattr *attr, void *data)
+{
+ wg_peer *peer = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGPEER_A_UNSPEC:
+ break;
+ case WGPEER_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGPEER_A_PRESHARED_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
+ if (!wg_key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ break;
+ case WGPEER_A_ENDPOINT: {
+ struct sockaddr *addr;
+
+ if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
+ break;
+ addr = mnl_attr_get_payload(attr);
+ if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
+ memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
+ else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
+ memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
+ break;
+ }
+ case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
+ break;
+ case WGPEER_A_LAST_HANDSHAKE_TIME:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
+ break;
+ case WGPEER_A_RX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->rx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_TX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->tx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_ALLOWEDIPS:
+ return mnl_attr_parse_nested(attr, parse_allowedips, peer);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_peers(const struct nlattr *attr, void *data)
+{
+ wg_device *device = data;
+ wg_peer *new_peer = calloc(1, sizeof(wg_peer));
+ int ret;
+
+ if (!new_peer)
+ return MNL_CB_ERROR;
+ if (!device->first_peer)
+ device->first_peer = device->last_peer = new_peer;
+ else {
+ device->last_peer->next_peer = new_peer;
+ device->last_peer = new_peer;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
+ if (!ret)
+ return ret;
+ if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
+ errno = ENXIO;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
+static int parse_device(const struct nlattr *attr, void *data)
+{
+ wg_device *device = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGDEVICE_A_UNSPEC:
+ break;
+ case WGDEVICE_A_IFINDEX:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->ifindex = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_IFNAME:
+ if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
+ strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
+ device->name[sizeof(device->name) - 1] = '\0';
+ }
+ break;
+ case WGDEVICE_A_PRIVATE_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
+ memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ break;
+ case WGDEVICE_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
+ memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
+ device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGDEVICE_A_LISTEN_PORT:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ device->listen_port = mnl_attr_get_u16(attr);
+ break;
+ case WGDEVICE_A_FWMARK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->fwmark = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_PEERS:
+ return mnl_attr_parse_nested(attr, parse_peers, device);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_device_cb(const struct nlmsghdr *nlh, void *data)
+{
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
+}
+
+static void coalesce_peers(wg_device *device)
+{
+ wg_peer *old_next_peer, *peer = device->first_peer;
+
+ while (peer && peer->next_peer) {
+ if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(wg_key))) {
+ peer = peer->next_peer;
+ continue;
+ }
+ if (!peer->first_allowedip) {
+ peer->first_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ } else {
+ peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ }
+ old_next_peer = peer->next_peer;
+ peer->next_peer = old_next_peer->next_peer;
+ free(old_next_peer);
+ }
+}
+
+int wg_get_device(wg_device **device, const char *device_name)
+{
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+try_again:
+ *device = calloc(1, sizeof(wg_device));
+ if (!*device)
+ return -errno;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg) {
+ wg_free_device(*device);
+ *device = NULL;
+ return -errno;
+ }
+
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, device_name);
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ coalesce_peers(*device);
+
+out:
+ if (nlg)
+ mnlg_socket_close(nlg);
+ if (ret) {
+ wg_free_device(*device);
+ if (ret == -EINTR)
+ goto try_again;
+ *device = NULL;
+ }
+ errno = -ret;
+ return ret;
+}
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *wg_list_device_names(void)
+{
+ struct inflatable_buffer buffer = { .len = MNL_SOCKET_BUFFER_SIZE };
+ int ret;
+
+ ret = -ENOMEM;
+ buffer.buffer = calloc(1, buffer.len);
+ if (!buffer.buffer)
+ goto err;
+
+ ret = fetch_device_names(&buffer);
+err:
+ errno = -ret;
+ if (errno) {
+ free(buffer.buffer);
+ return NULL;
+ }
+ return buffer.buffer;
+}
+
+int wg_add_device(const char *device_name)
+{
+ return add_del_iface(device_name, true);
+}
+
+int wg_del_device(const char *device_name)
+{
+ return add_del_iface(device_name, false);
+}
+
+void wg_free_device(wg_device *dev)
+{
+ wg_peer *peer, *np;
+ wg_allowedip *allowedip, *na;
+
+ if (!dev)
+ return;
+ for (peer = dev->first_peer, np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {
+ for (allowedip = peer->first_allowedip, na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
+ free(allowedip);
+ free(peer);
+ }
+ free(dev);
+}
+
+static void encode_base64(char dest[static 4], const uint8_t src[static 3])
+{
+ const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i)
+ dest[i] = input[i] + 'A'
+ + (((25 - input[i]) >> 8) & 6)
+ - (((51 - input[i]) >> 8) & 75)
+ - (((61 - input[i]) >> 8) & 15)
+ + (((62 - input[i]) >> 8) & 3);
+
+}
+
+void wg_key_to_base64(wg_key_b64_string base64, const wg_key key)
+{
+ unsigned int i;
+
+ for (i = 0; i < 32 / 3; ++i)
+ encode_base64(&base64[i * 4], &key[i * 3]);
+ encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
+ base64[sizeof(wg_key_b64_string) - 2] = '=';
+ base64[sizeof(wg_key_b64_string) - 1] = '\0';
+}
+
+static int decode_base64(const char src[static 4])
+{
+ int val = 0;
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+int wg_key_from_base64(wg_key key, const wg_key_b64_string base64)
+{
+ unsigned int i;
+ int val;
+ volatile uint8_t ret = 0;
+
+ if (strlen(base64) != sizeof(wg_key_b64_string) - 1 || base64[sizeof(wg_key_b64_string) - 2] != '=') {
+ errno = EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < 32 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ errno = EINVAL & ~((ret - 1) >> 8);
+out:
+ return -errno;
+}
+
+typedef int64_t fe[16];
+
+static __attribute__((noinline)) void memzero_explicit(void *s, size_t count)
+{
+ memset(s, 0, count);
+ __asm__ __volatile__("": :"r"(s) :"memory");
+}
+
+static void carry(fe o)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ o[(i + 1) % 16] += (i == 15 ? 38 : 1) * (o[i] >> 16);
+ o[i] &= 0xffff;
+ }
+}
+
+static void cswap(fe p, fe q, int b)
+{
+ int i;
+ int64_t t, c = ~(b - 1);
+
+ for (i = 0; i < 16; ++i) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+
+ memzero_explicit(&t, sizeof(t));
+ memzero_explicit(&c, sizeof(c));
+ memzero_explicit(&b, sizeof(b));
+}
+
+static void pack(uint8_t *o, const fe n)
+{
+ int i, j, b;
+ fe m, t;
+
+ memcpy(t, n, sizeof(t));
+ carry(t);
+ carry(t);
+ carry(t);
+ for (j = 0; j < 2; ++j) {
+ m[0] = t[0] - 0xffed;
+ for (i = 1; i < 15; ++i) {
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+ m[i - 1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+ b = (m[15] >> 16) & 1;
+ m[14] &= 0xffff;
+ cswap(t, m, 1 - b);
+ }
+ for (i = 0; i < 16; ++i) {
+ o[2 * i] = t[i] & 0xff;
+ o[2 * i + 1] = t[i] >> 8;
+ }
+
+ memzero_explicit(m, sizeof(m));
+ memzero_explicit(t, sizeof(t));
+ memzero_explicit(&b, sizeof(b));
+}
+
+static void add(fe o, const fe a, const fe b)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ o[i] = a[i] + b[i];
+}
+
+static void subtract(fe o, const fe a, const fe b)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ o[i] = a[i] - b[i];
+}
+
+static void multmod(fe o, const fe a, const fe b)
+{
+ int i, j;
+ int64_t t[31] = { 0 };
+
+ for (i = 0; i < 16; ++i) {
+ for (j = 0; j < 16; ++j)
+ t[i + j] += a[i] * b[j];
+ }
+ for (i = 0; i < 15; ++i)
+ t[i] += 38 * t[i + 16];
+ memcpy(o, t, sizeof(fe));
+ carry(o);
+ carry(o);
+
+ memzero_explicit(t, sizeof(t));
+}
+
+static void invert(fe o, const fe i)
+{
+ fe c;
+ int a;
+
+ memcpy(c, i, sizeof(c));
+ for (a = 253; a >= 0; --a) {
+ multmod(c, c, c);
+ if (a != 2 && a != 4)
+ multmod(c, c, i);
+ }
+ memcpy(o, c, sizeof(fe));
+
+ memzero_explicit(c, sizeof(c));
+}
+
+static void clamp_key(uint8_t *z)
+{
+ z[31] = (z[31] & 127) | 64;
+ z[0] &= 248;
+}
+
+void wg_generate_public_key(wg_key public_key, const wg_key private_key)
+{
+ int i, r;
+ uint8_t z[32];
+ fe a = { 1 }, b = { 9 }, c = { 0 }, d = { 1 }, e, f;
+
+ memcpy(z, private_key, sizeof(z));
+ clamp_key(z);
+
+ for (i = 254; i >= 0; --i) {
+ r = (z[i >> 3] >> (i & 7)) & 1;
+ cswap(a, b, r);
+ cswap(c, d, r);
+ add(e, a, c);
+ subtract(a, a, c);
+ add(c, b, d);
+ subtract(b, b, d);
+ multmod(d, e, e);
+ multmod(f, a, a);
+ multmod(a, c, a);
+ multmod(c, b, e);
+ add(e, a, c);
+ subtract(a, a, c);
+ multmod(b, a, a);
+ subtract(c, d, f);
+ multmod(a, c, (const fe){ 0xdb41, 1 });
+ add(a, a, d);
+ multmod(c, c, a);
+ multmod(a, d, f);
+ multmod(d, b, (const fe){ 9 });
+ multmod(b, e, e);
+ cswap(a, b, r);
+ cswap(c, d, r);
+ }
+ invert(c, c);
+ multmod(a, a, c);
+ pack(public_key, a);
+
+ memzero_explicit(&r, sizeof(r));
+ memzero_explicit(z, sizeof(z));
+ memzero_explicit(a, sizeof(a));
+ memzero_explicit(b, sizeof(b));
+ memzero_explicit(c, sizeof(c));
+ memzero_explicit(d, sizeof(d));
+ memzero_explicit(e, sizeof(e));
+ memzero_explicit(f, sizeof(f));
+}
+
+void wg_generate_private_key(wg_key private_key)
+{
+ wg_generate_preshared_key(private_key);
+ clamp_key(private_key);
+}
+
+void wg_generate_preshared_key(wg_key preshared_key)
+{
+ ssize_t ret;
+ size_t i;
+ int fd;
+#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
+ if (!getentropy(preshared_key, sizeof(wg_key)))
+ return;
+#endif
+#if defined(__NR_getrandom) && defined(__linux__)
+ if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key))
+ return;
+#endif
+ fd = open("/dev/urandom", O_RDONLY);
+ assert(fd >= 0);
+ for (i = 0; i < sizeof(wg_key); i += ret) {
+ ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
+ assert(ret > 0);
+ }
+ close(fd);
+}
* ConnMan VPN daemon
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include "vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define OC_MAX_READBUF_LEN 128
+
+enum opt_type {
+ OPT_STRING = 0,
+ OPT_BOOL = 1,
+};
struct {
- const char *cm_opt;
- const char *oc_opt;
- char has_value;
+ const char *cm_opt;
+ const char *oc_opt;
+ bool has_value;
+ bool enabled; // Use as task parameter
+ enum opt_type type;
} oc_options[] = {
- { "OpenConnect.NoCertCheck", "--no-cert-check", 0 },
+ { "OpenConnect.AllowSelfSignedCert", NULL, 1, 0, OPT_BOOL},
+ { "OpenConnect.AuthType", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.CACert", "--cafile", 1, 1, OPT_STRING},
+ { "OpenConnect.ClientCert", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.DisableIPv6", "--disable-ipv6", 1, 1, OPT_BOOL},
+ { "OpenConnect.PKCSClientCert", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.Protocol", "--protocol", 1, 1, OPT_STRING},
+ /* --no-cert-check is disabled in openconnect 8.02 */
+ { "OpenConnect.NoCertCheck", "--no-cert-check", 0, 0, OPT_BOOL},
+ { "OpenConnect.NoHTTPKeepalive", "--no-http-keepalive", 1, 1, OPT_BOOL},
+ { "OpenConnect.NoDTLS", "--no-dtls", 1, 1, OPT_BOOL},
+ { "OpenConnect.ServerCert", "--servercert", 1, 1, OPT_STRING},
+ { "OpenConnect.Usergroup", "--usergroup", 1, 1, OPT_STRING},
+ { "OpenConnect.UserPrivateKey", NULL, 1, 0, OPT_STRING},
+ { "VPN.MTU", "--base-mtu", 1, 1, OPT_STRING},
+};
+
+enum oc_connect_type {
+ OC_CONNECT_COOKIE = 0,
+ OC_CONNECT_COOKIE_WITH_USERPASS,
+ OC_CONNECT_USERPASS,
+ OC_CONNECT_PUBLICKEY,
+ OC_CONNECT_PKCS,
};
+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;
struct connman_task *task;
char *if_name;
+ char *dbus_sender;
vpn_provider_connect_cb_t cb;
void *user_data;
+ 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;
};
+static bool is_valid_protocol(const char* protocol)
+{
+ if (!protocol || !*protocol)
+ return false;
+
+ return g_strv_contains(protocols, protocol);
+}
+
+static void oc_connect_done(struct oc_private_data *data, int err)
+{
+ connman_info("data %p err %d/%s", data, err, strerror(err));
+
+ if (data && data->cb) {
+ vpn_provider_connect_cb_t cb = data->cb;
+ void *user_data = data->user_data;
+
+ /* Make sure we don't invoke this callback twice */
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+ }
+}
+
+static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
+{
+ int id = 0;
+
+ connman_info("data %p channel %p", data, 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) {
+ id = data->err_ch_id;
+ data->err_ch = NULL;
+ data->err_ch_id = 0;
+ } else {
+ return;
+ }
+
+ if (id)
+ g_source_remove(id);
+
+ g_io_channel_shutdown(channel, FALSE, NULL);
+ g_io_channel_unref(channel);
+}
+
static void free_private_data(struct oc_private_data *data)
{
+ connman_info("data %p", data);
+
+ if (!data || !data->provider)
+ return;
+
+ connman_info("provider %p", data->provider);
+
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ vpn_provider_unref(data->provider);
+
+ 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);
g_free(data->if_name);
g_free(data);
}
static int task_append_config_data(struct vpn_provider *provider,
struct connman_task *task)
{
- const char *option;
+ const char *option = NULL;
int i;
for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
- if (!oc_options[i].oc_opt)
+ if (!oc_options[i].oc_opt || !oc_options[i].enabled)
continue;
- option = vpn_provider_get_string(provider,
- oc_options[i].cm_opt);
- if (!option)
- continue;
+ if (oc_options[i].has_value) {
+ option = vpn_provider_get_string(provider,
+ oc_options[i].cm_opt);
+ if (!option)
+ continue;
+
+ /* Add boolean type values only if set as true. */
+ if (oc_options[i].type == OPT_BOOL) {
+ if (!vpn_provider_get_boolean(provider,
+ oc_options[i].cm_opt,
+ false))
+ continue;
+
+ /* No option is set for boolean type values. */
+ option = NULL;
+ }
+
+ /* Skip protocol if it is invalid. */
+ if (!g_strcmp0(oc_options[i].cm_opt,
+ "OpenConnect.Protocol")) {
+ if (!is_valid_protocol(option))
+ continue;
+ }
+ }
+
+ /*
+ * Add server certificate fingerprint only when self signed
+ * certificates are explicitly allowed. Using --servercert as
+ * parameter will accept any server with matching fingerprint,
+ * which would disregard the setting of AllowSelfSignedCert.
+ */
+ if (!g_strcmp0(oc_options[i].cm_opt,
+ "OpenConnect.ServerCert")) {
+ if (!vpn_provider_get_boolean(provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false))
+ continue;
+ }
if (connman_task_add_argument(task,
oc_options[i].oc_opt,
char *netmask = NULL, *gateway = NULL;
unsigned char prefix_len = 0;
struct connman_ipaddress *ipaddress;
+ struct oc_private_data *data;
+
+ connman_info("provider %p", provider);
+
+ data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
if (!provider) {
connman_error("No provider found");
+ oc_connect_done(data, ENOENT);
return VPN_STATE_FAILURE;
}
g_free(domain);
connman_ipaddress_free(ipaddress);
+ oc_connect_done(data, 0);
return VPN_STATE_CONNECT;
}
-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 ssize_t full_write(int fd, const void *buf, size_t len)
{
- const char *vpnhost, *vpncookie, *servercert, *mtu;
- int fd, err = 0, len;
+ ssize_t byte_write;
+
+ while (len) {
+ byte_write = write(fd, buf, len);
+ if (byte_write < 0) {
+ connman_error("failed to write config to openconnect: "
+ " %s\n", strerror(errno));
+ return byte_write;
+ }
+ len -= byte_write;
+ buf += byte_write;
+ }
- vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
- if (!vpnhost)
- vpnhost = vpn_provider_get_string(provider, "Host");
- vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
- servercert = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
+ return len;
+}
- if (!vpncookie || !servercert) {
- err = -EINVAL;
- goto done;
+static ssize_t write_data(int fd, const char *data)
+{
+ gchar *buf;
+ ssize_t len;
+
+ if (!data || !*data)
+ return -1;
+
+ buf = g_strdup_printf("%s\n", data);
+
+ len = full_write(fd, buf, strlen(buf));
+
+ g_free(buf);
+
+ return len;
+}
+
+static void oc_died(struct connman_task *task, int exit_code, void *user_data)
+{
+ struct oc_private_data *data = user_data;
+
+ connman_info("task %p data %p exit_code %d user_data %p", task, data,
+ exit_code, user_data);
+
+ if (!data)
+ return;
+
+ if (data->provider) {
+ connman_agent_cancel(data->provider);
+
+ if (task)
+ vpn_died(task, exit_code, data->provider);
}
- task_append_config_data(provider, task);
+ free_private_data(data);
+}
- connman_task_add_argument(task, "--servercert", servercert);
+static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ struct oc_private_data *data;
+ char *str;
+
+ data = user_data;
- mtu = vpn_provider_get_string(provider, "VPN.MTU");
+ if (data->out_ch != source)
+ return G_SOURCE_REMOVE;
- if (mtu)
- connman_task_add_argument(task, "--mtu", (char *)mtu);
+ if ((condition & G_IO_IN) &&
+ g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
- connman_task_add_argument(task, "--syslog", NULL);
- connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+ g_strchomp(str);
- connman_task_add_argument(task, "--script",
- SCRIPTDIR "/openconnect-script");
+ /* Only cookie is printed to stdout */
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Cookie", str);
- connman_task_add_argument(task, "--interface", if_name);
+ 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;
+
+ if (!strv || !str || !*str)
+ return false;
+
+ for (i = 0; strv[i]; i++) {
+ if (g_str_has_prefix(str, strv[i]))
+ return true;
+ }
+
+ return false;
+}
+
+static void clear_provider_credentials(struct vpn_provider *provider)
+{
+ const char *keys[] = { "OpenConnect.Username",
+ "OpenConnect.Password",
+ "OpenConnect.PKCSPassword",
+ "OpenConnect.Cookie",
+ NULL
+ };
+ int i;
+
+ connman_info("provider %p", provider);
+
+ for (i = 0; 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 int request_input_credentials(struct oc_private_data *data,
+ request_input_reply_cb_t cb);
+
+
+static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
+{
+ struct oc_private_data *data = user_data;
+ const char *password = NULL;
+ const char *key;
+ DBusMessageIter iter, dict;
+ int err;
+
+ connman_info("provider %p", data->provider);
+
+ if (!reply)
+ 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 */
+ data->cb = NULL;
+ data->user_data = NULL;
+ goto err;
+ }
+
+ if (!vpn_agent_check_reply_has_dict(reply))
+ goto err;
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
+ 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, &password);
+ vpn_provider_set_string_hide_value(data->provider, key,
+ password);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (data->connect_type != OC_CONNECT_PKCS || !password)
+ goto err;
+
+ if (write_data(data->fd_in, password) != 0) {
+ connman_error("openconnect failed to take PKCS pass phrase on"
+ " stdin");
+ goto err;
+ }
+
+ clear_provider_credentials(data->provider);
+
+ return;
+err:
+ oc_connect_done(data, EACCES);
+}
+
+static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
+ 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[] = {
+ "Failed to connect to",
+ "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";
+ char *str;
+ bool close = false;
+ int err = 0;
+
+ data = user_data;
+
+ if (!data)
+ return G_SOURCE_REMOVE;
+
+ if (source && data->err_ch != source)
+ return G_SOURCE_REMOVE;
+
+ 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';
+ }
+
+ connman_info("openconnect: %s", str);
+
+ if (err || !str || !*str) {
+ connman_info("error reading from openconnect");
+ } else if (g_str_has_prefix(str, server_key_hash)) {
+ const char *fingerprint;
+ int position;
+ bool allow_self_signed;
+
+ allow_self_signed = vpn_provider_get_boolean(
+ data->provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false);
+
+ if (allow_self_signed) {
+ position = strlen(server_key_hash) + 1;
+ fingerprint = g_strstrip(str + position);
+
+ connman_info("Set server key hash: \"%s\"",
+ fingerprint);
+
+ 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");
+
+ } 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;
+ 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;
+ } else if (strv_contains_prefix(conn_failures, str)) {
+ connman_warn("connection failed: %s", str);
+ err = ECONNREFUSED;
+ }
+
+ g_free(str);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ connman_info("Err channel termination");
+ close = true;
+ }
+
+ if (err) {
+ switch (err) {
+ case EACCES:
+ clear_provider_credentials(data->provider);
+ break;
+ case ECONNREFUSED:
+ /*
+ * This will trigger VPN_PROVIDER_ERROR_CONNECT_FAILED
+ * in vpn-provider.c:connect_cb().
+ */
+ default:
+ break;
+ }
+
+ 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)
+{
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ const char *vpnhost;
+ const char *vpncookie = NULL;
+ const char *username;
+ const char *password = NULL;
+ const char *certificate = NULL;
+ const char *private_key;
+ const char *setting_str;
+ bool setting;
+ bool use_stdout = false;
+ int fd_out = -1;
+ int fd_err;
+ int err = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ provider = data->provider;
+ task = data->task;
+
+ connman_info("provider %p task %p", provider, task);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+ break;
+ case OC_CONNECT_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;
+ }
+
+ connman_task_add_argument(task, "--cookieonly", NULL);
+ connman_task_add_argument(task, "--user", username);
+ connman_task_add_argument(task, "--passwd-on-stdin",
+ NULL);
+
+ /* Use stdout only when cookie is to be read. */
+ use_stdout = true;
+ } else {
+ connman_task_add_argument(task, "--cookie-on-stdin",
+ NULL);
+ }
+
+ 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;
+ }
+
+ connman_task_add_argument(task, "--user", username);
+ connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.ClientCert");
+ private_key = vpn_provider_get_string(provider,
+ "OpenConnect.UserPrivateKey");
+
+ if (!certificate || !private_key) {
+ err = -EACCES;
+ goto done;
+ }
+
+ 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;
+ }
+
+ connman_task_add_argument(task, "--certificate", certificate);
+
+ password = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSPassword");
+ /* Add password only if it is has been set */
+ if (!password || !g_strcmp0(password, "-"))
+ break;
+
+ connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ break;
+ }
+
+ vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ if (!vpnhost || !*vpnhost)
+ vpnhost = vpn_provider_get_string(provider, "Host");
+
+ task_append_config_data(provider, task);
+
+ /*
+ * 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.
+ */
+ 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);
+
+ setting = 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;
+ }
+
+ connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
+
+ connman_task_add_argument(task, "--interface", data->if_name);
connman_task_add_argument(task, (char *)vpnhost, NULL);
- err = connman_task_run(task, vpn_died, provider,
- &fd, NULL, NULL);
+ err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ?
+ &fd_out : NULL, &fd_err);
if (err < 0) {
- connman_error("openconnect failed to start");
err = -EIO;
goto done;
}
- len = strlen(vpncookie);
- if (write(fd, vpncookie, len) != (ssize_t)len ||
- write(fd, "\n", 1) != 1) {
- connman_error("openconnect failed to take cookie on stdin");
- err = -EIO;
+ 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, password) != 0) {
+ connman_error("openconnect failed to take PKCS "
+ "pass phrase on stdin");
+ err = -EIO;
+ }
+
+ break;
+ }
+
+ if (err) {
+ if (fd_out > 0)
+ close(fd_out);
+
+ if (fd_err > 0)
+ close(fd_err);
+
goto done;
}
+ 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 */
+ if (g_io_channel_set_encoding(data->err_ch, NULL, NULL) !=
+ G_IO_STATUS_NORMAL) {
+ close_io_channel(data, data->err_ch);
+ err = -EIO;
+ } else {
+ data->err_ch_id = g_io_add_watch(data->err_ch,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ (GIOFunc)io_channel_err_cb, data);
+ }
+
done:
- if (cb)
- cb(provider, user_data, err);
+ clear_provider_credentials(data->provider);
return err;
}
-static void request_input_append_informational(DBusMessageIter *iter,
- void *user_data)
+static void request_input_append(DBusMessageIter *iter,
+ const char *str_type, const char *str, void *user_data)
{
- const char *str;
+ const char *string;
- str = "string";
- connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &str);
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str_type);
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
- str = "informational";
- connman_dbus_dict_append_basic(iter, "Requirement", DBUS_TYPE_STRING,
- &str);
+ if (!user_data)
+ return;
+
+ string = user_data;
+ connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING,
+ &string);
+}
- str = user_data;
- connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
+static void request_input_append_informational(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "string", "informational", user_data);
}
static void request_input_append_mandatory(DBusMessageIter *iter,
void *user_data)
{
- char *str = "string";
+ request_input_append(iter, "string", "mandatory", user_data);
+}
- connman_dbus_dict_append_basic(iter, "Type",
- DBUS_TYPE_STRING, &str);
- str = "mandatory";
- connman_dbus_dict_append_basic(iter, "Requirement",
- DBUS_TYPE_STRING, &str);
+static void request_input_append_optional(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "string", "optional", user_data);
+}
+
+static void request_input_append_password(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "password", "mandatory", user_data);
+}
+
+static void request_input_append_to_dict(struct vpn_provider *provider,
+ DBusMessageIter *dict,
+ connman_dbus_append_cb_t function_cb, const char *key)
+{
+ const char *str;
+ bool immutable = false;
+
+ if (!provider || !dict || !function_cb || !key)
+ return;
+
+ str = vpn_provider_get_string(provider, key);
+ /* Ignore empty informational content */
+ if (!str && function_cb == request_input_append_informational)
+ return;
+
+ /* If value is "-", it is cleared by VPN agent */
+ if (!g_strcmp0(str, "-"))
+ str = NULL;
+
+ if (str)
+ immutable = vpn_provider_get_string_immutable(provider, key);
+
+ if (immutable) {
+ /* Hide immutable password types */
+ if (function_cb == request_input_append_password)
+ str = "********";
+
+ /* Send immutable as informational */
+ function_cb = request_input_append_informational;
+ }
+
+ connman_dbus_dict_append_dict(dict, key, function_cb,
+ str ? (void *)str : NULL);
}
-static void request_input_cookie_reply(DBusMessage *reply, void *user_data)
+static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
{
struct oc_private_data *data = user_data;
- char *cookie = NULL, *servercert = NULL, *vpnhost = NULL;
- char *key;
+ const char *cookie = NULL;
+ const char *servercert = NULL;
+ const char *vpnhost = NULL;
+ const char *username = NULL;
+ const char *password = NULL;
+ const char *pkcspassword = NULL;
+ const char *key;
DBusMessageIter iter, dict;
+ int err;
- DBG("provider %p", data->provider);
+ connman_info("provider %p", data->provider);
- if (!reply || dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ if (!reply)
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 */
+ data->cb = NULL;
+ data->user_data = NULL;
+ goto out;
+ }
+
if (!vpn_agent_check_reply_has_dict(reply))
goto err;
dbus_message_iter_get_basic(&value, &cookie);
vpn_provider_set_string_hide_value(data->provider,
key, cookie);
-
} else if (g_str_equal(key, "OpenConnect.ServerCert")) {
dbus_message_iter_next(&entry);
if (dbus_message_iter_get_arg_type(&entry)
break;
dbus_message_iter_get_basic(&value, &vpnhost);
vpn_provider_set_string(data->provider, key, vpnhost);
+ } else if (g_str_equal(key, "Username")) {
+ 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, &username);
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Username", username);
+ } else if (g_str_equal(key, "Password")) {
+ 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, &password);
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Password", password);
+ } else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
+ 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, &pkcspassword);
+ vpn_provider_set_string_hide_value(data->provider, key,
+ pkcspassword);
}
dbus_message_iter_next(&dict);
}
- if (!cookie || !servercert || !vpnhost)
- goto err;
-
- run_connect(data->provider, data->task, data->if_name, data->cb,
- data->user_data);
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ if (!cookie)
+ goto err;
+
+ break;
+ case OC_CONNECT_USERPASS:
+ /* fall through */
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ if (!username || !password)
+ goto err;
+
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ break; // This should not be reached.
+ case OC_CONNECT_PKCS:
+ if (!pkcspassword)
+ goto err;
+
+ break;
+ }
- free_private_data(data);
+ err = run_connect(data);
+ if (err != -EINPROGRESS)
+ goto err;
return;
err:
- vpn_provider_indicate_error(data->provider,
- VPN_PROVIDER_ERROR_AUTH_FAILED);
+ oc_connect_done(data, EACCES);
+out:
free_private_data(data);
}
-static int request_cookie_input(struct vpn_provider *provider,
- struct oc_private_data *data,
- const char *dbus_sender)
+static int request_input_credentials(struct oc_private_data *data,
+ request_input_reply_cb_t cb)
{
DBusMessage *message;
- const char *path, *agent_sender, *agent_path;
+ const char *path;
+ const char *agent_sender;
+ const char *agent_path;
+ const char *username;
DBusMessageIter iter;
DBusMessageIter dict;
- const char *str;
int err;
void *agent;
- agent = connman_agent_get_info(dbus_sender, &agent_sender,
- &agent_path);
- if (!provider || !agent || !agent_path)
+ if (!data || !cb)
+ return -ESRCH;
+
+ connman_info("provider %p", data->provider);
+
+ agent = connman_agent_get_info(data->dbus_sender,
+ &agent_sender, &agent_path);
+ if (!data->provider || !agent || !agent_path)
return -ESRCH;
message = dbus_message_new_method_call(agent_sender, agent_path,
dbus_message_iter_init_append(message, &iter);
- path = vpn_provider_get_path(provider);
- dbus_message_iter_append_basic(&iter,
- DBUS_TYPE_OBJECT_PATH, &path);
+ path = vpn_provider_get_path(data->provider);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
connman_dbus_dict_open(&iter, &dict);
- str = vpn_provider_get_string(provider, "OpenConnect.CACert");
- if (str)
- connman_dbus_dict_append_dict(&dict, "OpenConnect.CACert",
+ request_input_append_to_dict(data->provider, &dict,
request_input_append_informational,
- (void *)str);
-
- str = vpn_provider_get_string(provider, "OpenConnect.ClientCert");
- if (str)
- connman_dbus_dict_append_dict(&dict, "OpenConnect.ClientCert",
+ "OpenConnect.CACert");
+
+ /*
+ * For backwards compatibility add OpenConnect.ServerCert and
+ * OpenConnect.VPNHost as madnatory 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.
+ */
+ request_input_append_to_dict(data->provider, &dict,
+ data->connect_type == OC_CONNECT_COOKIE ?
+ request_input_append_optional :
request_input_append_informational,
- (void *)str);
-
- connman_dbus_dict_append_dict(&dict, "OpenConnect.ServerCert",
- request_input_append_mandatory, NULL);
+ "OpenConnect.ServerCert");
- connman_dbus_dict_append_dict(&dict, "OpenConnect.VPNHost",
- request_input_append_mandatory, NULL);
+ request_input_append_to_dict(data->provider, &dict,
+ data->connect_type == OC_CONNECT_COOKIE ?
+ request_input_append_optional :
+ request_input_append_informational,
+ "OpenConnect.VPNHost");
+
+ if (vpn_provider_get_authentication_errors(data->provider))
+ vpn_agent_append_auth_failure(&dict, data->provider, NULL);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_mandatory,
+ "OpenConnect.Cookie");
+ break;
+ /*
+ * The authentication is done with username and password to get the
+ * cookie for connection.
+ */
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ /* fallthrough */
+ case OC_CONNECT_USERPASS:
+ username = vpn_provider_get_string(data->provider,
+ "OpenConnect.Username");
+ vpn_agent_append_user_info(&dict, data->provider, username);
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ return -EINVAL;
+ case OC_CONNECT_PKCS:
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_informational,
+ "OpenConnect.PKCSClientCert");
- connman_dbus_dict_append_dict(&dict, "OpenConnect.Cookie",
- request_input_append_mandatory, NULL);
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_password,
+ "OpenConnect.PKCSPassword");
+ break;
+ }
- vpn_agent_append_host_and_name(&dict, provider);
+ vpn_agent_append_host_and_name(&dict, data->provider);
connman_dbus_dict_close(&iter, &dict);
- err = connman_agent_queue_message(provider, message,
- connman_timeout_input_request(),
- request_input_cookie_reply, data, agent);
+ err = connman_agent_queue_message(data->provider, message,
+ connman_timeout_input_request(), cb, data, agent);
- if (err < 0 && err != -EBUSY) {
- DBG("error %d sending agent request", err);
- dbus_message_unref(message);
+ dbus_message_unref(message);
+ if (err < 0 && err != -EBUSY) {
+ connman_error("cannot send agent request, error: %d", err);
return err;
}
- dbus_message_unref(message);
-
return -EINPROGRESS;
}
+static enum oc_connect_type get_authentication_type(
+ struct vpn_provider *provider)
+{
+ const char *auth;
+ enum oc_connect_type type;
+
+ auth = vpn_provider_get_string(provider, "OpenConnect.AuthType");
+ if (!auth)
+ goto out;
+
+ for (type = 0; connect_types[type]; type++) {
+ if (!g_strcmp0(auth, connect_types[type])) {
+ connman_info("auth type %d/%s", type,
+ connect_types[type]);
+ return type;
+ }
+ }
+
+out:
+ /* Default to cookie */
+ return OC_CONNECT_COOKIE;
+}
+
static int oc_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name,
vpn_provider_connect_cb_t cb,
const char *dbus_sender, void *user_data)
{
- const char *vpnhost, *vpncookie, *servercert;
+ struct oc_private_data *data;
+ const char *vpncookie;
+ const char *certificate;
+ const char *username;
+ const char *password;
+ const char *private_key;
int err;
- vpnhost = vpn_provider_get_string(provider, "Host");
- if (!vpnhost) {
- connman_error("Host not set; cannot enable VPN");
- return -EINVAL;
- }
+ connman_info("provider %p task %p", provider, task);
+
+ data = g_try_new0(struct oc_private_data, 1);
+ if (!data)
+ return -ENOMEM;
- vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
- servercert = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- if (!vpncookie || !servercert) {
- struct oc_private_data *data;
+ vpn_provider_set_plugin_data(provider, data);
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->if_name = g_strdup(if_name);
+ data->dbus_sender = g_strdup(dbus_sender);
+ data->cb = cb;
+ data->user_data = user_data;
+ data->connect_type = get_authentication_type(provider);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ if (!vpncookie || !g_strcmp0(vpncookie, "-"))
+ goto request_input;
+
+ 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, "-"))
+ goto request_input;
+
+ break;
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ /* Username and password must be set if cookie is missing */
+ if (!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, "-"))
+ goto request_input;
+ } else if (!g_strcmp0(vpncookie, "-")) {
+ goto request_input;
+ }
- data = g_try_new0(struct oc_private_data, 1);
- if (!data)
- return -ENOMEM;
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.ClientCert");
+ private_key = vpn_provider_get_string(provider,
+ "OpenConnect.UserPrivateKey");
- data->provider = provider;
- data->task = task;
- data->if_name = g_strdup(if_name);
- data->cb = cb;
- data->user_data = user_data;
+ if (!certificate || !private_key) {
+ connman_warn("missing certificate and/or private key");
+ oc_connect_done(data, EACCES);
+ free_private_data(data);
+ return -EACCES;
+ }
- err = request_cookie_input(provider, data, dbus_sender);
- if (err != -EINPROGRESS) {
- vpn_provider_indicate_error(data->provider,
- VPN_PROVIDER_ERROR_LOGIN_FAILED);
+ break;
+ case OC_CONNECT_PKCS:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.PKCSClientCert");
+ if (!certificate) {
+ connman_warn("missing PKCS certificate");
+ oc_connect_done(data, EACCES);
free_private_data(data);
+ return -EACCES;
}
- return err;
+
+ break;
}
- return run_connect(provider, task, if_name, cb, user_data);
+ return run_connect(data);
+
+request_input:
+ err = request_input_credentials(data, request_input_credentials_reply);
+ if (err != -EINPROGRESS) {
+ oc_connect_done(data, err);
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_LOGIN_FAILED);
+ free_private_data(data);
+ }
+
+ return err;
+}
+
+static void oc_disconnect(struct vpn_provider *provider)
+{
+ connman_info("provider %p", provider);
+
+ if (!provider)
+ return;
+
+ /*
+ * OpenConnect may be disconnect by timeout in connmand before running
+ * the openconnect process. In such case it is important to cancel the
+ * agent request to avoid having multiple ones visible.
+ */
+ connman_agent_cancel(provider);
}
static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
{
- const char *setting, *option;
+ const char *save_group;
+ const char *option;
int i;
- setting = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "OpenConnect.ServerCert", setting);
-
- setting = vpn_provider_get_string(provider,
- "OpenConnect.CACert");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "OpenConnect.CACert", setting);
-
- setting = vpn_provider_get_string(provider,
- "VPN.MTU");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "VPN.MTU", setting);
+ save_group = vpn_provider_get_save_group(provider);
for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
if (strncmp(oc_options[i].cm_opt, "OpenConnect.", 12) == 0) {
if (!option)
continue;
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
+ g_key_file_set_string(keyfile, save_group,
oc_options[i].cm_opt, option);
}
}
static int oc_error_code(struct vpn_provider *provider, int exit_code)
{
+ connman_info("%d", exit_code);
+
+ /* OpenConnect process return values are ambiguous in definition
+ * https://github.com/openconnect/openconnect/blob/master/main.c#L1693
+ * and it is safer not to rely on them. Login error cannot be
+ * differentiated from connection errors, e.g., when self signed
+ * certificate is rejected by user setting.
+ */
switch (exit_code) {
- case 1:
case 2:
- vpn_provider_set_string_hide_value(provider,
- "OpenConnect.Cookie", NULL);
+ /* Cookie has failed */
+ clear_provider_credentials(provider);
return VPN_PROVIDER_ERROR_LOGIN_FAILED;
+ case 1:
+ /* fall through */
default:
return VPN_PROVIDER_ERROR_UNKNOWN;
}
static struct vpn_driver vpn_driver = {
.notify = oc_notify,
.connect = oc_connect,
+ .disconnect = oc_disconnect,
.error_code = oc_error_code,
.save = oc_save,
.route_env_parse = oc_route_env_parse,
* ConnMan VPN daemon
*
* Copyright (C) 2010-2014 BMW Car IT GmbH.
+ * Copyright (C) 2016-2019 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
#include <stdio.h>
#include <net/if.h>
#include <linux/if_tun.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <glib.h>
#include <connman/task.h>
#include <connman/dbus.h>
#include <connman/ipconfig.h>
+#include <connman/agent.h>
+#include <connman/setting.h>
+#include <connman/vpn-dbus.h>
#include "../vpn-provider.h"
+#include "../vpn-agent.h"
#include "vpn.h"
+#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
{ "OpenVPN.CACert", "--ca", 1 },
{ "OpenVPN.Cert", "--cert", 1 },
{ "OpenVPN.Key", "--key", 1 },
- { "OpenVPN.MTU", "--mtu", 1 },
+ { "OpenVPN.MTU", "--tun-mtu", 1 },
{ "OpenVPN.NSCertType", "--ns-cert-type", 1 },
{ "OpenVPN.Proto", "--proto", 1 },
{ "OpenVPN.Port", "--port", 1 },
{ "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.Verb", "--verb", 1 },
};
+struct ov_private_data {
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ char *dbus_sender;
+ char *if_name;
+ vpn_provider_connect_cb_t cb;
+ void *user_data;
+ char *mgmt_path;
+ guint mgmt_timer_id;
+ guint mgmt_event_id;
+ GIOChannel *mgmt_channel;
+ int connect_attempts;
+ int failed_attempts_privatekey;
+};
+
+static void ov_connect_done(struct ov_private_data *data, int err)
+{
+ if (data && data->cb) {
+ vpn_provider_connect_cb_t cb = data->cb;
+ void *user_data = data->user_data;
+
+ /* Make sure we don't invoke this callback twice */
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+ }
+
+ if (!err)
+ data->failed_attempts_privatekey = 0;
+}
+
+static void free_private_data(struct ov_private_data *data)
+{
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ ov_connect_done(data, EIO);
+ vpn_provider_unref(data->provider);
+ g_free(data->dbus_sender);
+ g_free(data->if_name);
+ g_free(data->mgmt_path);
+ g_free(data);
+}
+
struct nameserver_entry {
int id;
char *nameserver;
char *address = NULL, *gateway = NULL, *peer = NULL, *netmask = NULL;
struct connman_ipaddress *ipaddress;
GSList *nameserver_list = NULL;
+ struct ov_private_data *data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
return VPN_STATE_FAILURE;
}
- if (strcmp(reason, "up"))
+ DBG("%p %s", vpn_provider_get_name(provider), reason);
+
+ if (strcmp(reason, "up")) {
+ ov_connect_done(data, EIO);
return VPN_STATE_DISCONNECT;
+ }
dbus_message_iter_recurse(&iter, &dict);
g_free(netmask);
connman_ipaddress_free(ipaddress);
+ ov_connect_done(data, 0);
return VPN_STATE_CONNECT;
}
if (!option)
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;
+
if (connman_task_add_argument(task,
ov_options[i].ov_opt,
- ov_options[i].has_value ? option : NULL) < 0) {
+ ov_options[i].has_value ? option : NULL) < 0)
return -EIO;
- }
+
}
return 0;
}
-static gboolean can_read_data(GIOChannel *chan,
- GIOCondition cond, gpointer data)
+static void close_management_interface(struct ov_private_data *data)
{
- void (*cbf)(const char *format, ...) = data;
- gchar *str;
- gsize size;
+ if (data->mgmt_path) {
+ if (unlink(data->mgmt_path) && errno != ENOENT)
+ connman_warn("Unable to unlink management socket %s: "
+ "%d", data->mgmt_path, errno);
+
+ g_free(data->mgmt_path);
+ data->mgmt_path = NULL;
+ }
- if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
- return FALSE;
+ if (data->mgmt_timer_id != 0) {
+ g_source_remove(data->mgmt_timer_id);
+ data->mgmt_timer_id = 0;
+ }
- g_io_channel_read_line(chan, &str, &size, NULL, NULL);
- cbf(str);
- g_free(str);
+ if (data->mgmt_event_id) {
+ g_source_remove(data->mgmt_event_id);
+ data->mgmt_event_id = 0;
+ }
- return TRUE;
+ if (data->mgmt_channel) {
+ g_io_channel_shutdown(data->mgmt_channel, FALSE, NULL);
+ g_io_channel_unref(data->mgmt_channel);
+ data->mgmt_channel = NULL;
+ }
}
-static int setup_log_read(int stdout_fd, int stderr_fd)
+static void ov_died(struct connman_task *task, int exit_code, void *user_data)
{
- GIOChannel *chan;
- int watch;
+ struct ov_private_data *data = user_data;
- chan = g_io_channel_unix_new(stdout_fd);
- g_io_channel_set_close_on_unref(chan, TRUE);
- watch = g_io_add_watch(chan, G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
- can_read_data, connman_debug);
- g_io_channel_unref(chan);
+ /* Cancel any pending agent requests */
+ connman_agent_cancel(data->provider);
- if (watch == 0)
- return -EIO;
+ close_management_interface(data);
- chan = g_io_channel_unix_new(stderr_fd);
- g_io_channel_set_close_on_unref(chan, TRUE);
- watch = g_io_add_watch(chan, G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
- can_read_data, connman_error);
- g_io_channel_unref(chan);
+ vpn_died(task, exit_code, data->provider);
- return watch == 0? -EIO : 0;
+ free_private_data(data);
}
-static int ov_connect(struct vpn_provider *provider,
- struct connman_task *task, const char *if_name,
- vpn_provider_connect_cb_t cb, const char *dbus_sender,
- void *user_data)
+static int run_connect(struct ov_private_data *data,
+ vpn_provider_connect_cb_t cb, void *user_data)
{
+ struct vpn_provider *provider = data->provider;
+ struct connman_task *task = data->task;
const char *option;
- int stdout_fd, stderr_fd;
int err = 0;
- option = vpn_provider_get_string(provider, "Host");
- if (!option) {
- connman_error("Host not set; cannot enable VPN");
- return -EINVAL;
- }
-
- task_append_config_data(provider, task);
-
option = vpn_provider_get_string(provider, "OpenVPN.ConfigFile");
if (!option) {
/*
connman_task_add_argument(task, "--client", NULL);
}
+ if (data->mgmt_path) {
+ connman_task_add_argument(task, "--management", NULL);
+ connman_task_add_argument(task, data->mgmt_path, NULL);
+ connman_task_add_argument(task, "unix", NULL);
+ connman_task_add_argument(task, "--management-query-passwords",
+ NULL);
+ connman_task_add_argument(task, "--auth-retry", "interact");
+ }
+
+ connman_task_add_argument(task, "--syslog", NULL);
+
connman_task_add_argument(task, "--script-security", "2");
connman_task_add_argument(task, "--up",
connman_task_add_argument(task, "CONNMAN_PATH",
connman_task_get_path(task));
- connman_task_add_argument(task, "--dev", if_name);
+ connman_task_add_argument(task, "--dev", data->if_name);
option = vpn_provider_get_string(provider, "OpenVPN.DeviceType");
if (option) {
connman_task_add_argument(task, "--dev-type", option);
* 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 accessable through the tunnel anymore and so we end up
+ * 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");
- err = connman_task_run(task, vpn_died, provider,
- NULL, &stdout_fd, &stderr_fd);
+ /*
+ * 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
+ * reconnecting as succesful one. The latter causes trouble if the
+ * retries are not limited to 1 (no retry) as the interface is up and
+ * connman regards it as the default route and network ceases to work,
+ * including DNS.
+ */
+ option = vpn_provider_get_string(provider, "OpenVPN.Proto");
+ if (option && g_str_has_prefix(option, "tcp"))
+ connman_task_add_argument(task, "--connect-retry-max", "1");
+
+ err = connman_task_run(task, ov_died, data, NULL, NULL, NULL);
if (err < 0) {
+ data->cb = NULL;
+ data->user_data = NULL;
connman_error("openvpn failed to start");
- err = -EIO;
- goto done;
+ return -EIO;
+ } else {
+ /* This lets the caller know that the actual result of
+ * the operation will be reported to the callback */
+ return -EINPROGRESS;
+ }
+}
+
+static void ov_quote_credential(GString *line, const char *cred)
+{
+ if (!line)
+ return;
+
+ g_string_append_c(line, '"');
+
+ while (*cred != '\0') {
+
+ switch (*cred) {
+ case ' ':
+ case '"':
+ case '\\':
+ g_string_append_c(line, '\\');
+ break;
+ default:
+ break;
+ }
+
+ g_string_append_c(line, *cred++);
+ }
+
+ g_string_append_c(line, '"');
+}
+
+static void ov_return_credentials(struct ov_private_data *data,
+ const char *username, const char *password)
+{
+ GString *reply_string;
+ gchar *reply;
+ gsize len;
+
+ reply_string = g_string_new(NULL);
+
+ g_string_append(reply_string, "username \"Auth\" ");
+ ov_quote_credential(reply_string, username);
+ g_string_append_c(reply_string, '\n');
+
+ g_string_append(reply_string, "password \"Auth\" ");
+ ov_quote_credential(reply_string, password);
+ g_string_append_c(reply_string, '\n');
+
+ len = reply_string->len;
+ reply = g_string_free(reply_string, FALSE);
+
+ g_io_channel_write_chars(data->mgmt_channel, reply, len, NULL, NULL);
+ g_io_channel_flush(data->mgmt_channel, NULL);
+
+ memset(reply, 0, len);
+ g_free(reply);
+}
+
+static void ov_return_private_key_password(struct ov_private_data *data,
+ const char *privatekeypass)
+{
+ GString *reply_string;
+ gchar *reply;
+ gsize len;
+
+ reply_string = g_string_new(NULL);
+
+ g_string_append(reply_string, "password \"Private Key\" ");
+ ov_quote_credential(reply_string, privatekeypass);
+ g_string_append_c(reply_string, '\n');
+
+ len = reply_string->len;
+ reply = g_string_free(reply_string, FALSE);
+
+ g_io_channel_write_chars(data->mgmt_channel, reply, len, NULL, NULL);
+ g_io_channel_flush(data->mgmt_channel, NULL);
+
+ memset(reply, 0, len);
+ g_free(reply);
+}
+
+static void request_input_append_informational(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "string";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "informational";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_mandatory(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "string";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "mandatory";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_password(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "password";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "mandatory";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_credentials_reply(DBusMessage *reply,
+ void *user_data)
+{
+ struct ov_private_data *data = user_data;
+ char *username = NULL;
+ char *password = NULL;
+ char *key;
+ DBusMessageIter iter, dict;
+ DBusError error;
+ int err = 0;
+
+ connman_info("provider %p", data->provider);
+
+ /*
+ * When connmand calls disconnect because of connection timeout no
+ * reply is received.
+ */
+ if (!reply) {
+ err = ENOENT;
+ goto err;
+ }
+
+ dbus_error_init(&error);
+
+ 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 */
+ data->cb = NULL;
+ data->user_data = NULL;
+ return;
+ }
+
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
+ goto err;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ if (g_str_equal(key, "OpenVPN.Password")) {
+ 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, &password);
+ vpn_provider_set_string_hide_value(data->provider,
+ key, password);
+
+ } else if (g_str_equal(key, "OpenVPN.Username")) {
+ 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, &username);
+ vpn_provider_set_string_hide_value(data->provider,
+ key, username);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (!password || !username) {
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ err = EACCES;
+ goto err;
+ }
+
+ ov_return_credentials(data, username, password);
+
+ return;
+
+err:
+ ov_connect_done(data, err);
+}
+
+static int request_credentials_input(struct ov_private_data *data)
+{
+ DBusMessage *message;
+ const char *path, *agent_sender, *agent_path;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ int err;
+ void *agent;
+
+ agent = connman_agent_get_info(data->dbus_sender, &agent_sender,
+ &agent_path);
+ if (!agent || !agent_path)
+ return -ESRCH;
+
+ message = dbus_message_new_method_call(agent_sender, agent_path,
+ VPN_AGENT_INTERFACE,
+ "RequestInput");
+ if (!message)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+
+ path = vpn_provider_get_path(data->provider);
+ dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_OBJECT_PATH, &path);
+
+ connman_dbus_dict_open(&iter, &dict);
+
+ if (vpn_provider_get_authentication_errors(data->provider))
+ vpn_agent_append_auth_failure(&dict, data->provider, NULL);
+
+ /* Request temporary properties to pass on to openvpn */
+ connman_dbus_dict_append_dict(&dict, "OpenVPN.Username",
+ request_input_append_mandatory, NULL);
+
+ connman_dbus_dict_append_dict(&dict, "OpenVPN.Password",
+ request_input_append_password, NULL);
+
+ vpn_agent_append_host_and_name(&dict, data->provider);
+
+ connman_dbus_dict_close(&iter, &dict);
+
+ err = connman_agent_queue_message(data->provider, message,
+ connman_timeout_input_request(),
+ request_input_credentials_reply, data, agent);
+
+ if (err < 0 && err != -EBUSY) {
+ connman_error("error %d sending agent request", err);
+ dbus_message_unref(message);
+
+ return err;
+ }
+
+ dbus_message_unref(message);
+
+ return -EINPROGRESS;
+}
+
+static void request_input_private_key_reply(DBusMessage *reply,
+ void *user_data)
+{
+ struct ov_private_data *data = user_data;
+ const char *privatekeypass = NULL;
+ const char *key;
+ DBusMessageIter iter, dict;
+ DBusError error;
+ int err = 0;
+
+ connman_info("provider %p", data->provider);
+
+ /*
+ * When connmand calls disconnect because of connection timeout no
+ * reply is received.
+ */
+ if (!reply) {
+ err = ENOENT;
+ goto err;
+ }
+
+ dbus_error_init(&error);
+
+ 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 */
+ data->cb = NULL;
+ data->user_data = NULL;
+ return;
+ }
+
+ if (!vpn_agent_check_reply_has_dict(reply)) {
+ err = ENOENT;
+ goto err;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ if (g_str_equal(key, "OpenVPN.PrivateKeyPassword")) {
+ 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, &privatekeypass);
+ vpn_provider_set_string_hide_value(data->provider,
+ key, privatekeypass);
+
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (!privatekeypass) {
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+
+ err = EACCES;
+ goto err;
+ }
+
+ ov_return_private_key_password(data, privatekeypass);
+
+ return;
+
+err:
+ ov_connect_done(data, err);
+}
+
+static int request_private_key_input(struct ov_private_data *data)
+{
+ DBusMessage *message;
+ const char *path, *agent_sender, *agent_path;
+ const char *privatekeypass;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ int err;
+ void *agent;
+
+ /*
+ * First check if this is the second attempt to get the key within
+ * this connection. In such case there has been invalid Private Key
+ * Password and it must be reset, and queried from user.
+ */
+ if (data->failed_attempts_privatekey) {
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenVPN.PrivateKeyPassword", NULL);
+ } else {
+ /* If the encrypted Private key password is kept in memory and
+ * use it first. If authentication fails this is cleared,
+ * likewise it is when connman-vpnd is restarted.
+ */
+ privatekeypass = vpn_provider_get_string(data->provider,
+ "OpenVPN.PrivateKeyPassword");
+ if (privatekeypass) {
+ ov_return_private_key_password(data, privatekeypass);
+ goto out;
+ }
+ }
+
+ agent = connman_agent_get_info(data->dbus_sender, &agent_sender,
+ &agent_path);
+ if (!agent || !agent_path)
+ return -ESRCH;
+
+ message = dbus_message_new_method_call(agent_sender, agent_path,
+ VPN_AGENT_INTERFACE,
+ "RequestInput");
+ if (!message)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+
+ path = vpn_provider_get_path(data->provider);
+ dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_OBJECT_PATH, &path);
+
+ connman_dbus_dict_open(&iter, &dict);
+
+ connman_dbus_dict_append_dict(&dict, "OpenVPN.PrivateKeyPassword",
+ request_input_append_password, NULL);
+
+ vpn_agent_append_host_and_name(&dict, data->provider);
+
+ /* Do not allow to store or retrieve the encrypted Private Key pass */
+ vpn_agent_append_allow_credential_storage(&dict, false);
+ vpn_agent_append_allow_credential_retrieval(&dict, false);
+
+ /*
+ * Indicate to keep credentials, the enc Private Key password should
+ * not affect the credential storing.
+ */
+ vpn_agent_append_keep_credentials(&dict, true);
+
+ connman_dbus_dict_append_dict(&dict, "Enter Private Key password",
+ request_input_append_informational, NULL);
+
+ connman_dbus_dict_close(&iter, &dict);
+
+ err = connman_agent_queue_message(data->provider, message,
+ connman_timeout_input_request(),
+ request_input_private_key_reply, data, agent);
+
+ if (err < 0 && err != -EBUSY) {
+ connman_error("error %d sending agent request", err);
+ dbus_message_unref(message);
+
+ return err;
+ }
+
+ dbus_message_unref(message);
+
+out:
+ return -EINPROGRESS;
+}
+
+static gboolean ov_management_handle_input(GIOChannel *source,
+ GIOCondition condition, gpointer user_data)
+{
+ struct ov_private_data *data = user_data;
+ char *str = NULL;
+ int err = 0;
+ bool close = false;
+
+ if (condition & G_IO_IN) {
+ /*
+ * Just return if line is not read and str is not allocated.
+ * Condition check handles closing of the channel later.
+ */
+ if (g_io_channel_read_line(source, &str, NULL, NULL, NULL) !=
+ G_IO_STATUS_NORMAL)
+ return true;
+
+ str[strlen(str) - 1] = '\0';
+ connman_warn("openvpn request %s", str);
+
+ if (g_str_has_prefix(str, ">PASSWORD:Need 'Auth'")) {
+ /*
+ * Request credentials from the user
+ */
+ err = request_credentials_input(data);
+ if (err != -EINPROGRESS)
+ close = true;
+ } else if (g_str_has_prefix(str,
+ ">PASSWORD:Need 'Private Key'")) {
+ err = request_private_key_input(data);
+ if (err != -EINPROGRESS)
+ close = true;
+ } else if (g_str_has_prefix(str,
+ ">PASSWORD:Verification Failed: 'Auth'")) {
+ /*
+ * Add error only, state change indication causes
+ * signal to be sent, which is not desired when
+ * OpenVPN is in interactive mode.
+ */
+ vpn_provider_add_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ /*
+ * According to the OpenVPN manual about management interface
+ * https://openvpn.net/community-resources/management-interface/
+ * this should be received but it does not seem to be reported
+ * when decrypting private key fails. This requires following
+ * patch for OpenVPN (at least <= 2.4.5) in order to work:
+ * https://git.sailfishos.org/mer-core/openvpn/blob/
+ * 4f4b4af116292a207416c8a990392e35a6fc41af/rpm/privatekey-
+ * passphrase-handling.diff
+ */
+ } else if (g_str_has_prefix(str, ">PASSWORD:Verification "
+ "Failed: 'Private Key'")) {
+ data->failed_attempts_privatekey++;
+ }
+
+ g_free(str);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ connman_warn("Management channel termination");
+ close = true;
}
- err = setup_log_read(stdout_fd, stderr_fd);
-done:
- if (cb)
- cb(provider, user_data, err);
+ if (close)
+ close_management_interface(data);
+
+ return true;
+}
+
+static int ov_management_connect_timer_cb(gpointer user_data)
+{
+ struct ov_private_data *data = user_data;
+
+ if (!data->mgmt_channel) {
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd >= 0) {
+ struct sockaddr_un remote;
+ int err;
+
+ memset(&remote, 0, sizeof(remote));
+ remote.sun_family = AF_UNIX;
+ g_strlcpy(remote.sun_path, data->mgmt_path,
+ sizeof(remote.sun_path));
+
+ err = connect(fd, (struct sockaddr *)&remote,
+ sizeof(remote));
+ if (err == 0) {
+ data->mgmt_channel = g_io_channel_unix_new(fd);
+ data->mgmt_event_id =
+ g_io_add_watch(data->mgmt_channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ ov_management_handle_input,
+ data);
+
+ connman_warn("Connected management socket");
+ data->mgmt_timer_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+ close(fd);
+ }
+ }
+
+ data->connect_attempts++;
+ if (data->connect_attempts > 30) {
+ connman_warn("Unable to connect management socket");
+ data->mgmt_timer_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static int ov_connect(struct vpn_provider *provider,
+ struct connman_task *task, const char *if_name,
+ vpn_provider_connect_cb_t cb, const char *dbus_sender,
+ void *user_data)
+{
+ const char *tmpdir;
+ struct ov_private_data *data;
+
+ data = g_try_new0(struct ov_private_data, 1);
+ if (!data)
+ return -ENOMEM;
+
+ vpn_provider_set_plugin_data(provider, data);
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->dbus_sender = g_strdup(dbus_sender);
+ data->if_name = g_strdup(if_name);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ /*
+ * We need to use the management interface to provide
+ * the user credentials and password for decrypting private key.
+ */
+
+ /* Use env TMPDIR for creating management socket, fall back to /tmp */
+ tmpdir = getenv("TMPDIR");
+ if (!tmpdir || !*tmpdir)
+ tmpdir = "/tmp";
+
+ /* Set up the path for the management interface */
+ data->mgmt_path = g_strconcat(tmpdir, "/connman-vpn-management-",
+ vpn_provider_get_ident(provider), NULL);
+ if (unlink(data->mgmt_path) != 0 && errno != ENOENT) {
+ connman_warn("Unable to unlink management socket %s: %d",
+ data->mgmt_path, errno);
+ }
+
+ data->mgmt_timer_id = g_timeout_add(200,
+ ov_management_connect_timer_cb, data);
+
+ task_append_config_data(provider, task);
+
+ return run_connect(data, cb, user_data);
+}
+
+static void ov_disconnect(struct vpn_provider *provider)
+{
+ if (!provider)
+ return;
+
+ connman_agent_cancel(provider);
- return err;
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_DISCONNECT);
}
static int ov_device_flags(struct vpn_provider *provider)
}
if (!g_str_equal(option, "tun")) {
- connman_warn("bad OpenVPN.DeviceType value, falling back to tun");
+ connman_warn("bad OpenVPN.DeviceType value "
+ "falling back to tun");
}
return IFF_TUN;
}
static int ov_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;
static struct vpn_driver vpn_driver = {
.notify = ov_notify,
.connect = ov_connect,
+ .disconnect = ov_disconnect,
.save = ov_save,
.device_flags = ov_device_flags,
.route_env_parse = ov_route_env_parse,
DBG("authentication failure");
vpn_provider_set_string(provider, "PPTP.User", NULL);
- vpn_provider_set_string(provider, "PPTP.Password", NULL);
+ vpn_provider_set_string_hide_value(provider, "PPTP.Password",
+ NULL);
return VPN_STATE_AUTH_FAILURE;
}
static void request_input_reply(DBusMessage *reply, void *user_data)
{
struct request_input_reply *pptp_reply = user_data;
+ struct pptp_private_data *data;
const char *error = NULL;
char *username = NULL, *password = NULL;
char *key;
DBusMessageIter iter, dict;
+ int err;
DBG("provider %p", pptp_reply->provider);
- if (!reply || dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
- if (reply)
- error = dbus_message_get_error_name(reply);
+ if (!reply)
+ goto done;
+
+ data = pptp_reply->user_data;
+
+ err = vpn_agent_check_and_process_reply_error(reply,
+ pptp_reply->provider, data->task, data->cb,
+ data->user_data);
+ if (err) {
+ /* Ensure cb is called only once */
+ data->cb = NULL;
+ data->user_data = NULL;
+ error = dbus_message_get_error_name(reply);
goto done;
}
connman_dbus_dict_open(&iter, &dict);
+ if (vpn_provider_get_authentication_errors(provider))
+ vpn_agent_append_auth_failure(&dict, provider, NULL);
+
vpn_agent_append_user_info(&dict, provider, "PPTP.User");
vpn_agent_append_host_and_name(&dict, provider);
char *str;
int err, i;
- host = vpn_provider_get_string(provider, "Host");
- if (!host) {
- connman_error("Host not set; cannot enable VPN");
- err = -EINVAL;
- goto done;
- }
-
if (!username || !password) {
DBG("Cannot connect username %s password %p",
username, password);
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) {
static void pptp_disconnect(struct vpn_provider *provider)
{
- vpn_provider_set_string(provider, "PPTP.Password", NULL);
+ if (!provider)
+ return;
+
+ vpn_provider_set_string_hide_value(provider, "PPTP.Password", NULL);
+
+ connman_agent_cancel(provider);
}
static struct vpn_driver vpn_driver = {
#include <sys/types.h>
#include <linux/if_tun.h>
#include <net/if.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include <dbus/dbus.h>
#include "../vpn-provider.h"
#include "vpn.h"
+#include "../vpn.h"
struct vpn_data {
struct vpn_provider *provider;
vpn_driver_data = g_hash_table_lookup(driver_hash, name);
if (vpn_driver_data && vpn_driver_data->vpn_driver &&
- vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+ vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN) {
+ vpn_driver_data->vpn_driver->disconnect(data->provider);
return 0;
+ }
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = data->tun_flags | IFF_NO_PI;
* We need to remove first the old address, just
* replacing the old address will not work as expected
* because the old address will linger in the interface
- * and not disapper so the clearing is needed here.
+ * and not disappear so the clearing is needed here.
*
* Also the state must change, otherwise the routes
* will not be set properly.
return ret;
}
+static gboolean is_numeric(const char *str)
+{
+ 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);
+ }
+
+ if (grp)
+ gid = grp->gr_gid;
+
+ return gid;
+}
+
+static gint 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);
+ }
+
+ if (pw)
+ uid = pw->pw_uid;
+
+ return uid;
+}
+
+static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
+{
+ gint group_count = 0;
+ gid_t *list = NULL;
+ int i;
+
+ if (groups) {
+ for(i = 0; groups[i]; i++) {
+ group_count++;
+
+ list = (gid_t*)g_try_realloc(list,
+ sizeof(gid_t) * group_count);
+
+ if (!list) {
+ DBG("cannot allocate supplementary group list");
+ break;
+ }
+
+ list[i] = get_gid(groups[i]);
+ }
+ }
+
+ *gid_list = list;
+
+ return group_count;
+}
+
+static void vpn_task_setup(gpointer user_data)
+{
+ struct vpn_plugin_data *data;
+ gint uid;
+ gint gid;
+ gid_t *gid_list = NULL;
+ size_t gid_list_size;
+ const gchar *user;
+ const gchar *group;
+ gchar **suppl_groups;
+
+ data = user_data;
+ user = vpn_settings_get_binary_user(data);
+ group = vpn_settings_get_binary_group(data);
+ suppl_groups = vpn_settings_get_binary_supplementary_groups(data);
+
+ uid = get_uid(user);
+ gid = get_gid(group);
+ gid_list_size = get_supplementary_gids(suppl_groups, &gid_list);
+
+ DBG("vpn_task_setup uid:%d gid:%d supplementary group list size:%zu",
+ uid, gid, gid_list_size);
+
+
+ /* Change group if proper group name was set, requires CAP_SETGID.*/
+ if (gid > 0 && setgid(gid))
+ connman_error("error setting gid %d %s", gid, strerror(errno));
+
+ /* Set the supplementary groups if list exists, requires CAP_SETGID. */
+ if (gid_list_size && gid_list && setgroups(gid_list_size, gid_list))
+ connman_error("error setting gid list %s", strerror(errno));
+
+ /* Change user for the task if set, requires CAP_SETUID */
+ if (uid > 0 && setuid(uid))
+ connman_error("error setting uid %d %s", uid, strerror(errno));
+}
+
+
+static gboolean update_provider_state(gpointer data)
+{
+ struct vpn_provider *provider = data;
+ struct vpn_data *vpn_data;
+ int index;
+
+ DBG("");
+
+ vpn_data = vpn_provider_get_data(provider);
+
+ index = vpn_provider_get_index(provider);
+ DBG("index to watch %d", index);
+ vpn_provider_ref(provider);
+ vpn_data->watch = vpn_rtnl_add_newlink_watch(index,
+ vpn_newlink, provider);
+ connman_inet_ifup(index);
+
+ return FALSE;
+}
+
static int vpn_connect(struct vpn_provider *provider,
vpn_provider_connect_cb_t cb,
const char *dbus_sender, void *user_data)
{
struct vpn_data *data = vpn_provider_get_data(provider);
struct vpn_driver_data *vpn_driver_data;
+ struct vpn_plugin_data *vpn_plugin_data;
const char *name;
int ret = 0, tun_flags = IFF_TUN;
enum vpn_state state = VPN_STATE_UNKNOWN;
goto exist_err;
}
- if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+ if (!(vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN)) {
if (vpn_driver_data->vpn_driver->device_flags) {
tun_flags = vpn_driver_data->vpn_driver->device_flags(provider);
}
goto exist_err;
}
- data->task = connman_task_create(vpn_driver_data->program);
+
+ if (vpn_driver_data && vpn_driver_data->vpn_driver &&
+ vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) {
+
+ ret = vpn_driver_data->vpn_driver->connect(provider,
+ NULL, NULL, NULL, NULL, NULL);
+ if (ret) {
+ stop_vpn(provider);
+ goto exist_err;
+ }
+
+ DBG("%s started with dev %s",
+ vpn_driver_data->provider_driver.name, data->if_name);
+
+ data->state = VPN_STATE_CONNECT;
+
+ g_timeout_add(1, update_provider_state, provider);
+ return -EINPROGRESS;
+ }
+
+ vpn_plugin_data =
+ vpn_settings_get_vpn_plugin_config(vpn_driver_data->name);
+ data->task = connman_task_create(vpn_driver_data->program,
+ vpn_task_setup, vpn_plugin_data);
if (!data->task) {
ret = -ENOMEM;
}
data->state = VPN_STATE_DISCONNECT;
- connman_task_stop(data->task);
+
+ if (!vpn_driver_data->vpn_driver->disconnect) {
+ DBG("Driver has no disconnect() implementation, set provider "
+ "state to disconnect.");
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_DISCONNECT);
+ }
+
+ if (data->task)
+ connman_task_stop(data->task);
return 0;
}
data->watch = 0;
}
- connman_task_stop(data->task);
+ if (data->task)
+ connman_task_stop(data->task);
g_usleep(G_USEC_PER_SEC);
stop_vpn(provider);
data->name = name;
data->program = program;
+ if (vpn_settings_parse_vpn_plugin_config(data->name) != 0)
+ DBG("No configuration provided for VPN plugin %s", data->name);
+
data->vpn_driver = vpn_driver;
data->provider_driver.name = name;
return;
vpn_provider_driver_unregister(&data->provider_driver);
+ vpn_settings_delete_vpn_plugin_config(name);
g_hash_table_remove(driver_hash, name);
extern "C" {
#endif
-#define VPN_FLAG_NO_TUN 1
+#define VPN_FLAG_NO_TUN 1
+#define VPN_FLAG_NO_DAEMON 2
enum vpn_state {
VPN_STATE_UNKNOWN = 0,
#include <connman/task.h>
#include <connman/ipconfig.h>
#include <connman/dbus.h>
+#include <connman/agent.h>
+#include <connman/setting.h>
+#include <connman/vpn-dbus.h>
#include "../vpn-provider.h"
+#include "../vpn-agent.h"
#include "vpn.h"
+#include "../vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-static DBusConnection *connection;
-
enum {
OPT_STRING = 1,
OPT_BOOLEAN = 2,
true },
};
+struct vc_private_data {
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ char *if_name;
+ vpn_provider_connect_cb_t cb;
+ void *user_data;
+ int err_ch_id;
+ GIOChannel *err_ch;
+};
+
+static void vc_connect_done(struct vc_private_data *data, int err)
+{
+ DBG("data %p err %d", data, err);
+
+ if (data && data->cb) {
+ vpn_provider_connect_cb_t cb = data->cb;
+ void *user_data = data->user_data;
+
+ /* Make sure we don't invoke this callback twice */
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+ }
+}
+
+static void close_io_channel(struct vc_private_data *data, GIOChannel *channel)
+{
+ if (!data || !channel)
+ return;
+
+ if (data->err_ch == channel) {
+ DBG("closing stderr");
+
+ if (data->err_ch_id) {
+ g_source_remove(data->err_ch_id);
+ data->err_ch_id = 0;
+ }
+
+ if (!data->err_ch)
+ return;
+
+ g_io_channel_shutdown(data->err_ch, FALSE, NULL);
+ g_io_channel_unref(data->err_ch);
+
+ data->err_ch = NULL;
+ }
+}
+
+static void free_private_data(struct vc_private_data *data)
+{
+ DBG("data %p", data);
+
+ if (!data || !data->provider)
+ return;
+
+ DBG("provider %p", data->provider);
+
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ vpn_provider_unref(data->provider);
+
+ g_free(data->if_name);
+ g_free(data);
+}
+
static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
char *address = NULL, *netmask = NULL, *gateway = NULL;
struct connman_ipaddress *ipaddress;
const char *reason, *key, *value;
+ struct vc_private_data *data;
+ int type;
+
+ data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
+ type = dbus_message_iter_get_arg_type(&iter);
+ if (type != DBUS_TYPE_STRING) {
+ DBG("invalid D-Bus arg type %d", type);
+ return VPN_STATE_FAILURE;
+ }
+
dbus_message_iter_get_basic(&iter, &reason);
dbus_message_iter_next(&iter);
if (!provider) {
connman_error("No provider found");
+ vc_connect_done(data, ENOENT);
return VPN_STATE_FAILURE;
}
- if (strcmp(reason, "connect"))
+ if (g_strcmp0(reason, "connect")) {
+ vc_connect_done(data, EIO);
return VPN_STATE_DISCONNECT;
+ }
dbus_message_iter_recurse(&iter, &dict);
DBusMessageIter entry;
dbus_message_iter_recurse(&dict, &entry);
+
+ type = dbus_message_iter_get_arg_type(&entry);
+ if (type != DBUS_TYPE_STRING)
+ continue;
+
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
+
+ type = dbus_message_iter_get_arg_type(&entry);
+ if (type != DBUS_TYPE_STRING)
+ continue;
+
dbus_message_iter_get_basic(&entry, &value);
DBG("%s = %s", key, value);
g_free(address);
g_free(netmask);
g_free(gateway);
-
+ vc_connect_done(data, EIO);
return VPN_STATE_FAILURE;
}
g_free(gateway);
connman_ipaddress_free(ipaddress);
+ vc_connect_done(data, 0);
return VPN_STATE_CONNECT;
}
return 0;
}
-static int vc_connect(struct vpn_provider *provider,
- struct connman_task *task, const char *if_name,
- vpn_provider_connect_cb_t cb, const char *dbus_sender,
- void *user_data)
+static void vc_died(struct connman_task *task, int exit_code, void *user_data)
{
- const char *option;
- int err = 0, fd;
+ struct vc_private_data *data = user_data;
- option = vpn_provider_get_string(provider, "Host");
- if (!option) {
- connman_error("Host not set; cannot enable VPN");
- err = -EINVAL;
- goto done;
+ DBG("task %p data %p exit_code %d user_data %p", task, data, exit_code,
+ user_data);
+
+ if (!data)
+ return;
+
+ if (data->provider) {
+ connman_agent_cancel(data->provider);
+
+ if (task)
+ vpn_died(task, exit_code, data->provider);
}
- option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
- if (!option) {
- connman_error("Group not set; cannot enable VPN");
- err = -EINVAL;
- goto done;
+
+ free_private_data(data);
+}
+
+static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ struct vc_private_data *data;
+ const char *auth_failures[] = {
+ VPNC ": hash comparison failed",
+ VPNC ": authentication unsuccessful",
+ VPNC ": expected xauth packet; rejected",
+ NULL
+ };
+ const char *conn_failures[] = {
+ VPNC ": unknown host",
+ VPNC ": no response from target",
+ VPNC ": receiving packet: No route to host",
+ NULL
+ };
+ char *str;
+ int i;
+
+ data = user_data;
+
+ if ((condition & G_IO_IN) &&
+ g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
+ str[strlen(str) - 1] = '\0';
+
+ for (i = 0; auth_failures[i]; i++) {
+ if (g_str_has_prefix(str, auth_failures[i])) {
+ DBG("authentication failed: %s", str);
+
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ }
+ }
+
+ for (i = 0; conn_failures[i]; i++) {
+ if (g_str_has_prefix(str, conn_failures[i])) {
+ DBG("connection failed: %s", str);
+
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_CONNECT_FAILED);
+ }
+ }
+
+ g_free(str);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ DBG("Channel termination");
+ close_io_channel(data, source);
+ return G_SOURCE_REMOVE;
}
+ return G_SOURCE_CONTINUE;
+}
+
+static int run_connect(struct vc_private_data *data)
+{
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username",
+ "VPNC.Xauth.Password", NULL};
+ const char *if_name;
+ const char *option;
+ int err;
+ int fd_in;
+ int fd_err;
+ int i;
+
+ provider = data->provider;
+ task = data->task;
+ if_name = data->if_name;
+
+ DBG("provider %p task %p interface %s user_data %p", provider, task,
+ if_name, data->user_data);
+
+ /*
+ * Change to use C locale, options should be in ASCII according to
+ * documentation. To be on the safe side, set both LANG and LC_ALL.
+ * This is required especially when the VPNC processe is ran using an
+ * user other than root.
+ */
+ connman_task_add_variable(task,"LANG", "C");
+ connman_task_add_variable(task,"LC_ALL", "C");
+
connman_task_add_argument(task, "--non-inter", NULL);
connman_task_add_argument(task, "--no-detach", NULL);
connman_task_add_argument(task, "--ifmode", "tun");
}
- connman_task_add_argument(task, "--script",
- SCRIPTDIR "/openconnect-script");
+ connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
option = vpn_provider_get_string(provider, "VPNC.Debug");
if (option)
connman_task_add_argument(task, "-", NULL);
- err = connman_task_run(task, vpn_died, provider,
- &fd, NULL, NULL);
+ err = connman_task_run(data->task, vc_died, data, &fd_in, NULL,
+ &fd_err);
if (err < 0) {
connman_error("vpnc failed to start");
err = -EIO;
goto done;
}
- err = vc_write_config_data(provider, fd);
+ err = vc_write_config_data(provider, fd_in);
+
+ if (err) {
+ DBG("config write error %s", strerror(err));
+ goto done;
+ }
+
+ err = -EINPROGRESS;
- close(fd);
+ data->err_ch = g_io_channel_unix_new(fd_err);
+ data->err_ch_id = g_io_add_watch(data->err_ch,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ (GIOFunc)io_channel_cb, data);
done:
- if (cb)
- cb(provider, user_data, err);
+ close(fd_in);
+
+ /*
+ * Clear out credentials if they are non-immutable. If this is called
+ * directly from vc_connect() all credentials are read from config and
+ * are set as immutable, so no change is done. In case a VPN agent is
+ * used these values should be reset to "-" in order to retrieve them
+ * from VPN agent next time VPN connection is established. This supports
+ * then partially defined credentials in .config and some can be
+ * retrieved using an agent.
+ */
+ for (i = 0; credentials[i]; i++) {
+ const char *key = credentials[i];
+ if (!vpn_provider_get_string_immutable(provider, key))
+ vpn_provider_set_string(provider, key, "-");
+ }
return err;
}
+static void request_input_append_mandatory(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "string";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "mandatory";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+
+ if (!user_data)
+ return;
+
+ str = user_data;
+ connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_password(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "password";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "mandatory";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+
+ if (!user_data)
+ return;
+
+ str = user_data;
+ connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_informational(DBusMessageIter *iter,
+ void *user_data)
+{
+ char *str = "password";
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+ str = "informational";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+
+ if (!user_data)
+ return;
+
+ str = user_data;
+ connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_to_dict(struct vpn_provider *provider,
+ DBusMessageIter *dict,
+ connman_dbus_append_cb_t function_cb, const char *key)
+{
+ const char *str;
+ bool immutable = false;
+
+ if (!provider || !dict || !function_cb || !key)
+ return;
+
+ str = vpn_provider_get_string(provider, key);
+
+ /* If value is "-", it is cleared by VPN agent */
+ if (!g_strcmp0(str, "-"))
+ str = NULL;
+
+ if (str)
+ immutable = vpn_provider_get_string_immutable(provider, key);
+
+ if (immutable) {
+ /* Hide immutable password types */
+ if (function_cb == request_input_append_password)
+ str = "********";
+
+ /* Send immutable as informational */
+ function_cb = request_input_append_informational;
+ }
+
+ connman_dbus_dict_append_dict(dict, key, function_cb, (void *)str);
+}
+
+static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
+{
+ struct vc_private_data *data = user_data;
+ char *secret = NULL, *username = NULL, *password = NULL;
+ const char *key;
+ DBusMessageIter iter, dict;
+ int err;
+
+ DBG("provider %p", data->provider);
+
+ if (!reply)
+ 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 */
+ data->cb = NULL;
+ data->user_data = NULL;
+ return;
+ }
+
+ if (!vpn_agent_check_reply_has_dict(reply))
+ goto err;
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &dict);
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ if (g_str_equal(key, "VPNC.IPSec.Secret")) {
+ 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, &secret);
+ vpn_provider_set_string_hide_value(data->provider,
+ key, secret);
+
+ } else if (g_str_equal(key, "VPNC.Xauth.Username")) {
+ 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, &username);
+ vpn_provider_set_string(data->provider, key, username);
+
+ } else if (g_str_equal(key, "VPNC.Xauth.Password")) {
+ 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, &password);
+ vpn_provider_set_string_hide_value(data->provider, key,
+ password);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+
+ if (!secret || !username || !password)
+ goto err;
+
+ err = run_connect(data);
+ if (err != -EINPROGRESS)
+ goto err;
+
+ return;
+
+err:
+ vc_connect_done(data, EACCES);
+}
+
+static int request_input_credentials(struct vc_private_data *data,
+ const char* dbus_sender)
+{
+ DBusMessage *message;
+ const char *path, *agent_sender, *agent_path;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ int err;
+ void *agent;
+
+ if (!data || !data->provider)
+ return -ENOENT;
+
+ DBG("data %p provider %p sender %s", data, data->provider, dbus_sender);
+
+ agent = connman_agent_get_info(dbus_sender, &agent_sender, &agent_path);
+ if (!agent || !agent_path)
+ return -ESRCH;
+
+ message = dbus_message_new_method_call(agent_sender, agent_path,
+ VPN_AGENT_INTERFACE,
+ "RequestInput");
+ if (!message)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+
+ path = vpn_provider_get_path(data->provider);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ connman_dbus_dict_open(&iter, &dict);
+
+ if (vpn_provider_get_authentication_errors(data->provider))
+ vpn_agent_append_auth_failure(&dict, data->provider, NULL);
+
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_password,
+ "VPNC.IPSec.Secret");
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_mandatory,
+ "VPNC.Xauth.Username");
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_password,
+ "VPNC.Xauth.Password");
+
+ vpn_agent_append_host_and_name(&dict, data->provider);
+
+ connman_dbus_dict_close(&iter, &dict);
+
+ err = connman_agent_queue_message(data->provider, message,
+ connman_timeout_input_request(),
+ request_input_credentials_reply, data, agent);
+
+ dbus_message_unref(message);
+
+ if (err < 0 && err != -EBUSY) {
+ DBG("error %d sending agent request", err);
+ return err;
+ }
+
+ return -EINPROGRESS;
+}
+
+static int vc_connect(struct vpn_provider *provider,
+ struct connman_task *task, const char *if_name,
+ vpn_provider_connect_cb_t cb, const char *dbus_sender,
+ void *user_data)
+{
+ struct vc_private_data *data;
+ const char *option;
+ bool username_set = false;
+ bool password_set = false;
+ bool ipsec_secret_set = false;
+ int err;
+
+ DBG("provider %p if_name %s user_data %p", provider, if_name, user_data);
+
+ option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
+ if (!option) {
+ connman_error("Group not set; cannot enable VPN");
+ return -EINVAL;
+ }
+
+ option = vpn_provider_get_string(provider, "VPNC.IPSec.Secret");
+ if (option && *option && g_strcmp0(option, "-"))
+ ipsec_secret_set = true;
+
+ option = vpn_provider_get_string(provider, "VPNC.Xauth.Username");
+ if (option && *option && g_strcmp0(option, "-"))
+ username_set = true;
+
+ option = vpn_provider_get_string(provider, "VPNC.Xauth.Password");
+ if (option && *option && g_strcmp0(option, "-"))
+ password_set = true;
+
+ data = g_try_new0(struct vc_private_data, 1);
+ if (!data)
+ return -ENOMEM;
+
+ vpn_provider_set_plugin_data(provider, data);
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->if_name = g_strdup(if_name);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ if (!ipsec_secret_set || !username_set || !password_set) {
+ err = request_input_credentials(data, dbus_sender);
+ if (err != -EINPROGRESS) {
+ vc_connect_done(data, ECONNABORTED);
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_LOGIN_FAILED);
+ free_private_data(data);
+ }
+
+ return err;
+ }
+
+ return run_connect(data);
+}
+
+static void vc_disconnect(struct vpn_provider *provider)
+{
+ if (!provider)
+ return;
+
+ connman_agent_cancel(provider);
+}
+
static int vc_error_code(struct vpn_provider *provider, int exit_code)
{
switch (exit_code) {
static struct vpn_driver vpn_driver = {
.notify = vc_notify,
.connect = vc_connect,
+ .disconnect = vc_disconnect,
.error_code = vc_error_code,
.save = vc_save,
.device_flags = vc_device_flags,
static int vpnc_init(void)
{
- connection = connman_dbus_get_connection();
-
return vpn_register("vpnc", &vpn_driver, VPNC);
}
static void vpnc_exit(void)
{
vpn_unregister("vpnc");
-
- dbus_connection_unref(connection);
}
CONNMAN_PLUGIN_DEFINE(vpnc, "vpnc plugin", VERSION,
--- /dev/null
+/*
+ * ConnMan VPN daemon
+ *
+ * Copyright (C) 2019 Daniel Wagner. 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/ipconfig.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/setting.h>
+#include <connman/vpn-dbus.h>
+
+#include "../vpn-provider.h"
+#include "../vpn.h"
+
+#include "vpn.h"
+#include "wireguard.h"
+
+static int parse_key(const char *str, wg_key key)
+{
+ unsigned char *buf;
+ size_t len;
+
+ buf = g_base64_decode(str, &len);
+
+ if (len != 32) {
+ g_free(buf);
+ return -EINVAL;
+ }
+
+ memcpy(key, buf, 32);
+
+ g_free(buf);
+ return 0;
+}
+
+static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
+{
+ struct wg_allowedip *curaip, *allowedip;
+ char buf[INET6_ADDRSTRLEN];
+ char **tokens, **toks;
+ char *send;
+ int i;
+
+ curaip = NULL;
+ tokens = g_strsplit(allowed_ips, ", ", -1);
+ for (i = 0; tokens[i]; i++) {
+ toks = g_strsplit(tokens[i], "/", -1);
+ if (g_strv_length(toks) != 2) {
+ DBG("Ignore AllowedIPs value %s", tokens[i]);
+ g_strfreev(toks);
+ continue;
+ }
+
+ allowedip = g_malloc0(sizeof(*allowedip));
+
+ if (inet_pton(AF_INET, toks[0], buf) == 1) {
+ allowedip->family = AF_INET;
+ memcpy(&allowedip->ip4, buf, sizeof(allowedip->ip4));
+ } else if (inet_pton(AF_INET6, toks[0], buf) == 1) {
+ allowedip->family = AF_INET6;
+ memcpy(&allowedip->ip6, buf, sizeof(allowedip->ip6));
+ } else {
+ DBG("Ignore AllowedIPs value %s", tokens[i]);
+ g_free(allowedip);
+ g_strfreev(toks);
+ continue;
+ }
+
+ allowedip->cidr = g_ascii_strtoull(toks[1], &send, 10);
+
+ if (!curaip)
+ peer->first_allowedip = allowedip;
+ else
+ curaip->next_allowedip = allowedip;
+
+ curaip = allowedip;
+ }
+
+ peer->last_allowedip = curaip;
+ g_strfreev(tokens);
+
+ return 0;
+}
+
+static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
+{
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ int sk;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ hints.ai_protocol = 0;
+
+ if (getaddrinfo(host, port, &hints, &result) < 0) {
+ DBG("Failed to resolve host address");
+ return -EINVAL;
+ }
+
+ for (rp = result; rp; rp = rp->ai_next) {
+ sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sk < 0)
+ continue;
+ if (connect(sk, rp->ai_addr, rp->ai_addrlen) != -1) {
+ /* success */
+ close(sk);
+ break;
+ }
+
+ close(sk);
+ }
+
+ if (!rp) {
+ freeaddrinfo(result);
+ return -EINVAL;
+ }
+
+ memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+ freeaddrinfo(result);
+
+ return 0;
+}
+
+static int parse_address(const char *address, const char *gateway,
+ struct connman_ipaddress **ipaddress)
+{
+ char buf[INET6_ADDRSTRLEN];
+ unsigned char prefixlen;
+ char **tokens;
+ char *end, *netmask;
+ int err;
+
+ tokens = g_strsplit(address, "/", -1);
+ if (g_strv_length(tokens) != 2) {
+ g_strfreev(tokens);
+ return -EINVAL;
+ }
+
+ prefixlen = g_ascii_strtoull(tokens[1], &end, 10);
+
+ if (inet_pton(AF_INET, tokens[0], buf) == 1) {
+ netmask = g_strdup_printf("%d.%d.%d.%d",
+ ((0xffffffff << (32 - prefixlen)) >> 24) & 0xff,
+ ((0xffffffff << (32 - prefixlen)) >> 16) & 0xff,
+ ((0xffffffff << (32 - prefixlen)) >> 8) & 0xff,
+ ((0xffffffff << (32 - prefixlen)) >> 0) & 0xff);
+
+ *ipaddress = connman_ipaddress_alloc(AF_INET);
+ err = connman_ipaddress_set_ipv4(*ipaddress, tokens[0],
+ netmask, gateway);
+ g_free(netmask);
+ } else if (inet_pton(AF_INET6, tokens[0], buf) == 1) {
+ *ipaddress = connman_ipaddress_alloc(AF_INET6);
+ err = connman_ipaddress_set_ipv6(*ipaddress, tokens[0],
+ prefixlen, gateway);
+ } else {
+ DBG("Invalid Wireguard.Address value");
+ err = -EINVAL;
+ }
+
+ g_strfreev(tokens);
+ if (err)
+ connman_ipaddress_free(*ipaddress);
+
+ return err;
+}
+
+struct ifname_data {
+ char *ifname;
+ bool found;
+};
+
+static void ifname_check_cb(int index, void *user_data)
+{
+ struct ifname_data *data = (struct ifname_data *)user_data;
+ char *ifname;
+
+ ifname = connman_inet_ifname(index);
+
+ if (!g_strcmp0(ifname, data->ifname))
+ data->found = true;
+}
+
+static char *get_ifname(void)
+{
+ struct ifname_data data;
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ data.ifname = g_strdup_printf("wg%d", i);
+ data.found = false;
+ __vpn_ipconfig_foreach(ifname_check_cb, &data);
+
+ if (!data.found)
+ return data.ifname;
+
+ g_free(data.ifname);
+ }
+
+ return NULL;
+}
+
+struct wireguard_info {
+ struct wg_device device;
+ struct wg_peer peer;
+};
+
+static int wg_connect(struct vpn_provider *provider,
+ struct connman_task *task, const char *if_name,
+ vpn_provider_connect_cb_t cb,
+ const char *dbus_sender, void *user_data)
+{
+ struct connman_ipaddress *ipaddress = NULL;
+ struct wireguard_info *info;
+ const char *option, *gateway;
+ char *ifname;
+ int err = -EINVAL;
+
+ info = g_malloc0(sizeof(struct wireguard_info));
+ info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
+ info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
+ info->device.first_peer = &info->peer;
+ info->device.last_peer = &info->peer;
+
+ vpn_provider_set_plugin_data(provider, info);
+
+ option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
+ if (option) {
+ char *end;
+ info->device.listen_port = g_ascii_strtoull(option, &end, 10);
+ info->device.flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+
+ option = vpn_provider_get_string(provider, "WireGuard.DNS");
+ if (option) {
+ err = vpn_provider_set_nameservers(provider, option);
+ if (err)
+ goto done;
+ }
+
+ option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
+ if (!option) {
+ DBG("WireGuard.PrivateKey is missing");
+ goto done;
+ }
+ err = parse_key(option, info->device.private_key);
+ if (err)
+ goto done;
+
+ option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
+ if (!option) {
+ DBG("WireGuard.PublicKey is missing");
+ goto done;
+ }
+ err = parse_key(option, info->peer.public_key);
+ if (err)
+ goto done;
+
+ option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
+ if (option) {
+ info->peer.flags |= WGPEER_HAS_PRESHARED_KEY;
+ err = parse_key(option, info->peer.preshared_key);
+ if (err)
+ goto done;
+ }
+
+ option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
+ if (!option) {
+ DBG("WireGuard.AllowedIPs is missing");
+ goto done;
+ }
+ err = parse_allowed_ips(option, &info->peer);
+ if (err)
+ goto done;
+
+ option = vpn_provider_get_string(provider,
+ "WireGuard.PersistentKeepalive");
+ if (option) {
+ char *end;
+ info->peer.persistent_keepalive_interval =
+ g_ascii_strtoull(option, &end, 10);
+ info->peer.flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+
+ option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
+ if (!option)
+ option = "51820";
+
+ gateway = vpn_provider_get_string(provider, "Host");
+ err = parse_endpoint(gateway, option, &info->peer);
+ if (err)
+ goto done;
+
+ option = vpn_provider_get_string(provider, "WireGuard.Address");
+ if (!option) {
+ DBG("Missing WireGuard.Address configuration");
+ goto done;
+ }
+ err = parse_address(option, gateway, &ipaddress);
+ if (err)
+ goto done;
+
+ ifname = get_ifname();
+ if (!ifname) {
+ DBG("Failed to find an usable device name");
+ err = -ENOENT;
+ goto done;
+ }
+ stpncpy(info->device.name, ifname, sizeof(info->device.name));
+ g_free(ifname);
+
+ err = wg_add_device(info->device.name);
+ if (err) {
+ DBG("Failed to creating WireGuard device %s", info->device.name);
+ goto done;
+ }
+
+ err = wg_set_device(&info->device);
+ if (err) {
+ DBG("Failed to configure WireGuard device %s", info->device.name);
+ wg_del_device(info->device.name);
+ }
+
+ vpn_set_ifname(provider, info->device.name);
+ if (ipaddress)
+ vpn_provider_set_ipaddress(provider, ipaddress);
+
+done:
+ if (cb)
+ cb(provider, user_data, err);
+
+ connman_ipaddress_free(ipaddress);
+
+ return err;
+}
+
+static void wg_disconnect(struct vpn_provider *provider)
+{
+ struct wireguard_info *info;
+
+ info = vpn_provider_get_plugin_data(provider);
+ if (!info)
+ return;
+ vpn_provider_set_plugin_data(provider, NULL);
+
+ wg_del_device(info->device.name);
+
+ g_free(info);
+}
+
+static struct vpn_driver vpn_driver = {
+ .flags = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
+ .connect = wg_connect,
+ .disconnect = wg_disconnect,
+};
+
+static int wg_init(void)
+{
+ return vpn_register("wireguard", &vpn_driver, NULL);
+}
+
+static void wg_exit(void)
+{
+ vpn_unregister("wireguard");
+}
+
+CONNMAN_PLUGIN_DEFINE(wireguard, "WireGuard VPN plugin", VERSION,
+ CONNMAN_PLUGIN_PRIORITY_DEFAULT, wg_init, wg_exit)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*
+ * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef WIREGUARD_H
+#define WIREGUARD_H
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef uint8_t wg_key[32];
+typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
+
+/* Cross platform __kernel_timespec */
+struct timespec64 {
+ int64_t tv_sec;
+ int64_t tv_nsec;
+};
+
+typedef struct wg_allowedip {
+ uint16_t family;
+ union {
+ struct in_addr ip4;
+ struct in6_addr ip6;
+ };
+ uint8_t cidr;
+ struct wg_allowedip *next_allowedip;
+} wg_allowedip;
+
+enum wg_peer_flags {
+ WGPEER_REMOVE_ME = 1U << 0,
+ WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_HAS_PUBLIC_KEY = 1U << 2,
+ WGPEER_HAS_PRESHARED_KEY = 1U << 3,
+ WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
+};
+
+typedef struct wg_peer {
+ enum wg_peer_flags flags;
+
+ wg_key public_key;
+ wg_key preshared_key;
+
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ } endpoint;
+
+ struct timespec64 last_handshake_time;
+ uint64_t rx_bytes, tx_bytes;
+ uint16_t persistent_keepalive_interval;
+
+ struct wg_allowedip *first_allowedip, *last_allowedip;
+ struct wg_peer *next_peer;
+} wg_peer;
+
+enum wg_device_flags {
+ WGDEVICE_REPLACE_PEERS = 1U << 0,
+ WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
+ WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
+ WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
+ WGDEVICE_HAS_FWMARK = 1U << 4
+};
+
+typedef struct wg_device {
+ char name[IFNAMSIZ];
+ uint32_t ifindex;
+
+ enum wg_device_flags flags;
+
+ wg_key public_key;
+ wg_key private_key;
+
+ uint32_t fwmark;
+ uint16_t listen_port;
+
+ struct wg_peer *first_peer, *last_peer;
+} wg_device;
+
+#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
+#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
+#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
+
+int wg_set_device(wg_device *dev);
+int wg_get_device(wg_device **dev, const char *device_name);
+int wg_add_device(const char *device_name);
+int wg_del_device(const char *device_name);
+void wg_free_device(wg_device *dev);
+char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
+void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
+int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
+bool wg_key_is_zero(const wg_key key);
+void wg_generate_public_key(wg_key public_key, const wg_key private_key);
+void wg_generate_private_key(wg_key private_key);
+void wg_generate_preshared_key(wg_key preshared_key);
+
+#endif
* Connection Manager
*
* Copyright (C) 2012 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <gdbus.h>
#include <connman/log.h>
#include <connman/agent.h>
+#include <connman/vpn-dbus.h>
+#include <connman/task.h>
#include <vpn/vpn-provider.h>
#include "vpn-agent.h"
struct user_info_data {
struct vpn_provider *provider;
const char *username_str;
+ const char *type_str;
};
static void request_input_append_user_info(DBusMessageIter *iter,
{
struct user_info_data *data = user_data;
struct vpn_provider *provider = data->provider;
- const char *str = "string";
+ const char *str = NULL;
connman_dbus_dict_append_basic(iter, "Type",
- DBUS_TYPE_STRING, &str);
+ DBUS_TYPE_STRING, &data->type_str);
str = "mandatory";
connman_dbus_dict_append_basic(iter, "Requirement",
DBUS_TYPE_STRING, &str);
.username_str = username_str
};
+ data.type_str = "string";
connman_dbus_dict_append_dict(iter, "Username",
request_input_append_user_info,
&data);
data.username_str = NULL;
+ data.type_str = "password";
connman_dbus_dict_append_dict(iter, "Password",
request_input_append_user_info,
&data);
}
+
+static void request_input_append_flag(DBusMessageIter *iter,
+ void *user_data)
+{
+ dbus_bool_t data = (dbus_bool_t)GPOINTER_TO_INT(user_data);
+ const char *str = NULL;
+
+ str = "boolean";
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str);
+
+ str = "control";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+
+ connman_dbus_dict_append_basic(iter, "Value",
+ DBUS_TYPE_BOOLEAN, &data);
+}
+
+void vpn_agent_append_allow_credential_storage(DBusMessageIter *iter,
+ bool allow)
+{
+ connman_dbus_dict_append_dict(iter, "AllowStoreCredentials",
+ request_input_append_flag,
+ GINT_TO_POINTER(allow));
+}
+
+void vpn_agent_append_allow_credential_retrieval(DBusMessageIter *iter,
+ bool allow)
+{
+ connman_dbus_dict_append_dict(iter, "AllowRetrieveCredentials",
+ request_input_append_flag,
+ GINT_TO_POINTER(allow));
+}
+
+void vpn_agent_append_keep_credentials(DBusMessageIter *iter, bool allow)
+{
+ connman_dbus_dict_append_dict(iter, "KeepCredentials",
+ request_input_append_flag,
+ GINT_TO_POINTER(allow));
+}
+
+struct failure_data {
+ struct vpn_provider *provider;
+ const char* type_str;
+ const char *key;
+ const char* str;
+};
+
+static void request_input_append_failure(DBusMessageIter *iter,
+ void *user_data)
+{
+ struct failure_data *data;
+ const char *str;
+
+ data = user_data;
+
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &data->type_str);
+ str = "informational";
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
+
+ str = data->str;
+
+ /* Try to get information from provider about error */
+ if (!str)
+ str = vpn_provider_get_string(data->provider, data->key);
+
+ if (str)
+ connman_dbus_dict_append_basic(iter, "Value",
+ DBUS_TYPE_STRING, &str);
+}
+
+void vpn_agent_append_auth_failure(DBusMessageIter *iter,
+ struct vpn_provider *provider,
+ const char* information)
+{
+ struct failure_data data;
+ unsigned int value;
+
+ /* Skip if there are no auth errors */
+ value = vpn_provider_get_authentication_errors(provider);
+ if (!value)
+ return;
+
+ data.provider = provider;
+ data.type_str = "string";
+ data.key = "VpnAgent.AuthFailure";
+ data.str = information;
+
+ connman_dbus_dict_append_dict(iter, data.key,
+ request_input_append_failure, &data);
+}
+
+int vpn_agent_check_and_process_reply_error(DBusMessage *reply,
+ struct vpn_provider *provider,
+ struct connman_task *task,
+ vpn_provider_connect_cb_t cb, void *user_data)
+{
+ DBusError error;
+ int err;
+
+ if (!reply || !provider)
+ return EINVAL;
+
+ dbus_error_init(&error);
+
+ if (!dbus_set_error_from_message(&error, reply))
+ return 0;
+
+ if (!g_strcmp0(error.name, VPN_AGENT_INTERFACE ".Error.Canceled"))
+ err = ECANCELED;
+ else if (!g_strcmp0(error.name, "org.freedesktop.DBus.Error.Timeout"))
+ err = ETIMEDOUT;
+ else if (!g_strcmp0(error.name, "org.freedesktop.DBus.Error.NoReply"))
+ err = ENOMSG;
+ else
+ err = EACCES;
+
+ dbus_error_free(&error);
+
+ if (cb)
+ cb(provider, user_data, err);
+
+ if (task)
+ connman_task_stop(task);
+
+ /*
+ * VPN agent dialog cancel, timeout, broken connection should set the
+ * VPN back to idle state
+ */
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
+
+ return err;
+}
void vpn_agent_append_user_info(DBusMessageIter *iter,
struct vpn_provider *provider,
const char *username_str);
+void vpn_agent_append_allow_credential_storage(DBusMessageIter *iter,
+ bool allow);
+void vpn_agent_append_allow_credential_retrieval(DBusMessageIter *iter,
+ bool allow);
+void vpn_agent_append_keep_credentials(DBusMessageIter *iter, bool allow);
+void vpn_agent_append_auth_failure(DBusMessageIter *iter,
+ struct vpn_provider *provider,
+ const char *information);
+int vpn_agent_check_and_process_reply_error(DBusMessage *reply,
+ struct vpn_provider *provider,
+ struct connman_task *task,
+ vpn_provider_connect_cb_t cb, void *user_data);
#ifdef __cplusplus
}
config_provider->config_entry = g_strdup_printf("provider_%s",
config_provider->ident);
- g_hash_table_insert(config->provider_table,
- config_provider->ident, config_provider);
-
err = __vpn_provider_create_from_config(
config_provider->setting_strings,
config_provider->config_ident,
goto err;
}
+ g_hash_table_insert(config->provider_table, config_provider->ident,
+ config_provider);
+
+
connman_info("Added provider configuration %s",
config_provider->ident);
return 0;
* ConnMan VPN daemon
*
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019 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
bool immutable;
struct connman_ipaddress *prev_ipv4_addr;
struct connman_ipaddress *prev_ipv6_addr;
+ void *plugin_data;
+ unsigned int auth_error_counter;
+ unsigned int conn_error_counter;
};
static void append_properties(DBusMessageIter *iter,
struct vpn_provider *provider);
+static int vpn_provider_save(struct vpn_provider *provider);
static void free_route(gpointer data)
{
return 0;
}
+/*
+ * Sort vpn_route struct based on (similarly to the route key in hash table):
+ * 1) IP protocol number
+ * 2) Network addresses
+ * 3) Netmask addresses
+ * 4) Gateway addresses
+ */
+static gint compare_route(gconstpointer a, gconstpointer b)
+{
+ const struct vpn_route *route_a = a;
+ const struct vpn_route *route_b = b;
+ int difference;
+
+ /* If IP families differ, prefer IPv6 over IPv4 */
+ if (route_a->family != route_b->family) {
+ if (route_a->family < route_b->family)
+ return -1;
+
+ if (route_a->family > route_b->family)
+ return 1;
+ }
+
+ /* If networks differ, return */
+ if ((difference = g_strcmp0(route_a->network, route_b->network)))
+ return difference;
+
+ /* If netmasks differ, return. */
+ if ((difference = g_strcmp0(route_a->netmask, route_b->netmask)))
+ return difference;
+
+ return g_strcmp0(route_a->gateway, route_b->gateway);
+}
+
static GSList *read_route_dict(GSList *routes, DBusMessageIter *dicts)
{
DBusMessageIter dict, value, entry;
} else {
switch (family) {
case '4':
+ case 4:
family = AF_INET;
break;
case '6':
+ case 6:
family = AF_INET6;
break;
default:
route->netmask = g_strdup(netmask);
route->gateway = g_strdup(gateway);
- routes = g_slist_prepend(routes, route);
+ routes = g_slist_insert_sorted(routes, route, compare_route);
return routes;
}
return reply;
}
+/* True when lists are equal, false otherwise */
+static bool compare_network_lists(GSList *a, GSList *b)
+{
+ struct vpn_route *route_a, *route_b;
+ GSList *iter_a, *iter_b;
+
+ if (!a && !b)
+ return true;
+
+ /*
+ * If either of lists is NULL or the lists are of different size, the
+ * lists are not equal.
+ */
+ if ((!a || !b) || (g_slist_length(a) != g_slist_length(b)))
+ return false;
+
+ /* Routes are in sorted list so items can be compared in order. */
+ for (iter_a = a, iter_b = b; iter_a && iter_b;
+ iter_a = iter_a->next, iter_b = iter_b->next) {
+
+ route_a = iter_a->data;
+ route_b = iter_b->data;
+
+ if (compare_route(route_a, route_b))
+ return false;
+ }
+
+ return true;
+}
+
+static int set_provider_property(struct vpn_provider *provider,
+ const char *name, DBusMessageIter *value, int type)
+{
+ int err = 0;
+
+ DBG("provider %p", provider);
+
+ if (!provider || !name || !value)
+ return -EINVAL;
+
+ if (g_str_equal(name, "UserRoutes")) {
+ GSList *networks;
+
+ if (type != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ networks = get_user_networks(value);
+
+ if (compare_network_lists(provider->user_networks, networks)) {
+ g_slist_free_full(networks, free_route);
+ return -EALREADY;
+ }
+
+ del_routes(provider);
+ provider->user_networks = networks;
+ set_user_networks(provider, provider->user_networks);
+
+ if (!handle_routes)
+ send_routes(provider, provider->user_routes,
+ "UserRoutes");
+ } else {
+ const char *str;
+
+ if (type != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(value, &str);
+
+ DBG("property %s value %s", name, str);
+
+ /* Empty string clears the value, similar to ClearProperty. */
+ err = vpn_provider_set_string(provider, name,
+ *str ? str : NULL);
+ }
+
+ return err;
+}
+
+static GString *append_to_gstring(GString *str, const char *value)
+{
+ if (!str)
+ return g_string_new(value);
+
+ g_string_append_printf(str, ",%s", value);
+
+ return str;
+}
+
+static DBusMessage *set_properties(DBusMessageIter *iter, DBusMessage *msg,
+ void *data)
+{
+ struct vpn_provider *provider = data;
+ DBusMessageIter dict;
+ const char *key;
+ bool change = false;
+ GString *invalid = NULL;
+ GString *denied = NULL;
+ int type;
+ int err;
+
+ for (dbus_message_iter_recurse(iter, &dict);
+ dbus_message_iter_get_arg_type(&dict) ==
+ DBUS_TYPE_DICT_ENTRY;
+ dbus_message_iter_next(&dict)) {
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ /*
+ * Ignore invalid types in order to process all values in the
+ * dict. If there is an invalid type in between the dict there
+ * may already be changes on some values and breaking out here
+ * would have the provider in an inconsistent state, leaving
+ * the rest, potentially correct property values untouched.
+ */
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ continue;
+
+ dbus_message_iter_get_basic(&entry, &key);
+
+ DBG("key %s", key);
+
+ dbus_message_iter_next(&entry);
+ /* Ignore and report back all non variant types. */
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT) {
+ invalid = append_to_gstring(invalid, key);
+ continue;
+ }
+
+ 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) {
+ invalid = append_to_gstring(invalid, key);
+ continue;
+ }
+
+ err = set_provider_property(provider, key, &value, type);
+ switch (err) {
+ case 0:
+ change = true;
+ break;
+ case -EINVAL:
+ invalid = append_to_gstring(invalid, key);
+ break;
+ case -EPERM:
+ denied = append_to_gstring(denied, key);
+ break;
+ }
+ }
+
+ if (change)
+ vpn_provider_save(provider);
+
+ if (invalid || denied) {
+ DBusMessage *error;
+ char *invalid_str = g_string_free(invalid, FALSE);
+ char *denied_str = g_string_free(denied, FALSE);
+
+ /*
+ * If there are both invalid and denied properties report
+ * back invalid arguments. Add also the failed properties to
+ * the error message.
+ */
+ error = g_dbus_create_error(msg, (invalid ?
+ CONNMAN_ERROR_INTERFACE ".InvalidProperty" :
+ CONNMAN_ERROR_INTERFACE ".PermissionDenied"),
+ "%s %s%s%s", (invalid ? "Invalid properties" :
+ "Permission denied"),
+ (invalid ? invalid_str : ""),
+ (invalid && denied ? "," : ""),
+ (denied ? denied_str : ""));
+
+ g_free(invalid_str);
+ g_free(denied_str);
+
+ return error;
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
DBusMessageIter iter, value;
const char *name;
int type;
+ int err;
DBG("conn %p", conn);
dbus_message_iter_recurse(&iter, &value);
type = dbus_message_iter_get_arg_type(&value);
+ if (type == DBUS_TYPE_ARRAY && g_str_equal(name, "Properties"))
+ return set_properties(&value, msg, data);
- if (g_str_equal(name, "UserRoutes")) {
- GSList *networks;
-
- if (type != DBUS_TYPE_ARRAY)
- return __connman_error_invalid_arguments(msg);
-
- networks = get_user_networks(&value);
- if (networks) {
- del_routes(provider);
- provider->user_networks = networks;
- set_user_networks(provider, provider->user_networks);
-
- if (!handle_routes)
- send_routes(provider, provider->user_routes,
- "UserRoutes");
- }
- } else {
- const char *str;
-
- dbus_message_iter_get_basic(&value, &str);
- vpn_provider_set_string(provider, name, str);
+ err = set_provider_property(provider, name, &value, type);
+ switch (err) {
+ case 0:
+ vpn_provider_save(provider);
+ break;
+ case -EALREADY:
+ break;
+ case -EINVAL:
+ return __connman_error_invalid_property(msg);
+ default:
+ return __connman_error_failed(msg, -err);
}
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
{
struct vpn_provider *provider = data;
const char *name;
+ bool change = false;
+ int err;
DBG("conn %p", conn);
DBUS_TYPE_INVALID);
if (g_str_equal(name, "UserRoutes")) {
+ /*
+ * If either user_routes or user_networks has any entries
+ * there is a change that is to be written to settings file.
+ */
+ if (g_hash_table_size(provider->user_routes) ||
+ provider->user_networks)
+ change = true;
+
del_routes(provider);
if (!handle_routes)
send_routes(provider, provider->user_routes, name);
} else if (vpn_provider_get_string(provider, name)) {
- vpn_provider_set_string(provider, name, NULL);
+ err = vpn_provider_set_string(provider, name, NULL);
+ switch (err) {
+ case 0:
+ change = true;
+ /* fall through */
+ case -EALREADY:
+ break;
+ case -EINVAL:
+ return __connman_error_invalid_property(msg);
+ default:
+ return __connman_error_failed(msg, -err);
+ }
} else {
return __connman_error_invalid_property(msg);
}
+ if (change)
+ vpn_provider_save(provider);
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
DBG("conn %p provider %p", conn, provider);
err = __vpn_provider_connect(provider, msg);
- if (err < 0)
+ if (err < 0 && err != -EINPROGRESS)
return __connman_error_failed(msg, -err);
return NULL;
return result;
}
+static void reset_error_counters(struct vpn_provider *provider)
+{
+ if (!provider)
+ return;
+
+ provider->auth_error_counter = provider->conn_error_counter = 0;
+}
+
static int vpn_provider_save(struct vpn_provider *provider)
{
GKeyFile *keyfile;
DBG("provider %p immutable %s", provider,
provider->immutable ? "yes" : "no");
+ reset_error_counters(provider);
+
+ if (provider->state == VPN_PROVIDER_STATE_FAILURE)
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
+
if (provider->immutable) {
/*
* Do not save providers that are provisioned via .config
if (reply)
g_dbus_send_message(connection, reply);
- vpn_provider_indicate_error(provider,
+ switch (error) {
+ case EACCES:
+ vpn_provider_indicate_error(provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
+ break;
+ case ENOENT:
+ /*
+ * No reply, disconnect called by connmand because of
+ * connection timeout.
+ */
+ break;
+ case ENOMSG:
+ /* fall through */
+ case ETIMEDOUT:
+ /* No reply or timed out -> cancel the agent request */
+ connman_agent_cancel(provider);
+ vpn_provider_indicate_error(provider,
+ VPN_PROVIDER_ERROR_UNKNOWN);
+ break;
+ case ECANCELED:
+ /* fall through */
+ case ECONNABORTED:
+ /*
+ * This can be called in other situations than when
+ * VPN agent error checker is called. In such case
+ * react to both ECONNABORTED and ECANCELED as if the
+ * connection was called to terminate and do full
+ * disconnect -> idle cycle when being connected or
+ * ready. Setting the state also using the driver
+ * callback (vpn_set_state()) ensures that the driver is
+ * being disconnected as well and eventually the vpn
+ * process gets killed and vpn_died() is called to make
+ * the provider back to idle state.
+ */
+ if (provider->state == VPN_PROVIDER_STATE_CONNECT ||
+ provider->state ==
+ VPN_PROVIDER_STATE_READY) {
+ if (provider->driver->set_state)
+ provider->driver->set_state(provider,
+ VPN_PROVIDER_STATE_DISCONNECT);
+
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_DISCONNECT);
+ }
+ break;
+ default:
+ vpn_provider_indicate_error(provider,
VPN_PROVIDER_ERROR_CONNECT_FAILED);
- vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
- } else
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_FAILURE);
+ }
+ } else {
+ reset_error_counters(provider);
g_dbus_send_reply(connection, pending, DBUS_TYPE_INVALID);
+ }
dbus_message_unref(pending);
}
int __vpn_provider_connect(struct vpn_provider *provider, DBusMessage *msg)
{
+ DBusMessage *reply;
int err;
- DBG("provider %p", provider);
+ DBG("provider %p state %d", provider, provider->state);
+
+ switch (provider->state) {
+ /*
+ * When previous connection has failed change state to idle and let
+ * the connmand to process this information as well. Return -EINPROGRESS
+ * to indicate that transition is in progress and next connection
+ * attempt will continue as normal.
+ */
+ case VPN_PROVIDER_STATE_FAILURE:
+ if (provider->driver && provider->driver->set_state)
+ provider->driver->set_state(provider,
+ VPN_PROVIDER_STATE_IDLE);
+
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
+ /* fall through */
+ /*
+ * If re-using a provider and it is being disconnected let it finish
+ * the disconnect process in order to let vpn.c:vpn_died() to get
+ * processed and everything cleaned up. Otherwise the reference
+ * counters are not decreased properly causing the previous interface
+ * being left up and its routes will remain in routing table. Return
+ * -EINPROGRESS to indicate that transition is in progress.
+ */
+ case VPN_PROVIDER_STATE_DISCONNECT:
+ /*
+ * Failure transition or disconnecting does not yield a
+ * message to be sent. Send in progress message to avoid
+ * D-Bus LimitsExceeded error message.
+ */
+ reply = __connman_error_in_progress(msg);
+ if (reply)
+ g_dbus_send_message(connection, reply);
+
+ return -EINPROGRESS;
+ case VPN_PROVIDER_STATE_UNKNOWN:
+ case VPN_PROVIDER_STATE_IDLE:
+ case VPN_PROVIDER_STATE_CONNECT:
+ case VPN_PROVIDER_STATE_READY:
+ break;
+ }
if (provider->driver && provider->driver->connect) {
const char *dbus_sender = dbus_message_get_sender(msg);
return -EINVAL;
}
+void vpn_provider_add_error(struct vpn_provider *provider,
+ enum vpn_provider_error error)
+{
+ switch (error) {
+ case VPN_PROVIDER_ERROR_UNKNOWN:
+ break;
+ case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+ ++provider->conn_error_counter;
+ break;
+ case VPN_PROVIDER_ERROR_LOGIN_FAILED:
+ case VPN_PROVIDER_ERROR_AUTH_FAILED:
+ ++provider->auth_error_counter;
+ break;
+ }
+}
+
int vpn_provider_indicate_error(struct vpn_provider *provider,
enum vpn_provider_error error)
{
vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
- switch (error) {
- case VPN_PROVIDER_ERROR_UNKNOWN:
- case VPN_PROVIDER_ERROR_CONNECT_FAILED:
- break;
-
- case VPN_PROVIDER_ERROR_LOGIN_FAILED:
- case VPN_PROVIDER_ERROR_AUTH_FAILED:
- vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
- break;
- }
+ vpn_provider_add_error(provider, error);
if (provider->driver && provider->driver->set_state)
provider->driver->set_state(provider, provider->state);
return provider;
}
+static void vpn_provider_put(const char *identifier)
+{
+ configuration_count_del();
+
+ g_hash_table_remove(provider_hash, identifier);
+}
+
static void provider_dbus_ident(char *ident)
{
int i, len = strlen(ident);
provider->config_file = g_strdup(config_ident);
provider->config_entry = g_strdup(config_entry);
- provider_register(provider);
-
provider_resolv_host_addr(provider);
}
return 0;
fail:
+ vpn_provider_put(ident);
g_free(ident);
g_slist_free_full(networks, free_route);
return reply;
}
-const char *__vpn_provider_get_ident(struct vpn_provider *provider)
+const char *vpn_provider_get_ident(struct vpn_provider *provider)
{
if (!provider)
return NULL;
hide_value ? "<not printed>" : value);
if (g_str_equal(key, "Type")) {
+ if (!g_strcmp0(provider->type, value))
+ return -EALREADY;
+
g_free(provider->type);
provider->type = g_ascii_strdown(value, -1);
send_value(provider->path, "Type", provider->type);
} else if (g_str_equal(key, "Name")) {
+ if (!g_strcmp0(provider->name, value))
+ return -EALREADY;
+
g_free(provider->name);
provider->name = g_strdup(value);
send_value(provider->path, "Name", provider->name);
} else if (g_str_equal(key, "Host")) {
+ if (!g_strcmp0(provider->host, value))
+ return -EALREADY;
+
g_free(provider->host);
provider->host = g_strdup(value);
send_value(provider->path, "Host", provider->host);
} else if (g_str_equal(key, "VPN.Domain") ||
g_str_equal(key, "Domain")) {
+ if (!g_strcmp0(provider->domain, value))
+ return -EALREADY;
+
g_free(provider->domain);
provider->domain = g_strdup(value);
send_value(provider->path, "Domain", provider->domain);
} else {
struct vpn_setting *setting;
+ bool replace = true;
setting = g_hash_table_lookup(provider->setting_strings, key);
- if (setting && !immutable &&
- setting->immutable) {
- DBG("Trying to set immutable variable %s", key);
- return -EPERM;
- }
+ if (setting) {
+ if (!immutable && setting->immutable) {
+ DBG("Trying to set immutable variable %s", key);
+ return -EPERM;
+ } else if (!g_strcmp0(setting->value, value)) {
+ return -EALREADY;
+ }
- setting = g_try_new0(struct vpn_setting, 1);
- if (!setting)
- return -ENOMEM;
+ g_free(setting->value);
+ replace = false;
+ } else {
+ setting = g_try_new0(struct vpn_setting, 1);
+ if (!setting)
+ return -ENOMEM;
+ }
setting->value = g_strdup(value);
setting->hide_value = hide_value;
if (!hide_value)
send_value(provider->path, key, setting->value);
- g_hash_table_replace(provider->setting_strings,
- g_strdup(key), setting);
+ if (replace)
+ g_hash_table_replace(provider->setting_strings,
+ g_strdup(key), setting);
}
return 0;
return setting->value;
}
+bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
+ bool default_value)
+{
+ struct vpn_setting *setting;
+
+ connman_info("provider %p key %s", provider, key);
+
+ setting = g_hash_table_lookup(provider->setting_strings, key);
+ if (!setting || !setting->value)
+ return default_value;
+
+ if (!g_strcmp0(setting->value, "true"))
+ return true;
+
+ if (!g_strcmp0(setting->value, "false"))
+ return false;
+
+ return default_value;
+}
+
+bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
+ const char *key)
+{
+ struct vpn_setting *setting;
+
+ /* These values can be changed if the provider is not immutable */
+ if (g_str_equal(key, "Type")) {
+ return provider->immutable;
+ } else if (g_str_equal(key, "Name")) {
+ return provider->immutable;
+ } else if (g_str_equal(key, "Host")) {
+ return provider->immutable;
+ } else if (g_str_equal(key, "HostIP")) {
+ return provider->immutable;
+ } else if (g_str_equal(key, "VPN.Domain") ||
+ g_str_equal(key, "Domain")) {
+ return provider->immutable;
+ }
+
+ setting = g_hash_table_lookup(provider->setting_strings, key);
+ if (!setting)
+ return true; /* Not found, regard as immutable - no changes */
+
+ return setting->immutable;
+}
+
bool __vpn_provider_check_routes(struct vpn_provider *provider)
{
if (!provider)
provider->driver_data = data;
}
+void *vpn_provider_get_plugin_data(struct vpn_provider *provider)
+{
+ return provider->plugin_data;
+}
+
+void vpn_provider_set_plugin_data(struct vpn_provider *provider, void *data)
+{
+ provider->plugin_data = data;
+}
+
void vpn_provider_set_index(struct vpn_provider *provider, int index)
{
DBG("index %d provider %p", index, provider);
provider->ipconfig_ipv4 = __vpn_ipconfig_create(index,
AF_INET);
if (!provider->ipconfig_ipv4) {
- DBG("Couldnt create ipconfig for IPv4");
+ DBG("Couldn't create ipconfig for IPv4");
goto done;
}
}
provider->ipconfig_ipv6 = __vpn_ipconfig_create(index,
AF_INET6);
if (!provider->ipconfig_ipv6) {
- DBG("Couldnt create ipconfig for IPv6");
+ DBG("Couldn't create ipconfig for IPv6");
goto done;
}
}
if (!nameservers)
return 0;
- provider->nameservers = g_strsplit(nameservers, " ", 0);
+ provider->nameservers = g_strsplit_set(nameservers, ", ", 0);
return 0;
}
struct vpn_provider *provider = value;
if (provider && provider->driver &&
- provider->driver->type == driver->type &&
g_strcmp0(provider->driver->name,
driver->name) == 0) {
provider->driver = NULL;
return provider->path;
}
+unsigned int vpn_provider_get_authentication_errors(
+ struct vpn_provider *provider)
+{
+ return provider->auth_error_counter;
+}
+
+unsigned int vpn_provider_get_connection_errors(
+ struct vpn_provider *provider)
+{
+ return provider->conn_error_counter;
+}
+
void vpn_provider_change_address(struct vpn_provider *provider)
{
switch (provider->family) {
* ConnMan VPN daemon
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019 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
* @short_description: Functions for handling providers
*/
-enum vpn_provider_type {
- VPN_PROVIDER_TYPE_UNKNOWN = 0,
- VPN_PROVIDER_TYPE_VPN = 1,
-};
-
enum vpn_provider_state {
VPN_PROVIDER_STATE_UNKNOWN = 0,
VPN_PROVIDER_STATE_IDLE = 1,
const char *key, const char *value);
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);
+bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
+ bool default_value);
int vpn_provider_set_state(struct vpn_provider *provider,
enum vpn_provider_state state);
+void vpn_provider_add_error(struct vpn_provider *provider,
+ enum vpn_provider_error error);
int vpn_provider_indicate_error(struct vpn_provider *provider,
enum vpn_provider_error error);
void vpn_provider_set_data(struct vpn_provider *provider, void *data);
void *vpn_provider_get_data(struct vpn_provider *provider);
+void vpn_provider_set_plugin_data(struct vpn_provider *provider, void *data);
+void *vpn_provider_get_plugin_data(struct vpn_provider *provider);
int vpn_provider_set_ipaddress(struct vpn_provider *provider,
struct connman_ipaddress *ipaddress);
int vpn_provider_set_pac(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);
+
+unsigned int vpn_provider_get_authentication_errors(
+ struct vpn_provider *provider);
+unsigned int vpn_provider_get_connection_errors(
+ struct vpn_provider *provider);
+
void vpn_provider_change_address(struct vpn_provider *provider);
void vpn_provider_clear_address(struct vpn_provider *provider, int family);
struct vpn_provider_driver {
const char *name;
- enum vpn_provider_type type;
int (*probe) (struct vpn_provider *provider);
int (*remove) (struct vpn_provider *provider);
int (*connect) (struct vpn_provider *provider,
--- /dev/null
+/*
+ * ConnMan VPN daemon settings
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2018-2019 Jolla Ltd. All rights reserved.
+ * 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <connman/log.h>
+
+#include "vpn.h"
+
+#define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
+#define PLUGIN_CONFIGDIR CONFIGDIR "/vpn-plugin"
+#define VPN_GROUP "DACPrivileges"
+
+static struct {
+ unsigned int timeout_inputreq;
+ char *binary_user;
+ char *binary_group;
+ char **binary_supplementary_groups;
+} connman_vpn_settings = {
+ .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
+ .binary_user = NULL,
+ .binary_group = NULL,
+ .binary_supplementary_groups = NULL,
+};
+
+struct vpn_plugin_data {
+ char *binary_user;
+ char *binary_group;
+ char **binary_supplementary_groups;
+};
+
+GHashTable *plugin_hash = NULL;
+
+const char *vpn_settings_get_binary_user(struct vpn_plugin_data *data)
+{
+ if (data && data->binary_user)
+ return data->binary_user;
+
+ return connman_vpn_settings.binary_user;
+}
+
+const char *vpn_settings_get_binary_group(struct vpn_plugin_data *data)
+{
+ if (data && data->binary_group)
+ return data->binary_group;
+
+ return connman_vpn_settings.binary_group;
+}
+
+char **vpn_settings_get_binary_supplementary_groups(struct vpn_plugin_data *data)
+{
+ if (data && data->binary_supplementary_groups)
+ return data->binary_supplementary_groups;
+
+ return connman_vpn_settings.binary_supplementary_groups;
+}
+
+unsigned int __vpn_settings_get_timeout_inputreq()
+{
+ return connman_vpn_settings.timeout_inputreq;
+}
+
+static char *get_string(GKeyFile *config, const char *group, const char *key)
+{
+ char *str = g_key_file_get_string(config, group, key, NULL);
+ return str ? g_strstrip(str) : NULL;
+}
+
+static char **get_string_list(GKeyFile *config, const char *group,
+ const char *key)
+{
+ gsize len = 0;
+ char **str = g_key_file_get_string_list(config, group, key, &len, NULL);
+
+ if (str) {
+ guint i = 0;
+
+ for (i = 0; i < len ; i++) {
+ str[i] = g_strstrip(str[i]);
+ }
+ }
+
+ return str;
+}
+
+static void parse_config(GKeyFile *config, const char *file)
+{
+ const char *group = "General";
+ GError *error = NULL;
+ int timeout;
+
+ if (!config)
+ return;
+
+ DBG("parsing %s", file);
+
+ timeout = g_key_file_get_integer(config, group,
+ "InputRequestTimeout", &error);
+ if (!error && timeout >= 0)
+ connman_vpn_settings.timeout_inputreq = timeout * 1000;
+
+ g_clear_error(&error);
+
+ connman_vpn_settings.binary_user = get_string(config, VPN_GROUP,
+ "User");
+ connman_vpn_settings.binary_group = get_string(config, VPN_GROUP,
+ "Group");
+ connman_vpn_settings.binary_supplementary_groups = get_string_list(
+ config, VPN_GROUP,
+ "SupplementaryGroups");
+}
+
+struct vpn_plugin_data *vpn_settings_get_vpn_plugin_config(const char *name)
+{
+ struct vpn_plugin_data *data = NULL;
+
+ if (plugin_hash)
+ data = g_hash_table_lookup(plugin_hash, name);
+
+ return data;
+}
+
+static void vpn_plugin_data_free(gpointer data)
+{
+ struct vpn_plugin_data *plugin_data = (struct vpn_plugin_data*)data;
+
+ g_free(plugin_data->binary_user);
+ g_free(plugin_data->binary_group);
+ g_strfreev(plugin_data->binary_supplementary_groups);
+
+ g_free(data);
+}
+
+int vpn_settings_parse_vpn_plugin_config(const char *name)
+{
+ struct vpn_plugin_data *data;
+ gchar *file;
+ gchar *ext = ".conf";
+ GKeyFile *config;
+ gint err = 0;
+
+ if (!name || !*name)
+ return -EINVAL;
+
+ if (vpn_settings_get_vpn_plugin_config(name))
+ return -EALREADY;
+
+ file = g_strconcat(PLUGIN_CONFIGDIR, "/", name, ext, NULL);
+
+ config = __vpn_settings_load_config(file);
+
+ if (!config) {
+ err = -ENOENT;
+ DBG("Cannot load config %s for %s", file, name);
+ goto out;
+ }
+
+ data = g_try_new0(struct vpn_plugin_data, 1);
+
+ data->binary_user = get_string(config, VPN_GROUP, "User");
+ data->binary_group = get_string(config, VPN_GROUP, "Group");
+ data->binary_supplementary_groups = get_string_list(config, VPN_GROUP,
+ "SupplementaryGroups");
+
+ DBG("Loaded settings for %s: %s - %s",
+ name, data->binary_user, data->binary_group);
+
+ if (!plugin_hash)
+ plugin_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, vpn_plugin_data_free);
+
+ g_hash_table_replace(plugin_hash, g_strdup(name), data);
+
+ g_key_file_unref(config);
+
+out:
+ g_free(file);
+ return err;
+}
+
+void vpn_settings_delete_vpn_plugin_config(const char *name)
+{
+ if (plugin_hash && name)
+ g_hash_table_remove(plugin_hash, name);
+}
+
+GKeyFile *__vpn_settings_load_config(const char *file)
+{
+ GError *err = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ if (err->code != G_FILE_ERROR_NOENT) {
+ connman_error("Parsing %s failed: %s", file,
+ err->message);
+ }
+
+ g_error_free(err);
+ g_key_file_unref(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+int __vpn_settings_init(const char *file)
+{
+ GKeyFile *config;
+
+ config = __vpn_settings_load_config(file);
+ parse_config(config, file);
+ if (config)
+ g_key_file_unref(config);
+
+ return 0;
+}
+
+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);
+
+ if (plugin_hash) {
+ g_hash_table_destroy(plugin_hash);
+ plugin_hash = NULL;
+ }
+}
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);
+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);
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);
+
+int __vpn_settings_init(const char *file);
+void __vpn_settings_cleanup(void);
+GKeyFile *__vpn_settings_load_config(const char *file);
+unsigned int __vpn_settings_get_timeout_inputreq(void);
+
+struct vpn_plugin_data;
+
+int vpn_settings_parse_vpn_plugin_config(const char* plugin_name);
+void vpn_settings_delete_vpn_plugin_config(const char *name);
+struct vpn_plugin_data* vpn_settings_get_vpn_plugin_config(const char *name);
+
+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);