Tizen 2.0 Release
authorHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 15:12:17 +0000 (00:12 +0900)
committerHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 15:12:17 +0000 (00:12 +0900)
222 files changed:
AUTHORS
COPYING
ChangeLog
HACKING [new file with mode: 0644]
Makefile.am
Makefile.plugins [new file with mode: 0644]
README
TODO [new file with mode: 0644]
acinclude.m4
bootstrap
bootstrap-configure
client/main.c [new file with mode: 0644]
configure.ac [new file with mode: 0644]
connman.manifest [new file with mode: 0644]
connman.pc.in [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/advanced-configuration.txt [new file with mode: 0644]
doc/agent-api.txt [new file with mode: 0644]
doc/backtrace.txt [new file with mode: 0644]
doc/behavior-api.txt [new file with mode: 0644]
doc/clock-api.txt [new file with mode: 0644]
doc/config-format.txt [new file with mode: 0644]
doc/connman-docs.xml [new file with mode: 0644]
doc/connman-introduction.xml [new file with mode: 0644]
doc/counter-api.txt [new file with mode: 0644]
doc/gtk-doc.make [new file with mode: 0644]
doc/ipconfig-api.txt [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/overview-api.txt [new file with mode: 0644]
doc/plugin-api.txt [new file with mode: 0644]
doc/rfc1035.txt [new file with mode: 0644]
doc/rfc2131.txt [new file with mode: 0644]
doc/rfc2132.txt [new file with mode: 0644]
doc/service-api.txt [new file with mode: 0644]
doc/session-api.txt [new file with mode: 0644]
doc/session-overview.txt [new file with mode: 0644]
doc/technology-api.txt [new file with mode: 0644]
doc/version.xml.in [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/polkit.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
gdhcp/client.c [new file with mode: 0644]
gdhcp/common.c [new file with mode: 0644]
gdhcp/common.h [new file with mode: 0644]
gdhcp/gdhcp.h [new file with mode: 0644]
gdhcp/ipv4ll.c [new file with mode: 0644]
gdhcp/ipv4ll.h [new file with mode: 0644]
gdhcp/server.c [new file with mode: 0644]
gsupplicant/dbus.c [new file with mode: 0644]
gsupplicant/dbus.h [new file with mode: 0644]
gsupplicant/gsupplicant.h [new file with mode: 0644]
gsupplicant/supplicant.c [new file with mode: 0644]
gweb/giognutls.c [new file with mode: 0644]
gweb/giognutls.h [new file with mode: 0644]
gweb/gionotls.c [new file with mode: 0644]
gweb/gresolv.c [new file with mode: 0644]
gweb/gresolv.h [new file with mode: 0644]
gweb/gweb.c [new file with mode: 0644]
gweb/gweb.h [new file with mode: 0644]
include/dbus.h [new file with mode: 0644]
include/device.h [new file with mode: 0644]
include/inet.h [new file with mode: 0644]
include/ipconfig.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/network.h [new file with mode: 0644]
include/notifier.h [new file with mode: 0644]
include/option.h [new file with mode: 0644]
include/plugin.h [new file with mode: 0644]
include/provider.h [new file with mode: 0644]
include/proxy.h [new file with mode: 0644]
include/resolver.h [new file with mode: 0644]
include/rtnl.h [new file with mode: 0644]
include/service.h [new file with mode: 0644]
include/setting.h [new file with mode: 0644]
include/storage.h [new file with mode: 0644]
include/task.h [new file with mode: 0644]
include/technology.h [new file with mode: 0644]
include/timeserver.h [new file with mode: 0644]
include/types.h [new file with mode: 0644]
include/utsname.h [new file with mode: 0644]
include/version.h.in [new file with mode: 0644]
packaging/connman.spec [new file with mode: 0644]
plugins/bluetooth.c [new file with mode: 0644]
plugins/connman-nmcompat.conf [new file with mode: 0644]
plugins/dundee.c [new file with mode: 0644]
plugins/ethernet.c [new file with mode: 0644]
plugins/hh2serial-gps.c [new file with mode: 0644]
plugins/iospm.c [new file with mode: 0644]
plugins/iwmx.c [new file with mode: 0644]
plugins/iwmx.h [new file with mode: 0644]
plugins/iwmxsdk.c [new file with mode: 0644]
plugins/l2tp.c [new file with mode: 0644]
plugins/loopback.c [new file with mode: 0644]
plugins/mcc.h [new file with mode: 0644]
plugins/nmcompat.c [new file with mode: 0644]
plugins/ofono.c [new file with mode: 0644]
plugins/openconnect.c [new file with mode: 0644]
plugins/openvpn.c [new file with mode: 0644]
plugins/pacrunner.c [new file with mode: 0644]
plugins/polkit.c [new file with mode: 0644]
plugins/polkit.policy [new file with mode: 0644]
plugins/pptp.c [new file with mode: 0644]
plugins/telephony.c [new file with mode: 0644]
plugins/tist.c [new file with mode: 0644]
plugins/vpn.c [new file with mode: 0644]
plugins/vpn.h [new file with mode: 0644]
plugins/vpnc.c [new file with mode: 0644]
plugins/wifi.c [new file with mode: 0644]
resources/etc/rc.d/init.d/connman [new file with mode: 0755]
resources/usr/share/dbus-1/services/net.connman.service [new file with mode: 0644]
resources/var/lib/connman/settings [new file with mode: 0644]
scripts/connman.in [new file with mode: 0644]
scripts/libppp-plugin.c [new file with mode: 0644]
scripts/openconnect-script.c [new file with mode: 0644]
scripts/openvpn-script.c [new file with mode: 0644]
src/6to4.c [new file with mode: 0644]
src/agent.c [new file with mode: 0644]
src/bridge.c [new file with mode: 0644]
src/clock.c [new file with mode: 0644]
src/config.c [new file with mode: 0644]
src/connection.c [new file with mode: 0644]
src/connman-dbus.conf [new file with mode: 0644]
src/connman-polkit.conf [new file with mode: 0644]
src/connman.h [new file with mode: 0644]
src/connman.service.in [new file with mode: 0644]
src/connman.ver [new file with mode: 0644]
src/counter.c [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/detect.c [new file with mode: 0644]
src/device.c [new file with mode: 0644]
src/dhcp.c [new file with mode: 0644]
src/dhcpv6.c [new file with mode: 0644]
src/dnsproxy.c [new file with mode: 0644]
src/error.c [new file with mode: 0644]
src/genbuiltin [new file with mode: 0755]
src/inet.c [new file with mode: 0644]
src/ipconfig.c [new file with mode: 0644]
src/ippool.c [new file with mode: 0644]
src/iptables.c [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/main.conf [new file with mode: 0644]
src/manager.c [new file with mode: 0644]
src/nat.c [new file with mode: 0644]
src/network.c [new file with mode: 0644]
src/notifier.c [new file with mode: 0644]
src/ntp.c [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/provider.c [new file with mode: 0644]
src/proxy.c [new file with mode: 0644]
src/resolver.c [new file with mode: 0644]
src/rfkill.c [new file with mode: 0644]
src/rtnl.c [new file with mode: 0644]
src/service.c [new file with mode: 0644]
src/session.c [new file with mode: 0644]
src/stats.c [new file with mode: 0644]
src/storage.c [new file with mode: 0644]
src/task.c [new file with mode: 0644]
src/technology.c [new file with mode: 0644]
src/tethering.c [new file with mode: 0644]
src/timeserver.c [new file with mode: 0644]
src/timezone.c [new file with mode: 0644]
src/utsname.c [new file with mode: 0644]
src/wispr.c [new file with mode: 0644]
src/wpad.c [new file with mode: 0644]
test/backtrace [new file with mode: 0755]
test/connect-vpn [new file with mode: 0755]
test/disable-tethering [new file with mode: 0755]
test/disconnect-vpn [new file with mode: 0755]
test/enable-tethering [new file with mode: 0755]
test/get-global-timeservers [new file with mode: 0755]
test/get-proxy-autoconfig [new file with mode: 0755]
test/get-services [new file with mode: 0755]
test/get-state [new file with mode: 0755]
test/list-services [new file with mode: 0755]
test/monitor-connman [new file with mode: 0755]
test/monitor-services [new file with mode: 0755]
test/service-move-before [new file with mode: 0755]
test/set-domains [new file with mode: 0755]
test/set-global-timeservers [new file with mode: 0755]
test/set-ipv4-method [new file with mode: 0755]
test/set-ipv6-method [new file with mode: 0755]
test/set-nameservers [new file with mode: 0755]
test/set-proxy [new file with mode: 0755]
test/set-timeservers [new file with mode: 0755]
test/show-introspection [new file with mode: 0755]
test/simple-agent [new file with mode: 0755]
test/test-clock [new file with mode: 0755]
test/test-compat [new file with mode: 0755]
test/test-connman [new file with mode: 0755]
test/test-counter [new file with mode: 0755]
test/test-manager [new file with mode: 0755]
test/test-new-supplicant [new file with mode: 0755]
test/test-session [new file with mode: 0755]
test/test-supplicant [new file with mode: 0755]
tools/addr-test.c [new file with mode: 0644]
tools/dbus-test.c [new file with mode: 0644]
tools/dhcp-server-test.c [new file with mode: 0644]
tools/dhcp-test.c [new file with mode: 0644]
tools/iptables-test.c [new file with mode: 0644]
tools/polkit-test.c [new file with mode: 0644]
tools/private-network-test.c [new file with mode: 0644]
tools/resolv-test.c [new file with mode: 0644]
tools/stats-tool.c [new file with mode: 0644]
tools/supplicant-dbus.c [new file with mode: 0644]
tools/supplicant-dbus.h [new file with mode: 0644]
tools/supplicant-test.c [new file with mode: 0644]
tools/supplicant.c [new file with mode: 0644]
tools/supplicant.h [new file with mode: 0644]
tools/tap-test.c [new file with mode: 0644]
tools/web-test.c [new file with mode: 0644]
tools/wispr.c [new file with mode: 0644]
tools/wpad-test.c [new file with mode: 0644]
unit/manager-api.c [new file with mode: 0644]
unit/session-api.c [new file with mode: 0644]
unit/test-connman.h [new file with mode: 0644]
unit/test-ippool.c [new file with mode: 0644]
unit/test-nat.c [new file with mode: 0644]
unit/test-session.c [new file with mode: 0644]
unit/utils.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 48482e8..33404c4 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,48 @@
 Marcel Holtmann <marcel@holtmann.org>
+Inaky Perez-Gonzalez <inaky@linux.intel.com>
+Samuel Ortiz <sameo@linux.intel.com>
+Joshua Lock <josh@linux.intel.com>
+Richard Purdie <rpurdie@linux.intel.com>
+Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
+Martin Xu <martin.xu@intel.com>
+Sam Leffler <sleffler@google.com>
+Daniel Wagner <daniel.wagner@bmw-carit.de>
+Forest Bond <forest@alittletooquiet.net>
+Kalle Valo <kalle.valo@canonical.com>
+Fabien Marotte <fabienx.marotte@linux.intel.com>
+Pekka Pessi <pekka.pessi@nokia.com>
+Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+Cristiano Fernandes <cristiano.fernandes@hp.com>
+Joey Lee <jlee@novell.com>
+Leena Gunda <leena.gunda@wipro.com>
+Patrik Flykt <patrik.flykt@linux.intel.com>
+David Woodhouse <dwmw2@infradead.org>
+Gustavo Padovan <gustavo@padovan.org>
+Julien Massot <jmassot@aldebaran-robotics.com>
+Jukka Rissanen <jukka.rissanen@linux.intel.com>
+Grant Erickson <marathon96@gmail.com>
+Guillaume Lucas <guillaumex.lucas@intel.com>
+Henri Bragge <henri.bragge@ixonos.com>
+Alok Barsode <alok.barsode@intel.com>
+Sébastien Bianti <sebastien.bianti@linux.intel.com>
+Yu A Wang <yu.a.wang@intel.com>
+Thierry Boureille <thierry.boureille@gmail.com>
+Paolo Pellegrino <paolo.pellegrino@zirak.it>
+Bertrand Aygon <bertrand.aygon@intel.com>
+Jeff Zheng <jeff.zheng@intel.com>
+Philippe Nunes <philippe.nunes@linux.intel.com>
+Danny Jeongseok Seo <s.seo@samsung.com>
+Flávio Ceolin <flavio.ceolin@profusion.mobi>
+Arjan van de Ven <arjan@linux.intel.com>
+Daniel Mack <zonque@gmail.com>
+Guillaume Zajac <guillaume.zajac@linux.intel.com>
+Manfred Kober <manfred.kober@gmx.de>
+Mario Domenech Goulart <mario.goulart@gmail.com>
+Otavio Salvador <otavio@ossystems.com.br>
+Tim Sander <tim01@iss.tu-darmstadt.de>
+Adrien Bustany <adrien.bustany@nokia.com>
+Henrique Dante de Almeida <hdante@profusion.mobi>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Elena Tebesoi <elena.tebesoi@gmail.com>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Paulo Pizarro <paulo.pizarro@gmail.com>
diff --git a/COPYING b/COPYING
index 3912109..6d45519 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-\f
+
                    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-\f
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-\f
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-\f
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 
                     END OF TERMS AND CONDITIONS
-\f
+
            How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
index e69de29..a069a74 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,753 @@
+ver 1.3:
+       Fix issue with default configuration values.
+       Fix issue with timeserver canonical name entries.
+       Fix issue with crash from cellular dummy context.
+       Fix issue with incorrect index for private networks.
+
+ver 1.2:
+       Fix issue with not handling WiFi security changes.
+       Fix issue with not stopping WiFi scanning on shutdown.
+       Fix issue with auto-scanning and network discovery.
+       Fix issue with D-Bus reply for hidden WiFi networks.
+       Fix issue with overlapping memory areas and DNS requests.
+       Add support for randomized DNS transaction identifiers.
+       Add support for DNS caching over TCP connections.
+       Add support for using default IPv6 privacy setting.
+       Add support for providing previous passphrase to agent.
+       Add support for configuration unprovisioning handling.
+       Add support for NetworkInterfaceBlacklist configuration.
+       Add support for Bluetooth DUN daemon (dundee).
+
+ver 1.1:
+       Fix issue with missing message type and DHCPv4 support.
+       Fix issue with potential NULL pointer in DHCPv6 handling.
+       Fix issue with potential NULL pointer in VPN handling.
+       Fix issue with potential NULL pointer for WiFi SSID.
+       Fix issue with missing conversion of raw WiFi PSK input.
+       Fix issue with missing stop for WiFi auto-scanning handling.
+       Fix issue with uninitialized IPv6 prefix length in oFono plugin.
+       Fix issue with domain search list handling according to RFC 6106.
+       Fix issue with domain name list notifications.
+       Fix issue with nameserver list notifications.
+       Fix issue with incorrect fixed IP configuration.
+       Fix issue with incorrect cleanup of resolver timers.
+       Fix issue with handling of RDNSS lifetime expiration.
+       Fix issue with crash on wrong domain length information.
+       Add support for favorite service database migration.
+       Add support for disabling WISPr functionality.
+       Add support for configurable agent timeouts.
+
+ver 1.0:
+       Fix issue with missing WiFi disconnecting flag.
+       Fix issue with missing GPRS context attached check.
+       Fix issue with potential crash and supplicant handling.
+       Fix issue with potential crash and VPN provider.
+       Fix issue with potential crash and host routes.
+
+ver 0.85:
+       Fix issue with duplicate service timeservers.
+       Fix issue with failure state when aborting agent request.
+       Fix issue with automatic scanning for hidden WiFi networks.
+       Fix issue with missing hostname/domainname validity checks.
+       Fix issue with missing DHCP packet length checks.
+       Add support for browser launching from WISPr login.
+
+ver 0.84:
+       Fix issue with handling changed WiFi security of access points.
+       Fix issue with state notification of NetworkManager compatibility.
+
+ver 0.83:
+       Fix issue with Ethernet not being enabled by default.
+       Fix issue with missing host routes for WISPr request.
+       Fix issue with missing HTTPS handling for WISPr support.
+       Fix issue with agent asking for passphrase for open service.
+       Fix issue with endless online check for preferred technology.
+       Fix issue with IPv6 RDNSS and DNS server creation.
+       Fix issue with WiFi roaming state change handling.
+       Fix issue with broken handling of WPS PBC method.
+
+ver 0.82:
+       Fix issue with global online state handling.
+       Fix issue with timeserver handling in case of VPN.
+       Fix issue with automatic WiFi scanning handling.
+       Fix issue with WPS PIN length of zero and PBC.
+
+ver 0.81:
+       Update default configuration options.
+       Add support for WPS PBC advertising handling.
+       Add support for wpa_supplicant background scanning.
+       Fix issue with showing cellular services without APN.
+       Fix issue with missing timeservers changed signal.
+
+ver 0.80:
+       Update to support additional D-Bus API changes.
+       Add support for preferred technologies switching.
+       Add support for default auto connect technologies option.
+       Add support for service specific timeserver configuration.
+       Add support for extended timeserver fallback functionality.
+       Add support for extended nameserver fallback functionality.
+       Add support for user supplied routes for providers.
+       Add support for checking WiFi passphrase validity.
+       Fix issue with WISPr support and proxy handling.
+       Fix issue with Ethernet and auto connect handling.
+       Fix issue with too early IPv6 auto connection.
+       Fix issue with too early 6to4 connection checks.
+       Fix issue with oFono interaction on disconnect.
+       Fix issue with DNS servers behind point-to-point links.
+       Fix issue with pending DNS proxy requests.
+       Fix multiple issues with VPN handling.
+       Fix multiple issues with default gateway handling.
+
+ver 0.79:
+       Update to support changed D-Bus API.
+       Add support for WiFi background scanning.
+       Add support for showing hidden WiFi networks as services.
+       Add support for generic stateless DHCPv6 handling.
+       Add support for DHCPv4 client identifier handling.
+       Add support for generic IP address pool handling.
+       Add support for global DNS cache handling.
+       Add support for internal NTP time handling.
+       Add support for updated oFono handling.
+
+ver 0.78:
+       Fix multiple issues with service connection states.
+       Fix multiple issues with technology handling.
+       Fix issue with DHCP file descriptor leakage.
+       Fix issue with open access points and WPS.
+       Fix issue with handling of cellular devices.
+       Fix issue with DNS proxy hostname resolving.
+       Add support for PPTP and L2TP VPN tunneling.
+       Add support for organized settings storage.
+       Add support for WiFi fast connect handling.
+       Add support for better WiFi error handling.
+       Add support for integrated WISPr handling.
+
+ver 0.77:
+       Fix issue with iptables API breakage.
+       Fix issue with agent input handling.
+       Fix issue with empty cellular operator name.
+       Fix issue with reference counting for network objects.
+       Fix issue with missing D-Bus signals for proxy changes.
+       Fix issue with group identifier and hidden WiFi networks.
+       Fix issue with setting wrong gateway for PPP connections.
+       Fix issue with mismatch of stored IP configuration settings.
+       Fix issue with not stopping DHCP for IPv4 configuration.
+       Add support for remembering last IP address from DHCP.
+       Add support for EAP-GTC authentication method.
+
+ver 0.76:
+       Fix issue with loopback interface setup.
+       Fix issue with /etc/localtime being a symlink.
+       Fix issue with not handling dummy network devices.
+       Fix issue with not provisioning existing services.
+       Fix issue with running WPAD on IPv6 connections.
+       Fix issue with client certificate for TTLS/PEAP.
+       Remove internal element infrastructure.
+
+ver 0.75:
+       Fix issue with 3G connect timeout handling.
+       Fix issue with WiFi raw key PSK handling.
+       Fix issue with DHCP renewal timeout handling.
+       Fix issue with DHCP and empty nameserver list.
+       Add support for unit testing.
+
+ver 0.74:
+       Fix issue with race condition in ready/online handling.
+       Fix issue with DHCP release callback handling.
+       Fix multiple issues with session API handling.
+       Add support for using DNS proxy for Tethering.
+       Add support for Private Network API.
+       Add support for Clock API.
+
+ver 0.73:
+       Update support for session API handling.
+       Add support for more advanced roaming policies.
+       Add support for EAP identity and passphrase queries.
+       Add support for IPv6 handling with cellular bearer.
+       Add support for main configuration file.
+       Remove deprecated profile interface.
+
+ver 0.72:
+       Fix issue with overwriting DNS servers from DHCP.
+       Fix issue with DHCP renewal with same configuration.
+       Fix issue with multiple memory leaks.
+       Add support for 6to4 tunneling.
+
+ver 0.71:
+       Fix issue with not storing IPv6 privacy settings.
+       Fix issue with storing fixed and manual IP settings.
+       Fix issue with service state when PAN connection fails.
+       Fix issue with tethering and WiFi bridge handling.
+       Fix issue with autonomously activated contexts.
+       Fix issue with nameserver array for PACrunner.
+       Fix issue with network information memory leak.
+       Fix issue with double-free in statistics handling.
+       Fix issue with handling malformed profiles.
+       Fix issue with pending DHCP client requests.
+       Add initial support for TI shared transport handling.
+
+ver 0.70:
+       Add support for reporting invalid WiFi passphrases.
+       Add support for IPv6 privacy extension.
+       Add support for IPv6 advanced features.
+       Add support for IPv6 nameserver settings.
+       Remove deprecated APN service settings.
+
+ver 0.69:
+       Fix issue with not handling DNS proxy failures gracefully.
+       Fix issue with double free in statistics handling.
+       Fix issue with early tethering bridge creation.
+       Add support for tethering property per technology.
+       Add support for initial WiFi tethering feature.
+       Add support for using PACrunner as proxy driver.
+       Add support for WPS as part of the security property.
+
+ver 0.68:
+       Fix issue with wrong name of PolicyKit configuration file.
+       Fix issue with inconsistency of WiFi scan states.
+       Fix issue with missing auto-reconnect and oFono.
+       Add support for vpnc based private networks.
+       Add support for WiFi Protected Setup handling.
+       Remove legacy WiFi plugin.
+
+ver 0.67:
+       Fix issue with oFono plugin and multiple online calls.
+       Fix issue with checking for AutoConnect service property.
+       Remove deprecated MCC and MNC service properties.
+
+ver 0.66:
+       Fix multiple set of memory leaks.
+       Fix issue with WPA supplicant phase2 values.
+       Fix issue with WiFi access points after kill/restart.
+       Fix issue with correct PACrunner configuration loading.
+       Add support for loading provision files at runtime.
+       Add support for setting proxy auto-configuration.
+       Add support for IPv6 auto-configured addresses.
+
+ver 0.65:
+       Use new WiFi plugin by default.
+       Fix issue with handling already powered devices.
+       Fix issue with handling proxy PAC option from DHCP.
+       Add support for handling regulatory domain settings.
+       Add support for handling IPv6 router advertisements.
+       Add support for handling IPv6 nameservers.
+       Add support for handling multiple search domains.
+       Add support for handling oFono modem lockdown.
+       Add support for handling IPv6 DNS connections.
+       Add support for IPv4 Link-Local negotiation.
+       Add support for USB CDC Tethering functionality.
+
+ver 0.64:
+       Update service name to net.connman domain.
+       Fix issue with disabling a technology twice.
+       Fix issue with using wrong string for Proxy.Method.
+       Fix issue with TCP connection lookup and DNS proxy.
+       Fix issue with TCP receive busy waits and DNS proxy.
+       Fix various issues with WPA Supplicant interaction.
+       Add support for chunk encoding to HTTP client.
+       Add support for internal HTTP client for portal detection.
+       Add support for internal DHCP server setup.
+       Add support for internal NAT and IP forwarding setup.
+       Add support for Bluetooth Tethering functionality.
+       Remove deprecated device and network D-Bus interfaces.
+       Remove support for dhclient plugin.
+
+ver 0.63:
+       Change to use nl80211/cfg80211 WiFi management by default.
+       Fix various issues with new WPA Supplicant interface.
+       Fix issue with not connecting failed networks at boot.
+       Fix issue with properly tracking RFKILL blocked state.
+       Fix issue with missing signals for IPv4/IPv6 gateway details.
+       Add support for using RTNL for setting IPv4/IPv6 details.
+       Add support for using PHONET_PIPE GPRS interfaces.
+       Add support for setting manual proxy configurations.
+       Add support for exporting proxy configurations to PACrunner.
+       Add support for combined home and roaming statistics.
+       Add support for OpenVPN connections.
+       Remove dependency on udev.
+
+ver 0.62:
+       Fix crash with non-existent or extra DHCP result options.
+       Fix crash when doing PEAP/TTLS authentication.
+       Fix issue with handling multiple data counters.
+       Fix issue with Bluetooth adapters without address.
+       Fix issue with multiple scanning attempts after disconnects.
+       Fix issue with VPN services when switching into offline mode.
+       Add support for storing statistics information in separate files.
+       Add support for verification of configuration file parameters.
+       Add support for handling time server values from DHCP.
+       Add support for allowing DNS over TCP within the DNS proxy.
+       Add support for loading proxy configurations into PACrunner.
+       Add support for WiFi plugin using new WPA Supplicant D-Bus API.
+       Add support for requesting passphrases via agents.
+       Remove default support for EDNS0 option.
+
+ver 0.61:
+       Add support for using the internal DHCP client by default.
+       Add support for latest PolicyKit framework.
+       Add support for new oFono D-Bus interfaces.
+
+ver 0.60:
+       Fix issue with missing reset of proxy settings.
+       Fix issue with missing Ethernet property changed signal.
+       Fix issue with offline operation on already blocked devices.
+       Fix issue with offline mode and device powered changes.
+       Fix issue with portal detection and DHCP renewals.
+       Fix issue with connection attempts for removed networks.
+       Fix issue with stale pointers of networks.
+
+ver 0.59:
+       Fix issue with D-Bus object paths of VPN providers.
+
+ver 0.58:
+       Fix various issues around offline mode.
+       Fix various issues with VPN nameserver handling.
+       Add support for home/roaming network statistics.
+       Add support for EAP-TTLS WiFi configuration.
+       Add support for creating backtraces.
+
+ver 0.57:
+       Fix missing default profile creation.
+       Fix missing service integration of VPN providers.
+       Fix missing export of PAC information retrieved from DHCP.
+       Fix issue with detection of new Bluetooth devices.
+       Fix issue with offline mode handling.
+       Fix issue with device power handling.
+
+ver 0.56:
+       Fix issues with offline mode handling.
+       Fix service integration with VPN providers.
+       Add internal asynchronous resolver library.
+       Add internal DHCP client library.
+       Add support for using internal DHCP client.
+       Add support for WPAD proxy auto-configuration.
+       Add support for static IPv6 configuration.
+       Add support for DHCP provided domain names.
+       Add initial support for on-demand connections.
+       Remove uDHCP and resolvconf plugins.
+
+ver 0.55:
+       Fix issue with 3G roaming status indication.
+       Fix issue with using -H option with dhclient.
+       Fix issue with loading WiFi SSID details for scanning.
+       Add support for setting host routes for DNS servers.
+       Add support for more detailed statistics counters.
+       Add support for internal DHCP client library.
+
+ver 0.54:
+       Fix issue with root requests and EDNS0 OPT records.
+       Fix issue with default gateway when route deletion fails.
+       Fix issue with group identifiers for cellular networks.
+       Fix issue with fixed IP settings from cellular networks.
+       Fix issue with nameserver settings and manual configuration.
+       Add support for cellular network name changes.
+       Add support for cellular signal strength changes.
+       Add support for actively scanning for hidden networks.
+       Add support for ASCII based WEP keys.
+       Add support for NTP timeserver updates.
+       Add support for PPP default route settings.
+
+ver 0.53:
+       Fix issue with supplicant and device scanning state cleaning.
+       Fix issue with Bluetooth PAN networks stay in connected state.
+       Fix issue with reference counting and connected state.
+       Fix issue with technology disabling on device removal.
+       Fix issue with two default gateways when using VPN.
+       Fix issue with static IPv4 configuration and signals.
+       Add support for splitting DHCP provided nameserver results.
+       Add support multiple nameservers in /etc/resolv.conf.
+       Add support for setting manual DNS server configuration.
+       Add support for exporting IPv4 gateway information.
+       Add support for newer versions of oFono API.
+
+ver 0.52:
+       Fix issue with new "connected" states.
+       Fix issue with hidden networks and PSK.
+       Fix issue with DHCP and Bluetooth PAN.
+       Fix issue when disconnecting PAN networks.
+       Add support for application sessions.
+       Add plugin for hh2serial GPS support.
+
+ver 0.51:
+       Fix issue with missing device power toggling.
+       Fix issue with D-Bus object path on device removal.
+       Add support for WiFi portal detection.
+       Add support for configuring static gateways.
+       Remove unneeded plugin for Option HSO support.
+       Remove unneeded plugin for Ericsson MBM support.
+
+ver 0.50:
+       Fix configuration loading for unknown services.
+       Fix IP method setting of Ethernet plugin.
+
+ver 0.49:
+       Fix issue with WiFi power changes.
+       Fix issue with Bluetooth device startup.
+       Fix issue with host route settings for VPN.
+       Fix issue with processing of RFKILL events.
+       Fix some WPA Enterprise privacy issues.
+       Add support for basic Ethernet information.
+       Add support for static IP settings.
+
+ver 0.48:
+       Fix signal strength calculation when quality is not provided.
+       Fix issues with wpa_supplicant state tracking.
+       Fix faulty removal of IP address from interface.
+       Fix permissions of newly created /etc/resolv.conf file.
+       Fix DNS proxy handling when in offline mode.
+       Add support for EDNS0 resolver option.
+       Add workaround for large EDNS0 queries.
+       Add workaround for DHCP startup failures with WiFi networks.
+       Add support for handling hostnames and domainnames.
+       Add support for IPv4 configuration via service interface.
+       Add support for fixed and manual IPv4 configuration.
+       Add support for default service changed notifier.
+       Add support for clearing failure state via service removal.
+       Add support for OpenConnect VPN connections.
+       Add support for IEEE 802.1x WiFi networks.
+       Add support for roaming between WPA and WPA2 networks.
+       Add various generic D-Bus helpers and use them.
+       Remove special handling of Ethernet devices.
+
+ver 0.47:
+       Fix segmentation fault on resolver shutdown.
+       Fix issue with adding nameserver that doesn't exist.
+       Fix issue when no broadcast address is given.
+       Fix issue with missing property changed signal.
+       Add checks for invalid supplicant state transitions.
+       Add initial version of oFono GPRS support.
+       Add support for dynamic debug framework.
+
+ver 0.46:
+       Fix reconnect issue when power off or disabling the device.
+       Remove problematic retry on failure code path.
+
+ver 0.45:
+       Fix crash with connect timeout and second connect attempt.
+       Fix reconnect issues after suspend or roaming attempt.
+
+ver 0.44:
+       Fix command line options for device filtering.
+       Fix issue with network reference in MBM support.
+       Fix handling when losing network access in MBM plugin.
+       Fix broken libiWmxSDK callback parameter handling.
+       Add work around Intel WiMAX SDK API breakage.
+
+ver 0.43:
+       Fix issue with missing scanning after power up.
+       Fix issue with udev versus /dev/rfkill event processing.
+       Fix issue with powered down device on connection attempt.
+       Add support for multiple connection attempts.
+       Add support for tracking the operation state.
+       Add full support for Ericsson MBM cellular devices.
+
+ver 0.42:
+       Fix issue with switching between hidden WiFi networks.
+       Fix issue with missing scanning after disconnect.
+       Fix issue with not triggering auto-connect in some cases.
+
+ver 0.41:
+       Fix race condition with WiFi devices and RFKILL.
+       Fix issue with WiFi connect/disconnect and some drivers.
+       Fix issue with WEP encryption and staging drivers.
+       Fix issue with wrong setup of loopback interfaces.
+
+ver 0.40:
+       Fix issue with wrong setting of initial AutoConnect value.
+       Fix issue with IP configuration and loopback devices.
+       Fix issue with build system and include directory.
+       Fix wrong variable for dhclient-script location.
+       Fix disconnect race condition with Bluetooth service.
+       Add support for ignoring bonding Ethernet interfaces.
+
+ver 0.39:
+       Fix file permissions for profile storage.
+       Fix service resorting when they are in different states.
+       Fix support for handling Bluetooth PAN devices.
+       Add support for AutoConnect property of services.
+       Add support for creating, modifying and removing profiles.
+       Add support for fully flexible task handling framework.
+       Add support for more generic RTNL handling and notifications.
+       Add support for full non-recursive build.
+
+ver 0.38:
+       Fix broken check for security modes.
+       Fix requirement of inotify when loopback support is disabled.
+
+ver 0.37:
+       Fix missing update of signal strength from scan results.
+       Fix error handling in case when passphrase is required.
+       Add support for PassphraseRequired property.
+       Add missing check for WiFi security modes.
+
+ver 0.36:
+       Fix missing reset of network reference when disconnecting.
+       Fix wrong variable reference when sending technology replies.
+       Fix wrong identifiers of D-Bus error names.
+
+ver 0.35:
+       Fix missing auto-connect trigger on Ethernet device removal.
+       Fix availability listing for devices without attached drivers.
+       Fix signals for connected and default technologies.
+       Fix notification to use service types instead of device types.
+       Fix potential pending scan result reply messages after removal.
+       Add support for blocking enable and disable technology changes.
+
+ver 0.34:
+       Fix setup of udev context before loading any plugins.
+       Fix rearming the scan trigger if a device got disabled.
+       Fix device power state changes tracking with RFKILL notifications.
+       Fix wrong usage of device types instead of service types.
+       Fix connect method to handle non-WiFi services.
+
+ver 0.33:
+       Add support for RFKILL changes of the WiFi subsystem.
+       Fix state value of Network Manager compatibility support.
+
+ver 0.32:
+       Fix broken device unregistration on removal.
+       Fix WiMAX device detection handling.
+
+ver 0.31:
+       Fix missing enforcement of offline mode for new devices.
+       Add support for persistent storage of offline mode.
+       Add support for persistent storage of device power state.
+       Remove deprecated and unused network storage callbacks.
+
+ver 0.30:
+       Fix issue where hidden network could show up in service list.
+       Fix issue with asynchronous notification of scan requests.
+       Fix message reference leak when adding interface fails.
+       Fix problem when removing network during inactive state.
+       Remove broken and unused callback for joining networks.
+       Remove deprecated device and network interface methods.
+       Remove test scripts for deprecated interface methods.
+
+ver 0.29:
+       Fix missing signal emission for offline mode changes.
+       Fix signal emission for changes in technology properties.
+       Rename Technologies property to AvailableTechnologies.
+
+ver 0.28:
+       Fix another reference counting imbalance when adding networks.
+       Revert supplicant change to always reset scanning after results.
+
+ver 0.27:
+       Fix missing disarming of the connection timeout.
+       Fix handling of multiple supplicant disconnect attempts.
+       Fix simultaneous connects from different technologies limitation.
+
+ver 0.26:
+       Fix broken handling of auto-connect logic.
+       Fix handling of out-of-range access points.
+       Fix support for connecting to hidden networks.
+       Fix reference counting for networks with same SSID.
+       Fix issue with WiFi interfaces not getting switched off.
+       Fix problems with delayed service list updates.
+       Fix disconnect/abort of connection attempts.
+
+ver 0.25:
+       Fix showing of WiFi networks with less than 25% signal strength.
+       Fix potential segmentation fault with network passphrases.
+
+ver 0.24:
+       Fix handling of initial device powered state.
+       Fix missing Powered property changed signals.
+       Fix canceling of a network connection attempt.
+       Fix stalled configuration issue with supplicant.
+       Fix detection of association errors from supplicant.
+       Fix issue with wrong scanning state information.
+       Fix hidden SSID detection routines.
+       Fix visible Ethernet services even without carrier.
+       Add global method call to request scanning.
+       Add support for global technologies list.
+       Add support for delaying service list updates.
+       Update the overall D-Bus API documentation.
+
+ver 0.23:
+       Fix dhclient probe/remove race condition.
+       Fix handling of disconnected services during auto-connect.
+       Add support for proper group name of hidden networks.
+       Add support for storing SSID details of hidden networks.
+
+ver 0.22:
+       Fix wrong auto-connect procedure after user connection.
+       Fix invalid update of already connected network.
+       Fix idle state handling after disconnecting device.
+       Fix disconnect race condition in WiFi supplicant.
+       Fix WiFi signal strength reporting.
+
+ver 0.21:
+       Add udev based network device detection.
+       Add support for global auto-connect feature.
+       Add support for basic service drag and drop.
+       Fix missing passphrase cleanup on service removal.
+       Fix potential duplicate network creation.
+       Fix handling of WEP shared keys.
+
+ver 0.20:
+       Add plugin for Intel WiMAX SDK support.
+       Add special handling for default vendor SSIDs.
+       Add support for default gateway in different network.
+       Add support for automatic switching of default gateway.
+       Add support for asynchronous handling of Powered property.
+       Add support for connecting/disconnecting Ethernet services.
+       Add support for more detailed error states of services.
+       Add support for clearing error state via ClearProperty.
+       Fix error code for invalid or unknown properties.
+       Fix various timeout handling issues.
+       Remove Policy and Priority device and network properties.
+
+ver 0.19:
+       Add hidden networks to the service list.
+       Add support for storing the service name.
+       Fix service list sorting for connected services.
+       Fix missing cancel command when operation times out.
+       Fix various issues with service favorite handling.
+       Remove Available and Remember network properties.
+
+ver 0.18:
+       Add support for asynchronous service connect method.
+       Fix broken storage of service favorite details.
+
+ver 0.17:
+       Add AT chat library implementation.
+       Fix service lookup for WiFi and WiMAX devices.
+       Fix service state signal emission and error handling.
+       Fix storing and loading of configured passphrases for services.
+
+ver 0.16:
+       Update Intel OSPM support to latest specification.
+       Add initial support for new service interface.
+       Add support for builtin plugins.
+       Add extra warning if no nameserver is defined.
+       Add error reporting for state and storage directory creation.
+       Add error message for network and device storing failures
+       Fix stale entry in gateway list after connection changes.
+       Fix handling of DHCP results with no nameserver.
+       Fix infinite loop for service lookup.
+       Fix various format string warnings.
+
+ver 0.15:
+       Detect VMware network interface and ignore them.
+       Fix setting of scan_ssid for hidden networks.
+       Fix empty network name property.
+
+ver 0.14:
+       Add support for detecting DHCP failures.
+       Add support for joining hidden WiFi networks.
+       Add support for device and network address property.
+       Add support for default /etc/resolv.conf generation.
+       Fix issue with wrong address setting for loopback.
+       Fix detection of WiFi access point changes.
+       Fix crash with blob properties.
+
+ver 0.13:
+       Add support for notification infrastructure.
+       Add fully dynamic property storage capabilities.
+       Fix broken loading of last network on bootup.
+       Fix crash when unplugging WiFi devices.
+       Rename OSPM plugin to Intel OSPM plugin.
+       Rename WiMAX plugin to Intel WiMAX SDK plugin.
+
+ver 0.12:
+       Fix connection state change handling.
+       Fix network list enumeration.
+       Fix broken driver matching for devices.
+       Fix issue with network identifier lookup.
+
+ver 0.11:
+       Add plugin priority handling.
+       Add network type for WiMAX.
+       Fix network protocol selection for Bluetooth PAN.
+       Fix parameters for Bluetooth PAN disconnect method.
+
+ver 0.10:
+       Fix races with connection signals.
+       Fix automatic switching of default connection.
+
+ver 0.9:
+       Rename FlightMode to OfflineMode.
+       Add static IPv4 setting support for Ethernet devices.
+       Add extra options to exclude devices and plugins.
+       Add support for toggling debug output.
+       Add support for ScanInterval property.
+       Fix handling of disconnect commands from applications.
+       Fix detection of networks that are out of range.
+       Fix setting network remember status.
+       Fix argument type checking of properties.
+
+ver 0.8:
+       Add Device and Network property to connection interface.
+       Add option to disable installation of data files.
+       Add command line option to show version number.
+       Fix signal emission for network changes.
+
+ver 0.7:
+       Add basic support for flight mode.
+       Add support for multiple storage drivers.
+       Add support for RTNL newlink watch API.
+       Add support for different security privileges.
+       Add support for device and network priorities.
+       Add functions for setting network properties.
+       Fix issue with listing devices without a driver.
+       Fix issue with WiFi scanning indication.
+       Fix detection of WiFi security changes.
+       Update WiFi driver to use new network helpers.
+       Install different D-Bus configuration for PolicyKit.
+
+ver 0.6:
+       Add CONNMAN_API_SUBJECT_TO_CHANGE definition.
+       Add detailed configuration options.
+       Add various D-Bus helper functions.
+       Add generic device driver infrastructure.
+       Add generic network driver infrastructure.
+       Add property for WiFi network mode.
+       Add property for network interface name.
+       Add property for global connection policy.
+       Add support for verbose compiler warnings.
+       Add support for device detection via udev.
+       Add support for systems with udhcpc.
+       Add support for Bluetooth PAN networks.
+       Fix WiFi issue with DHCP restart after handshake.
+       Fix exported symbols list creation.
+       Remove deprecated and unused plugins.
+
+ver 0.5:
+       Add support for handling Bluetooth adapters.
+       Add support for activating wpa_supplicant on demand.
+       Add Device property to network objects.
+       Add Scanning property to device objects.
+       Fix Name property of device objects.
+       Fix WiFi SSID to object path conversion.
+       Fix duplicate wireless scan results.
+       Fix built issue with libudev and uClibc.
+       Fix issues with element registration failures.
+
+ver 0.4:
+       Add DNS proxy resolver plugin.
+       Add support for default connections.
+       Add support for gateway change notifications.
+       Add signal strength property for connections.
+       Add property for connection type.
+       Fix issue with carrier detection.
+       Fix broken resolvconf plugin.
+
+ver 0.3:
+       Add support for automatically connecting known networks.
+       Add improved framework for handling resolver details.
+       Add generic signal strength property.
+       Fix bridge and WiMAX device detection.
+       Fix network listing for Ethernet devices.
+
+ver 0.2:
+       Add support for indicating network changes.
+       Add support for signal strength property.
+       Add support for unique device names.
+       Fix broken device enumeration.
+       Fix issue with device removal callback.
+       Fix issue with wpa_supplicant disconnecting.
+       Fix D-Bus access policy configuration.
+
+ver 0.1:
+       Initial public release.
diff --git a/HACKING b/HACKING
new file mode 100644 (file)
index 0000000..05fb69c
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,144 @@
+Hacking on Connection Manager
+*****************************
+
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+  Check version
+    # dpkg -l '*automake*'
+
+  Install new version
+    # apt-get install automake1.10
+    # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+  Checkout repository
+    # git clone git://git.kernel.org/pub/scm/network/connman/connman.git
+    # cd connman
+
+  Configure and build
+    # ./bootstrap-configure
+    # make
+
+  Check installation
+    # make install DESTDIR=$PWD/x
+    # find x
+    # rm -rf x
+
+  Check distribution
+    # make distcheck
+
+  Final installation
+    # sudo make install
+
+  Remove autogenerated files
+    # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "connmand". The "bootstrap-configure"
+automatically includes this option.
+
+  Run daemon in foreground with debugging
+    # sudo ./src/connmand -n -d 'plugins/*'
+
+The debugging option -d takes an argument. This argument can be a comma
+separated list of file names like 'plugins/wifi.c,plugins/ethernet.c' to
+enable debugs in these files. Simple glob style pattern matching is
+supported in this list.
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+Some times it is important to restrict the available interfaces. For example
+in cases where testing happens over a network connection. The "-i" command
+line switch allows to specify a glob pattern for the interface names.
+
+  Run daemon for wireless interfaces
+    # sudo ./src/connmand -n -i wlan*
+
+
+Debugging the D-Bus interface during runtime
+============================================
+
+Running the daemon with debugging information in the foreground is quite
+verbose and sometimes not really helpful. The "monitor-connman" script
+allows to monitor "PropertyChanged" D-Bus signals from various interfaces.
+
+Every "PropertyChanged" signal will generate a line of output. Some of them
+can get very complex. The first detail inside "{ ... }" is the interface
+name (without its service name prefix). The second detail inside "[ ... ]"
+is the object path. And after that it is followed by a key and value of
+the property that changed.
+
+
+Generating source code documentation
+====================================
+
+The source code is annotated using the gtk-doc style documentation. This
+allows an easy way of generating API documentation. The "bootstrap-configure"
+script will use the "--enable-gtk-doc" configure to enable the generation of
+the documentation.
+
+To make the gtk-doc process work, the gtk-doc tools need to be installed.
+Every distribution should provide a package for this, but the naming of the
+package might be different:
+
+  Debian
+    # apt-get install gtk-doc-tools
+
+  Ubuntu
+    # apt-get install gtk-doc-utils
+
+  Fedora
+    # yum install gtk-doc
+
+In case "bootstrap-configure" is not used, the manual steps for generating
+the documentation files are like this:
+
+  Configuring the repository
+    # ./configure --enable-gtk-doc
+
+  Generate the documentation
+    # cd doc && make
+
+  View documentation
+    # firefox doc/html/index.html
+
index 4a0cec8..dd222d8 100644 (file)
@@ -1,4 +1,277 @@
 
+AM_MAKEFLAGS = --no-print-directory
+
+includedir = @includedir@/connman
+
+include_HEADERS = include/types.h include/log.h include/plugin.h \
+                       include/notifier.h include/service.h \
+                       include/resolver.h include/ipconfig.h \
+                       include/device.h include/network.h include/inet.h \
+                       include/storage.h
+
+nodist_include_HEADERS = include/version.h
+
+noinst_HEADERS = include/rtnl.h include/task.h \
+                       include/dbus.h include/option.h \
+                       include/provider.h \
+                       include/utsname.h include/timeserver.h include/proxy.h \
+                       include/technology.h include/setting.h
+
+local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \
+                       $(noinst_HEADERS), include/connman/$(notdir $(file)))
+
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+gdhcp_sources = gdhcp/gdhcp.h gdhcp/common.h gdhcp/common.c gdhcp/client.c \
+               gdhcp/server.c gdhcp/ipv4ll.h gdhcp/ipv4ll.c
+
+gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c
+
+if WISPR
+gweb_sources += gweb/giognutls.h gweb/giognutls.c
+else
+gweb_sources += gweb/giognutls.h gweb/gionotls.c
+endif
+
+if DATAFILES
+
+if NMCOMPAT
+nmcompat_conf = plugins/connman-nmcompat.conf
+endif
+
+dbusconfdir = @DBUS_CONFDIR@
+
+dbusconf_DATA = src/connman.conf $(nmcompat_conf)
+
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/connman.service
+endif
+endif
+
+plugin_LTLIBRARIES =
+
+plugin_objects =
+
+builtin_modules =
+builtin_sources =
+builtin_libadd =
+builtin_cflags =
+
+noinst_PROGRAMS =
+
+unit_objects =
+
+sbin_PROGRAMS = src/connmand
+
+src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) $(gweb_sources) \
+                       $(builtin_sources) src/connman.ver \
+                       src/main.c src/connman.h src/log.c \
+                       src/error.c src/plugin.c src/task.c \
+                       src/device.c src/network.c src/connection.c \
+                       src/manager.c src/service.c \
+                       src/clock.c src/timezone.c \
+                       src/agent.c src/notifier.c src/provider.c \
+                       src/resolver.c src/ipconfig.c src/detect.c src/inet.c \
+                       src/dhcp.c src/dhcpv6.c src/rtnl.c src/proxy.c \
+                       src/utsname.c src/timeserver.c src/rfkill.c \
+                       src/storage.c src/dbus.c src/config.c \
+                       src/technology.c src/counter.c src/ntp.c \
+                       src/session.c src/tethering.c src/wpad.c src/wispr.c \
+                       src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
+                       src/ippool.c src/bridge.c src/nat.c
+
+src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
+                               @XTABLES_LIBS@ @GNUTLS_LIBS@ -lresolv -ldl
+
+src_connmand_LDFLAGS = -Wl,--export-dynamic \
+                               -Wl,--version-script=$(srcdir)/src/connman.ver
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+CLEANFILES = src/connman.conf $(BUILT_SOURCES)
+
+statedir = $(localstatedir)/run/connman
+
+plugindir = $(libdir)/connman/plugins
+
+scriptdir = $(libdir)/connman/scripts
+
+storagedir = $(localstatedir)/lib/connman
+
+configdir = ${sysconfdir}/connman
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+build_scriptdir = $(abs_top_srcdir)/scripts
+else
+build_plugindir = $(plugindir)
+build_scriptdir = $(scriptdir)
+endif
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+                               @GNUTLS_CFLAGS@ $(builtin_cflags) \
+                               -DCONNMAN_PLUGIN_BUILTIN \
+                               -DSTATEDIR=\""$(statedir)"\" \
+                               -DPLUGINDIR=\""$(build_plugindir)"\" \
+                               -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+                               -DSTORAGEDIR=\""$(storagedir)\"" \
+                               -DSYSCONFDIR=\""$(sysconfdir)\"" \
+                               -DCONFIGDIR=\""$(configdir)\""
+
+INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
+
+EXTRA_DIST = src/genbuiltin src/connman-dbus.conf src/connman-polkit.conf \
+                                               plugins/connman-nmcompat.conf
+
+
+script_DATA =
+script_PROGRAMS =
+script_LTLIBRARIES =
+
+include Makefile.plugins
+
+if CLIENT
+noinst_PROGRAMS += client/cm
+
+client_cm_SOURCES = client/main.c
+client_cm_LDADD = @DBUS_LIBS@
+endif
+
+if WISPR
+noinst_PROGRAMS += tools/wispr
+
+tools_wispr_SOURCES = $(gweb_sources) tools/wispr.c
+tools_wispr_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+endif
+
+if TOOLS
+noinst_PROGRAMS += tools/supplicant-test \
+                       tools/dhcp-test tools/dhcp-server-test \
+                       tools/addr-test tools/web-test tools/resolv-test \
+                       tools/dbus-test tools/polkit-test \
+                       tools/iptables-test tools/tap-test tools/wpad-test \
+                       tools/stats-tool tools/private-network-test \
+                       unit/test-session unit/test-ippool unit/test-nat
+
+tools_supplicant_test_SOURCES = $(gdbus_sources) tools/supplicant-test.c \
+                       tools/supplicant-dbus.h tools/supplicant-dbus.c \
+                       tools/supplicant.h tools/supplicant.c
+tools_supplicant_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_web_test_SOURCES = $(gweb_sources) tools/web-test.c
+tools_web_test_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+
+tools_resolv_test_SOURCES = gweb/gresolv.h gweb/gresolv.c tools/resolv-test.c
+tools_resolv_test_LDADD = @GLIB_LIBS@ -lresolv
+
+tools_wpad_test_SOURCES = gweb/gresolv.h gweb/gresolv.c tools/wpad-test.c
+tools_wpad_test_LDADD = @GLIB_LIBS@ -lresolv
+
+tools_stats_tool_LDADD = @GLIB_LIBS@
+
+tools_dhcp_test_SOURCES = $(gdhcp_sources) tools/dhcp-test.c
+tools_dhcp_test_LDADD = @GLIB_LIBS@
+
+tools_dhcp_server_test_SOURCES = $(gdhcp_sources) tools/dhcp-server-test.c
+tools_dhcp_server_test_LDADD = @GLIB_LIBS@
+
+tools_dbus_test_SOURCES = $(gdbus_sources) tools/dbus-test.c
+tools_dbus_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_polkit_test_LDADD = @DBUS_LIBS@
+
+tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@
+
+tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+unit_test_session_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+               unit/test-session.c unit/utils.c unit/manager-api.c \
+               unit/session-api.c unit/test-connman.h
+unit_test_session_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
+unit_objects += $(unit_test_session_OBJECTS)
+
+unit_test_ippool_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+                src/ippool.c unit/test-ippool.c
+unit_test_ippool_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
+unit_objects += $(unit_test_ippool_OBJECTS)
+
+unit_test_nat_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+               src/iptables.c  src/nat.c unit/test-nat.c
+unit_test_nat_LDADD = @GLIB_LIBS@ @DBUS_LIBS@  @XTABLES_LIBS@ -ldl
+unit_objects += $(unit_nat_ippool_OBJECTS)
+endif
+
+test_scripts = test/get-state test/list-services \
+               test/monitor-services test/test-clock \
+               test/simple-agent test/show-introspection test/test-compat \
+               test/test-manager test/test-connman test/monitor-connman \
+               test/connect-vpn test/disconnect-vpn \
+               test/test-counter test/set-ipv4-method test/set-ipv6-method \
+               test/get-services test/get-proxy-autoconfig test/set-proxy \
+               test/enable-tethering test/disable-tethering test/backtrace \
+               test/test-session test/test-supplicant \
+               test/test-new-supplicant test/service-move-before \
+               test/set-global-timeservers test/get-global-timeservers \
+               test/set-nameservers test/set-domains
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST += $(test_scripts)
+
+EXTRA_DIST += doc/overview-api.txt doc/behavior-api.txt \
+                               doc/ipconfig-api.txt doc/plugin-api.txt \
+                               doc/manager-api.txt doc/agent-api.txt \
+                               doc/service-api.txt doc/technology-api.txt \
+                               doc/counter-api.txt doc/config-format.txt \
+                               doc/clock-api.txt doc/session-api.txt \
+                               doc/session-overview.txt doc/backtrace.txt \
+                               doc/advanced-configuration.txt
+
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = connman.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
+                               --disable-datafiles \
+                               --enable-hh2serial-gps \
+                               --enable-openconnect \
+                               --enable-openvpn \
+                               --enable-vpnc \
+                               --enable-nmcompat \
+                               --enable-polkit
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
 MAINTAINERCLEANFILES = Makefile.in \
        aclocal.m4 configure config.h.in config.sub config.guess \
-       ltmain.sh depcomp missing install-sh mkinstalldirs
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs
+
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+src/connman.conf: src/connman-dbus.conf src/connman-polkit.conf
+if POLKIT
+       $(AM_V_GEN)cp $(srcdir)/src/connman-polkit.conf $@
+else
+       $(AM_V_GEN)cp $(srcdir)/src/connman-dbus.conf $@
+endif
+
+include/connman/version.h: include/version.h
+       $(AM_V_at)$(MKDIR_P) include/connman
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+include/connman/%.h: $(abs_top_srcdir)/include/%.h
+       $(AM_V_at)$(MKDIR_P) include/connman
+       $(AM_V_GEN)$(LN_S) $< $@
+
+clean-local:
+       @$(RM) -rf include/connman
diff --git a/Makefile.plugins b/Makefile.plugins
new file mode 100644 (file)
index 0000000..3dbea39
--- /dev/null
@@ -0,0 +1,243 @@
+
+plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+                                       @DBUS_CFLAGS@ @GLIB_CFLAGS@
+plugin_ldflags = -no-undefined -module -avoid-version
+
+script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+                                       @DBUS_CFLAGS@
+
+if LOOPBACK
+builtin_modules += loopback
+builtin_sources += plugins/loopback.c
+endif
+
+if ETHERNET
+builtin_modules += ethernet
+builtin_sources += plugins/ethernet.c
+endif
+
+gsupplicant_sources = gsupplicant/gsupplicant.h gsupplicant/dbus.h \
+                       gsupplicant/supplicant.c gsupplicant/dbus.c
+
+if WIFI
+builtin_modules += wifi
+builtin_sources += plugins/wifi.c $(gsupplicant_sources)
+endif
+
+if BLUETOOTH
+builtin_modules += bluetooth
+builtin_sources += plugins/bluetooth.c
+endif
+
+if HH2SERIAL_GPS
+if HH2SERIAL_GPS_BUILTIN
+builtin_modules += hh2serial_gps
+builtin_sources += plugins/hh2serial-gps.c
+else
+plugin_LTLIBRARIES += plugins/hh2serial-gps.la
+plugin_objects += $(plugins_hh2serial_gps_la_OBJECTS)
+plugins_hh2serial_gps_la_CFLAGS = $(plugin_cflags)
+plugins_hh2serial_gps_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if OFONO
+builtin_modules += ofono
+builtin_sources += plugins/mcc.h plugins/ofono.c
+endif
+
+if DUNDEE
+builtin_modules += dundee
+builtin_sources += plugins/dundee.c
+endif
+
+if TELEPHONY
+if TELEPHONY_BUILTIN
+builtin_modules += telephony
+builtin_sources += plugins/telephony.c
+else
+plugin_LTLIBRARIES += plugins/telephony.la
+plugin_objects += $(plugins_telephony_la_OBJECTS)
+plugins_telephony_la_CFLAGS = $(plugin_cflags)
+plugins_telephony_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if OPENCONNECT
+if OPENCONNECT_BUILTIN
+builtin_modules += openconnect
+builtin_sources += plugins/openconnect.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
+else
+plugin_LTLIBRARIES += plugins/openconnect.la
+plugin_objects += $(plugins_openconnect_la_OBJECTS)
+plugins_openconnect_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/openconnect.c
+plugins_openconnect_la_CFLAGS = $(plugin_cflags) -DOPENCONNECT=\"@OPENCONNECT@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if OPENVPN
+if OPENVPN_BUILTIN
+builtin_modules += openvpn
+builtin_sources += plugins/openvpn.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DOPENVPN=\"@OPENVPN@\"
+else
+plugin_LTLIBRARIES += plugins/openvpn.la
+plugin_objects += $(plugins_openvpn_la_OBJECTS)
+plugins_openvpn_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/openvpn.c
+plugins_openvpn_la_CFLAGS = $(plugin_cflags) -DOPENVPN=\"@OPENVPN@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_openvpn_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if VPNC
+if VPNC_BUILTIN
+builtin_modules += vpnc
+builtin_sources += plugins/vpnc.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DVPNC=\"@VPNC@\"
+else
+plugin_LTLIBRARIES += plugins/vpnc.la
+plugin_objects += $(plugins_vpnc_la_OBJECTS)
+plugins_vpnc_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/vpnc.c
+plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if L2TP
+if L2TP_BUILTIN
+builtin_modules += l2tp
+builtin_sources += plugins/l2tp.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DL2TP=\"@L2TP@\"
+else
+plugin_LTLIBRARIES += plugins/l2tp.la
+plugin_objects += $(plugins_l2tp_la_OBJECTS)
+plugins_l2tp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/l2tp.c
+plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_l2tp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if PPTP
+if PPTP_BUILTIN
+builtin_modules += pptp
+builtin_sources += plugins/pptp.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
+else
+plugin_LTLIBRARIES += plugins/pptp.la
+plugin_objects += $(plugins_pptp_la_OBJECTS)
+plugins_pptp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/pptp.c
+plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
+                                       -DPPTP=\"@PPTP@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if PPTP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
+else
+if L2TP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
+endif
+endif
+
+builtin_sources += $(builtin_vpn_sources)
+
+if PACRUNNER
+builtin_modules += pacrunner
+builtin_sources += plugins/pacrunner.c
+endif
+
+if POLKIT
+builtin_modules += polkit
+builtin_sources += plugins/polkit.c
+
+if DATAFILES
+policydir = @POLKIT_DATADIR@
+
+policy_DATA = plugins/net.connman.policy
+endif
+endif
+
+if IWMX
+plugin_LTLIBRARIES += plugins/iwmxsdk.la
+plugin_objects += $(plugins_iwmxsdk_la_OBJECTS)
+plugins_iwmxsdk_la_SOURCES = plugins/iwmx.h plugins/iwmx.c plugins/iwmxsdk.c
+plugins_iwmxsdk_la_CFLAGS = $(plugin_cflags) @IWMXSDK_CFLAGS@
+plugins_iwmxsdk_la_LIBADD = @IWMXSDK_LIBS@ @GLIB_LIBS@
+plugins_iwmxsdk_la_LDFLAGS = $(plugin_ldflags)
+endif
+
+if IOSPM
+plugin_LTLIBRARIES += plugins/iospm.la
+plugin_objects += $(plugins_iospm_la_OBJECTS)
+plugins_iospm_la_CFLAGS = $(plugin_cflags)
+plugins_iospm_la_LDFLAGS = $(plugin_ldflags)
+endif
+
+if OPENCONNECT
+script_PROGRAMS += scripts/openconnect-script
+
+scripts_openconnect_script_LDADD = @DBUS_LIBS@
+else
+if VPNC
+script_PROGRAMS += scripts/openconnect-script
+
+scripts_openconnect_script_LDADD = @DBUS_LIBS@
+endif
+endif
+
+if OPENVPN
+script_PROGRAMS += scripts/openvpn-script
+
+scripts_openvpn_script_LDADD = @DBUS_LIBS@
+endif
+
+if NMCOMPAT
+builtin_modules += nmcompat
+builtin_sources += plugins/nmcompat.c
+endif
+
+if TIST
+if TIST_BUILTIN
+builtin_modules += tist
+builtin_sources += plugins/tist.c
+else
+plugin_LTLIBRARIES += plugins/tist.la
+plugin_objects += $(plugins_tist_la_OBJECTS)
+plugins_tist_la_CFLAGS = $(plugin_cflags)
+plugins_tist_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+EXTRA_DIST += plugins/polkit.policy
+
+plugins/net.connman.policy: plugins/polkit.policy
+if POLKIT
+       $(AM_V_GEN)cp $< $@
+endif
diff --git a/README b/README
index e69de29..6215e96 100644 (file)
--- a/README
+++ b/README
@@ -0,0 +1,232 @@
+Connection Manager
+******************
+
+Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+
+
+Functionality and features
+==========================
+
+The following features are built-in into Connection Manager:
+       - Generic plugin infrastructure
+       - Device and network abstraction (with basic storage support)
+       - IPv4, IPv4-LL (link-local) and DHCP
+       - IPv6, DHCPv6 and 6to4 tunnels
+       - Advanced routing and DNS configuration
+       - Built-in DNS proxy and intelligent caching
+       - Built-in WISPr hotspot logins and portal detection
+       - Time and timezone configuration (manual and automatic with NTP)
+       - Proxy handling (manual and automatic with WPAD)
+       - Tethering support (USB, Bluetooth and WiFi AP mode)
+       - Detailed statistics handling (home and roaming)
+
+Various plugins can be enabled for networking support:
+       - Ethernet plugin
+       - WiFi plugin with WEP40/WEP128 and WPA/WPA2 (personal and enterprise)
+       - Bluetooth plugin (using BlueZ)
+       - 2G/3G/4G plugin (using oFono)
+
+Also plugins with additional features are available:
+       - Loopback interface setup
+       - PACrunner proxy handling
+       - PolicyKit authorization support
+
+
+Compilation and installation
+============================
+
+In order to compile Connection Manager you need following software packages:
+       - GCC compiler
+       - GLib library
+       - D-Bus library
+       - IP-Tables library
+       - GnuTLS library (optional)
+       - PolicyKit (optional)
+
+To configure run:
+       ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+       make && make install
+
+
+Configuration and options
+=========================
+
+For a working system, certain configuration options need to be enabled:
+
+       --disable-ethernet
+
+               Disable support for Ethernet network cards
+
+               By default Ethernet technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if Ethernet support is not required.
+
+       --disable-wifi
+
+               Disable support for WiFi devices
+
+               By default WiFi technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if WiFi support is not required.
+
+               It is safe to build a daemon with WiFi support and no
+               running wpa_supplicant. The start of wpa_supplicant is
+               automatically detected and only a runtime dependency. It
+               is not needed to build ConnMan.
+
+       --disable-bluetooth
+
+               Disable support for Bluetooth devices
+
+               By default Bluetooth technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system if Bluetooth support is not required.
+
+               It is safe to build a daemon with Bluetooth support and no
+               running bluetoothd. The start of bluetoothd is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
+
+       --disable-ofono
+
+               Disable support for cellular 2G/3G/4G devices
+
+               By default oFono technology support is built-in and
+               enabled. This option can be used to build a small daemon
+               for a specific system where oFono is not used.
+
+               It is safe to build a daemon with oFono support and no
+               running ofonod. That start of ofonod is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
+
+       --disable-dundee
+
+               Disable support for Bluetooth DUN devices
+
+               By default Bluetooth DUN technology (dundee) support is
+               built-in and enabled. This option can be used to build a
+               small daemon for a specific system where dundee is not used.
+
+               It is safe to build a daemon with dundee support and no
+               running dundee. That start of dundee is automatically
+               detected and only a runtime dependency. It is not needed to
+               build ConnMan.
+
+       --disable-pacrunner
+
+               Disable support for PACrunner proxy handling
+
+               By default PACrunner support is built-in and enabled. This
+               option can be used to build a small daemon for a specific
+               system where PACrunner is not used.
+
+               It is safe to build a daemon with PACrunner support and no
+               pacrunner daemon. It will detect and start a PACrunner
+               process if needed at runtime. The presence is not needed
+               to build ConnMan.
+
+       --disable-loopback
+
+               Disable setup of loopback device
+
+               For distributions with a really minimal init system and no
+               networking scripts this can take care of setting up the
+               loopback device and enabling it.
+
+               It is safe to leave this selected even if networking
+               scripts are in place. It detects an already configured
+               loopback device and leaves it as it is.
+
+       --disable-wispr
+
+               Disable support for WISPr hotspot logins
+
+               For systems with really minimal memory requirements, this
+               will disable the support for WISPr hotspot logins. The code
+               for WISPr will be still compiled into the daemon, but its
+               requirement on GnuTLS for secure connections will be lifted.
+
+               The missing GnuTLS support shrinks the memory requirements
+               by about 30% and for systems that are more stationary and do
+               not log into hotspots this might be a better trade off.
+
+               Disabling WISPr support is not disabling the portal detection
+               support. A portal will still be detected, but instead of being
+               asked for login credentials, the request for a browser session
+               will be made through the agent.
+
+       --enable-polkit
+
+               Enable support for PolicyKit authorization
+
+               This allows to check every D-Bus access against a security
+               policy and so restrict access to certain functionality.
+
+       --enable-nmcompat
+
+               Enable support for NetworkManager compatibility interfaces
+
+               This allows to expose a minimal set of NetworkManager
+               interfaces. It is useful for systems with applications
+               written to use NetworkManager to detect online/offline
+               status and have not yet been converted to use ConnMan.
+
+
+wpa_supplicant configuration
+============================
+
+In order to get wpa_supplicant and Connection Manager working properly
+together you should edit wpa_supplicant .config file and set:
+
+CONFIG_WPS=y
+CONFIG_AP=y
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+and, add:
+
+CONFIG_BGSCAN_SIMPLE=y
+
+This last option will enable the support of background scanning while being
+connected, which is necessary when roaming on wifi.
+
+It is recommended to use wpa_supplicant 0.8.x or 1.x or later.
+
+
+VPN
+===
+
+In order to compile pptp and l2tp VPN plugins, you need ppp development
+package.
+
+To run l2tp you will need
+       - xl2tpd, http://www.xelerance.com/services/software/xl2tpd
+
+To run pptp you will need
+       - pptp client, http://pptpclient.sourceforge.net
+
+Both l2tp and pptp also need pppd.
+
+
+OpenVPN
+=======
+
+Up to version 2.2 of OpenVPN, pushing additional routes from the
+server will not always work. Some of the symptons are that additional
+routes will not be set by ConnMan if the uplink is a cellular
+network. While the same setup works well for a WiFi or ethernet
+uplink.
+
+
+Information
+===========
+
+Mailing list:
+       connman@connman.net
+
+For additional information about the project visit ConnMan web site:
+       http://www.connman.net
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..a9287b7
--- /dev/null
+++ b/TODO
@@ -0,0 +1,149 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.
+   The complexity scale is exponential, with complexity 1 being the
+   lowest complexity. Complexity is a function of both task 'complexity'
+   and task 'scope'.
+
+Core
+====
+
+- Session API implementation
+
+   Priority: High
+   Complexity: C4
+   Owner: Daniel Wagner <daniel.wagner@bmw-carit.de>
+   Owner: Patrik Flykt <patrik.flykt@linux.intel.com>
+
+   The session API should provide a connection abstraction in order to
+   prioritize applications network accesses, prevent or allow network
+   and bearer roaming, or provide applications with a way to request
+   for periodic network connections. On-demand connections will be
+   implemented through this API as well.
+   See http://www.mail-archive.com/connman@connman.net/msg01653.html
+
+
+- Personal firewall
+
+   Priority: Low
+   Complexity: C8
+   Owner: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+
+   Discuss and implement a basic and safe firewalling strategy into
+   Connman. Provide a D-Bus API for personal firewalling.
+
+
+- PACRunner extensions
+
+   Priority: Low
+   Complexity: C4
+
+   Support more URI schemes, support multiple connections, tighter
+   security integration.
+
+
+- Favorite service migration removal
+
+  Priority: Medium
+  Complexity: C1
+  When: 12/2012
+
+  Remove service migration code that moves services from default.profile
+  to the current directory-based structure.
+
+
+WiFi
+====
+
+- EAP-AKA/SIM
+
+   Priority: Medium
+   Complexity: C2
+   Owner: Samuel Ortiz <sameo@linux.intel.com>
+
+   This EAP is needed for SIM card based network authentication.
+   ConnMan here plays a minor role: Once wpa_supplicant is set up for
+   starting and EAP-AKA/SIM authentication, it will talk to a SIM card
+   through its pcsc-lite API.
+
+
+- EAP-FAST
+
+   Priority: Low
+   Complexity: C1
+
+
+- Wireless background scan
+
+   Priority: Medium
+   Complexity: C4
+   Owner: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+
+   While connected ConnMan should offload the background scanning to
+   wpa_supplicant. Wpa_supplicant allows for a fancy background scan
+   which frequency increases after the current connection signal level
+   drops below a defined threshold.
+   We should also be able to have wpa_supplicant do the background
+   scanning for us when we're not connected, if we have configured our
+   favorite networks.
+
+
+- WiFi p2p
+
+   Priority: Medium
+   Complexity: C2
+
+
+- Previous WPS pin code sending
+
+  Priority: Medium
+  Complexity: C2
+
+  Provide previous WPS pin code in PreviousPassphrase field if WPS was used.
+
+
+Bluetooth
+=========
+
+
+Cellular
+========
+
+
+VPN
+===
+
+- IPsec
+
+   Priority: Medium
+   Complexity: C4
+   Owner: Jukka Rissanen <jukka.rissanen@linux.intel.com>
+
+
+Tools
+=====
+
+- Command line tool
+
+   Priority: Low
+   Complexity: C4
+   Owner: Patrik Flykt <patrik.flykt@linux.intel.com>
+
+   For platforms not running python, it could prove useful to provide them
+   with a native single binary command line tool.
+
+
+User Interface
+==============
+
+- GNOME3 UI
+
+   Priority: Low
+   Complexity: C4
+   Owner: Alok Barsode <alok.barsode@linux.intel.com>
+
+   A GNOME3 shell user interface would make it easier for mainstream distros
+   users to use ConnMan.
index 95486c3..0a59871 100644 (file)
@@ -9,3 +9,53 @@ AC_DEFUN([AC_PROG_CC_PIE], [
                rm -rf conftest*
        ])
 ])
+
+AC_DEFUN([COMPILER_FLAGS], [
+       if (test "${CFLAGS}" = ""); then
+               CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2"
+       fi
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               CFLAGS+=" -Werror -Wextra"
+               CFLAGS+=" -Wno-unused-parameter"
+               CFLAGS+=" -Wno-missing-field-initializers"
+               CFLAGS+=" -Wdeclaration-after-statement"
+               CFLAGS+=" -Wmissing-declarations"
+               CFLAGS+=" -Wredundant-decls"
+               CFLAGS+=" -Wcast-align"
+               CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+       fi
+])
+
+AC_DEFUN([GTK_DOC_CHECK],
+[
+  AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+  AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+  dnl for overriding the documentation installation directory
+  AC_ARG_WITH([html-dir],
+    AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),,
+    [with_html_dir='${datadir}/gtk-doc/html'])
+  HTML_DIR="$with_html_dir"
+  AC_SUBST([HTML_DIR])
+
+  dnl enable/disable documentation building
+  AC_ARG_ENABLE([gtk-doc],
+    AS_HELP_STRING([--enable-gtk-doc],
+                   [use gtk-doc to build documentation [[default=no]]]),,
+    [enable_gtk_doc=no])
+
+  if test x$enable_gtk_doc = xyes; then
+    ifelse([$1],[],
+      [PKG_CHECK_EXISTS([gtk-doc],,
+                        AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))],
+      [PKG_CHECK_EXISTS([gtk-doc >= $1],,
+                        AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build gtk-doc]))])
+  fi
+
+  AC_MSG_CHECKING([whether to build gtk-doc documentation])
+  AC_MSG_RESULT($enable_gtk_doc)
+
+  AC_PATH_PROGS(GTKDOC_CHECK,gtkdoc-check,)
+
+  AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes])
+  AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"])
+])
index 053f5c8..0dd71d9 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -2,6 +2,6 @@
 
 aclocal && \
     autoheader && \
-        libtoolize --automake --copy --force &&
+        libtoolize --automake --copy --force && \
            automake --add-missing --copy && \
                autoconf
index 2d56ff3..69c00d0 100755 (executable)
@@ -4,8 +4,20 @@ if [ -f config.status ]; then
        make maintainer-clean
 fi
 
+if [ ! -f doc/gtk-doc.make ]; then
+       gtkdocize --copy --docdir doc
+fi
+
 ./bootstrap && \
     ./configure --enable-maintainer-mode \
+               --enable-debug \
                --prefix=/usr \
                --mandir=/usr/share/man \
-               --sysconfdir=/etc
+               --localstatedir=/var \
+               --sysconfdir=/etc \
+               --disable-datafiles \
+               --enable-openconnect=builtin \
+               --enable-openvpn=builtin \
+               --enable-vpnc=builtin \
+               --enable-nmcompat \
+               --enable-polkit $*
diff --git a/client/main.c b/client/main.c
new file mode 100644 (file)
index 0000000..4a88d6e
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#define CONNMAN_SERVICE                        "net.connman"
+
+#define CONNMAN_MANAGER_INTERFACE      CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH           "/"
+
+struct service_data {
+       const char *path;
+       const char *name;
+       dbus_bool_t favorite;
+};
+
+static DBusMessage *get_services(DBusConnection *connection)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "GetServices");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get properties\n");
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+static DBusMessage *lookup_service(DBusConnection *connection,
+                                                       const char *pattern)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "LookupService");
+       if (message == NULL)
+               return NULL;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &pattern,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get properties\n");
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+static void extract_properties(DBusMessageIter *dict,
+                                       struct service_data *service)
+{
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               //type = dbus_message_iter_get_arg_type(&value);
+               //dbus_message_iter_get_basic(&value, &val);
+
+               if (strcmp(key, "Name") == 0)
+                       dbus_message_iter_get_basic(&value, &service->name);
+               else if (strcmp(key, "Favorite") == 0)
+                       dbus_message_iter_get_basic(&value, &service->favorite);
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static void extract_services(DBusMessage *message)
+{
+       DBusMessageIter iter, array;
+
+       dbus_message_iter_init(message, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter entry, dict;
+               struct service_data service;
+               const char *path;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &path);
+
+               service.path = strrchr(path, '/') + 1;
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &dict);
+               extract_properties(&dict, &service);
+
+               printf("%c %-20s { %-50s }\n",
+                               service.favorite == TRUE ? '*' : ' ',
+                                               service.name, service.path);
+
+               dbus_message_iter_next(&array);
+       }
+}
+
+static int cmd_list_services(DBusConnection *connection)
+{
+       DBusMessage *message;
+
+       message = get_services(connection);
+       if (message == NULL)
+               return -1;
+
+       extract_services(message);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static int cmd_show_service(DBusConnection *connection, const char *pattern)
+{
+       DBusMessage *message;
+       const char *path;
+
+       message = lookup_service(connection, pattern);
+       if (message == NULL)
+               return -1;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       printf("Service: %s\n", path);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static void usage(const char *program)
+{
+       printf("ConnMan utility ver %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\t%s <command> [options]\n\n", program);
+
+       printf("Commands:\n"
+               "\thelp\n"
+               "\tlist\n"
+               "\tshow <service>\n"
+               "\n");
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+
+       if (argc > 1 && strcmp(argv[1], "help") == 0) {
+               usage(argv[0]);
+               exit(0);
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus\n");
+               exit(1);
+       }
+
+       if (argc > 1) {
+               if (strcmp(argv[1], "list") == 0)
+                       cmd_list_services(conn);
+               else if (strcmp(argv[1], "show") == 0) {
+                       if (argc > 2)
+                               cmd_show_service(conn, argv[2]);
+                       else
+                               usage(argv[0]);
+               }
+       } else
+               usage(argv[0]);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..603f831
--- /dev/null
@@ -0,0 +1,432 @@
+AC_PREREQ(2.60)
+AC_INIT(connman, 1.3)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+PKG_PROG_PKG_CONFIG
+
+COMPILER_FLAGS
+
+AC_SUBST(abs_top_srcdir)
+AC_SUBST(abs_top_builddir)
+
+AC_LANG_C
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AM_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+                       [disable code optimization through compiler]), [
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -O0"
+       fi
+])
+
+GTK_DOC_CHECK
+
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+                       [enable compiling with debugging information]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_g}" = "yes"); then
+               CFLAGS="$CFLAGS -g"
+       fi
+])
+
+AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+                       [enable position independent executables flag]), [
+       if (test "${enableval}" = "yes" &&
+                               test "${ac_cv_prog_cc_pie}" = "yes"); then
+               CFLAGS="$CFLAGS -fPIE"
+               LDFLAGS="$LDFLAGS -pie"
+       fi
+])
+
+AC_ARG_ENABLE(threads,
+       AC_HELP_STRING([--enable-threads], [enable threading support]),
+                       [enable_threads=${enableval}], [enable_threads="no"])
+
+AC_ARG_ENABLE(hh2serial-gps,
+       AC_HELP_STRING([--enable-hh2serial-gps], [enable hh2serial GPS support]),
+                       [enable_hh2serial_gps=${enableval}], [enable_hh2serial_gps="no"])
+AM_CONDITIONAL(HH2SERIAL_GPS, test "${enable_hh2serial_gps}" != "no")
+AM_CONDITIONAL(HH2SERIAL_GPS_BUILTIN, test "${enable_hh2serial_gps}" = "builtin")
+
+AC_ARG_ENABLE(telephony,
+       AC_HELP_STRING([--enable-telephony], [enable Telephony support]),
+                       [enable_telephony=${enableval}], [enable_telephony="yes"])
+AM_CONDITIONAL(TELEPHONY, test "${enable_telephony}" != "no")
+AM_CONDITIONAL(TELEPHONY_BUILTIN, test "${enable_telephony}" = "builtin")
+
+AC_ARG_WITH(openconnect, AC_HELP_STRING([--with-openconnect=PROGRAM],
+        [specify location of openconnect binary]), [path_openconnect=${withval}])
+
+AC_ARG_ENABLE(openconnect,
+       AC_HELP_STRING([--enable-openconnect], [enable openconnect support]),
+                       [enable_openconnect=${enableval}], [enable_openconnect="no"])
+if (test "${enable_openconnect}" != "no"); then
+       if (test -z "${path_openconnect}"); then
+               AC_PATH_PROG(OPENCONNECT, [openconnect], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${OPENCONNECT}"); then
+                       AC_MSG_ERROR(openconnect binary not found)
+               fi
+       else
+               OPENCONNECT="${path_openconnect}"
+               AC_SUBST(OPENCONNECT)
+       fi
+fi
+AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no")
+AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin")
+
+AC_ARG_WITH(openvpn, AC_HELP_STRING([--with-openvpn=PROGRAM],
+        [specify location of openvpn binary]), [path_openvpn=${withval}])
+
+AC_ARG_ENABLE(openvpn,
+       AC_HELP_STRING([--enable-openvpn], [enable openvpn support]),
+                       [enable_openvpn=${enableval}], [enable_openvpn="no"])
+if (test "${enable_openvpn}" != "no"); then
+       if (test -z "${path_openvpn}"); then
+               AC_PATH_PROG(OPENVPN, [openvpn], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${OPENVPN}"); then
+                       AC_MSG_ERROR(openvpn binary not found)
+               fi
+       else
+               OPENVPN="${path_openvpn}"
+               AC_SUBST(OPENVPN)
+       fi
+fi
+AM_CONDITIONAL(OPENVPN, test "${enable_openvpn}" != "no")
+AM_CONDITIONAL(OPENVPN_BUILTIN, test "${enable_openvpn}" = "builtin")
+
+AC_ARG_WITH(vpnc, AC_HELP_STRING([--with-vpnc=PROGRAM],
+       [specify location of vpnc binary]), [path_vpnc=${withval}])
+
+AC_ARG_ENABLE(vpnc,
+       AC_HELP_STRING([--enable-vpnc], [enable vpnc support]),
+               [enable_vpnc=${enableval}], [enable_vpnc="no"])
+if (test "${enable_vpnc}" != "no"); then
+       if (test -z "${path_vpnc}"); then
+               AC_PATH_PROG(VPNC, [vpnc], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${VPNC}"); then
+                       AC_MSG_ERROR(vpnc binary not found)
+               fi
+       else
+               VPNC="${path_vpnc}"
+               AC_SUBST(VPNC)
+       fi
+fi
+AM_CONDITIONAL(VPNC, test "${enable_vpnc}" != "no")
+AM_CONDITIONAL(VPNC_BUILTIN, test "${enable_vpnc}" = "builtin")
+
+AC_ARG_ENABLE(l2tp,
+       AC_HELP_STRING([--enable-l2tp], [enable l2tp support]),
+                       [enable_l2tp=${enableval}], [enable_l2tp="no"])
+if (test "${enable_l2tp}" != "no"); then
+       if (test -z "${path_pppd}"); then
+               AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+       else
+               PPPD="${path_pppd}"
+               AC_SUBST(PPPD)
+       fi
+       AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+                       AC_MSG_ERROR(ppp header files are required))
+       if (test -z "${path_l2tp}"); then
+               AC_PATH_PROG(L2TP, [xl2tpd], [/usr/sbin/xl2tpd], $PATH:/sbin:/usr/sbin)
+       else
+               L2TP="${path_l2tp}"
+               AC_SUBST(L2TP)
+       fi
+fi
+AM_CONDITIONAL(L2TP, test "${enable_l2tp}" != "no")
+AM_CONDITIONAL(L2TP_BUILTIN, test "${enable_l2tp}" = "builtin")
+
+AC_ARG_ENABLE(pptp,
+       AC_HELP_STRING([--enable-pptp], [enable pptp support]),
+                       [enable_pptp=${enableval}], [enable_pptp="no"])
+if (test "${enable_pptp}" != "no"); then
+       if (test -z "${path_pppd}"); then
+               AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+       else
+               PPPD="${path_pppd}"
+               AC_SUBST(PPPD)
+       fi
+       AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+                       AC_MSG_ERROR(ppp header files are required))
+       if (test -z "${path_pptp}"); then
+               AC_PATH_PROG(PPTP, [pptp], [/usr/sbin/pptp], $PATH:/sbin:/usr/sbin)
+       else
+               PPTP="${path_pptp}"
+               AC_SUBST(PPTP)
+       fi
+fi
+AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
+AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
+
+AC_CHECK_HEADERS(resolv.h, dummy=yes,
+       AC_MSG_ERROR(resolver header files are required))
+AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [
+       AC_CHECK_LIB(resolv, __ns_initparse, dummy=yes,
+               AC_MSG_ERROR(resolver library support is required))
+])
+
+AC_CHECK_FUNC(signalfd, dummy=yes,
+                       AC_MSG_ERROR(signalfd support is required))
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+                       AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_ARG_WITH(iwmxsdk, AC_HELP_STRING([--with-iwmxsdk=PATH],
+                                               [path to Intel WiMAX SDK]),
+                               [pkgconfig_iwmxsdk=${withval}/lib/pkgconfig])
+
+AC_ARG_ENABLE(iwmx, AC_HELP_STRING([--enable-iwmx],
+               [enable Intel WiMAX support]), [enable_iwmx=${enableval}])
+if (test "${enable_iwmx}" = "yes"); then
+       enable_threads="yes"
+       export PKG_CONFIG_PATH="${pkgconfig_iwmxsdk}"
+       PKG_CHECK_MODULES(IWMXSDK, libiWmxSdk-0, dummy=yes,
+                               AC_MSG_ERROR(Intel WiMAX SDK is required))
+       PKG_CONFIG_PATH=""
+       AC_SUBST(IWMXSDK_CFLAGS)
+       AC_SUBST(IWMXSDK_LIBS)
+
+        # Fix API compat breakage from 1.4 to 1.5...
+        CPPFLAGS_save=$CPPFLAGS
+        CPPFLAGS="$IWMXSDK_CFLAGS $CPPFLAGS"
+        AH_TEMPLATE([HAVE_IWMXSDK_STATUS_IDLE],
+                    [WIMAX_API_DEVICE_STATUS_Connection_Idle is present])
+        AC_CHECK_DECL(WIMAX_API_DEVICE_STATUS_Connection_Idle,
+                      [AC_DEFINE([HAVE_IWMXSDK_STATUS_IDLE], [1], [])],
+                      [],
+                      [[#include <WiMaxType.h>]])
+
+        AH_TEMPLATE([HAVE_WIMAX_API_DEVICE_ID],
+                    [WIMAX_API_DEVICE_ID is present])
+        AC_CHECK_TYPE(WIMAX_API_DEVICE_ID,
+                      [AC_DEFINE([HAVE_WIMAX_API_DEVICE_ID], [1], [])],
+                      [],
+                      [[#include <WiMaxType.h>]])
+
+        AH_TEMPLATE([HAVE_WIMAX_API_HW_DEVICE_ID],
+                    [WIMAX_API_HW_DEVICE_ID is present])
+        AC_CHECK_TYPE(WIMAX_API_HW_DEVICE_ID,
+                      [AC_DEFINE([HAVE_WIMAX_API_HW_DEVICE_ID], [1], [])],
+                      [],
+                      [[#include <WiMaxType.h>]])
+
+        AH_TEMPLATE([HAVE_WIMAX_API_NSP_INFO_EX],
+                    [WIMAX_API_NSP_INFO_EX is present])
+        AC_CHECK_TYPE(WIMAX_API_NSP_INFO_EX,
+                      [AC_DEFINE([HAVE_WIMAX_API_NSP_INFO_EX], [1], [])],
+                      [],
+                      [[#include <WiMaxType.h>] 
+                       [#include <WiMaxTypesEx.h>]])
+
+        AH_TEMPLATE([HAVE_WIMAX_API_CONNECTED_NSP_INFO],
+                    [WIMAX_API_CONNECTED_NSP_INFO is present])
+        AC_CHECK_TYPE(WIMAX_API_CONNECTED_NSP_INFO,
+                      [AC_DEFINE([HAVE_WIMAX_API_CONNECTED_NSP_INFO], [1], [])],
+                      [],
+                      [[#include <WiMaxType.h>]])
+
+        CPPFLAGS=$CPPFLAGS_save
+fi
+AM_CONDITIONAL(IWMX, test "${enable_iwmx}" = "yes")
+
+AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
+               [enable Intel OSPM support]), [enable_iospm=${enableval}])
+AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
+
+AC_ARG_ENABLE(tist,
+       AC_HELP_STRING([--enable-tist], [enable TI Shared Transport support]),
+                       [enable_tist=${enableval}], [enable_tist="no"])
+AM_CONDITIONAL(TIST, test "${enable_tist}" != "no")
+AM_CONDITIONAL(TIST_BUILTIN, test "${enable_tist}" = "builtin")
+
+AC_ARG_WITH(stats-max-file-size, AC_HELP_STRING([--with-stats-max-file-size=SIZE],
+                       [Maximal size of a statistics round robin file]),
+                       [stats_max_file_size=${withval}])
+
+if (test -z "${stats_max_file_size}"); then
+   # default size is 16 kByte
+   stats_max_file_size="16 * 8 * 128"
+fi
+
+AC_DEFINE_UNQUOTED([STATS_MAX_FILE_SIZE], (${stats_max_file_size}), [Maximal size of a statistics round robin file])
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.28 is required))
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+if (test "${enable_threads}" = "yes"); then
+       AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
+       PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
+                               AC_MSG_ERROR(GThread >= 2.16 is required))
+       GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+       GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.4 is required))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+AC_ARG_WITH(dbusconfdir, AC_HELP_STRING([--with-dbusconfdir=PATH],
+       [path to D-Bus config directory]), [path_dbusconf=${withval}],
+               [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"])
+if (test -z "${path_dbusconf}"); then
+       DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d"
+else
+       DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
+fi
+AC_SUBST(DBUS_CONFDIR)
+
+AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
+       [path to D-Bus data directory]), [path_dbusdata=${withval}],
+               [path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"])
+if (test -z "${path_dbusdata}"); then
+       DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+       DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+AC_SUBST(DBUS_DATADIR)
+
+AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
+       [path to systemd service directory]), [path_systemdunit=${withval}],
+               [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+if (test -n "${path_systemdunit}"); then
+       SYSTEMD_UNITDIR="${path_systemdunit}"
+       AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
+
+PKG_CHECK_MODULES(XTABLES, xtables, dummy=yes,
+                               AC_MSG_ERROR(Xtables library is required))
+AC_SUBST(XTABLES_CFLAGS)
+AC_SUBST(XTABLES_LIBS)
+
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+               [enable test/example scripts]), [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(nmcompat, AC_HELP_STRING([--enable-nmcompat],
+                               [enable Network Manager support]),
+                       [enable_nmcompat=${enableval}], [enable_nmcompat="no"])
+AM_CONDITIONAL(NMCOMPAT, test "${enable_nmcompat}" != "no")
+
+AC_ARG_ENABLE(polkit, AC_HELP_STRING([--enable-polkit],
+                               [enable PolicyKit support]),
+                       [enable_polkit=${enableval}], [enable_polkit="no"])
+if (test "${enable_polkit}" != "no"); then
+       POLKIT_DATADIR="`$PKG_CONFIG --variable=actiondir polkit`"
+       POLKIT_DATADIR=""
+       if (test -z "${POLKIT_DATADIR}"); then
+               POLKIT_DATADIR="${datadir}/polkit-1/actions"
+       fi
+       AC_SUBST(POLKIT_DATADIR)
+fi
+AM_CONDITIONAL(POLKIT, test "${enable_polkit}" != "no")
+
+AC_ARG_ENABLE(loopback, AC_HELP_STRING([--disable-loopback],
+                               [disable loopback support]),
+                                       [enable_loopback=${enableval}])
+AM_CONDITIONAL(LOOPBACK, test "${enable_loopback}" != "no")
+
+AC_ARG_ENABLE(ethernet, AC_HELP_STRING([--disable-ethernet],
+                               [disable Ethernet support]),
+                                       [enable_ethernet=${enableval}])
+AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
+
+AC_ARG_ENABLE(wifi, AC_HELP_STRING([--disable-wifi],
+                               [disable WiFi support]),
+                                       [enable_wifi=${enableval}])
+AM_CONDITIONAL(WIFI, test "${enable_wifi}" != "no")
+
+AC_ARG_ENABLE(bluetooth, AC_HELP_STRING([--disable-bluetooth],
+                               [disable Bluetooth support]),
+                                       [enable_bluetooth=${enableval}])
+AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
+
+AC_ARG_ENABLE(ofono, AC_HELP_STRING([--disable-ofono],
+                               [disable oFono support]),
+                                       [enable_ofono=${enableval}])
+AM_CONDITIONAL(OFONO, test "${enable_ofono}" != "no")
+
+AC_ARG_ENABLE(dundee, AC_HELP_STRING([--disable-dundee],
+                               [disable dundee support (Bluetooth DUN)]),
+                                       [enable_dundee=${enableval}])
+AM_CONDITIONAL(DUNDEE, test "${enable_dundee}" != "no")
+
+AC_ARG_ENABLE(pacrunner, AC_HELP_STRING([--disable-pacrunner],
+                               [disable PACrunner support]),
+                                       [enable_pacrunner=${enableval}])
+AM_CONDITIONAL(PACRUNNER, test "${enable_pacrunner}" != "no")
+
+AC_ARG_ENABLE(wispr, AC_HELP_STRING([--disable-wispr],
+                               [disable WISPr support]),
+                                       [enable_wispr=${enableval}])
+AM_CONDITIONAL(WISPR, test "${enable_wispr}" != "no")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools],
+                               [disable testing tools]),
+                                       [enable_tools=${enableval}])
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
+
+AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
+                               [disable command line client]),
+                                       [disable_client=${enableval}])
+AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
+
+if (test "${enable_wispr}" != "no"); then
+       PKG_CHECK_MODULES(GNUTLS, gnutls, dummy=yes,
+                               AC_MSG_ERROR(GnuTLS library is required))
+else
+       GNUTLS_CFLAGS=""
+       GNUTLS_LIBS=""
+fi
+AC_SUBST(GNUTLS_CFLAGS)
+AC_SUBST(GNUTLS_LIBS)
+
+if (test "${enable_loopback}" != "no"); then
+       AC_CHECK_HEADERS(sys/inotify.h, dummy=yes,
+                       AC_MSG_ERROR(inotify header files are required))
+
+       AC_CHECK_LIB(c, inotify_init, dummy=yes,
+                       AC_MSG_ERROR(inotify library support is required))
+fi
+
+if (test "${enable_wifi}" != "no"); then
+       AC_PATH_PROG(WPASUPPLICANT, [wpa_supplicant], [],
+                                               $PATH:/sbin:/usr/sbin)
+fi
+
+AC_ARG_ENABLE(linklocaladdr, AC_HELP_STRING([--disable-linklocaladdr],
+                       [disable link local address when dhcp failed]), [
+       if (test "${enableval}" = "no"); then
+               CFLAGS="$CFLAGS -DDISABLE_LINK_LOCAL_ADDR"
+       fi
+])
+
+AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
+                       [don't install configuration and data files]),
+                                       [enable_datafiles=${enableval}])
+AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
+
+AC_OUTPUT(Makefile include/version.h src/connman.service
+                               scripts/connman doc/version.xml connman.pc)
diff --git a/connman.manifest b/connman.manifest
new file mode 100644 (file)
index 0000000..b5f2272
--- /dev/null
@@ -0,0 +1,20 @@
+<manifest>
+       <define>
+               <domain name="connman"/>
+               <request>
+                       <smack request="system::use_internet" type="w"/>
+               </request>
+       </define>
+       <assign>
+               <filesystem path="/etc/rc.d/init.d/connman" label="_" exec_label="none"/>
+               <filesystem path="/etc/rc.d/rc3.d/S61connman" label="_" exec_label="none"/>
+               <filesystem path="/etc/rc.d/rc5.d/S61connman" label="_" exec_label="none"/>
+               <filesystem path="/etc/dbus-1/system.d/connman.conf" label="_"/>
+               <filesystem path="/usr/lib/systemd/system/connman.service" label="_"/>
+               <filesystem path="/usr/lib/systemd/system/network.target.wants/connman.service" label="_"/>
+               <filesystem path="/usr/share/dbus-1/services/net.connman.service" label="_"/>
+       </assign>
+       <request>
+               <domain name="connman"/>
+       </request>
+</manifest>
diff --git a/connman.pc.in b/connman.pc.in
new file mode 100644 (file)
index 0000000..2af3c2b
--- /dev/null
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+plugindir=${libdir}/connman/plugins
+scriptdir=${libdir}/connman/scripts
+
+Name: connman
+Description: Connection Manager
+Requires: glib-2.0 dbus-1
+Version: @VERSION@
+Libs: -module -avoid-version -export-symbols-regex connman_plugin_desc
+Cflags: -I${includedir}
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..ce3e433
--- /dev/null
@@ -0,0 +1,38 @@
+
+DOC_MODULE = connman
+
+DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
+
+DOC_SOURCE_DIR = ../src
+
+SCAN_OPTIONS = --rebuild-sections --source-dir=../include
+
+MKDB_OPTIONS = --sgml-mode --output-format=xml --tmpl-dir=. \
+                                               --ignore-files=connman \
+                                               --source-dir=../include \
+                                               --source-suffixes=c,h
+
+MKTMPL_OPTIONS = --output-dir=.
+
+HFILE_GLOB = $(top_srcdir)/include/*.h
+CFILE_GLOB = $(top_srcdir)/src/*.c $(top_srcdir)/src/*.h
+
+IGNORE_HFILES = connman connman.h
+
+HTML_IMAGES =
+
+content_files = connman-introduction.xml
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/gdbus \
+       $(GTHREAD_CFLAGS) $(GMODULE_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS)
+
+GTKDOC_LIBS = $(DBUS_LIBS) $(GLIB_LIBS) $(GMODULE_LIBS) $(GTHREAD_LIBS)
+
+MAINTAINERCLEANFILES = Makefile.in \
+       $(DOC_MODULE).types $(DOC_MODULE)-*.txt *.sgml *.bak
+
+if ENABLE_GTK_DOC
+include $(top_srcdir)/doc/gtk-doc.make
+else
+EXTRA_DIST = $(DOC_MAIN_SGML_FILE) connman-introduction.xml
+endif
diff --git a/doc/advanced-configuration.txt b/doc/advanced-configuration.txt
new file mode 100644 (file)
index 0000000..3c08c8f
--- /dev/null
@@ -0,0 +1,61 @@
+Advanced configuration interface
+********************************
+
+
+Configuration basics
+====================
+
+The default configuration method for all servers is automatic or something
+like DHCP. In almost every case that should be just good enough, but if it
+is not, Connection Manager supports manual configuration for Ethernet and
+IP settings.
+
+
+Configuration interface
+=======================
+
+Every service contains two properties. One represents the current active
+configuration and the other one allows manual configuration via the user.
+
+For IPv4 they are named "IPv4" and IPv4.Configuration".
+
+[ /profile/default/wifi_001122334455_42696720696e204a6170616e_managed_psk ]
+    Type = wifi
+    Name = Big in Japan
+    Mode = managed
+    Strength = 82
+    Security = rsn
+    Favorite = true
+    State = ready
+    IPv4.Configuration = { Method=dhcp }
+    IPv4 = { Netmask=255.255.255.0 Method=dhcp Address=192.168.1.198 }
+
+The above WiFi network shows how the default configuration would look like
+with a connected service. The configuration method is DHCP and the current
+IP address is 192.168.1.198.
+
+The "IPv4" property is read-only and will emit PropertyChanged signals in
+case the IP address of this interface changes. The "IPv4.Configuration"
+property is read-write and allows changes. For example to use a static IP
+configuration this call could be used:
+
+    service.SetProperty("IPv4.Configuration", { "Method": "manual",
+                                               "Address": "192.168.1.100",
+                                               "Netmask": "255.255.255.0" })
+
+The configuration itself is a dictionary with various fields. Not all of
+them need to present. A lot of combinations are valid.
+
+For example the "Method" field has valid settings of "off", "fixed", "manual"
+and "dhcp". The "fixed" value however can not be set by any user program. It
+is an internal value that some 3G cards require. Switching to "off" will
+remove and IP configuration from the interface. The "manual" method allows
+for static address configuration. And "dhcp" will use DHCP to retrieve all
+required information automatically.
+
+With a manual configuration, the fields "Address" and "Netmask" should be
+given. In case "Netmask" is left out, the best netmask will be calculated.
+
+The "Gateway" field can be used to indicate the default route/gateway for
+this interface.
+
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644 (file)
index 0000000..91371af
--- /dev/null
@@ -0,0 +1,237 @@
+Agent hierarchy
+===============
+
+Service                unique name
+Interface      net.connman.Agent
+Object path    freely definable
+
+Methods                void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the agent. An agent can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       agent, because when this method gets called it has
+                       already been unregistered.
+
+               void ReportError(object service, string error)
+
+                       This method gets called when an error has to be
+                       reported to the user.
+
+                       A special return value can be used to trigger a
+                       retry of the failed transaction.
+
+                       Possible Errors: net.connman.Agent.Error.Retry
+
+               void RequestBrowser(object service, string url)
+
+                       This method gets called when it is required
+                       to ask the user to open a website to procceed
+                       with login handling.
+
+                       This can happen if connected to a hotspot portal
+                       page without WISPr support.
+
+                       Possible Errors: net.connman.Agent.Error.Canceled
+
+               dict RequestInput(object service, dict fields)
+
+                       This method gets called when trying to connect to
+                       a service and some extra input is required. For
+                       example a passphrase or the name of a hidden network.
+
+                       The return value should be a dictionary where the
+                       keys are the field names and the values are the
+                       actual fields. Alternatively an error indicating that
+                       the request got canceled can be returned.
+
+                       Most common return field names are "Name" and of
+                       course "Passphrase".
+
+                       The dictionary arguments contains field names with
+                       their input parameters.
+
+                       In case of WISPr credentials requests and if the user
+                       prefers to login through the browser by himself, agent
+                       will have to return a LaunchBrowser error (see below).
+
+                       Possible Errors: net.connman.Agent.Error.Canceled
+                                        net.connman.Agent.Error.LaunchBrowser
+
+               void Cancel()
+
+                       This method gets called to indicate that the agent
+                       request failed before a reply was returned.
+
+Fields         string Name
+
+                       The name of a network. This field will be requested
+                       when trying to connect to a hidden network.
+
+               array{byte} SSID
+
+                       This field is an alternative to "Name" for WiFi
+                       networks and can be used to return the exact binary
+                       representation of a network name.
+
+                       Normally returning the "Name" field is the better
+                       option here.
+
+               string Identity
+
+                       Identity (username) for EAP authentication methods.
+
+               string Passphrase
+
+                       The passphrase for authentication. For example a WEP
+                       key, a PSK passphrase or a passphrase for EAP
+                       authentication methods.
+
+               string PreviousPassphrase
+
+                       The previous passphrase successfully saved, i.e.
+                       which lead to a successfull 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
+                       a RequestInput is raised after a retry.
+
+               string WPS
+
+                       This field requests the use of WPS to get associated.
+                       This is an alternate choice against Passphrase when
+                       requested service supports WPS. The reply can contain
+                       either empty pin, if user wants to use push-button
+                       method, or a pin code if user wants to use the pin
+                       method.
+
+               string Username
+
+                       Username for WISPr authentication. This field will be
+                       requested when connecting to a WISPr-enabled hotspot.
+
+               string Password
+
+                       Password for WISPr authentication. This field will be
+                       requested when connecting to a WISPr-enabled hotspot.
+
+Arguments      string Type
+
+                       Contains the type of a field. For example "psk", "wep"
+                       "passphrase", "response", "ssid", "wpspin" or plain
+                       "string".
+
+               string Requirement
+
+                       Contains the requirement option. Valid values are
+                       "mandatory", "optional", "alternate" or
+                       "informational".
+
+                       The "alternate" value specifies that this field can be
+                       returned as an alternative to another one. An example
+                       would be the network name or SSID.
+
+                       All "mandatory" fields must be returned, while the
+                       "optional" can be returned if available.
+
+                       Nothing needs to be returned for "informational", as it
+                       is here only to provide an information so a value is
+                       attached to it.
+
+               array{string} Alternates
+
+                       Contains the list of alternate field names this
+                       field can be represented by.
+
+               string Value
+
+                       Contains data as a string, relatively to an
+                       "informational" argument.
+
+Examples       Requesting a passphrase for WPA2 network
+
+                       RequestInput("/service1",
+                               { "Passphrase" : { "Type"        : "psk",
+                                                  "Requirement" : "mandatory"
+                                                }
+                               }
+                       ==> { "Passphrase" : "secret123" }
+
+               Requesting a passphrase after an error on the previous one:
+
+                       RequestInput("/service1",
+                               { "Passphrase" : { "Type"        : "psk",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "PreviousPassphrase" :
+                                       { "Type"       : "psk",
+                                         "Requirement : "informational",
+                                         "Value"      : "secret123"
+                                       }
+                               }
+
+               Requesting name for hidden network
+
+                       RequestInput("/service2",
+                               { "Name" : { "Type"        : "string",
+                                            "Requirement" : "mandatory",
+                                            "Alternates"  : [ "SSID" ]
+                                          },
+                                 "SSID" : { "Type"        : "ssid",
+                                            "Requirement" : "alternate"
+                                          }
+                               }
+                       ==> { "Name" : "My hidden network" }
+
+               Requesting a passphrase for a WPA2 network with WPS alternative:
+
+                       RequestInput("/service3",
+                               { "Passphrase" : { "Type"        : "psk",
+                                                  "Requirement" : "mandatory",
+                                                  "Alternates"  : [ "WPS" ]
+                                                },
+                                 "WPS"        : { "Type"        : "wpspin",
+                                                  "Requirement" : "alternate"
+                                                }
+                               }
+
+                       ==> { "WPS" : "123456" }
+
+               Requesting passphrase for a WPA-Enterprise network:
+
+                       RequestInput("/service4",
+                               { "Identity"   : { "Type"        : "string",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "Passphrase" : { "Type"        : "passphrase",
+                                                  "Requirement" : "mandatory"
+                                                }
+                               }
+
+                       ==> { "Identity" : "alice", "Passphrase": "secret123" }
+
+               Requesting challenge response for a WPA-Enterprise network:
+
+                       RequestInput("/service4",
+                               { "Identity"   : { "Type"        : "string",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "Passphrase" : { "Type"        : "response",
+                                                  "Requirement" : "mandatory"
+                                                }
+                               }
+
+                       ==> { "Identity" : "bob", "Passphrase": "secret123" }
+
+               Requesting username and password for a WISPr-enabled hotspot:
+
+                       RequestInput("/service5",
+                               { "Username"   : { "Type"        : "string",
+                                                  "Requirement" : "mandatory"
+                                                },
+                                 "Password"   : { "Type"        : "passphrase",
+                                                  "Requirement" : "mandatory"
+                                                }
+                               }
+
+                       ==> { "Username" : "foo", "Password": "secret" }
diff --git a/doc/backtrace.txt b/doc/backtrace.txt
new file mode 100644 (file)
index 0000000..ac8472c
--- /dev/null
@@ -0,0 +1,28 @@
+ConnMan backtraces
+******************
+
+ConnMan dumps backtraces upon segmentation faults, bus errors and other
+crashing signals. Regardless of the debug level you started connmand with, the
+backtrace will be dumped to syslog.
+
+The ConnMan backtraces start with the following line:
+       -------- backtrace --------
+and will try to display function names if those can be resolved from the stack
+addresses. All static functions name will not appear for example.
+
+For a more complete and useful stack frame output you can use the
+test/backtrace script. It takes the actual binary that crashed and the
+connmand logs. The logs can contain any connman debug strings on top of the
+backtrace.
+
+Here is an example of the backtrace script usage:
+
+me@localhost:[~]$ backtrace /sbin/connmand connman.log
+-------- backtrace --------
+[0]: __connman_debug_list_available() [log.c:117]
+[1]: connman_driver_register() [element.c:515]
+[2]: __connman_driver_rescan() [element.c:490]
+[3]: disable_technology() [manager.c:391]
+[4]: generic_message() [object.c:262]
+-----------------------------------
+
diff --git a/doc/behavior-api.txt b/doc/behavior-api.txt
new file mode 100644 (file)
index 0000000..5feea76
--- /dev/null
@@ -0,0 +1,11 @@
+Interface behavior description
+******************************
+
+
+Ethernet service
+================
+
+The Ethernet based service is a special case since it has no children, but
+still can be manually connected and disconnected while also has an implicit
+behavior when physically plugging in or removing an Ethernet cable.
+
diff --git a/doc/clock-api.txt b/doc/clock-api.txt
new file mode 100644 (file)
index 0000000..6818f5a
--- /dev/null
@@ -0,0 +1,87 @@
+Clock hierarchy
+===============
+
+Service                net.connman
+Interface      net.connman.Clock
+Object path    /
+
+Methods                dict GetProperties()  [experimental]
+
+                       Returns all system clock properties. See the
+                       properties section for available properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               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.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+Signals                PropertyChanged(string name, variant value)  [experimental]
+
+                       This signal indicates a changed value of the given
+                       property.
+
+
+Properties     uint64 Time [readonly or readwrite]  [experimental]
+
+                       Current system time in seconds since epoch.
+
+                       This value is present for changing the system time
+                       if TimeUpdates is set to manual.
+
+                       It is not present for driving an updated display
+                       of the system time. PropertyChanged signal for this
+                       value are only send out if it gets changed or jumps
+                       unexpectedly.
+
+                       In general application interested in the current
+                       time should be using gettimeofday() and related
+                       system calls.
+
+               string TimeUpdates [readwrite]  [experimental]
+
+                       Possible values are "manual" and "auto" to indicate
+                       time update policy.
+
+                       With the "auto" setting the system tries to use as
+                       many sources as possible to determine the correct
+                       and updated time.
+
+               string Timezone [readonly or readwrite]  [experimental]
+
+                       Current system timezone string. Allowed values
+                       are from the standard timezone data (tzdata)
+                       package under /usr/share/zoneinfo. For example
+                       strings like "America/Vancouver" or "Europe/Berlin".
+
+                       This value is present for changing the timezone
+                       if TimezoneUpdates is set to manual.
+
+                       When the timezone gets changed a PropertyChanged
+                       signal will be send out.
+
+               string TimezoneUpdates [readwrite]  [experimental]
+
+                       Possible values are "manual" and "auto" to indicate
+                       timezone update policy.
+
+                       With the "auto" setting the system tries to use as
+                       many sources as possible to determine the correct
+                       timezone.
+
+               array{string} Timeservers [readwrite]  [experimental]
+
+                       List of global default NTP servers. The list should
+                       be sorted in order of preference.
+
+                       If a service configuration provides NTP servers,
+                       then they are preferred over the global ones.
+
+                       This list of servers is used when TimeUpdates is set
+                       to auto.
diff --git a/doc/config-format.txt b/doc/config-format.txt
new file mode 100644 (file)
index 0000000..4f76832
--- /dev/null
@@ -0,0 +1,99 @@
+Connman configuration file format
+*********************************
+
+Connman uses configuration files to provision existing services. Connman will
+be looking for its configuration files at STORAGEDIR which by default points
+to /var/lib/connman/. Configuration file names must not include other
+characters than letters or numbers and must have a .config suffix.
+Those configuration files are text files with a simple format and we typically
+have one file per provisioned network.
+
+If the config file is removed, then Connman tries to remove the
+provisioned service. If individual service entry inside config is removed,
+then the corresponding provisioned service is removed. If service
+entry is changed, then corresponding service is removed and then
+immediately re-provisioned.
+
+
+Global entry [global]
+=====================
+
+These files can have an optional global entry describing the actual file.
+The 2 allowed fields for that entry are:
+- Name: Name of the network.
+- Description: Description of the network.
+- Protected: Configuration protection against being removed, modified or
+overwritten by a Manager.ProvisionService() call. If unset, this value defaults
+to TRUE, i.e. configs are protected by default.
+
+
+Service entry [service_*]
+=========================
+
+Each provisioned service must start with the [service_*] tag. Replace * with
+an identifier unique to the config file.
+
+Allowed fields:
+- Type: Service type. We currently only support wifi.
+- Name: A string representation of an 802.11 SSID. If the SSID field is
+  present, the Name field is ignored.
+- SSID: A hexadecimal representation of an 802.11 SSID. If the SSID field is
+  omitted, the Name field is used instead.
+- EAP: EAP type. We currently only support tls, ttls or peap.
+- CACertFile: File path to CA certificate file (PEM/DER).
+- ClientCertFile: File path to client certificate file (PEM/DER).
+- PrivateKeyFile: File path to client private key file (PEM/DER/PFX).
+- PrivateKeyPassphrase: Password/passphrase for private key file.
+- PrivateKeyPassphraseType: We only support the fsid passphrase type for now.
+  This is for private keys generated by using their own filesystem UUID as the
+  passphrase. The PrivateKeyPassphrase field is ignored when this field is set
+  to fsid.
+- Identity: Identity string for EAP.
+- Phase2: Phase2 (inner authentication with TLS tunnel) authentication method.
+  Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
+  authentication method (should only be used with EAP = TTLS).
+- Passphrase: RSN/WPA/WPA2 Passphrase
+
+
+Example
+=======
+
+This is a configuration file for a network providing EAP-TLS, EAP-TTLS and
+EAP-PEAP services.
+The respective SSIDs are tls_ssid, ttls_ssid and peap_ssid and the file name
+is example.config.
+Please note that the SSID entry is for hexadecimal encoded SSID (e.g. "SSID =
+746c735f73736964"). If your SSID does not contain any exotic character then
+you should use the Name entry instead (e.g. "Name = tls_ssid").
+
+
+example@example:[~]$ cat /var/lib/connman/example.config
+[global]
+Name = Example
+Description = Example network configuration
+
+[service_tls]
+Type = wifi
+SSID = 746c735f73736964
+EAP = tls
+CACertFile = /home/user/.certs/ca.pem
+ClientCertFile = /home/user/devlp/.certs/client.pem
+PrivateKeyFile = /home/user/.certs/client.fsid.pem
+PrivateKeyPassphraseType = fsid
+Identity = user
+
+[service_ttls]
+Type = wifi
+Name = ttls_ssid
+EAP = ttls
+CACertFile = /home/user/.cert/ca.pem
+Phase2 = MSCHAPV2
+Identity = user
+
+[service_peap]
+Type = wifi
+Name = peap_ssid
+EAP = peap
+CACertFile = /home/user/.cert/ca.pem
+Phase2 = MSCHAPV2
+Identity = user
diff --git a/doc/connman-docs.xml b/doc/connman-docs.xml
new file mode 100644 (file)
index 0000000..d4059a4
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <bookinfo>
+    <title>Connection Manager Reference Manual</title>
+    <releaseinfo>Version &version;</releaseinfo>
+    <authorgroup>
+      <author>
+       <firstname>Marcel</firstname>
+       <surname>Holtmann</surname>
+       <affiliation>
+         <address>
+           <email>marcel@holtmann.org</email>
+         </address>
+       </affiliation>
+      </author>
+    </authorgroup>
+
+    <copyright>
+      <year>2007-2008</year>
+      <holder>Intel Corporation. All rights reserved.</holder>
+    </copyright>
+
+    <legalnotice>
+      <para>
+       Permission is granted to copy, distribute and/or modify this
+       document under the terms of the <citetitle>GNU Free
+       Documentation License</citetitle>, Version 1.1 or any later
+       version published by the Free Software Foundation with no
+       Invariant Sections, no Front-Cover Texts, and no Back-Cover
+       Texts. You may obtain a copy of the <citetitle>GNU Free
+       Documentation License</citetitle> from the Free Software
+       Foundation by visiting <ulink type="http"
+       url="http://www.fsf.org">their Web site</ulink> or by writing
+       to:
+
+       <address>
+         The Free Software Foundation, Inc.,
+         <street>59 Temple Place</street> - Suite 330,
+         <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode>,
+         <country>USA</country>
+       </address>
+      </para>
+    </legalnotice>
+  </bookinfo>
+
+  <reference id="design">
+    <title>Design Overview</title>
+    <partintro>
+      <para>
+       This part presents the design documentation for Connection Manager.
+      </para>
+    </partintro>
+    <xi:include href="connman-introduction.xml" />
+  </reference>
+
+  <reference id="manager">
+    <title>Manager interface</title>
+    <para>
+<programlisting><xi:include href="manager-api.txt" parse="text" /></programlisting>
+    </para>
+  </reference>
+
+  <reference id="device">
+    <title>Device interface</title>
+    <para>
+<programlisting><xi:include href="device-api.txt" parse="text" /></programlisting>
+    </para>
+  </reference>
+
+  <reference id="network">
+    <title>Network interface</title>
+    <para>
+<programlisting><xi:include href="network-api.txt" parse="text" /></programlisting>
+    </para>
+  </reference>
+
+  <reference id="service">
+    <title>Service interface</title>
+    <para>
+<programlisting><xi:include href="service-api.txt" parse="text" /></programlisting>
+    </para>
+  </reference>
+
+  <reference id="connection">
+    <title>Connection interface</title>
+    <para>
+<programlisting><xi:include href="connection-api.txt" parse="text" /></programlisting>
+    </para>
+  </reference>
+
+  <reference id="reference">
+    <title>Plugin API Reference</title>
+    <partintro>
+      <para>
+       This part presents the function reference for Connection Manager.
+      </para>
+    </partintro>
+    <xi:include href="xml/log.xml" />
+    <xi:include href="xml/plugin.xml" />
+    <xi:include href="xml/storage.xml" />
+    <xi:include href="xml/security.xml" />
+    <xi:include href="xml/resolver.xml" />
+    <!-- <xi:include href="xml/device.xml" /> -->
+    <!-- <xi:include href="xml/network.xml" /> -->
+  </reference>
+
+  <appendix id="license">
+    <title>License</title>
+    <para>
+<programlisting><xi:include href="../COPYING" parse="text" /></programlisting>
+    </para>
+  </appendix>
+
+  <index>
+    <title>Index</title>
+  </index>
+</book>
diff --git a/doc/connman-introduction.xml b/doc/connman-introduction.xml
new file mode 100644 (file)
index 0000000..4672c2c
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+
+<chapter id="introduction">
+  <title>Introduction</title>
+  
+  <sect1 id="intro-about">
+    <title>About</title>
+    
+    <para>
+    </para>
+  </sect1>
+
+</chapter>
diff --git a/doc/counter-api.txt b/doc/counter-api.txt
new file mode 100644 (file)
index 0000000..32411d5
--- /dev/null
@@ -0,0 +1,70 @@
+Counter hierarchy
+=================
+
+Service                unique name
+Interface      net.connman.Counter
+Object path    freely definable
+
+Methods                void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the counter. A counter can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       counter, because when this method gets called it has
+                       already been unregistered.
+
+               void Usage(object service, dict home, dict roaming)
+
+                       This signal indicates a change in the counter values
+                       for the service object. The counter is reset by calling
+                       the service ResetCounters method.
+
+                       When registering a new counter this method will be
+                       called once with all details for "home" and "roaming"
+                       counters filled in. Every further method call will
+                       only include the changed values.
+
+                       When "home" counter is active, then "roaming" counter
+                       will contain an empty dictionary and vise-versa.
+
+                       The dictionary argument contains the following entries:
+
+                               RX.Packets
+
+                                       Total number of packets received.
+
+                               TX.Bytes
+
+                                       Total number of packets sent.
+
+                               RX.Bytes
+
+                                       Total number of bytes received.
+
+                               TX.Bytes
+
+                                       Total number of bytes sent.
+
+                               RX.Errors
+
+                                       Total number of erronous packets
+                                       received.
+
+                               TX.Errors
+
+                                       Total number of erronous packets
+                                       sent.
+
+                               RX.Dropped
+
+                                       Total number of dropped packets
+                                       while receiving.
+
+                               TX.Dropped
+
+                                       Total number of dropped packets
+                                       while sending.
+
+                               Time
+
+                                       Total number of seconds online.
diff --git a/doc/gtk-doc.make b/doc/gtk-doc.make
new file mode 100644 (file)
index 0000000..354ffb7
--- /dev/null
@@ -0,0 +1,173 @@
+# -*- mode: makefile -*-
+
+####################################
+# Everything below here is generic #
+####################################
+
+if GTK_DOC_USE_LIBTOOL
+GTKDOC_CC = $(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+else
+GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+endif
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
+
+EXTRA_DIST =                           \
+       $(content_files)                \
+       $(HTML_IMAGES)                  \
+       $(DOC_MAIN_SGML_FILE)           \
+       $(DOC_MODULE)-sections.txt      \
+       $(DOC_MODULE)-overrides.txt
+
+DOC_STAMPS=scan-build.stamp tmpl-build.stamp sgml-build.stamp html-build.stamp \
+          $(srcdir)/tmpl.stamp $(srcdir)/sgml.stamp $(srcdir)/html.stamp
+
+SCANOBJ_FILES =                 \
+       $(DOC_MODULE).args       \
+       $(DOC_MODULE).hierarchy  \
+       $(DOC_MODULE).interfaces \
+       $(DOC_MODULE).prerequisites \
+       $(DOC_MODULE).signals
+
+REPORT_FILES = \
+       $(DOC_MODULE)-undocumented.txt \
+       $(DOC_MODULE)-undeclared.txt \
+       $(DOC_MODULE)-unused.txt
+
+CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
+
+if ENABLE_GTK_DOC
+all-local: html-build.stamp
+else
+all-local:
+endif
+
+docs: html-build.stamp
+
+#### scan ####
+
+scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB)
+       @echo 'gtk-doc: Scanning header files'
+       @-chmod -R u+w $(srcdir)
+       cd $(srcdir) && \
+         gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES)
+       if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
+           CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" CFLAGS="$(GTKDOC_CFLAGS)" LDFLAGS="$(GTKDOC_LIBS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \
+       else \
+           cd $(srcdir) ; \
+           for i in $(SCANOBJ_FILES) ; do \
+               test -f $$i || touch $$i ; \
+           done \
+       fi
+       touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+       @true
+
+#### templates ####
+
+tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
+       @echo 'gtk-doc: Rebuilding template files'
+       @-chmod -R u+w $(srcdir)
+       cd $(srcdir) && gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS)
+       touch tmpl-build.stamp
+
+tmpl.stamp: tmpl-build.stamp
+       @true
+
+tmpl/*.sgml:
+       @true
+
+
+#### xml ####
+
+sgml-build.stamp: tmpl.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files)
+       @echo 'gtk-doc: Building XML'
+       @-chmod -R u+w $(srcdir)
+       cd $(srcdir) && \
+       gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS)
+       touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+       @true
+
+#### html ####
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
+       @echo 'gtk-doc: Building HTML'
+       @-chmod -R u+w $(srcdir)
+       rm -rf $(srcdir)/html
+       mkdir $(srcdir)/html
+       cd $(srcdir)/html && gtkdoc-mkhtml $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
+       test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html )
+       @echo 'gtk-doc: Fixing cross-references'
+       cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+       touch html-build.stamp
+
+##############
+
+clean-local:
+       rm -f *~ *.bak
+       rm -rf .libs
+
+distclean-local:
+       cd $(srcdir) && \
+         rm -rf xml $(REPORT_FILES) \
+                $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+
+maintainer-clean-local: clean
+       cd $(srcdir) && rm -rf xml html
+
+install-data-local:
+       -installfiles=`echo $(srcdir)/html/*`; \
+       if test "$$installfiles" = '$(srcdir)/html/*'; \
+       then echo '-- Nothing to install' ; \
+       else \
+         $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+         for i in $$installfiles; do \
+           echo '-- Installing '$$i ; \
+           $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+         done; \
+         echo '-- Installing $(srcdir)/html/index.sgml' ; \
+         $(INSTALL_DATA) $(srcdir)/html/index.sgml $(DESTDIR)$(TARGET_DIR) || :; \
+         which gtkdoc-rebase >/dev/null && \
+           gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) ; \
+       fi
+       
+
+uninstall-local:
+       rm -f $(DESTDIR)$(TARGET_DIR)/*
+
+#
+# Require gtk-doc when making dist
+#
+if ENABLE_GTK_DOC
+dist-check-gtkdoc:
+else
+dist-check-gtkdoc:
+       @echo "*** gtk-doc must be installed and enabled in order to make dist"
+       @false
+endif
+
+dist-hook: dist-check-gtkdoc dist-hook-local
+       mkdir $(distdir)/tmpl
+       mkdir $(distdir)/xml
+       mkdir $(distdir)/html
+       -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
+       -cp $(srcdir)/xml/*.xml $(distdir)/xml
+       cp $(srcdir)/html/* $(distdir)/html
+       -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+       -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+       cd $(distdir) && rm -f $(DISTCLEANFILES)
+       -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
diff --git a/doc/ipconfig-api.txt b/doc/ipconfig-api.txt
new file mode 100644 (file)
index 0000000..080fdac
--- /dev/null
@@ -0,0 +1,41 @@
+IP configuration handling
+*************************
+
+
+IP basics
+=========
+
+The core IP handling is designed around network interfaces or more precisely
+what the Linux kernel handles as struct net_device. Via RTNL every interface
+is tracked and an IP device created for it.
+
+       +--------+           +---- eth0 -----+
+       |        |           |               |
+       |  RTNL  +-----+---->|   IP device   |
+       |        |     |     |               |
+       +--------+     |     +---------------+
+                       |
+                       |     +---- wlan0 ----+
+                      |     |               |
+                      +---->|   IP device   |
+                            |               |
+                            +---------------+
+
+The IP device tracks link configuration, IP address setting and routing
+information for that interface. Every IP device also contains a configuration
+element. That element contains an operation table for callbacks based on
+different events.
+
+       struct connman_ipconfig_ops {
+               void (*up)         (struct connman_ipconfig *);
+               void (*down)       (struct connman_ipconfig *);
+               void (*lower_up)   (struct connman_ipconfig *);
+               void (*lower_down) (struct connman_ipconfig *);
+               void (*ip_bound)   (struct connman_ipconfig *);
+               void (*ip_release) (struct connman_ipconfig *);
+       };
+
+All configuration objects created directly by RTNL are tightly bound to the
+IP device. They will trigger DHCP or other configuration helpers.
+
+
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644 (file)
index 0000000..3bd201d
--- /dev/null
@@ -0,0 +1,234 @@
+Manager hierarchy
+=================
+
+Service                net.connman
+Interface      net.connman.Manager
+Object path    /
+
+Methods                dict GetProperties()
+
+                       Returns all global system properties. See the
+                       properties section for available properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       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.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+               array{object,dict} GetTechnologies()
+
+                       Returns a list of tuples with technology object
+                       path and dictionary of technology properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               array{object,dict} GetServices()
+
+                       Returns a sorted list of tuples with service
+                       object path and dictionary of service properties.
+
+                       This list will not contain sensitive information
+                       like passphrases etc.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               object ConnectProvider(dict provider)
+
+                       Connect to a VPN specified by the given provider
+                       properties.
+
+                       When successful this method will return the object
+                       path of the VPN service object.
+
+                       This method can also be used to connect to an
+                       already existing VPN.
+
+                       This method call will only return in case of an
+                       error or when the service is fully connected. So
+                       setting a longer D-Bus timeout might be a really
+                       good idea.
+
+                       When 'SessionMode' property is enabled, this method
+                       call is disallowed.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void RegisterAgent(object path)
+
+                       Register new agent for handling user requests.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void UnregisterAgent(object path)
+
+                       Unregister an existing agent.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void RegisterCounter(object path, uint32 accuracy, uint32 period)  [experimental]
+
+                       Register a new counter for user notifications.
+
+                       The accuracy is specified in kilo-bytes and defines
+                       a threshold for counter updates. Together with the
+                       period value it defines how often user space needs
+                       to be updated. The period value is in seconds.
+
+                       This interface is not meant for time tracking. If
+                       the time needs to be tracked down to the second, it
+                       is better to have a real timer running inside the
+                       application than using this interface.
+
+                       Also getting notified for every kilo-byte is a bad
+                       choice (even if the interface supports it). Something
+                       like 10 kilo-byte units or better 1 mega-byte seems
+                       to be a lot more reasonable and better for the user.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void UnregisterCounter(object path)  [experimental]
+
+                       Unregister an existing counter.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               object CreateSession(dict settings, object notifier)  [experimental]
+
+                       Create a new session for the application. Every
+                       application can create multiple session with
+                       different settings. The settings are described
+                       as part of the session interface.
+
+                       The notifier allows asynchronous notification about
+                       session specific changes. These changes can be
+                       for online/offline state or IP address changes or
+                       similar things the application is required to
+                       handle.
+
+                       Every application should at least create one session
+                       to inform about its requirements and it purpose.
+
+               void DestroySession(object session)  [experimental]
+
+                       Remove the previously created session.
+
+                       If an application exits unexpectatly the session
+                       will be automatically destroyed.
+
+               object path, dict, fd RequestPrivateNetwork(dict options)
+                                                               [experimental]
+
+                       Request a new Private Network, which includes the
+                       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
+                       with IP settings are returned.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.NotSupported
+
+               void ReleasePrivateNetwork(object path) [experimental]
+
+                       Releases a private network.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+Signals                TechnologyAdded(object path, dict properties)
+
+                       Signal that is sent when a new technology is added.
+
+                       It contains the object path of the technology and
+                       also its properties.
+
+               TechnologyRemoved(object path)
+
+                       Signal that is sent when a modem has been removed.
+
+                       The object path is no longer accessible after this
+                       signal and only emitted for reference.
+
+               ServicesChanged(array{object, dict}, array{object})
+
+                       Signals a list of services that have been changed
+                       via the first array. And a list of service that
+                       have been removed via the second array.
+
+                       The list of added services is sorted. The dictionary
+                       with the properties might be empty in case none of
+                       the properties have changed. Or only contains the
+                       properties that have changed.
+
+                       For newly added services the whole set of properties
+                       will be present.
+
+                       The list of removed services can be empty.
+
+                       This signal will only be triggered when the sort
+                       order of the service list or the number of services
+                       changes. It will not be emitted if only a property
+                       of the service object changes. For that it is
+                       required to watch the PropertyChanged signal of
+                       the service object.
+
+               PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State [readonly]
+
+                       The global connection state of a system. Possible
+                       values are "offline", "idle", "ready" and "online".
+
+                       If the device is in offline mode, the value "offline"
+                       indicates this special global state. It can also be
+                       retrieved via the OfflineMode property, but is kept
+                       here for consistency and to differentiate from "idle".
+
+                       However when OfflineMode property is true, the State
+                       property can still be "idle", "ready" or "online"
+                       since it is possible by the end user to re-enable
+                       individual technologies like WiFi and Bluetooth while
+                       in offline mode.
+
+                       The states "idle", "ready" and "online" match to
+                       states from the services. If no service is in
+                       either "ready" or "online" state it will indicate
+                       the "idle" state.
+
+                       If at least one service is in "ready" state and no
+                       service is in "online" state, then it will indicate
+                       the "ready" state.
+
+                       When at least one service is in "online" state,
+                       this property will indicate "online" as well.
+
+               boolean OfflineMode [readwrite]
+
+                       The offline mode indicates the global setting for
+                       switching all radios on or off. Changing offline mode
+                       to true results in powering down all devices. When
+                       leaving offline mode the individual policy of each
+                       device decides to switch the radio back on or not.
+
+                       During offline mode, it is still possible to switch
+                       certain technologies manually back on. For example
+                       the limited usage of WiFi or Bluetooth devices might
+                       be allowed in some situations.
+
+               boolean SessionMode [readwrite]  [experminental]
+
+                       This disables the auto connect feature. It should be
+                       enabled when the Session API is used. When SessionMode
+                       is enabled, 'ConnectService' and 'ConnectProvider'
+                       method calls are disallowed.
+
+                       The default value is false.
diff --git a/doc/overview-api.txt b/doc/overview-api.txt
new file mode 100644 (file)
index 0000000..92ce2b9
--- /dev/null
@@ -0,0 +1,408 @@
+Application programming interface
+*********************************
+
+
+Service basics
+==============
+
+Inside Connection Manager there exists one advanced interface to allow the
+user interface an easy access to networking details and user chosen
+preferences. This is the service list and interface.
+
+The basic idea is that Connection Manager maintains a single flat and sorted
+list of all available, preferred or previously used services. A service here
+can be either a Ethernet device, a WiFi network, a WiMAX service provider
+or a remote Bluetooth device (for example a mobile phone).
+
+This list of service is sorted by Connection Manager and there is no need
+for the user interface to implement its own sorting. User decisions will
+need to be done via Connection Manager and it is then responsible to update
+the order of services in this list.
+
+       +---------------------------------------+
+       | Ethernet                              |
+       +---------------------------------------+
+       | Bluetooth phone                       |
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |
+       +---------------------------------------+
+       | Clear WiMAX      (strength 70)        |
+       +---------------------------------------+
+       | Other AP         (strength 70, rsn)   |
+       +---------------------------------------+
+       | Friends AP       (strength 70, wep)   |
+       +---------------------------------------+
+       | Other WiMAX      (strength 50)        |
+       +---------------------------------------+
+
+If none of the services has been used before the sorting order will be done
+with these priorities:
+
+       1. Ethernet     (lower index numbers first)
+       2. Bluetooth    (last used devices first)
+       3. GSM/UTMS/3G  (if SIM card is present, activated and not roaming)
+       3. WiFi/WiMAX   (signal strength first, then prefer WiMAX over WiFi,
+                        then more secure network first)
+
+The Ethernet devices are always sorted first since they are physically built
+into the system and will be always present. In cases they are switched off
+manually they will not be showing in this list.
+
+Since every Bluetooth device has to be configured/paired first, the user
+already made a choice here that these are important. Connection Manager will
+only show devices with PAN or DUN profile support. While Bluetooth devices
+do have a signal strength, it is mostly unknown since background scanning
+in Bluetooth is too expensive. The choice here is to sort the last used
+Bluetooth device before the others.
+
+The WiFi and WiMAX networks are treated equally since both provide signal
+strength information and networks closer in the proximity should be shown
+before others since it is more likely they are selected first. The signal
+strength value is normalized to 0-100 (effectively a percentage) and allows
+an easy sorting.
+
+If the signal strength is identical then the WiMAX network should be shown
+first since it is a licensed spectrum and more reliable. Also the number
+of WiMAX networks will be smaller than the number of WiFi since that operates
+in an unlicensed spectrum.
+
+WiFi networks with the same signal strength are then sorted by their security
+setting. WPA2 encrypted networks should be preferred over WPA/WEP and also
+unencrypted ones. After that they will be sorted by the SSID in alphabetical
+order.
+
+In the case the WiFi network uses WPS for setup and it is clearly detectable
+that a network waits for Connection Manager to connect to it (for example via
+a push-to-connect button press on the AP), then this network should be shown
+first before any other WiFi or WiMAX network. The reason here is that the
+user already made a choice via the access point. However this depends on
+technical details if it is possible to detect these situations.
+
+
+Service order
+=============
+
+All unused services will have the internal order number of 0 and then will
+be sorted according to the rules above. For Bluetooth the user already made
+the decision to setup their device and by that means select it. However
+until the first connection attempt it might have been setup for total
+different reason (like audio usage) and thus it still counts as unused from
+a networking point of view.
+
+Selecting the "My WiFi AP" and successfully connecting to it makes it a
+favorite device and it will become an order number bigger than 0. All
+order numbers are internally. They are given only to service that are marked
+as favorite. For WiFi, WiMAX and Bluetooth a successful connection attempt
+makes these services automatically a favorite. For Ethernet the plugging
+of a cable makes it a favorite. Disconnecting from a network doesn't remove
+the favorite setting. It is a manual operation and is equal to users pressing
+delete/remove button.
+
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=1 - favorite=yes
+       +---------------------------------------+
+       | Ethernet                              |  order=0
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+Ethernet is special here since the unplugging of the network cable will
+remove the favorite selection.
+
+       +---------------------------------------+
+       | Ethernet with cable                   |  order=1 - favorite=yes
+       +---------------------------------------+
+       | Ethernet without cable                |  order=0 - favorite=no
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+This means that all services with an order > 0 have favorite=yes and all
+others have favorite=no setting. The favorite setting is exposed via a
+property over the service interface. As mentioned above, the order number
+is only used internally.
+
+Within Connection Manager many services can be connected at the same time and
+also have an IP assignment. However only one can have the default route. The
+service with the default route will always be sorted at the top of the
+list.
+
+       +---------------------------------------+
+       | Ethernet                              |  order=2 - connected=yes
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=1 - connected=yes
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+To change the default connection to your access point, the user needs to
+manually drag the access point service to the top of the list. Connection
+Manager will not take down default routes if there is no reason to do so.
+A working connection is considered top priority.
+
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=2 - connected=yes
+       +---------------------------------------+
+       | Ethernet                              |  order=1 - connected=yes
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+Another possible user interaction would be to unplug the Ethernet cable and
+in this case the favorite setting will be removed and the service falls back
+down in the list.
+
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=1 - connected=yes
+       +---------------------------------------+
+       | Ethernet                              |  order=0
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+If the service on the top of the list changes the default route will be
+automatically adjusted as needed. The user can trigger this by disconnecting
+from a network, if the network becomes unavailable (out of range) or if the
+cable gets unplugged.
+
+As described above, the pure case of disconnecting from a network will not
+remove the favorite setting. So previously selected networks are still present
+and are sorted above all others.
+
+       +---------------------------------------+
+       | Ethernet                              |  order=2 - connected=yes
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=1 - connected=no
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+Unplugging the Ethernet cable will remove the favorite setting, but due to
+the basic ordering of services it will be at the top of the services with an
+order number of 0 (directly after all favorite services).
+
+       +---------------------------------------+
+       | My WiFi AP       (strength 80, rsn)   |  order=1 - connected=no
+       +---------------------------------------+
+       | Ethernet                              |  order=0 - connected=no
+       +---------------------------------------+
+       | Guest            (strength 90, none)  |  order=0
+       +---------------------------------------+
+       |                                       |
+
+
+Service tweaks
+==============
+
+The interfaces of Connection Manager will always export all services that are
+currently known. The Ethernet devices with no cable plugged are actually not
+included in this list. They will only show up once a carrier is detected.
+
+The service interface is not meant for basic device configuration task. So
+switching a device on and off (via RFKILL for example) should be done via
+the device interface.
+
+Due to limited screen size of small devices and the big amount of WiFi
+access points that are deployed right now it might be sensible to not show
+certain WiFi networks in the user interface.
+
+The choice to hide a WiFi network from the user interface should be purely
+done by the signal strength. The optimal cut-off value here still has to be
+determined, but in the end that is a user interface policy.
+
+
+Service naming
+==============
+
+Every service will have a name property that allows the user interface to
+display them directly. All names will be already converted into UTF-8. It
+derives from the netork details.
+
+In case of WiFi this will be the SSID value. The SSID is a binary array and
+will be converted into printable form. Unprintable characters are replaced
+with spaces.
+
+For WiMAX networks the provider name like Clear or X-OHM will be used. This
+name either comes directly from the network itself or from a provisioning
+database of the WiMAX service.
+
+For Bluetooth the device alias is used. The alias is different since it
+can be overwritten by the user via the Bluetooth service. The identification
+is still done based on its address, but the display name might change. In
+most cases the alias is equal to the Bluetooth remote friendly name.
+
+For Ethernet device no name will be provided. The type property will indicate
+that this service is Ethernet and then it is up to the user interface to
+provide a proper localized name for it.
+
+
+Service states
+==============
+
+Every service can have multiple states that indicate what is currently
+going on with it. The choice to have multiple states instead of a simple
+connected yes/no value comes from the fact that it is important to let the
+user interface name if a service is in process of connecting/disconnecting.
+
+The basic state of every service is "idle". This means that this service
+is not in use at all at the moment. It also is not attempting to connect
+or do anything else.
+
+The "association" state indicates that this service tries to establish a
+low-level connection to the network. For example associating/connecting
+with a WiFi access point.
+
+With the "configuration" state the service indicates that it is trying
+to retrieve/configure IP settings.
+
+The "ready" state signals a successful connected device. This doesn't mean
+it has the default route, but basic IP operations will succeed.
+
+With the "disconnect" state a service indicates that it is going to terminate
+the current connection and will return to the "idle" state.
+
+In addition a "failure" state indicates a wrong behavior. It is similar to
+the "idle" state since the service is not connected.
+
+               +---------------+
+               | idle          |<-------------------------------+
+               +---------------+                                |
+                     |                                          |
+                     |                      +-------------+     |
+                     +----------------------| failure     |     |
+                     | service.Connect()    +-------------+     |
+                     V                           A              |
+               +---------------+                 |              |
+               | association   |-----------------+              |
+               +---------------+      error      |              |
+                     |                           |              |
+                     | success                   |              |
+                     V                           |              |
+               +---------------+                 |              |
+               | configuration |-----------------+              |
+               +---------------+      error                     |
+                     |                                          |
+                     | success                                  |
+                     V                                          |
+               +---------------+                                |
+               | ready         |                                |
+               +---------------+                                |
+                     |                                          |
+                     | success                                  |
+                     |                                          |
+                     V                                          |
+               +---------------+                                |
+               | online        |<----------------+              |
+               +---------------+                 |              |
+                     |                           |              |
+                     | service.Disconnect()      |              |
+                     V                           |              |
+               +---------------+                 |              |
+               | disconnect    |-----------------+              |
+               +---------------+      error                     |
+                     |                                          |
+                     +------------------------------------------+
+
+The different states should no be used by the user interface to trigger
+advanced actions. The state transitions are provided for the sole purpose
+to give the user feedback on what is currently going on. Especially in
+cases where networks are flaky or DHCP servers take a long time these
+information are helpful for the user.
+
+Some services might require special authentication procedure like a web
+based confirmation. The LoginRequired property should be used to check
+for this.
+
+
+Application basics
+==================
+
+All applications should use D-Bus to communicate with Connection Manager. The
+main entry point is the manager object. Currently the manager object is
+located at "/", but this might change to allow full namespacing of the API
+in the future. The manager interface is documented in manager-api.txt and
+contains a set of global properties and methods.
+
+A simple way to retrieve all global properties looks like this:
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                               "net.connman.Manager")
+
+       properties = manager.GetProperties()
+
+Changing a global property is also pretty simple. For example enabling the
+so called offline mode (aka flight mode) it is enough to just set that
+property:
+
+       manager.SetProperty("OfflineMode", dbus.Boolean(1))
+
+The manager object contains references to profiles, devices, services and
+connections. All these references represent other interfaces that allow
+detailed control of Connection Manager. The profiles and devices interfaces
+are more for advanced features and most applications don't need them at all.
+
+The services are represented as a list of object paths. Every of these object
+paths contains a service interface. A service is a global collection for
+Ethernet devices, WiFi networks, Bluetooth services, WiMAX providers etc. and
+all these different types are treated equally.
+
+Every local Ethernet card will show up as exactly one service. WiFi networks
+will be grouped by SSID, mode and security setting. Bluetooth PAN and DUN
+service will show up per remote device. For WiMAX the provider name will
+be used for grouping different base stations and access providers. This
+creates a simple list that can be directly displayed to the users since these
+are the exact details users should care about.
+
+       properties = manager.GetProperties()
+
+       for path in properties["Services"]:
+               service = dbus.Interface(bus.get_object("net.connman", path),
+                                                       "net.connman.Service")
+
+               service_properties = service.GetProperties()
+
+The service interface is documented in service-api.txt and contains common
+properties valid for all services. It also contains method to connect or
+disconnect a specific service. This allows users to select a specific service.
+Connection Manager can also auto-connect services based on his policies or
+via external events (like plugging in an Ethernet cable).
+
+Connecting (or disconnecting) a specific service manually is as simple as
+just telling it to actually connect:
+
+       service.Connect()  or  service.Disconnect()
+
+It is possible to connect multiple services if the underlying technology
+allows it. For example it would be possible to connect to a WiFi network
+and a Bluetooth service at the same time. Trying to connect to a second WiFi
+network with the same WiFi hardware would result in an automatic disconnect
+of the currently connected network. Connection Manager handles all of this
+for the applications in the background. Trying to connect an Ethernet service
+will result in an error if no cable is plugged in. All connection attempts
+can fail for one reason or another. Application should be able to handle
+such errors and will also be notified of changes via signals.
+
+In future versions Connection Manager will interact with an agent to confirm
+certain transactions with the user. This functionality is currently not
+implemented.
+
+To monitor the current status of a service the state property can be used. It
+gives detailed information about the current progress.
+
+       properties = service.GetProperties()
+
+       print properties["State"]
+
+All state changes are also sent via the PropertyChanged signal on the
+service interface. This allows asynchronous monitoring without having to poll
+Connection Manager for changes.
diff --git a/doc/plugin-api.txt b/doc/plugin-api.txt
new file mode 100644 (file)
index 0000000..b39c043
--- /dev/null
@@ -0,0 +1,24 @@
+Plugin programming interface
+****************************
+
+
+Plugin basics
+=============
+
+The Connection Manager supports plugins for various actions. The basic plugin
+contains of plugin description via CONNMAN_PLUGIN_DEFINE and also init/exit
+callbacks defined through that description.
+
+#include <connman/plugin.h>
+
+static int example_init(void)
+{
+       return 0;
+}
+
+static void example_exit(void)
+{
+}
+
+CONNMAN_PLUGIN_DEFINE(example, "Example plugin", CONNMAN_VERSION,
+                                               example_init, example_exit)
diff --git a/doc/rfc1035.txt b/doc/rfc1035.txt
new file mode 100644 (file)
index 0000000..b1a9bf5
--- /dev/null
@@ -0,0 +1,3077 @@
+Network Working Group                                     P. Mockapetris
+Request for Comments: 1035                                           ISI
+                                                           November 1987
+Obsoletes: RFCs 882, 883, 973
+
+            DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+
+
+1. STATUS OF THIS MEMO
+
+This RFC describes the details of the domain system and protocol, and
+assumes that the reader is familiar with the concepts discussed in a
+companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034].
+
+The domain system is a mixture of functions and data types which are an
+official protocol and functions and data types which are still
+experimental.  Since the domain system is intentionally extensible, new
+data types and experimental behavior should always be expected in parts
+of the system beyond the official protocol.  The official protocol parts
+include standard queries, responses and the Internet class RR data
+formats (e.g., host addresses).  Since the previous RFC set, several
+definitions have changed, so some previous definitions are obsolete.
+
+Experimental or obsolete features are clearly marked in these RFCs, and
+such information should be used with caution.
+
+The reader is especially cautioned not to depend on the values which
+appear in examples to be current or complete, since their purpose is
+primarily pedagogical.  Distribution of this memo is unlimited.
+
+                           Table of Contents
+
+  1. STATUS OF THIS MEMO                                              1
+  2. INTRODUCTION                                                     3
+      2.1. Overview                                                   3
+      2.2. Common configurations                                      4
+      2.3. Conventions                                                7
+          2.3.1. Preferred name syntax                                7
+          2.3.2. Data Transmission Order                              8
+          2.3.3. Character Case                                       9
+          2.3.4. Size limits                                         10
+  3. DOMAIN NAME SPACE AND RR DEFINITIONS                            10
+      3.1. Name space definitions                                    10
+      3.2. RR definitions                                            11
+          3.2.1. Format                                              11
+          3.2.2. TYPE values                                         12
+          3.2.3. QTYPE values                                        12
+          3.2.4. CLASS values                                        13
+
+
+
+Mockapetris                                                     [Page 1]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+          3.2.5. QCLASS values                                       13
+      3.3. Standard RRs                                              13
+          3.3.1. CNAME RDATA format                                  14
+          3.3.2. HINFO RDATA format                                  14
+          3.3.3. MB RDATA format (EXPERIMENTAL)                      14
+          3.3.4. MD RDATA format (Obsolete)                          15
+          3.3.5. MF RDATA format (Obsolete)                          15
+          3.3.6. MG RDATA format (EXPERIMENTAL)                      16
+          3.3.7. MINFO RDATA format (EXPERIMENTAL)                   16
+          3.3.8. MR RDATA format (EXPERIMENTAL)                      17
+          3.3.9. MX RDATA format                                     17
+          3.3.10. NULL RDATA format (EXPERIMENTAL)                   17
+          3.3.11. NS RDATA format                                    18
+          3.3.12. PTR RDATA format                                   18
+          3.3.13. SOA RDATA format                                   19
+          3.3.14. TXT RDATA format                                   20
+      3.4. ARPA Internet specific RRs                                20
+          3.4.1. A RDATA format                                      20
+          3.4.2. WKS RDATA format                                    21
+      3.5. IN-ADDR.ARPA domain                                       22
+      3.6. Defining new types, classes, and special namespaces       24
+  4. MESSAGES                                                        25
+      4.1. Format                                                    25
+          4.1.1. Header section format                               26
+          4.1.2. Question section format                             28
+          4.1.3. Resource record format                              29
+          4.1.4. Message compression                                 30
+      4.2. Transport                                                 32
+          4.2.1. UDP usage                                           32
+          4.2.2. TCP usage                                           32
+  5. MASTER FILES                                                    33
+      5.1. Format                                                    33
+      5.2. Use of master files to define zones                       35
+      5.3. Master file example                                       36
+  6. NAME SERVER IMPLEMENTATION                                      37
+      6.1. Architecture                                              37
+          6.1.1. Control                                             37
+          6.1.2. Database                                            37
+          6.1.3. Time                                                39
+      6.2. Standard query processing                                 39
+      6.3. Zone refresh and reload processing                        39
+      6.4. Inverse queries (Optional)                                40
+          6.4.1. The contents of inverse queries and responses       40
+          6.4.2. Inverse query and response example                  41
+          6.4.3. Inverse query processing                            42
+
+
+
+
+
+
+Mockapetris                                                     [Page 2]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+      6.5. Completion queries and responses                          42
+  7. RESOLVER IMPLEMENTATION                                         43
+      7.1. Transforming a user request into a query                  43
+      7.2. Sending the queries                                       44
+      7.3. Processing responses                                      46
+      7.4. Using the cache                                           47
+  8. MAIL SUPPORT                                                    47
+      8.1. Mail exchange binding                                     48
+      8.2. Mailbox binding (Experimental)                            48
+  9. REFERENCES and BIBLIOGRAPHY                                     50
+  Index                                                              54
+
+2. INTRODUCTION
+
+2.1. Overview
+
+The goal of domain names is to provide a mechanism for naming resources
+in such a way that the names are usable in different hosts, networks,
+protocol families, internets, and administrative organizations.
+
+From the user's point of view, domain names are useful as arguments to a
+local agent, called a resolver, which retrieves information associated
+with the domain name.  Thus a user might ask for the host address or
+mail information associated with a particular domain name.  To enable
+the user to request a particular type of information, an appropriate
+query type is passed to the resolver with the domain name.  To the user,
+the domain tree is a single information space; the resolver is
+responsible for hiding the distribution of data among name servers from
+the user.
+
+From the resolver's point of view, the database that makes up the domain
+space is distributed among various name servers.  Different parts of the
+domain space are stored in different name servers, although a particular
+data item will be stored redundantly in two or more name servers.  The
+resolver starts with knowledge of at least one name server.  When the
+resolver processes a user query it asks a known name server for the
+information; in return, the resolver either receives the desired
+information or a referral to another name server.  Using these
+referrals, resolvers learn the identities and contents of other name
+servers.  Resolvers are responsible for dealing with the distribution of
+the domain space and dealing with the effects of name server failure by
+consulting redundant databases in other servers.
+
+Name servers manage two kinds of data.  The first kind of data held in
+sets called zones; each zone is the complete database for a particular
+"pruned" subtree of the domain space.  This data is called
+authoritative.  A name server periodically checks to make sure that its
+zones are up to date, and if not, obtains a new copy of updated zones
+
+
+
+Mockapetris                                                     [Page 3]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+from master files stored locally or in another name server.  The second
+kind of data is cached data which was acquired by a local resolver.
+This data may be incomplete, but improves the performance of the
+retrieval process when non-local data is repeatedly accessed.  Cached
+data is eventually discarded by a timeout mechanism.
+
+This functional structure isolates the problems of user interface,
+failure recovery, and distribution in the resolvers and isolates the
+database update and refresh problems in the name servers.
+
+2.2. Common configurations
+
+A host can participate in the domain name system in a number of ways,
+depending on whether the host runs programs that retrieve information
+from the domain system, name servers that answer queries from other
+hosts, or various combinations of both functions.  The simplest, and
+perhaps most typical, configuration is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+    +---------+               +----------+         |  +--------+
+    |         | user queries  |          |queries  |  |        |
+    |  User   |-------------->|          |---------|->|Foreign |
+    | Program |               | Resolver |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    |         | user responses|          |responses|  |        |
+    +---------+               +----------+         |  +--------+
+                                |     A            |
+                cache additions |     | references |
+                                V     |            |
+                              +----------+         |
+                              |  cache   |         |
+                              +----------+         |
+
+User programs interact with the domain name space through resolvers; the
+format of user queries and user responses is specific to the host and
+its operating system.  User queries will typically be operating system
+calls, and the resolver and its cache will be part of the host operating
+system.  Less capable hosts may choose to implement the resolver as a
+subroutine to be linked in with every program that needs its services.
+Resolvers answer user queries with information they acquire via queries
+to foreign name servers and the local cache.
+
+Note that the resolver may have to make several queries to several
+different foreign name servers to answer a particular user query, and
+hence the resolution of a user query may involve several network
+accesses and an arbitrary amount of time.  The queries to foreign name
+servers and the corresponding responses have a standard format described
+
+
+
+Mockapetris                                                     [Page 4]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+in this memo, and may be datagrams.
+
+Depending on its capabilities, a name server could be a stand alone
+program on a dedicated machine or a process or processes on a large
+timeshared host.  A simple configuration might be:
+
+                 Local Host                        |  Foreign
+                                                   |
+      +---------+                                  |
+     /         /|                                  |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+
+Here a primary name server acquires information about one or more zones
+by reading master files from its local file system, and answers queries
+about those zones that arrive from foreign resolvers.
+
+The DNS requires that all zones be redundantly supported by more than
+one name server.  Designated secondary servers can acquire zones and
+check for updates from the primary server using the zone transfer
+protocol of the DNS.  This configuration is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+      +---------+                                  |
+     /         /|                                  |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+                                A     |maintenance |  +--------+
+                                |     +------------|->|        |
+                                |      queries     |  |Foreign |
+                                |                  |  |  Name  |
+                                +------------------|--| Server |
+                             maintenance responses |  +--------+
+
+In this configuration, the name server periodically establishes a
+virtual circuit to a foreign name server to acquire a copy of a zone or
+to check that an existing copy has not changed.  The messages sent for
+
+
+
+Mockapetris                                                     [Page 5]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+these maintenance activities follow the same form as queries and
+responses, but the message sequences are somewhat different.
+
+The information flow in a host that supports all aspects of the domain
+name system is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+    +---------+               +----------+         |  +--------+
+    |         | user queries  |          |queries  |  |        |
+    |  User   |-------------->|          |---------|->|Foreign |
+    | Program |               | Resolver |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    |         | user responses|          |responses|  |        |
+    +---------+               +----------+         |  +--------+
+                                |     A            |
+                cache additions |     | references |
+                                V     |            |
+                              +----------+         |
+                              |  Shared  |         |
+                              | database |         |
+                              +----------+         |
+                                A     |            |
+      +---------+     refreshes |     | references |
+     /         /|               |     V            |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+                                A     |maintenance |  +--------+
+                                |     +------------|->|        |
+                                |      queries     |  |Foreign |
+                                |                  |  |  Name  |
+                                +------------------|--| Server |
+                             maintenance responses |  +--------+
+
+The shared database holds domain space data for the local name server
+and resolver.  The contents of the shared database will typically be a
+mixture of authoritative data maintained by the periodic refresh
+operations of the name server and cached data from previous resolver
+requests.  The structure of the domain data and the necessity for
+synchronization between name servers and resolvers imply the general
+characteristics of this database, but the actual format is up to the
+local implementor.
+
+
+
+
+Mockapetris                                                     [Page 6]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Information flow can also be tailored so that a group of hosts act
+together to optimize activities.  Sometimes this is done to offload less
+capable hosts so that they do not have to implement a full resolver.
+This can be appropriate for PCs or hosts which want to minimize the
+amount of new network code which is required.  This scheme can also
+allow a group of hosts can share a small number of caches rather than
+maintaining a large number of separate caches, on the premise that the
+centralized caches will have a higher hit ratio.  In either case,
+resolvers are replaced with stub resolvers which act as front ends to
+resolvers located in a recursive server in one or more name servers
+known to perform that service:
+
+                   Local Hosts                     |  Foreign
+                                                   |
+    +---------+                                    |
+    |         | responses                          |
+    | Stub    |<--------------------+              |
+    | Resolver|                     |              |
+    |         |----------------+    |              |
+    +---------+ recursive      |    |              |
+                queries        |    |              |
+                               V    |              |
+    +---------+ recursive     +----------+         |  +--------+
+    |         | queries       |          |queries  |  |        |
+    | Stub    |-------------->| Recursive|---------|->|Foreign |
+    | Resolver|               | Server   |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    +---------+ responses     |          |responses|  |        |
+                              +----------+         |  +--------+
+                              |  Central |         |
+                              |   cache  |         |
+                              +----------+         |
+
+In any case, note that domain components are always replicated for
+reliability whenever possible.
+
+2.3. Conventions
+
+The domain system has several conventions dealing with low-level, but
+fundamental, issues.  While the implementor is free to violate these
+conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
+ALL behavior observed from other hosts.
+
+2.3.1. Preferred name syntax
+
+The DNS specifications attempt to be as general as possible in the rules
+for constructing domain names.  The idea is that the name of any
+existing object can be expressed as a domain name with minimal changes.
+
+
+
+Mockapetris                                                     [Page 7]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+However, when assigning a domain name for an object, the prudent user
+will select a name which satisfies both the rules of the domain system
+and any existing rules for the object, whether these rules are published
+or implied by existing programs.
+
+For example, when naming a mail domain, the user should satisfy both the
+rules of this memo and those in RFC-822.  When creating a new host name,
+the old rules for HOSTS.TXT should be followed.  This avoids problems
+when old software is converted to use domain names.
+
+The following syntax will result in fewer problems with many
+
+applications that use domain names (e.g., mail, TELNET).
+
+<domain> ::= <subdomain> | " "
+
+<subdomain> ::= <label> | <subdomain> "." <label>
+
+<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+
+<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+
+<let-dig-hyp> ::= <let-dig> | "-"
+
+<let-dig> ::= <letter> | <digit>
+
+<letter> ::= any one of the 52 alphabetic characters A through Z in
+upper case and a through z in lower case
+
+<digit> ::= any one of the ten digits 0 through 9
+
+Note that while upper and lower case letters are allowed in domain
+names, no significance is attached to the case.  That is, two names with
+the same spelling but different case are to be treated as if identical.
+
+The labels must follow the rules for ARPANET host names.  They must
+start with a letter, end with a letter or digit, and have as interior
+characters only letters, digits, and hyphen.  There are also some
+restrictions on the length.  Labels must be 63 characters or less.
+
+For example, the following strings identify hosts in the Internet:
+
+A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
+
+2.3.2. Data Transmission Order
+
+The order of transmission of the header and data described in this
+document is resolved to the octet level.  Whenever a diagram shows a
+
+
+
+Mockapetris                                                     [Page 8]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+group of octets, the order of transmission of those octets is the normal
+order in which they are read in English.  For example, in the following
+diagram, the octets are transmitted in the order they are numbered.
+
+     0                   1
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       1       |       2       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       3       |       4       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       5       |       6       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Whenever an octet represents a numeric quantity, the left most bit in
+the diagram is the high order or most significant bit.  That is, the bit
+labeled 0 is the most significant bit.  For example, the following
+diagram represents the value 170 (decimal).
+
+     0 1 2 3 4 5 6 7
+    +-+-+-+-+-+-+-+-+
+    |1 0 1 0 1 0 1 0|
+    +-+-+-+-+-+-+-+-+
+
+Similarly, whenever a multi-octet field represents a numeric quantity
+the left most bit of the whole field is the most significant bit.  When
+a multi-octet quantity is transmitted the most significant octet is
+transmitted first.
+
+2.3.3. Character Case
+
+For all parts of the DNS that are part of the official protocol, all
+comparisons between character strings (e.g., labels, domain names, etc.)
+are done in a case-insensitive manner.  At present, this rule is in
+force throughout the domain system without exception.  However, future
+additions beyond current usage may need to use the full binary octet
+capabilities in names, so attempts to store domain names in 7-bit ASCII
+or use of special bytes to terminate labels, etc., should be avoided.
+
+When data enters the domain system, its original case should be
+preserved whenever possible.  In certain circumstances this cannot be
+done.  For example, if two RRs are stored in a database, one at x.y and
+one at X.Y, they are actually stored at the same place in the database,
+and hence only one casing would be preserved.  The basic rule is that
+case can be discarded only when data is used to define structure in a
+database, and two names are identical when compared in a case
+insensitive manner.
+
+
+
+
+Mockapetris                                                     [Page 9]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Loss of case sensitive data must be minimized.  Thus while data for x.y
+and X.Y may both be stored under a single location x.y or X.Y, data for
+a.x and B.X would never be stored under A.x, A.X, b.x, or b.X.  In
+general, this preserves the case of the first label of a domain name,
+but forces standardization of interior node labels.
+
+Systems administrators who enter data into the domain database should
+take care to represent the data they supply to the domain system in a
+case-consistent manner if their system is case-sensitive.  The data
+distribution system in the domain system will ensure that consistent
+representations are preserved.
+
+2.3.4. Size limits
+
+Various objects and parameters in the DNS have size limits.  They are
+listed below.  Some could be easily changed, others are more
+fundamental.
+
+labels          63 octets or less
+
+names           255 octets or less
+
+TTL             positive values of a signed 32 bit number.
+
+UDP messages    512 octets or less
+
+3. DOMAIN NAME SPACE AND RR DEFINITIONS
+
+3.1. Name space definitions
+
+Domain names in messages are expressed in terms of a sequence of labels.
+Each label is represented as a one octet length field followed by that
+number of octets.  Since every domain name ends with the null label of
+the root, a domain name is terminated by a length byte of zero.  The
+high order two bits of every length octet must be zero, and the
+remaining six bits of the length field limit the label to 63 octets or
+less.
+
+To simplify implementations, the total length of a domain name (i.e.,
+label octets and label length octets) is restricted to 255 octets or
+less.
+
+Although labels can contain any 8 bit values in octets that make up a
+label, it is strongly recommended that labels follow the preferred
+syntax described elsewhere in this memo, which is compatible with
+existing host naming conventions.  Name servers and resolvers must
+compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
+with zero parity.  Non-alphabetic codes must match exactly.
+
+
+
+Mockapetris                                                    [Page 10]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.2. RR definitions
+
+3.2.1. Format
+
+All RRs have the same top level format shown below:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                                               /
+    /                      NAME                     /
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+
+where:
+
+NAME            an owner name, i.e., the name of the node to which this
+                resource record pertains.
+
+TYPE            two octets containing one of the RR TYPE codes.
+
+CLASS           two octets containing one of the RR CLASS codes.
+
+TTL             a 32 bit signed integer that specifies the time interval
+                that the resource record may be cached before the source
+                of the information should again be consulted.  Zero
+                values are interpreted to mean that the RR can only be
+                used for the transaction in progress, and should not be
+                cached.  For example, SOA records are always distributed
+                with a zero TTL to prohibit caching.  Zero values can
+                also be used for extremely volatile data.
+
+RDLENGTH        an unsigned 16 bit integer that specifies the length in
+                octets of the RDATA field.
+
+
+
+Mockapetris                                                    [Page 11]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+RDATA           a variable length string of octets that describes the
+                resource.  The format of this information varies
+                according to the TYPE and CLASS of the resource record.
+
+3.2.2. TYPE values
+
+TYPE fields are used in resource records.  Note that these types are a
+subset of QTYPEs.
+
+TYPE            value and meaning
+
+A               1 a host address
+
+NS              2 an authoritative name server
+
+MD              3 a mail destination (Obsolete - use MX)
+
+MF              4 a mail forwarder (Obsolete - use MX)
+
+CNAME           5 the canonical name for an alias
+
+SOA             6 marks the start of a zone of authority
+
+MB              7 a mailbox domain name (EXPERIMENTAL)
+
+MG              8 a mail group member (EXPERIMENTAL)
+
+MR              9 a mail rename domain name (EXPERIMENTAL)
+
+NULL            10 a null RR (EXPERIMENTAL)
+
+WKS             11 a well known service description
+
+PTR             12 a domain name pointer
+
+HINFO           13 host information
+
+MINFO           14 mailbox or mail list information
+
+MX              15 mail exchange
+
+TXT             16 text strings
+
+3.2.3. QTYPE values
+
+QTYPE fields appear in the question part of a query.  QTYPES are a
+superset of TYPEs, hence all TYPEs are valid QTYPEs.  In addition, the
+following QTYPEs are defined:
+
+
+
+Mockapetris                                                    [Page 12]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+AXFR            252 A request for a transfer of an entire zone
+
+MAILB           253 A request for mailbox-related records (MB, MG or MR)
+
+MAILA           254 A request for mail agent RRs (Obsolete - see MX)
+
+*               255 A request for all records
+
+3.2.4. CLASS values
+
+CLASS fields appear in resource records.  The following CLASS mnemonics
+and values are defined:
+
+IN              1 the Internet
+
+CS              2 the CSNET class (Obsolete - used only for examples in
+                some obsolete RFCs)
+
+CH              3 the CHAOS class
+
+HS              4 Hesiod [Dyer 87]
+
+3.2.5. QCLASS values
+
+QCLASS fields appear in the question section of a query.  QCLASS values
+are a superset of CLASS values; every CLASS is a valid QCLASS.  In
+addition to CLASS values, the following QCLASSes are defined:
+
+*               255 any class
+
+3.3. Standard RRs
+
+The following RR definitions are expected to occur, at least
+potentially, in all classes.  In particular, NS, SOA, CNAME, and PTR
+will be used in all classes, and have the same format in all classes.
+Because their RDATA format is known, all domain names in the RDATA
+section of these RRs may be compressed.
+
+<domain-name> is a domain name represented as a series of labels, and
+terminated by a label with zero length.  <character-string> is a single
+length octet followed by that number of characters.  <character-string>
+is treated as binary information, and can be up to 256 characters in
+length (including the length octet).
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 13]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.1. CNAME RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     CNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CNAME           A <domain-name> which specifies the canonical or primary
+                name for the owner.  The owner name is an alias.
+
+CNAME RRs cause no additional section processing, but name servers may
+choose to restart the query at the canonical name in certain cases.  See
+the description of name server logic in [RFC-1034] for details.
+
+3.3.2. HINFO RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                      CPU                      /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                       OS                      /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CPU             A <character-string> which specifies the CPU type.
+
+OS              A <character-string> which specifies the operating
+                system type.
+
+Standard values for CPU and OS can be found in [RFC-1010].
+
+HINFO records are used to acquire general information about a host.  The
+main use is for protocols such as FTP that can use special procedures
+when talking between machines or operating systems of the same type.
+
+3.3.3. MB RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has the
+                specified mailbox.
+
+
+
+Mockapetris                                                    [Page 14]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+MB records cause additional section processing which looks up an A type
+RRs corresponding to MADNAME.
+
+3.3.4. MD RDATA format (Obsolete)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has a mail
+                agent for the domain which should be able to deliver
+                mail for the domain.
+
+MD records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MD is obsolete.  See the definition of MX and [RFC-974] for details of
+the new scheme.  The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 0.
+
+3.3.5. MF RDATA format (Obsolete)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has a mail
+                agent for the domain which will accept mail for
+                forwarding to the domain.
+
+MF records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MF is obsolete.  See the definition of MX and [RFC-974] for details ofw
+the new scheme.  The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 10.
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 15]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.6. MG RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MGMNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MGMNAME         A <domain-name> which specifies a mailbox which is a
+                member of the mail group specified by the domain name.
+
+MG records cause no additional section processing.
+
+3.3.7. MINFO RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                    RMAILBX                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                    EMAILBX                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+RMAILBX         A <domain-name> which specifies a mailbox which is
+                responsible for the mailing list or mailbox.  If this
+                domain name names the root, the owner of the MINFO RR is
+                responsible for itself.  Note that many existing mailing
+                lists use a mailbox X-request for the RMAILBX field of
+                mailing list X, e.g., Msgroup-request for Msgroup.  This
+                field provides a more general mechanism.
+
+
+EMAILBX         A <domain-name> which specifies a mailbox which is to
+                receive error messages related to the mailing list or
+                mailbox specified by the owner of the MINFO RR (similar
+                to the ERRORS-TO: field which has been proposed).  If
+                this domain name names the root, errors should be
+                returned to the sender of the message.
+
+MINFO records cause no additional section processing.  Although these
+records can be associated with a simple mailbox, they are usually used
+with a mailing list.
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 16]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.8. MR RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   NEWNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NEWNAME         A <domain-name> which specifies a mailbox which is the
+                proper rename of the specified mailbox.
+
+MR records cause no additional section processing.  The main use for MR
+is as a forwarding entry for a user who has moved to a different
+mailbox.
+
+3.3.9. MX RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                  PREFERENCE                   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   EXCHANGE                    /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PREFERENCE      A 16 bit integer which specifies the preference given to
+                this RR among others at the same owner.  Lower values
+                are preferred.
+
+EXCHANGE        A <domain-name> which specifies a host willing to act as
+                a mail exchange for the owner name.
+
+MX records cause type A additional section processing for the host
+specified by EXCHANGE.  The use of MX RRs is explained in detail in
+[RFC-974].
+
+3.3.10. NULL RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                  <anything>                   /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+Anything at all may be in the RDATA field so long as it is 65535 octets
+or less.
+
+
+
+
+Mockapetris                                                    [Page 17]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+NULL records cause no additional section processing.  NULL RRs are not
+allowed in master files.  NULLs are used as placeholders in some
+experimental extensions of the DNS.
+
+3.3.11. NS RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   NSDNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NSDNAME         A <domain-name> which specifies a host which should be
+                authoritative for the specified class and domain.
+
+NS records cause both the usual additional section processing to locate
+a type A record, and, when used in a referral, a special search of the
+zone in which they reside for glue information.
+
+The NS RR states that the named host should be expected to have a zone
+starting at owner name of the specified class.  Note that the class may
+not indicate the protocol family which should be used to communicate
+with the host, although it is typically a strong hint.  For example,
+hosts which are name servers for either Internet (IN) or Hesiod (HS)
+class information are normally queried using IN class protocols.
+
+3.3.12. PTR RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   PTRDNAME                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PTRDNAME        A <domain-name> which points to some location in the
+                domain name space.
+
+PTR records cause no additional section processing.  These RRs are used
+in special domains to point to some other location in the domain space.
+These records are simple data, and don't imply any special processing
+similar to that performed by CNAME, which identifies aliases.  See the
+description of the IN-ADDR.ARPA domain for an example.
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 18]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.13. SOA RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     MNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     RNAME                     /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    SERIAL                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    REFRESH                    |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     RETRY                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    EXPIRE                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    MINIMUM                    |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MNAME           The <domain-name> of the name server that was the
+                original or primary source of data for this zone.
+
+RNAME           A <domain-name> which specifies the mailbox of the
+                person responsible for this zone.
+
+SERIAL          The unsigned 32 bit version number of the original copy
+                of the zone.  Zone transfers preserve this value.  This
+                value wraps and should be compared using sequence space
+                arithmetic.
+
+REFRESH         A 32 bit time interval before the zone should be
+                refreshed.
+
+RETRY           A 32 bit time interval that should elapse before a
+                failed refresh should be retried.
+
+EXPIRE          A 32 bit time value that specifies the upper limit on
+                the time interval that can elapse before the zone is no
+                longer authoritative.
+
+
+
+
+
+Mockapetris                                                    [Page 19]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+MINIMUM         The unsigned 32 bit minimum TTL field that should be
+                exported with any RR from this zone.
+
+SOA records cause no additional section processing.
+
+All times are in units of seconds.
+
+Most of these fields are pertinent only for name server maintenance
+operations.  However, MINIMUM is used in all query operations that
+retrieve RRs from a zone.  Whenever a RR is sent in a response to a
+query, the TTL field is set to the maximum of the TTL field from the RR
+and the MINIMUM field in the appropriate SOA.  Thus MINIMUM is a lower
+bound on the TTL field for all RRs in a zone.  Note that this use of
+MINIMUM should occur when the RRs are copied into the response and not
+when the zone is loaded from a master file or via a zone transfer.  The
+reason for this provison is to allow future dynamic update facilities to
+change the SOA RR with known semantics.
+
+
+3.3.14. TXT RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   TXT-DATA                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+TXT-DATA        One or more <character-string>s.
+
+TXT RRs are used to hold descriptive text.  The semantics of the text
+depends on the domain where it is found.
+
+3.4. Internet specific RRs
+
+3.4.1. A RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ADDRESS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS         A 32 bit Internet address.
+
+Hosts that have multiple Internet addresses will have multiple A
+records.
+
+
+
+
+
+Mockapetris                                                    [Page 20]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+A records cause no additional section processing.  The RDATA section of
+an A line in a master file is an Internet address expressed as four
+decimal numbers separated by dots without any imbedded spaces (e.g.,
+"10.2.0.52" or "192.0.5.6").
+
+3.4.2. WKS RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ADDRESS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |       PROTOCOL        |                       |
+    +--+--+--+--+--+--+--+--+                       |
+    |                                               |
+    /                   <BIT MAP>                   /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS         An 32 bit Internet address
+
+PROTOCOL        An 8 bit IP protocol number
+
+<BIT MAP>       A variable length bit map.  The bit map must be a
+                multiple of 8 bits long.
+
+The WKS record is used to describe the well known services supported by
+a particular protocol on a particular internet address.  The PROTOCOL
+field specifies an IP protocol number, and the bit map has one bit per
+port of the specified protocol.  The first bit corresponds to port 0,
+the second to port 1, etc.  If the bit map does not include a bit for a
+protocol of interest, that bit is assumed zero.  The appropriate values
+and mnemonics for ports and protocols are specified in [RFC-1010].
+
+For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+25 (SMTP).  If this bit is set, a SMTP server should be listening on TCP
+port 25; if zero, SMTP service is not supported on the specified
+address.
+
+The purpose of WKS RRs is to provide availability information for
+servers for TCP and UDP.  If a server supports both TCP and UDP, or has
+multiple Internet addresses, then multiple WKS RRs are used.
+
+WKS RRs cause no additional section processing.
+
+In master files, both ports and protocols are expressed using mnemonics
+or decimal numbers.
+
+
+
+
+Mockapetris                                                    [Page 21]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.5. IN-ADDR.ARPA domain
+
+The Internet uses a special domain to support gateway location and
+Internet address to host mapping.  Other classes may employ a similar
+strategy in other domains.  The intent of this domain is to provide a
+guaranteed method to perform host address to host name mapping, and to
+facilitate queries to locate all gateways on a particular network in the
+Internet.
+
+Note that both of these services are similar to functions that could be
+performed by inverse queries; the difference is that this part of the
+domain name space is structured according to address, and hence can
+guarantee that the appropriate data can be located without an exhaustive
+search of the domain space.
+
+The domain begins at IN-ADDR.ARPA and has a substructure which follows
+the Internet addressing structure.
+
+Domain names in the IN-ADDR.ARPA domain are defined to have up to four
+labels in addition to the IN-ADDR.ARPA suffix.  Each label represents
+one octet of an Internet address, and is expressed as a character string
+for a decimal value in the range 0-255 (with leading zeros omitted
+except in the case of a zero octet which is represented by a single
+zero).
+
+Host addresses are represented by domain names that have all four labels
+specified.  Thus data for Internet address 10.2.0.52 is located at
+domain name 52.0.2.10.IN-ADDR.ARPA.  The reversal, though awkward to
+read, allows zones to be delegated which are exactly one network of
+address space.  For example, 10.IN-ADDR.ARPA can be a zone containing
+data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
+MILNET.  Address nodes are used to hold pointers to primary host names
+in the normal domain space.
+
+Network numbers correspond to some non-terminal nodes at various depths
+in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
+2, or 3 octets.  Network nodes are used to hold pointers to the primary
+host names of gateways attached to that network.  Since a gateway is, by
+definition, on more than one network, it will typically have two or more
+network nodes which point at it.  Gateways will also have host level
+pointers at their fully qualified addresses.
+
+Both the gateway pointers at network nodes and the normal host pointers
+at full address nodes use the PTR RR to point back to the primary domain
+names of the corresponding hosts.
+
+For example, the IN-ADDR.ARPA domain will contain information about the
+ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
+
+
+
+Mockapetris                                                    [Page 22]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU.  Assuming that ISI
+gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
+GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
+and a name GW.LCS.MIT.EDU, the domain database would contain:
+
+    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+    18.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+    26.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    22.0.2.10.IN-ADDR.ARPA.    PTR MILNET-GW.ISI.EDU.
+    103.0.0.26.IN-ADDR.ARPA.   PTR MILNET-GW.ISI.EDU.
+    77.0.0.10.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
+    4.0.10.18.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
+    103.0.3.26.IN-ADDR.ARPA.   PTR A.ISI.EDU.
+    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
+
+Thus a program which wanted to locate gateways on net 10 would originate
+a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA.  It
+would receive two RRs in response:
+
+    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+
+The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
+GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
+these gateways.
+
+A resolver which wanted to find the host name corresponding to Internet
+host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
+QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
+
+    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
+
+Several cautions apply to the use of these services:
+   - Since the IN-ADDR.ARPA special domain and the normal domain
+     for a particular host or gateway will be in different zones,
+     the possibility exists that that the data may be inconsistent.
+
+   - Gateways will often have two names in separate domains, only
+     one of which can be primary.
+
+   - Systems that use the domain database to initialize their
+     routing tables must start with enough gateway information to
+     guarantee that they can access the appropriate name server.
+
+   - The gateway data only reflects the existence of a gateway in a
+     manner equivalent to the current HOSTS.TXT file.  It doesn't
+     replace the dynamic availability information from GGP or EGP.
+
+
+
+Mockapetris                                                    [Page 23]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.6. Defining new types, classes, and special namespaces
+
+The previously defined types and classes are the ones in use as of the
+date of this memo.  New definitions should be expected.  This section
+makes some recommendations to designers considering additions to the
+existing facilities.  The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
+forum where general discussion of design issues takes place.
+
+In general, a new type is appropriate when new information is to be
+added to the database about an existing object, or we need new data
+formats for some totally new object.  Designers should attempt to define
+types and their RDATA formats that are generally applicable to all
+classes, and which avoid duplication of information.  New classes are
+appropriate when the DNS is to be used for a new protocol, etc which
+requires new class-specific data formats, or when a copy of the existing
+name space is desired, but a separate management domain is necessary.
+
+New types and classes need mnemonics for master files; the format of the
+master files requires that the mnemonics for type and class be disjoint.
+
+TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
+respectively.
+
+The present system uses multiple RRs to represent multiple values of a
+type rather than storing multiple values in the RDATA section of a
+single RR.  This is less efficient for most applications, but does keep
+RRs shorter.  The multiple RRs assumption is incorporated in some
+experimental work on dynamic update methods.
+
+The present system attempts to minimize the duplication of data in the
+database in order to insure consistency.  Thus, in order to find the
+address of the host for a mail exchange, you map the mail domain name to
+a host name, then the host name to addresses, rather than a direct
+mapping to host address.  This approach is preferred because it avoids
+the opportunity for inconsistency.
+
+In defining a new type of data, multiple RR types should not be used to
+create an ordering between entries or express different formats for
+equivalent bindings, instead this information should be carried in the
+body of the RR and a single type used.  This policy avoids problems with
+caching multiple types and defining QTYPEs to match multiple types.
+
+For example, the original form of mail exchange binding used two RR
+types one to represent a "closer" exchange (MD) and one to represent a
+"less close" exchange (MF).  The difficulty is that the presence of one
+RR type in a cache doesn't convey any information about the other
+because the query which acquired the cached information might have used
+a QTYPE of MF, MD, or MAILA (which matched both).  The redesigned
+
+
+
+Mockapetris                                                    [Page 24]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+service used a single type (MX) with a "preference" value in the RDATA
+section which can order different RRs.  However, if any MX RRs are found
+in the cache, then all should be there.
+
+4. MESSAGES
+
+4.1. Format
+
+All communications inside of the domain protocol are carried in a single
+format called a message.  The top level format of message is divided
+into 5 sections (some of which are empty in certain cases) shown below:
+
+    +---------------------+
+    |        Header       |
+    +---------------------+
+    |       Question      | the question for the name server
+    +---------------------+
+    |        Answer       | RRs answering the question
+    +---------------------+
+    |      Authority      | RRs pointing toward an authority
+    +---------------------+
+    |      Additional     | RRs holding additional information
+    +---------------------+
+
+The header section is always present.  The header includes fields that
+specify which of the remaining sections are present, and also specify
+whether the message is a query or a response, a standard query or some
+other opcode, etc.
+
+The names of the sections after the header are derived from their use in
+standard queries.  The question section contains fields that describe a
+question to a name server.  These fields are a query type (QTYPE), a
+query class (QCLASS), and a query domain name (QNAME).  The last three
+sections have the same format: a possibly empty list of concatenated
+resource records (RRs).  The answer section contains RRs that answer the
+question; the authority section contains RRs that point toward an
+authoritative name server; the additional records section contains RRs
+which relate to the query, but are not strictly answers for the
+question.
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 25]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+4.1.1. Header section format
+
+The header contains the following fields:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      ID                       |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    QDCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ANCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    NSCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ARCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ID              A 16 bit identifier assigned by the program that
+                generates any kind of query.  This identifier is copied
+                the corresponding reply and can be used by the requester
+                to match up replies to outstanding queries.
+
+QR              A one bit field that specifies whether this message is a
+                query (0), or a response (1).
+
+OPCODE          A four bit field that specifies kind of query in this
+                message.  This value is set by the originator of a query
+                and copied into the response.  The values are:
+
+                0               a standard query (QUERY)
+
+                1               an inverse query (IQUERY)
+
+                2               a server status request (STATUS)
+
+                3-15            reserved for future use
+
+AA              Authoritative Answer - this bit is valid in responses,
+                and specifies that the responding name server is an
+                authority for the domain name in question section.
+
+                Note that the contents of the answer section may have
+                multiple owner names because of aliases.  The AA bit
+
+
+
+Mockapetris                                                    [Page 26]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                corresponds to the name which matches the query name, or
+                the first owner name in the answer section.
+
+TC              TrunCation - specifies that this message was truncated
+                due to length greater than that permitted on the
+                transmission channel.
+
+RD              Recursion Desired - this bit may be set in a query and
+                is copied into the response.  If RD is set, it directs
+                the name server to pursue the query recursively.
+                Recursive query support is optional.
+
+RA              Recursion Available - this be is set or cleared in a
+                response, and denotes whether recursive query support is
+                available in the name server.
+
+Z               Reserved for future use.  Must be zero in all queries
+                and responses.
+
+RCODE           Response code - this 4 bit field is set as part of
+                responses.  The values have the following
+                interpretation:
+
+                0               No error condition
+
+                1               Format error - The name server was
+                                unable to interpret the query.
+
+                2               Server failure - The name server was
+                                unable to process this query due to a
+                                problem with the name server.
+
+                3               Name Error - Meaningful only for
+                                responses from an authoritative name
+                                server, this code signifies that the
+                                domain name referenced in the query does
+                                not exist.
+
+                4               Not Implemented - The name server does
+                                not support the requested kind of query.
+
+                5               Refused - The name server refuses to
+                                perform the specified operation for
+                                policy reasons.  For example, a name
+                                server may not wish to provide the
+                                information to the particular requester,
+                                or a name server may not wish to perform
+                                a particular operation (e.g., zone
+
+
+
+Mockapetris                                                    [Page 27]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                                transfer) for particular data.
+
+                6-15            Reserved for future use.
+
+QDCOUNT         an unsigned 16 bit integer specifying the number of
+                entries in the question section.
+
+ANCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the answer section.
+
+NSCOUNT         an unsigned 16 bit integer specifying the number of name
+                server resource records in the authority records
+                section.
+
+ARCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the additional records section.
+
+4.1.2. Question section format
+
+The question section is used to carry the "question" in most queries,
+i.e., the parameters that define what is being asked.  The section
+contains QDCOUNT (usually 1) entries, each of the following format:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                     QNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QTYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QCLASS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+QNAME           a domain name represented as a sequence of labels, where
+                each label consists of a length octet followed by that
+                number of octets.  The domain name terminates with the
+                zero length octet for the null label of the root.  Note
+                that this field may be an odd number of octets; no
+                padding is used.
+
+QTYPE           a two octet code which specifies the type of the query.
+                The values for this field include all codes valid for a
+                TYPE field, together with some more general codes which
+                can match more than one type of RR.
+
+
+
+Mockapetris                                                    [Page 28]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+QCLASS          a two octet code that specifies the class of the query.
+                For example, the QCLASS field is IN for the Internet.
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same
+format: a variable number of resource records, where the number of
+records is specified in the corresponding count field in the header.
+Each resource record has the following format:
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                                               /
+    /                      NAME                     /
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NAME            a domain name to which this resource record pertains.
+
+TYPE            two octets containing one of the RR type codes.  This
+                field specifies the meaning of the data in the RDATA
+                field.
+
+CLASS           two octets which specify the class of the data in the
+                RDATA field.
+
+TTL             a 32 bit unsigned integer that specifies the time
+                interval (in seconds) that the resource record may be
+                cached before it should be discarded.  Zero values are
+                interpreted to mean that the RR can only be used for the
+                transaction in progress, and should not be cached.
+
+
+
+
+
+Mockapetris                                                    [Page 29]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+RDLENGTH        an unsigned 16 bit integer that specifies the length in
+                octets of the RDATA field.
+
+RDATA           a variable length string of octets that describes the
+                resource.  The format of this information varies
+                according to the TYPE and CLASS of the resource record.
+                For example, the if the TYPE is A and the CLASS is IN,
+                the RDATA field is a 4 octet ARPA Internet address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, the domain system utilizes a
+compression scheme which eliminates the repetition of domain names in a
+message.  In this scheme, an entire domain name or a list of labels at
+the end of a domain name is replaced with a pointer to a prior occurance
+of the same name.
+
+The pointer takes the form of a two octet sequence:
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    | 1  1|                OFFSET                   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The first two bits are ones.  This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less.  (The 10 and 01 combinations
+are reserved for future use.)  The OFFSET field specifies an offset from
+the start of the message (i.e., the first octet of the ID field in the
+domain header).  A zero offset specifies the first byte of the ID field,
+etc.
+
+The compression scheme allows a domain name in a message to be
+represented as either:
+
+   - a sequence of labels ending in a zero octet
+
+   - a pointer
+
+   - a sequence of labels ending with a pointer
+
+Pointers can only be used for occurances of a domain name where the
+format is not class specific.  If this were not the case, a name server
+or resolver would be required to know the format of all RRs it handled.
+As yet, there are no such cases, but they may occur in future RDATA
+formats.
+
+If a domain name is contained in a part of the message subject to a
+length field (such as the RDATA section of an RR), and compression is
+
+
+
+Mockapetris                                                    [Page 30]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+used, the length of the compressed name is used in the length
+calculation, rather than the length of the expanded name.
+
+Programs are free to avoid using pointers in messages they generate,
+although this will reduce datagram capacity, and may cause truncation.
+However all programs are required to understand arriving messages that
+contain pointers.
+
+For example, a datagram might need to use the domain names F.ISI.ARPA,
+FOO.F.ISI.ARPA, ARPA, and the root.  Ignoring the other fields of the
+message, these domain names might be represented as:
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    20 |           1           |           F           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    22 |           3           |           I           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    24 |           S           |           I           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    26 |           4           |           A           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    28 |           R           |           P           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    30 |           A           |           0           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    40 |           3           |           F           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    42 |           O           |           O           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    44 | 1  1|                20                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    64 | 1  1|                26                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    92 |           0           |                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The domain name for F.ISI.ARPA is shown at offset 20.  The domain name
+FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
+concatenate a label for FOO to the previously defined F.ISI.ARPA.  The
+domain name ARPA is defined at offset 64 using a pointer to the ARPA
+component of the name F.ISI.ARPA at 20; note that this pointer relies on
+ARPA being the last label in the string at 20.  The root domain name is
+
+
+
+Mockapetris                                                    [Page 31]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+defined by a single octet of zeros at 92; the root domain name has no
+labels.
+
+4.2. Transport
+
+The DNS assumes that messages will be transmitted as datagrams or in a
+byte stream carried by a virtual circuit.  While virtual circuits can be
+used for any DNS activity, datagrams are preferred for queries due to
+their lower overhead and better performance.  Zone refresh activities
+must use virtual circuits because of the need for reliable transfer.
+
+The Internet supports name server access using TCP [RFC-793] on server
+port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
+port 53 (decimal).
+
+4.2.1. UDP usage
+
+Messages sent using UDP user server port 53 (decimal).
+
+Messages carried by UDP are restricted to 512 bytes (not counting the IP
+or UDP headers).  Longer messages are truncated and the TC bit is set in
+the header.
+
+UDP is not acceptable for zone transfers, but is the recommended method
+for standard queries in the Internet.  Queries sent using UDP may be
+lost, and hence a retransmission strategy is required.  Queries or their
+responses may be reordered by the network, or by processing in name
+servers, so resolvers should not depend on them being returned in order.
+
+The optimal UDP retransmission policy will vary with performance of the
+Internet and the needs of the client, but the following are recommended:
+
+   - The client should try other servers and server addresses
+     before repeating a query to a specific address of a server.
+
+   - The retransmission interval should be based on prior
+     statistics if possible.  Too aggressive retransmission can
+     easily slow responses for the community at large.  Depending
+     on how well connected the client is to its expected servers,
+     the minimum retransmission interval should be 2-5 seconds.
+
+More suggestions on server selection and retransmission policy can be
+found in the resolver section of this memo.
+
+4.2.2. TCP usage
+
+Messages sent over TCP connections use server port 53 (decimal).  The
+message is prefixed with a two byte length field which gives the message
+
+
+
+Mockapetris                                                    [Page 32]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+length, excluding the two byte length field.  This length field allows
+the low-level processing to assemble a complete message before beginning
+to parse it.
+
+Several connection management policies are recommended:
+
+   - The server should not block other activities waiting for TCP
+     data.
+
+   - The server should support multiple connections.
+
+   - The server should assume that the client will initiate
+     connection closing, and should delay closing its end of the
+     connection until all outstanding client requests have been
+     satisfied.
+
+   - If the server needs to close a dormant connection to reclaim
+     resources, it should wait until the connection has been idle
+     for a period on the order of two minutes.  In particular, the
+     server should allow the SOA and AXFR request sequence (which
+     begins a refresh operation) to be made on a single connection.
+     Since the server would be unable to answer queries anyway, a
+     unilateral close or reset may be used instead of a graceful
+     close.
+
+5. MASTER FILES
+
+Master files are text files that contain RRs in text form.  Since the
+contents of a zone can be expressed in the form of a list of RRs a
+master file is most often used to define a zone, though it can be used
+to list a cache's contents.  Hence, this section first discusses the
+format of RRs in a master file, and then the special considerations when
+a master file is used to create a zone in some name server.
+
+5.1. Format
+
+The format of these files is a sequence of entries.  Entries are
+predominantly line-oriented, though parentheses can be used to continue
+a list of items across a line boundary, and text literals can contain
+CRLF within the text.  Any combination of tabs and spaces act as a
+delimiter between the separate items that make up an entry.  The end of
+any line in the master file can end with a comment.  The comment starts
+with a ";" (semicolon).
+
+The following entries are defined:
+
+    <blank>[<comment>]
+
+
+
+
+Mockapetris                                                    [Page 33]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+    $ORIGIN <domain-name> [<comment>]
+
+    $INCLUDE <file-name> [<domain-name>] [<comment>]
+
+    <domain-name><rr> [<comment>]
+
+    <blank><rr> [<comment>]
+
+Blank lines, with or without comments, are allowed anywhere in the file.
+
+Two control entries are defined: $ORIGIN and $INCLUDE.  $ORIGIN is
+followed by a domain name, and resets the current origin for relative
+domain names to the stated name.  $INCLUDE inserts the named file into
+the current file, and may optionally specify a domain name that sets the
+relative domain name origin for the included file.  $INCLUDE may also
+have a comment.  Note that a $INCLUDE entry never changes the relative
+origin of the parent file, regardless of changes to the relative origin
+made within the included file.
+
+The last two forms represent RRs.  If an entry for an RR begins with a
+blank, then the RR is assumed to be owned by the last stated owner.  If
+an RR entry begins with a <domain-name>, then the owner name is reset.
+
+<rr> contents take one of the following forms:
+
+    [<TTL>] [<class>] <type> <RDATA>
+
+    [<class>] [<TTL>] <type> <RDATA>
+
+The RR begins with optional TTL and class fields, followed by a type and
+RDATA field appropriate to the type and class.  Class and type use the
+standard mnemonics, TTL is a decimal integer.  Omitted class and TTL
+values are default to the last explicitly stated values.  Since type and
+class mnemonics are disjoint, the parse is unique.  (Note that this
+order is different from the order used in examples and the order used in
+the actual RRs; the given order allows easier parsing and defaulting.)
+
+<domain-name>s make up a large share of the data in the master file.
+The labels in the domain name are expressed as character strings and
+separated by dots.  Quoting conventions allow arbitrary characters to be
+stored in domain names.  Domain names that end in a dot are called
+absolute, and are taken as complete.  Domain names which do not end in a
+dot are called relative; the actual domain name is the concatenation of
+the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
+an argument to the master file loading routine.  A relative name is an
+error when no origin is available.
+
+
+
+
+
+Mockapetris                                                    [Page 34]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+<character-string> is expressed in one or two ways: as a contiguous set
+of characters without interior spaces, or as a string beginning with a "
+and ending with a ".  Inside a " delimited string any character can
+occur, except for a " itself, which must be quoted using \ (back slash).
+
+Because these files are text files several special encodings are
+necessary to allow arbitrary data to be loaded.  In particular:
+
+                of the root.
+
+@               A free standing @ is used to denote the current origin.
+
+\X              where X is any character other than a digit (0-9), is
+                used to quote that character so that its special meaning
+                does not apply.  For example, "\." can be used to place
+                a dot character in a label.
+
+\DDD            where each D is a digit is the octet corresponding to
+                the decimal number described by DDD.  The resulting
+                octet is assumed to be text and is not checked for
+                special meaning.
+
+( )             Parentheses are used to group data that crosses a line
+                boundary.  In effect, line terminations are not
+                recognized within parentheses.
+
+;               Semicolon is used to start a comment; the remainder of
+                the line is ignored.
+
+5.2. Use of master files to define zones
+
+When a master file is used to load a zone, the operation should be
+suppressed if any errors are encountered in the master file.  The
+rationale for this is that a single error can have widespread
+consequences.  For example, suppose that the RRs defining a delegation
+have syntax errors; then the server will return authoritative name
+errors for all names in the subzone (except in the case where the
+subzone is also present on the server).
+
+Several other validity checks that should be performed in addition to
+insuring that the file is syntactically correct:
+
+   1. All RRs in the file should have the same class.
+
+   2. Exactly one SOA RR should be present at the top of the zone.
+
+   3. If delegations are present and glue information is required,
+      it should be present.
+
+
+
+Mockapetris                                                    [Page 35]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   4. Information present outside of the authoritative nodes in the
+      zone should be glue information, rather than the result of an
+      origin or similar error.
+
+5.3. Master file example
+
+The following is an example file which might be used to define the
+ISI.EDU zone.and is loaded with an origin of ISI.EDU:
+
+@   IN  SOA     VENERA      Action\.domains (
+                                 20     ; SERIAL
+                                 7200   ; REFRESH
+                                 600    ; RETRY
+                                 3600000; EXPIRE
+                                 60)    ; MINIMUM
+
+        NS      A.ISI.EDU.
+        NS      VENERA
+        NS      VAXA
+        MX      10      VENERA
+        MX      20      VAXA
+
+A       A       26.3.0.103
+
+VENERA  A       10.1.0.52
+        A       128.9.0.32
+
+VAXA    A       10.2.0.27
+        A       128.9.0.33
+
+
+$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
+
+Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
+
+    MOE     MB      A.ISI.EDU.
+    LARRY   MB      A.ISI.EDU.
+    CURLEY  MB      A.ISI.EDU.
+    STOOGES MG      MOE
+            MG      LARRY
+            MG      CURLEY
+
+Note the use of the \ character in the SOA RR to specify the responsible
+person mailbox "Action.domains@E.ISI.EDU".
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 36]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+6. NAME SERVER IMPLEMENTATION
+
+6.1. Architecture
+
+The optimal structure for the name server will depend on the host
+operating system and whether the name server is integrated with resolver
+operations, either by supporting recursive service, or by sharing its
+database with a resolver.  This section discusses implementation
+considerations for a name server which shares a database with a
+resolver, but most of these concerns are present in any name server.
+
+6.1.1. Control
+
+A name server must employ multiple concurrent activities, whether they
+are implemented as separate tasks in the host's OS or multiplexing
+inside a single name server program.  It is simply not acceptable for a
+name server to block the service of UDP requests while it waits for TCP
+data for refreshing or query activities.  Similarly, a name server
+should not attempt to provide recursive service without processing such
+requests in parallel, though it may choose to serialize requests from a
+single client, or to regard identical requests from the same client as
+duplicates.  A name server should not substantially delay requests while
+it reloads a zone from master files or while it incorporates a newly
+refreshed zone into its database.
+
+6.1.2. Database
+
+While name server implementations are free to use any internal data
+structures they choose, the suggested structure consists of three major
+parts:
+
+   - A "catalog" data structure which lists the zones available to
+     this server, and a "pointer" to the zone data structure.  The
+     main purpose of this structure is to find the nearest ancestor
+     zone, if any, for arriving standard queries.
+
+   - Separate data structures for each of the zones held by the
+     name server.
+
+   - A data structure for cached data. (or perhaps separate caches
+     for different classes)
+
+All of these data structures can be implemented an identical tree
+structure format, with different data chained off the nodes in different
+parts: in the catalog the data is pointers to zones, while in the zone
+and cache data structures, the data will be RRs.  In designing the tree
+framework the designer should recognize that query processing will need
+to traverse the tree using case-insensitive label comparisons; and that
+
+
+
+Mockapetris                                                    [Page 37]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+in real data, a few nodes have a very high branching factor (100-1000 or
+more), but the vast majority have a very low branching factor (0-1).
+
+One way to solve the case problem is to store the labels for each node
+in two pieces: a standardized-case representation of the label where all
+ASCII characters are in a single case, together with a bit mask that
+denotes which characters are actually of a different case.  The
+branching factor diversity can be handled using a simple linked list for
+a node until the branching factor exceeds some threshold, and
+transitioning to a hash structure after the threshold is exceeded.  In
+any case, hash structures used to store tree sections must insure that
+hash functions and procedures preserve the casing conventions of the
+DNS.
+
+The use of separate structures for the different parts of the database
+is motivated by several factors:
+
+   - The catalog structure can be an almost static structure that
+     need change only when the system administrator changes the
+     zones supported by the server.  This structure can also be
+     used to store parameters used to control refreshing
+     activities.
+
+   - The individual data structures for zones allow a zone to be
+     replaced simply by changing a pointer in the catalog.  Zone
+     refresh operations can build a new structure and, when
+     complete, splice it into the database via a simple pointer
+     replacement.  It is very important that when a zone is
+     refreshed, queries should not use old and new data
+     simultaneously.
+
+   - With the proper search procedures, authoritative data in zones
+     will always "hide", and hence take precedence over, cached
+     data.
+
+   - Errors in zone definitions that cause overlapping zones, etc.,
+     may cause erroneous responses to queries, but problem
+     determination is simplified, and the contents of one "bad"
+     zone can't corrupt another.
+
+   - Since the cache is most frequently updated, it is most
+     vulnerable to corruption during system restarts.  It can also
+     become full of expired RR data.  In either case, it can easily
+     be discarded without disturbing zone data.
+
+A major aspect of database design is selecting a structure which allows
+the name server to deal with crashes of the name server's host.  State
+information which a name server should save across system crashes
+
+
+
+Mockapetris                                                    [Page 38]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+includes the catalog structure (including the state of refreshing for
+each zone) and the zone data itself.
+
+6.1.3. Time
+
+Both the TTL data for RRs and the timing data for refreshing activities
+depends on 32 bit timers in units of seconds.  Inside the database,
+refresh timers and TTLs for cached data conceptually "count down", while
+data in the zone stays with constant TTLs.
+
+A recommended implementation strategy is to store time in two ways:  as
+a relative increment and as an absolute time.  One way to do this is to
+use positive 32 bit numbers for one type and negative numbers for the
+other.  The RRs in zones use relative times; the refresh timers and
+cache data use absolute times.  Absolute numbers are taken with respect
+to some known origin and converted to relative values when placed in the
+response to a query.  When an absolute TTL is negative after conversion
+to relative, then the data is expired and should be ignored.
+
+6.2. Standard query processing
+
+The major algorithm for standard query processing is presented in
+[RFC-1034].
+
+When processing queries with QCLASS=*, or some other QCLASS which
+matches multiple classes, the response should never be authoritative
+unless the server can guarantee that the response covers all classes.
+
+When composing a response, RRs which are to be inserted in the
+additional section, but duplicate RRs in the answer or authority
+sections, may be omitted from the additional section.
+
+When a response is so long that truncation is required, the truncation
+should start at the end of the response and work forward in the
+datagram.  Thus if there is any data for the authority section, the
+answer section is guaranteed to be unique.
+
+The MINIMUM value in the SOA should be used to set a floor on the TTL of
+data distributed from a zone.  This floor function should be done when
+the data is copied into a response.  This will allow future dynamic
+update protocols to change the SOA MINIMUM field without ambiguous
+semantics.
+
+6.3. Zone refresh and reload processing
+
+In spite of a server's best efforts, it may be unable to load zone data
+from a master file due to syntax errors, etc., or be unable to refresh a
+zone within the its expiration parameter.  In this case, the name server
+
+
+
+Mockapetris                                                    [Page 39]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+should answer queries as if it were not supposed to possess the zone.
+
+If a master is sending a zone out via AXFR, and a new version is created
+during the transfer, the master should continue to send the old version
+if possible.  In any case, it should never send part of one version and
+part of another.  If completion is not possible, the master should reset
+the connection on which the zone transfer is taking place.
+
+6.4. Inverse queries (Optional)
+
+Inverse queries are an optional part of the DNS.  Name servers are not
+required to support any form of inverse queries.  If a name server
+receives an inverse query that it does not support, it returns an error
+response with the "Not Implemented" error set in the header.  While
+inverse query support is optional, all name servers must be at least
+able to return the error response.
+
+6.4.1. The contents of inverse queries and responses          Inverse
+queries reverse the mappings performed by standard query operations;
+while a standard query maps a domain name to a resource, an inverse
+query maps a resource to a domain name.  For example, a standard query
+might bind a domain name to a host address; the corresponding inverse
+query binds the host address to a domain name.
+
+Inverse queries take the form of a single RR in the answer section of
+the message, with an empty question section.  The owner name of the
+query RR and its TTL are not significant.  The response carries
+questions in the question section which identify all names possessing
+the query RR WHICH THE NAME SERVER KNOWS.  Since no name server knows
+about all of the domain name space, the response can never be assumed to
+be complete.  Thus inverse queries are primarily useful for database
+management and debugging activities.  Inverse queries are NOT an
+acceptable method of mapping host addresses to host names; use the IN-
+ADDR.ARPA domain instead.
+
+Where possible, name servers should provide case-insensitive comparisons
+for inverse queries.  Thus an inverse query asking for an MX RR of
+"Venera.isi.edu" should get the same response as a query for
+"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
+produce the same result as an inverse query for "IBM-pc unix".  However,
+this cannot be guaranteed because name servers may possess RRs that
+contain character strings but the name server does not know that the
+data is character.
+
+When a name server processes an inverse query, it either returns:
+
+   1. zero, one, or multiple domain names for the specified
+      resource as QNAMEs in the question section
+
+
+
+Mockapetris                                                    [Page 40]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   2. an error code indicating that the name server doesn't support
+      inverse mapping of the specified resource type.
+
+When the response to an inverse query contains one or more QNAMEs, the
+owner name and TTL of the RR in the answer section which defines the
+inverse query is modified to exactly match an RR found at the first
+QNAME.
+
+RRs returned in the inverse queries cannot be cached using the same
+mechanism as is used for the replies to standard queries.  One reason
+for this is that a name might have multiple RRs of the same type, and
+only one would appear.  For example, an inverse query for a single
+address of a multiply homed host might create the impression that only
+one address existed.
+
+6.4.2. Inverse query and response example          The overall structure
+of an inverse query for retrieving the domain name that corresponds to
+Internet address 10.1.0.52 is shown below:
+
+                         +-----------------------------------------+
+           Header        |          OPCODE=IQUERY, ID=997          |
+                         +-----------------------------------------+
+          Question       |                 <empty>                 |
+                         +-----------------------------------------+
+           Answer        |        <anyname> A IN 10.1.0.52         |
+                         +-----------------------------------------+
+          Authority      |                 <empty>                 |
+                         +-----------------------------------------+
+         Additional      |                 <empty>                 |
+                         +-----------------------------------------+
+
+This query asks for a question whose answer is the Internet style
+address 10.1.0.52.  Since the owner name is not known, any domain name
+can be used as a placeholder (and is ignored).  A single octet of zero,
+signifying the root, is usually used because it minimizes the length of
+the message.  The TTL of the RR is not significant.  The response to
+this query might be:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 41]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                         +-----------------------------------------+
+           Header        |         OPCODE=RESPONSE, ID=997         |
+                         +-----------------------------------------+
+          Question       |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
+                         +-----------------------------------------+
+           Answer        |  VENERA.ISI.EDU  A IN 10.1.0.52         |
+                         +-----------------------------------------+
+          Authority      |                 <empty>                 |
+                         +-----------------------------------------+
+         Additional      |                 <empty>                 |
+                         +-----------------------------------------+
+
+Note that the QTYPE in a response to an inverse query is the same as the
+TYPE field in the answer section of the inverse query.  Responses to
+inverse queries may contain multiple questions when the inverse is not
+unique.  If the question section in the response is not empty, then the
+RR in the answer section is modified to correspond to be an exact copy
+of an RR at the first QNAME.
+
+6.4.3. Inverse query processing
+
+Name servers that support inverse queries can support these operations
+through exhaustive searches of their databases, but this becomes
+impractical as the size of the database increases.  An alternative
+approach is to invert the database according to the search key.
+
+For name servers that support multiple zones and a large amount of data,
+the recommended approach is separate inversions for each zone.  When a
+particular zone is changed during a refresh, only its inversions need to
+be redone.
+
+Support for transfer of this type of inversion may be included in future
+versions of the domain system, but is not supported in this version.
+
+6.5. Completion queries and responses
+
+The optional completion services described in RFC-882 and RFC-883 have
+been deleted.  Redesigned services may become available in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 42]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+7. RESOLVER IMPLEMENTATION
+
+The top levels of the recommended resolver algorithm are discussed in
+[RFC-1034].  This section discusses implementation details assuming the
+database structure suggested in the name server implementation section
+of this memo.
+
+7.1. Transforming a user request into a query
+
+The first step a resolver takes is to transform the client's request,
+stated in a format suitable to the local OS, into a search specification
+for RRs at a specific name which match a specific QTYPE and QCLASS.
+Where possible, the QTYPE and QCLASS should correspond to a single type
+and a single class, because this makes the use of cached data much
+simpler.  The reason for this is that the presence of data of one type
+in a cache doesn't confirm the existence or non-existence of data of
+other types, hence the only way to be sure is to consult an
+authoritative source.  If QCLASS=* is used, then authoritative answers
+won't be available.
+
+Since a resolver must be able to multiplex multiple requests if it is to
+perform its function efficiently, each pending request is usually
+represented in some block of state information.  This state block will
+typically contain:
+
+   - A timestamp indicating the time the request began.
+     The timestamp is used to decide whether RRs in the database
+     can be used or are out of date.  This timestamp uses the
+     absolute time format previously discussed for RR storage in
+     zones and caches.  Note that when an RRs TTL indicates a
+     relative time, the RR must be timely, since it is part of a
+     zone.  When the RR has an absolute time, it is part of a
+     cache, and the TTL of the RR is compared against the timestamp
+     for the start of the request.
+
+     Note that using the timestamp is superior to using a current
+     time, since it allows RRs with TTLs of zero to be entered in
+     the cache in the usual manner, but still used by the current
+     request, even after intervals of many seconds due to system
+     load, query retransmission timeouts, etc.
+
+   - Some sort of parameters to limit the amount of work which will
+     be performed for this request.
+
+     The amount of work which a resolver will do in response to a
+     client request must be limited to guard against errors in the
+     database, such as circular CNAME references, and operational
+     problems, such as network partition which prevents the
+
+
+
+Mockapetris                                                    [Page 43]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+     resolver from accessing the name servers it needs.  While
+     local limits on the number of times a resolver will retransmit
+     a particular query to a particular name server address are
+     essential, the resolver should have a global per-request
+     counter to limit work on a single request.  The counter should
+     be set to some initial value and decremented whenever the
+     resolver performs any action (retransmission timeout,
+     retransmission, etc.)  If the counter passes zero, the request
+     is terminated with a temporary error.
+
+     Note that if the resolver structure allows one request to
+     start others in parallel, such as when the need to access a
+     name server for one request causes a parallel resolve for the
+     name server's addresses, the spawned request should be started
+     with a lower counter.  This prevents circular references in
+     the database from starting a chain reaction of resolver
+     activity.
+
+   - The SLIST data structure discussed in [RFC-1034].
+
+     This structure keeps track of the state of a request if it
+     must wait for answers from foreign name servers.
+
+7.2. Sending the queries
+
+As described in [RFC-1034], the basic task of the resolver is to
+formulate a query which will answer the client's request and direct that
+query to name servers which can provide the information.  The resolver
+will usually only have very strong hints about which servers to ask, in
+the form of NS RRs, and may have to revise the query, in response to
+CNAMEs, or revise the set of name servers the resolver is asking, in
+response to delegation responses which point the resolver to name
+servers closer to the desired information.  In addition to the
+information requested by the client, the resolver may have to call upon
+its own services to determine the address of name servers it wishes to
+contact.
+
+In any case, the model used in this memo assumes that the resolver is
+multiplexing attention between multiple requests, some from the client,
+and some internally generated.  Each request is represented by some
+state information, and the desired behavior is that the resolver
+transmit queries to name servers in a way that maximizes the probability
+that the request is answered, minimizes the time that the request takes,
+and avoids excessive transmissions.  The key algorithm uses the state
+information of the request to select the next name server address to
+query, and also computes a timeout which will cause the next action
+should a response not arrive.  The next action will usually be a
+transmission to some other server, but may be a temporary error to the
+
+
+
+Mockapetris                                                    [Page 44]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+client.
+
+The resolver always starts with a list of server names to query (SLIST).
+This list will be all NS RRs which correspond to the nearest ancestor
+zone that the resolver knows about.  To avoid startup problems, the
+resolver should have a set of default servers which it will ask should
+it have no current NS RRs which are appropriate.  The resolver then adds
+to SLIST all of the known addresses for the name servers, and may start
+parallel requests to acquire the addresses of the servers when the
+resolver has the name, but no addresses, for the name servers.
+
+To complete initialization of SLIST, the resolver attaches whatever
+history information it has to the each address in SLIST.  This will
+usually consist of some sort of weighted averages for the response time
+of the address, and the batting average of the address (i.e., how often
+the address responded at all to the request).  Note that this
+information should be kept on a per address basis, rather than on a per
+name server basis, because the response time and batting average of a
+particular server may vary considerably from address to address.  Note
+also that this information is actually specific to a resolver address /
+server address pair, so a resolver with multiple addresses may wish to
+keep separate histories for each of its addresses.  Part of this step
+must deal with addresses which have no such history; in this case an
+expected round trip time of 5-10 seconds should be the worst case, with
+lower estimates for the same local network, etc.
+
+Note that whenever a delegation is followed, the resolver algorithm
+reinitializes SLIST.
+
+The information establishes a partial ranking of the available name
+server addresses.  Each time an address is chosen and the state should
+be altered to prevent its selection again until all other addresses have
+been tried.  The timeout for each transmission should be 50-100% greater
+than the average predicted value to allow for variance in response.
+
+Some fine points:
+
+   - The resolver may encounter a situation where no addresses are
+     available for any of the name servers named in SLIST, and
+     where the servers in the list are precisely those which would
+     normally be used to look up their own addresses.  This
+     situation typically occurs when the glue address RRs have a
+     smaller TTL than the NS RRs marking delegation, or when the
+     resolver caches the result of a NS search.  The resolver
+     should detect this condition and restart the search at the
+     next ancestor zone, or alternatively at the root.
+
+
+
+
+
+Mockapetris                                                    [Page 45]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   - If a resolver gets a server error or other bizarre response
+     from a name server, it should remove it from SLIST, and may
+     wish to schedule an immediate transmission to the next
+     candidate server address.
+
+7.3. Processing responses
+
+The first step in processing arriving response datagrams is to parse the
+response.  This procedure should include:
+
+   - Check the header for reasonableness.  Discard datagrams which
+     are queries when responses are expected.
+
+   - Parse the sections of the message, and insure that all RRs are
+     correctly formatted.
+
+   - As an optional step, check the TTLs of arriving data looking
+     for RRs with excessively long TTLs.  If a RR has an
+     excessively long TTL, say greater than 1 week, either discard
+     the whole response, or limit all TTLs in the response to 1
+     week.
+
+The next step is to match the response to a current resolver request.
+The recommended strategy is to do a preliminary matching using the ID
+field in the domain header, and then to verify that the question section
+corresponds to the information currently desired.  This requires that
+the transmission algorithm devote several bits of the domain ID field to
+a request identifier of some sort.  This step has several fine points:
+
+   - Some name servers send their responses from different
+     addresses than the one used to receive the query.  That is, a
+     resolver cannot rely that a response will come from the same
+     address which it sent the corresponding query to.  This name
+     server bug is typically encountered in UNIX systems.
+
+   - If the resolver retransmits a particular request to a name
+     server it should be able to use a response from any of the
+     transmissions.  However, if it is using the response to sample
+     the round trip time to access the name server, it must be able
+     to determine which transmission matches the response (and keep
+     transmission times for each outgoing message), or only
+     calculate round trip times based on initial transmissions.
+
+   - A name server will occasionally not have a current copy of a
+     zone which it should have according to some NS RRs.  The
+     resolver should simply remove the name server from the current
+     SLIST, and continue.
+
+
+
+
+Mockapetris                                                    [Page 46]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+7.4. Using the cache
+
+In general, we expect a resolver to cache all data which it receives in
+responses since it may be useful in answering future client requests.
+However, there are several types of data which should not be cached:
+
+   - When several RRs of the same type are available for a
+     particular owner name, the resolver should either cache them
+     all or none at all.  When a response is truncated, and a
+     resolver doesn't know whether it has a complete set, it should
+     not cache a possibly partial set of RRs.
+
+   - Cached data should never be used in preference to
+     authoritative data, so if caching would cause this to happen
+     the data should not be cached.
+
+   - The results of an inverse query should not be cached.
+
+   - The results of standard queries where the QNAME contains "*"
+     labels if the data might be used to construct wildcards.  The
+     reason is that the cache does not necessarily contain existing
+     RRs or zone boundary information which is necessary to
+     restrict the application of the wildcard RRs.
+
+   - RR data in responses of dubious reliability.  When a resolver
+     receives unsolicited responses or RR data other than that
+     requested, it should discard it without caching it.  The basic
+     implication is that all sanity checks on a packet should be
+     performed before any of it is cached.
+
+In a similar vein, when a resolver has a set of RRs for some name in a
+response, and wants to cache the RRs, it should check its cache for
+already existing RRs.  Depending on the circumstances, either the data
+in the response or the cache is preferred, but the two should never be
+combined.  If the data in the response is from authoritative data in the
+answer section, it is always preferred.
+
+8. MAIL SUPPORT
+
+The domain system defines a standard for mapping mailboxes into domain
+names, and two methods for using the mailbox information to derive mail
+routing information.  The first method is called mail exchange binding
+and the other method is mailbox binding.  The mailbox encoding standard
+and mail exchange binding are part of the DNS official protocol, and are
+the recommended method for mail routing in the Internet.  Mailbox
+binding is an experimental feature which is still under development and
+subject to change.
+
+
+
+
+Mockapetris                                                    [Page 47]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+The mailbox encoding standard assumes a mailbox name of the form
+"<local-part>@<mail-domain>".  While the syntax allowed in each of these
+sections varies substantially between the various mail internets, the
+preferred syntax for the ARPA Internet is given in [RFC-822].
+
+The DNS encodes the <local-part> as a single label, and encodes the
+<mail-domain> as a domain name.  The single label from the <local-part>
+is prefaced to the domain name from <mail-domain> to form the domain
+name corresponding to the mailbox.  Thus the mailbox HOSTMASTER@SRI-
+NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA.  If the
+<local-part> contains dots or other special characters, its
+representation in a master file will require the use of backslash
+quoting to ensure that the domain name is properly encoded.  For
+example, the mailbox Action.domains@ISI.EDU would be represented as
+Action\.domains.ISI.EDU.
+
+8.1. Mail exchange binding
+
+Mail exchange binding uses the <mail-domain> part of a mailbox
+specification to determine where mail should be sent.  The <local-part>
+is not even consulted.  [RFC-974] specifies this method in detail, and
+should be consulted before attempting to use mail exchange support.
+
+One of the advantages of this method is that it decouples mail
+destination naming from the hosts used to support mail service, at the
+cost of another layer of indirection in the lookup function.  However,
+the addition layer should eliminate the need for complicated "%", "!",
+etc encodings in <local-part>.
+
+The essence of the method is that the <mail-domain> is used as a domain
+name to locate type MX RRs which list hosts willing to accept mail for
+<mail-domain>, together with preference values which rank the hosts
+according to an order specified by the administrators for <mail-domain>.
+
+In this memo, the <mail-domain> ISI.EDU is used in examples, together
+with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
+ISI.EDU.  If a mailer had a message for Mockapetris@ISI.EDU, it would
+route it by looking up MX RRs for ISI.EDU.  The MX RRs at ISI.EDU name
+VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
+addresses.
+
+8.2. Mailbox binding (Experimental)
+
+In mailbox binding, the mailer uses the entire mail destination
+specification to construct a domain name.  The encoded domain name for
+the mailbox is used as the QNAME field in a QTYPE=MAILB query.
+
+Several outcomes are possible for this query:
+
+
+
+Mockapetris                                                    [Page 48]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   1. The query can return a name error indicating that the mailbox
+      does not exist as a domain name.
+
+      In the long term, this would indicate that the specified
+      mailbox doesn't exist.  However, until the use of mailbox
+      binding is universal, this error condition should be
+      interpreted to mean that the organization identified by the
+      global part does not support mailbox binding.  The
+      appropriate procedure is to revert to exchange binding at
+      this point.
+
+   2. The query can return a Mail Rename (MR) RR.
+
+      The MR RR carries new mailbox specification in its RDATA
+      field.  The mailer should replace the old mailbox with the
+      new one and retry the operation.
+
+   3. The query can return a MB RR.
+
+      The MB RR carries a domain name for a host in its RDATA
+      field.  The mailer should deliver the message to that host
+      via whatever protocol is applicable, e.g., b,SMTP.
+
+   4. The query can return one or more Mail Group (MG) RRs.
+
+      This condition means that the mailbox was actually a mailing
+      list or mail group, rather than a single mailbox.  Each MG RR
+      has a RDATA field that identifies a mailbox that is a member
+      of the group.  The mailer should deliver a copy of the
+      message to each member.
+
+   5. The query can return a MB RR as well as one or more MG RRs.
+
+      This condition means the the mailbox was actually a mailing
+      list.  The mailer can either deliver the message to the host
+      specified by the MB RR, which will in turn do the delivery to
+      all members, or the mailer can use the MG RRs to do the
+      expansion itself.
+
+In any of these cases, the response may include a Mail Information
+(MINFO) RR.  This RR is usually associated with a mail group, but is
+legal with a MB.  The MINFO RR identifies two mailboxes.  One of these
+identifies a responsible person for the original mailbox name.  This
+mailbox should be used for requests to be added to a mail group, etc.
+The second mailbox name in the MINFO RR identifies a mailbox that should
+receive error messages for mail failures.  This is particularly
+appropriate for mailing lists when errors in member names should be
+reported to a person other than the one who sends a message to the list.
+
+
+
+Mockapetris                                                    [Page 49]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+New fields may be added to this RR in the future.
+
+
+9. REFERENCES and BIBLIOGRAPHY
+
+[Dyer 87]       S. Dyer, F. Hsu, "Hesiod", Project Athena
+                Technical Plan - Name Service, April 1987, version 1.9.
+
+                Describes the fundamentals of the Hesiod name service.
+
+[IEN-116]       J. Postel, "Internet Name Server", IEN-116,
+                USC/Information Sciences Institute, August 1979.
+
+                A name service obsoleted by the Domain Name System, but
+                still in use.
+
+[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
+                Communications of the ACM, October 1986, volume 29, number
+                10.
+
+[RFC-742]       K. Harrenstien, "NAME/FINGER", RFC-742, Network
+                Information Center, SRI International, December 1977.
+
+[RFC-768]       J. Postel, "User Datagram Protocol", RFC-768,
+                USC/Information Sciences Institute, August 1980.
+
+[RFC-793]       J. Postel, "Transmission Control Protocol", RFC-793,
+                USC/Information Sciences Institute, September 1981.
+
+[RFC-799]       D. Mills, "Internet Name Domains", RFC-799, COMSAT,
+                September 1981.
+
+                Suggests introduction of a hierarchy in place of a flat
+                name space for the Internet.
+
+[RFC-805]       J. Postel, "Computer Mail Meeting Notes", RFC-805,
+                USC/Information Sciences Institute, February 1982.
+
+[RFC-810]       E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
+                Internet Host Table Specification", RFC-810, Network
+                Information Center, SRI International, March 1982.
+
+                Obsolete.  See RFC-952.
+
+[RFC-811]       K. Harrenstien, V. White, and E. Feinler, "Hostnames
+                Server", RFC-811, Network Information Center, SRI
+                International, March 1982.
+
+
+
+
+Mockapetris                                                    [Page 50]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                Obsolete.  See RFC-953.
+
+[RFC-812]       K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
+                Network Information Center, SRI International, March
+                1982.
+
+[RFC-819]       Z. Su, and J. Postel, "The Domain Naming Convention for
+                Internet User Applications", RFC-819, Network
+                Information Center, SRI International, August 1982.
+
+                Early thoughts on the design of the domain system.
+                Current implementation is completely different.
+
+[RFC-821]       J. Postel, "Simple Mail Transfer Protocol", RFC-821,
+                USC/Information Sciences Institute, August 1980.
+
+[RFC-830]       Z. Su, "A Distributed System for Internet Name Service",
+                RFC-830, Network Information Center, SRI International,
+                October 1982.
+
+                Early thoughts on the design of the domain system.
+                Current implementation is completely different.
+
+[RFC-882]       P. Mockapetris, "Domain names - Concepts and
+                Facilities," RFC-882, USC/Information Sciences
+                Institute, November 1983.
+
+                Superceeded by this memo.
+
+[RFC-883]       P. Mockapetris, "Domain names - Implementation and
+                Specification," RFC-883, USC/Information Sciences
+                Institute, November 1983.
+
+                Superceeded by this memo.
+
+[RFC-920]       J. Postel and J. Reynolds, "Domain Requirements",
+                RFC-920, USC/Information Sciences Institute,
+                October 1984.
+
+                Explains the naming scheme for top level domains.
+
+[RFC-952]       K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
+                Table Specification", RFC-952, SRI, October 1985.
+
+                Specifies the format of HOSTS.TXT, the host/address
+                table replaced by the DNS.
+
+
+
+
+
+Mockapetris                                                    [Page 51]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+[RFC-953]       K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
+                RFC-953, SRI, October 1985.
+
+                This RFC contains the official specification of the
+                hostname server protocol, which is obsoleted by the DNS.
+                This TCP based protocol accesses information stored in
+                the RFC-952 format, and is used to obtain copies of the
+                host table.
+
+[RFC-973]       P. Mockapetris, "Domain System Changes and
+                Observations", RFC-973, USC/Information Sciences
+                Institute, January 1986.
+
+                Describes changes to RFC-882 and RFC-883 and reasons for
+                them.
+
+[RFC-974]       C. Partridge, "Mail routing and the domain system",
+                RFC-974, CSNET CIC BBN Labs, January 1986.
+
+                Describes the transition from HOSTS.TXT based mail
+                addressing to the more powerful MX system used with the
+                domain system.
+
+[RFC-1001]      NetBIOS Working Group, "Protocol standard for a NetBIOS
+                service on a TCP/UDP transport: Concepts and Methods",
+                RFC-1001, March 1987.
+
+                This RFC and RFC-1002 are a preliminary design for
+                NETBIOS on top of TCP/IP which proposes to base NetBIOS
+                name service on top of the DNS.
+
+[RFC-1002]      NetBIOS Working Group, "Protocol standard for a NetBIOS
+                service on a TCP/UDP transport: Detailed
+                Specifications", RFC-1002, March 1987.
+
+[RFC-1010]      J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
+                USC/Information Sciences Institute, May 1987.
+
+                Contains socket numbers and mnemonics for host names,
+                operating systems, etc.
+
+[RFC-1031]      W. Lazear, "MILNET Name Domain Transition", RFC-1031,
+                November 1987.
+
+                Describes a plan for converting the MILNET to the DNS.
+
+[RFC-1032]      M. Stahl, "Establishing a Domain - Guidelines for
+                Administrators", RFC-1032, November 1987.
+
+
+
+Mockapetris                                                    [Page 52]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                Describes the registration policies used by the NIC to
+                administer the top level domains and delegate subzones.
+
+[RFC-1033]      M. Lottor, "Domain Administrators Operations Guide",
+                RFC-1033, November 1987.
+
+                A cookbook for domain administrators.
+
+[Solomon 82]    M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
+                Name Server", Computer Networks, vol 6, nr 3, July 1982.
+
+                Describes a name service for CSNET which is independent
+                from the DNS and DNS use in the CSNET.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 53]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Index
+
+          *   13
+
+          ;   33, 35
+
+          <character-string>   35
+          <domain-name>   34
+
+          @   35
+
+          \   35
+
+          A   12
+
+          Byte order   8
+
+          CH   13
+          Character case   9
+          CLASS   11
+          CNAME   12
+          Completion   42
+          CS   13
+
+          Hesiod   13
+          HINFO   12
+          HS   13
+
+          IN   13
+          IN-ADDR.ARPA domain   22
+          Inverse queries   40
+
+          Mailbox names   47
+          MB   12
+          MD   12
+          MF   12
+          MG   12
+          MINFO   12
+          MINIMUM   20
+          MR   12
+          MX   12
+
+          NS   12
+          NULL   12
+
+          Port numbers   32
+          Primary server   5
+          PTR   12, 18
+
+
+
+Mockapetris                                                    [Page 54]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+          QCLASS   13
+          QTYPE   12
+
+          RDATA   12
+          RDLENGTH  11
+
+          Secondary server   5
+          SOA   12
+          Stub resolvers   7
+
+          TCP   32
+          TXT   12
+          TYPE   11
+
+          UDP   32
+
+          WKS   12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 55]
+\f
diff --git a/doc/rfc2131.txt b/doc/rfc2131.txt
new file mode 100644 (file)
index 0000000..f45d9b8
--- /dev/null
@@ -0,0 +1,2523 @@
+
+
+
+
+
+
+Network Working Group                                           R. Droms
+Request for Comments: 2131                           Bucknell University
+Obsoletes: 1541                                               March 1997
+Category: Standards Track
+
+                  Dynamic Host Configuration Protocol
+
+Status of this memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   The Dynamic Host Configuration Protocol (DHCP) provides a framework
+   for passing configuration information to hosts on a TCPIP network.
+   DHCP is based on the Bootstrap Protocol (BOOTP) [7], adding the
+   capability of automatic allocation of reusable network addresses and
+   additional configuration options [19].  DHCP captures the behavior of
+   BOOTP relay agents [7, 21], and DHCP participants can interoperate
+   with BOOTP participants [9].
+
+Table of Contents
+
+   1.  Introduction. . . . . . . . . . . . . . . . . . . . . . . . .  2
+   1.1 Changes to RFC1541. . . . . . . . . . . . . . . . . . . . . .  3
+   1.2 Related Work. . . . . . . . . . . . . . . . . . . . . . . . .  4
+   1.3 Problem definition and issues . . . . . . . . . . . . . . . .  4
+   1.4 Requirements. . . . . . . . . . . . . . . . . . . . . . . . .  5
+   1.5 Terminology . . . . . . . . . . . . . . . . . . . . . . . . .  6
+   1.6 Design goals. . . . . . . . . . . . . . . . . . . . . . . . .  6
+   2.  Protocol Summary. . . . . . . . . . . . . . . . . . . . . . .  8
+   2.1 Configuration parameters repository . . . . . . . . . . . . . 11
+   2.2 Dynamic allocation of network addresses . . . . . . . . . . . 12
+   3.  The Client-Server Protocol. . . . . . . . . . . . . . . . . . 13
+   3.1 Client-server interaction - allocating a network address. . . 13
+   3.2 Client-server interaction - reusing a  previously allocated
+       network address . . . . . . . . . . . . . . . . . . . . . . . 17
+   3.3 Interpretation and representation of time values. . . . . . . 20
+   3.4 Obtaining parameters with externally configured network
+       address . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
+   3.5 Client parameters in DHCP . . . . . . . . . . . . . . . . . . 21
+   3.6 Use of DHCP in clients with multiple interfaces . . . . . . . 22
+   3.7 When clients should use DHCP. . . . . . . . . . . . . . . . . 22
+   4.  Specification of the DHCP client-server protocol. . . . . . . 22
+
+
+
+Droms                       Standards Track                     [Page 1]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   4.1 Constructing and sending DHCP messages. . . . . . . . . . . . 22
+   4.2 DHCP server administrative controls . . . . . . . . . . . . . 25
+   4.3 DHCP server behavior. . . . . . . . . . . . . . . . . . . . . 26
+   4.4 DHCP client behavior. . . . . . . . . . . . . . . . . . . . . 34
+   5.  Acknowledgments. . . . . . . . . . . . . . . . . . . . . . . .42
+   6.  References . . . . . . . . . . . . . . . . . . . . . . . . . .42
+   7.  Security Considerations. . . . . . . . . . . . . . . . . . . .43
+   8.  Author's Address . . . . . . . . . . . . . . . . . . . . . . .44
+   A.  Host Configuration Parameters  . . . . . . . . . . . . . . . .45
+List of Figures
+   1. Format of a DHCP message . . . . . . . . . . . . . . . . . . .  9
+   2. Format of the 'flags' field. . . . . . . . . . . . . . . . . . 11
+   3. Timeline diagram of messages exchanged between DHCP client and
+      servers when allocating a new network address. . . . . . . . . 15
+   4. Timeline diagram of messages exchanged between DHCP client and
+      servers when reusing a previously allocated network address. . 18
+   5. State-transition diagram for DHCP clients. . . . . . . . . . . 34
+List of Tables
+   1. Description of fields in a DHCP message. . . . . . . . . . . . 10
+   2. DHCP messages. . . . . . . . . . . . . . . . . . . . . . . . . 14
+   3. Fields and options used by DHCP servers. . . . . . . . . . . . 28
+   4. Client messages from various states. . . . . . . . . . . . . . 33
+   5. Fields and options used by DHCP clients. . . . . . . . . . . . 37
+
+1. Introduction
+
+   The Dynamic Host Configuration Protocol (DHCP) provides configuration
+   parameters to Internet hosts.  DHCP consists of two components: a
+   protocol for delivering host-specific configuration parameters from a
+   DHCP server to a host and a mechanism for allocation of network
+   addresses to hosts.
+
+   DHCP is built on a client-server model, where designated DHCP server
+   hosts allocate network addresses and deliver configuration parameters
+   to dynamically configured hosts.  Throughout the remainder of this
+   document, the term "server" refers to a host providing initialization
+   parameters through DHCP, and the term "client" refers to a host
+   requesting initialization parameters from a DHCP server.
+
+   A host should not act as a DHCP server unless explicitly configured
+   to do so by a system administrator.  The diversity of hardware and
+   protocol implementations in the Internet would preclude reliable
+   operation if random hosts were allowed to respond to DHCP requests.
+   For example, IP requires the setting of many parameters within the
+   protocol implementation software.  Because IP can be used on many
+   dissimilar kinds of network hardware, values for those parameters
+   cannot be guessed or assumed to have correct defaults.  Also,
+   distributed address allocation schemes depend on a polling/defense
+
+
+
+Droms                       Standards Track                     [Page 2]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   mechanism for discovery of addresses that are already in use.  IP
+   hosts may not always be able to defend their network addresses, so
+   that such a distributed address allocation scheme cannot be
+   guaranteed to avoid allocation of duplicate network addresses.
+
+   DHCP supports three mechanisms for IP address allocation.  In
+   "automatic allocation", DHCP assigns a permanent IP address to a
+   client.  In "dynamic allocation", DHCP assigns an IP address to a
+   client for a limited period of time (or until the client explicitly
+   relinquishes the address).  In "manual allocation", a client's IP
+   address is assigned by the network administrator, and DHCP is used
+   simply to convey the assigned address to the client.  A particular
+   network will use one or more of these mechanisms, depending on the
+   policies of the network administrator.
+
+   Dynamic allocation is the only one of the three mechanisms that
+   allows automatic reuse of an address that is no longer needed by the
+   client to which it was assigned.  Thus, dynamic allocation is
+   particularly useful for assigning an address to a client that will be
+   connected to the network only temporarily or for sharing a limited
+   pool of IP addresses among a group of clients that do not need
+   permanent IP addresses.  Dynamic allocation may also be a good choice
+   for assigning an IP address to a new client being permanently
+   connected to a network where IP addresses are sufficiently scarce
+   that it is important to reclaim them when old clients are retired.
+   Manual allocation allows DHCP to be used to eliminate the error-prone
+   process of manually configuring hosts with IP addresses in
+   environments where (for whatever reasons) it is desirable to manage
+   IP address assignment outside of the DHCP mechanisms.
+
+   The format of DHCP messages is based on the format of BOOTP messages,
+   to capture the BOOTP relay agent behavior described as part of the
+   BOOTP specification [7, 21] and to allow interoperability of existing
+   BOOTP clients with DHCP servers.  Using BOOTP relay agents eliminates
+   the necessity of having a DHCP server on each physical network
+   segment.
+
+1.1 Changes to RFC 1541
+
+   This document updates the DHCP protocol specification that appears in
+   RFC1541.  A new DHCP message type, DHCPINFORM, has been added; see
+   section 3.4, 4.3 and 4.4 for details.  The classing mechanism for
+   identifying DHCP clients to DHCP servers has been extended to include
+   "vendor" classes as defined in sections 4.2 and 4.3.  The minimum
+   lease time restriction has been removed.  Finally, many editorial
+   changes have been made to clarify the text as a result of experience
+   gained in DHCP interoperability tests.
+
+
+
+
+Droms                       Standards Track                     [Page 3]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+1.2 Related Work
+
+   There are several Internet protocols and related mechanisms that
+   address some parts of the dynamic host configuration problem.  The
+   Reverse Address Resolution Protocol (RARP) [10] (through the
+   extensions defined in the Dynamic RARP (DRARP) [5]) explicitly
+   addresses the problem of network address discovery, and includes an
+   automatic IP address assignment mechanism.  The Trivial File Transfer
+   Protocol (TFTP) [20] provides for transport of a boot image from a
+   boot server.  The Internet Control Message Protocol (ICMP) [16]
+   provides for informing hosts of additional routers via "ICMP
+   redirect" messages.  ICMP also can provide subnet mask information
+   through the "ICMP mask request" message and other information through
+   the (obsolete) "ICMP information request" message.  Hosts can locate
+   routers through the ICMP router discovery mechanism [8].
+
+   BOOTP is a transport mechanism for a collection of configuration
+   information.  BOOTP is also extensible, and official extensions [17]
+   have been defined for several configuration parameters.  Morgan has
+   proposed extensions to BOOTP for dynamic IP address assignment [15].
+   The Network Information Protocol (NIP), used by the Athena project at
+   MIT, is a distributed mechanism for dynamic IP address assignment
+   [19].  The Resource Location Protocol RLP [1] provides for location
+   of higher level services.  Sun Microsystems diskless workstations use
+   a boot procedure that employs RARP, TFTP and an RPC mechanism called
+   "bootparams" to deliver configuration information and operating
+   system code to diskless hosts.  (Sun Microsystems, Sun Workstation
+   and SunOS are trademarks of Sun Microsystems, Inc.)  Some Sun
+   networks also use DRARP and an auto-installation mechanism to
+   automate the configuration of new hosts in an existing network.
+
+   In other related work, the path minimum transmission unit (MTU)
+   discovery algorithm can determine the MTU of an arbitrary internet
+   path [14].  The Address Resolution Protocol (ARP) has been proposed
+   as a transport protocol for resource location and selection [6].
+   Finally, the Host Requirements RFCs [3, 4] mention specific
+   requirements for host reconfiguration and suggest a scenario for
+   initial configuration of diskless hosts.
+
+1.3 Problem definition and issues
+
+   DHCP is designed to supply DHCP clients with the configuration
+   parameters defined in the Host Requirements RFCs.  After obtaining
+   parameters via DHCP, a DHCP client should be able to exchange packets
+   with any other host in the Internet.  The TCP/IP stack parameters
+   supplied by DHCP are listed in Appendix A.
+
+
+
+
+
+Droms                       Standards Track                     [Page 4]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Not all of these parameters are required for a newly initialized
+   client.  A client and server may negotiate for the transmission of
+   only those parameters required by the client or specific to a
+   particular subnet.
+
+   DHCP allows but does not require the configuration of client
+   parameters not directly related to the IP protocol.  DHCP also does
+   not address registration of newly configured clients with the Domain
+   Name System (DNS) [12, 13].
+
+   DHCP is not intended for use in configuring routers.
+
+1.4 Requirements
+
+   Throughout this document, the words that are used to define the
+   significance of particular requirements are capitalized.  These words
+   are:
+
+      o "MUST"
+
+        This word or the adjective "REQUIRED" means that the
+        item is an absolute requirement of this specification.
+
+      o "MUST NOT"
+
+        This phrase means that the item is an absolute prohibition
+        of this specification.
+
+      o "SHOULD"
+
+        This word or the adjective "RECOMMENDED" means that there
+        may exist valid reasons in particular circumstances to ignore
+        this item, but the full implications should be understood and
+        the case carefully weighed before choosing a different course.
+
+      o "SHOULD NOT"
+
+        This phrase means that there may exist valid reasons in
+        particular circumstances when the listed behavior is acceptable
+        or even useful, but the full implications should be understood
+        and the case carefully weighed before implementing any behavior
+        described with this label.
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 5]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      o "MAY"
+
+        This word or the adjective "OPTIONAL" means that this item is
+        truly optional.  One vendor may choose to include the item
+        because a particular marketplace requires it or because it
+        enhances the product, for example; another vendor may omit the
+        same item.
+
+1.5 Terminology
+
+   This document uses the following terms:
+
+      o "DHCP client"
+
+      A DHCP client is an Internet host using DHCP to obtain
+      configuration parameters such as a network address.
+
+      o "DHCP server"
+
+      A DHCP server is an Internet host that returns configuration
+      parameters to DHCP clients.
+
+      o "BOOTP relay agent"
+
+      A BOOTP relay agent or relay agent is an Internet host or router
+      that passes DHCP messages between DHCP clients and DHCP servers.
+      DHCP is designed to use the same relay agent behavior as specified
+      in the BOOTP protocol specification.
+
+      o "binding"
+
+      A binding is a collection of configuration parameters, including
+      at least an IP address, associated with or "bound to" a DHCP
+      client.  Bindings are managed by DHCP servers.
+
+1.6 Design goals
+
+   The following list gives general design goals for DHCP.
+
+      o DHCP should be a mechanism rather than a policy.  DHCP must
+        allow local system administrators control over configuration
+        parameters where desired; e.g., local system administrators
+        should be able to enforce local policies concerning allocation
+        and access to local resources where desired.
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 6]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      o Clients should require no manual configuration.  Each client
+        should be able to discover appropriate local configuration
+        parameters without user intervention and incorporate those
+        parameters into its own configuration.
+
+      o Networks should require no manual configuration for individual
+        clients.  Under normal circumstances, the network manager
+        should not have to enter any per-client configuration
+        parameters.
+
+      o DHCP should not require a server on each subnet.  To allow for
+        scale and economy, DHCP must work across routers or through the
+        intervention of BOOTP relay agents.
+
+      o A DHCP client must be prepared to receive multiple responses
+        to a request for configuration parameters.  Some installations
+        may include multiple, overlapping DHCP servers to enhance
+        reliability and increase performance.
+
+      o DHCP must coexist with statically configured, non-participating
+        hosts and with existing network protocol implementations.
+
+      o DHCP must interoperate with the BOOTP relay agent behavior as
+        described by RFC 951 and by RFC 1542 [21].
+
+      o DHCP must provide service to existing BOOTP clients.
+
+   The following list gives design goals specific to the transmission of
+   the network layer parameters.  DHCP must:
+
+      o Guarantee that any specific network address will not be in
+        use by more than one DHCP client at a time,
+
+      o Retain DHCP client configuration across DHCP client reboot.  A
+        DHCP client should, whenever possible, be assigned the same
+        configuration parameters (e.g., network address) in response
+        to each request,
+
+      o Retain DHCP client configuration across server reboots, and,
+        whenever possible, a DHCP client should be assigned the same
+        configuration parameters despite restarts of the DHCP mechanism,
+
+      o Allow automated assignment of configuration parameters to new
+        clients to avoid hand configuration for new clients,
+
+      o Support fixed or permanent allocation of configuration
+        parameters to specific clients.
+
+
+
+
+Droms                       Standards Track                     [Page 7]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+2. Protocol Summary
+
+   From the client's point of view, DHCP is an extension of the BOOTP
+   mechanism.  This behavior allows existing BOOTP clients to
+   interoperate with DHCP servers without requiring any change to the
+   clients' initialization software.  RFC 1542 [2] details the
+   interactions between BOOTP and DHCP clients and servers [9].  There
+   are some new, optional transactions that optimize the interaction
+   between DHCP clients and servers that are described in sections 3 and
+   4.
+
+   Figure 1 gives the format of a DHCP message and table 1 describes
+   each of the fields in the DHCP message.  The numbers in parentheses
+   indicate the size of each field in octets.  The names for the fields
+   given in the figure will be used throughout this document to refer to
+   the fields in DHCP messages.
+
+   There are two primary differences between DHCP and BOOTP.  First,
+   DHCP defines mechanisms through which clients can be assigned a
+   network address for a finite lease, allowing for serial reassignment
+   of network addresses to different clients.  Second, DHCP provides the
+   mechanism for a client to acquire all of the IP configuration
+   parameters that it needs in order to operate.
+
+   DHCP introduces a small change in terminology intended to clarify the
+   meaning of one of the fields.  What was the "vendor extensions" field
+   in BOOTP has been re-named the "options" field in DHCP. Similarly,
+   the tagged data items that were used inside the BOOTP "vendor
+   extensions" field, which were formerly referred to as "vendor
+   extensions," are now termed simply "options."
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 8]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   0                   1                   2                   3
+   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
+   +---------------+---------------+---------------+---------------+
+   |                            xid (4)                            |
+   +-------------------------------+-------------------------------+
+   |           secs (2)            |           flags (2)           |
+   +-------------------------------+-------------------------------+
+   |                          ciaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          yiaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          siaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          giaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          chaddr  (16)                         |
+   |                                                               |
+   |                                                               |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          sname   (64)                         |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          file    (128)                        |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          options (variable)                   |
+   +---------------------------------------------------------------+
+
+                  Figure 1:  Format of a DHCP message
+
+   DHCP defines a new 'client identifier' option that is used to pass an
+   explicit client identifier to a DHCP server.  This change eliminates
+   the overloading of the 'chaddr' field in BOOTP messages, where
+   'chaddr' is used both as a hardware address for transmission of BOOTP
+   reply messages and as a client identifier.  The 'client identifier'
+   is an opaque key, not to be interpreted by the server; for example,
+   the 'client identifier' may contain a hardware address, identical to
+   the contents of the 'chaddr' field, or it may contain another type of
+   identifier, such as a DNS name.  The 'client identifier' chosen by a
+   DHCP client MUST be unique to that client within the subnet to which
+   the client is attached. If the client uses a 'client identifier' in
+   one message, it MUST use that same identifier in all subsequent
+   messages, to ensure that all servers correctly identify the client.
+
+
+
+
+Droms                       Standards Track                     [Page 9]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   DHCP clarifies the interpretation of the 'siaddr' field as the
+   address of the server to use in the next step of the client's
+   bootstrap process.  A DHCP server may return its own address in the
+   'siaddr' field, if the server is prepared to supply the next
+   bootstrap service (e.g., delivery of an operating system executable
+   image).  A DHCP server always returns its own address in the 'server
+   identifier' option.
+
+   FIELD      OCTETS       DESCRIPTION
+   -----      ------       -----------
+
+   op            1  Message op code / message type.
+                    1 = BOOTREQUEST, 2 = BOOTREPLY
+   htype         1  Hardware address type, see ARP section in "Assigned
+                    Numbers" RFC; e.g., '1' = 10mb ethernet.
+   hlen          1  Hardware address length (e.g.  '6' for 10mb
+                    ethernet).
+   hops          1  Client sets to zero, optionally used by relay agents
+                    when booting via a relay agent.
+   xid           4  Transaction ID, a random number chosen by the
+                    client, used by the client and server to associate
+                    messages and responses between a client and a
+                    server.
+   secs          2  Filled in by client, seconds elapsed since client
+                    began address acquisition or renewal process.
+   flags         2  Flags (see figure 2).
+   ciaddr        4  Client IP address; only filled in if client is in
+                    BOUND, RENEW or REBINDING state and can respond
+                    to ARP requests.
+   yiaddr        4  'your' (client) IP address.
+   siaddr        4  IP address of next server to use in bootstrap;
+                    returned in DHCPOFFER, DHCPACK by server.
+   giaddr        4  Relay agent IP address, used in booting via a
+                    relay agent.
+   chaddr       16  Client hardware address.
+   sname        64  Optional server host name, null terminated string.
+   file        128  Boot file name, null terminated string; "generic"
+                    name or null in DHCPDISCOVER, fully qualified
+                    directory-path name in DHCPOFFER.
+   options     var  Optional parameters field.  See the options
+                    documents for a list of defined options.
+
+           Table 1:  Description of fields in a DHCP message
+
+   The 'options' field is now variable length. A DHCP client must be
+   prepared to receive DHCP messages with an 'options' field of at least
+   length 312 octets.  This requirement implies that a DHCP client must
+   be prepared to receive a message of up to 576 octets, the minimum IP
+
+
+
+Droms                       Standards Track                    [Page 10]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   datagram size an IP host must be prepared to accept [3].  DHCP
+   clients may negotiate the use of larger DHCP messages through the
+   'maximum DHCP message size' option.  The options field may be further
+   extended into the 'file' and 'sname' fields.
+
+   In the case of a client using DHCP for initial configuration (before
+   the client's TCP/IP software has been completely configured), DHCP
+   requires creative use of the client's TCP/IP software and liberal
+   interpretation of RFC 1122.  The TCP/IP software SHOULD accept and
+   forward to the IP layer any IP packets delivered to the client's
+   hardware address before the IP address is configured; DHCP servers
+   and BOOTP relay agents may not be able to deliver DHCP messages to
+   clients that cannot accept hardware unicast datagrams before the
+   TCP/IP software is configured.
+
+   To work around some clients that cannot accept IP unicast datagrams
+   before the TCP/IP software is configured as discussed in the previous
+   paragraph, DHCP uses the 'flags' field [21].  The leftmost bit is
+   defined as the BROADCAST (B) flag.  The semantics of this flag are
+   discussed in section 4.1 of this document.  The remaining bits of the
+   flags field are reserved for future use.  They MUST be set to zero by
+   clients and ignored by servers and relay agents.  Figure 2 gives the
+   format of the 'flags' field.
+
+                                    1 1 1 1 1 1
+                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                |B|             MBZ             |
+                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                B:  BROADCAST flag
+
+                MBZ:  MUST BE ZERO (reserved for future use)
+
+                Figure 2:  Format of the 'flags' field
+
+2.1 Configuration parameters repository
+
+   The first service provided by DHCP is to provide persistent storage
+   of network parameters for network clients.  The model of DHCP
+   persistent storage is that the DHCP service stores a key-value entry
+   for each client, where the key is some unique identifier (for
+   example, an IP subnet number and a unique identifier within the
+   subnet) and the value contains the configuration parameters for the
+   client.
+
+   For example, the key might be the pair (IP-subnet-number, hardware-
+   address) (note that the "hardware-address" should be typed by the
+
+
+
+Droms                       Standards Track                    [Page 11]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   type of hardware to accommodate possible duplication of hardware
+   addresses resulting from bit-ordering problems in a mixed-media,
+   bridged network) allowing for serial or concurrent reuse of a
+   hardware address on different subnets, and for hardware addresses
+   that may not be globally unique.  Alternately, the key might be the
+   pair (IP-subnet-number, hostname), allowing the server to assign
+   parameters intelligently to a DHCP client that has been moved to a
+   different subnet or has changed hardware addresses (perhaps because
+   the network interface failed and was replaced). The protocol defines
+   that the key will be (IP-subnet-number, hardware-address) unless the
+   client explicitly supplies an identifier using the 'client
+   identifier' option.           A client can query the DHCP service to
+   retrieve its configuration parameters.  The client interface to the
+   configuration parameters repository consists of protocol messages to
+   request configuration parameters and responses from the server
+   carrying the configuration parameters.
+
+2.2 Dynamic allocation of network addresses
+
+   The second service provided by DHCP is the allocation of temporary or
+   permanent network (IP) addresses to clients.  The basic mechanism for
+   the dynamic allocation of network addresses is simple: a client
+   requests the use of an address for some period of time.  The
+   allocation mechanism (the collection of DHCP servers) guarantees not
+   to reallocate that address within the requested time and attempts to
+   return the same network address each time the client requests an
+   address.  In this document, the period over which a network address
+   is allocated to a client is referred to as a "lease" [11].  The
+   client may extend its lease with subsequent requests.  The client may
+   issue a message to release the address back to the server when the
+   client no longer needs the address.  The client may ask for a
+   permanent assignment by asking for an infinite lease.  Even when
+   assigning "permanent" addresses, a server may choose to give out
+   lengthy but non-infinite leases to allow detection of the fact that
+   the client has been retired.
+
+   In some environments it will be necessary to reassign network
+   addresses due to exhaustion of available addresses.  In such
+   environments, the allocation mechanism will reuse addresses whose
+   lease has expired.  The server should use whatever information is
+   available in the configuration information repository to choose an
+   address to reuse.  For example, the server may choose the least
+   recently assigned address.  As a consistency check, the allocating
+   server SHOULD probe the reused address before allocating the address,
+   e.g., with an ICMP echo request, and the client SHOULD probe the
+   newly received address, e.g., with ARP.
+
+
+
+
+
+Droms                       Standards Track                    [Page 12]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+3. The Client-Server Protocol
+
+   DHCP uses the BOOTP message format defined in RFC 951 and given in
+   table 1 and figure 1.  The 'op' field of each DHCP message sent from
+   a client to a server contains BOOTREQUEST. BOOTREPLY is used in the
+   'op' field of each DHCP message sent from a server to a client.
+
+   The first four octets of the 'options' field of the DHCP message
+   contain the (decimal) values 99, 130, 83 and 99, respectively (this
+   is the same magic cookie as is defined in RFC 1497 [17]).  The
+   remainder of the 'options' field consists of a list of tagged
+   parameters that are called "options".  All of the "vendor extensions"
+   listed in RFC 1497 are also DHCP options.  RFC 1533 gives the
+   complete set of options defined for use with DHCP.
+
+   Several options have been defined so far.  One particular option -
+   the "DHCP message type" option - must be included in every DHCP
+   message.  This option defines the "type" of the DHCP message.
+   Additional options may be allowed, required, or not allowed,
+   depending on the DHCP message type.
+
+   Throughout this document, DHCP messages that include a 'DHCP message
+   type' option will be referred to by the type of the message; e.g., a
+   DHCP message with 'DHCP message type' option type 1 will be referred
+   to as a "DHCPDISCOVER" message.
+
+3.1 Client-server interaction - allocating a network address
+
+   The following summary of the protocol exchanges between clients and
+   servers refers to the DHCP messages described in table 2.  The
+   timeline diagram in figure 3 shows the timing relationships in a
+   typical client-server interaction.  If the client already knows its
+   address, some steps may be omitted; this abbreviated interaction is
+   described in section 3.2.
+
+   1. The client broadcasts a DHCPDISCOVER message on its local physical
+      subnet.  The DHCPDISCOVER message MAY include options that suggest
+      values for the network address and lease duration.  BOOTP relay
+      agents may pass the message on to DHCP servers not on the same
+      physical subnet.
+
+   2. Each server may respond with a DHCPOFFER message that includes an
+      available network address in the 'yiaddr' field (and other
+      configuration parameters in DHCP options).  Servers need not
+      reserve the offered network address, although the protocol will
+      work more efficiently if the server avoids allocating the offered
+      network address to another client.  When allocating a new address,
+      servers SHOULD check that the offered network address is not
+
+
+
+Droms                       Standards Track                    [Page 13]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      already in use; e.g., the server may probe the offered address
+      with an ICMP Echo Request.  Servers SHOULD be implemented so that
+      network administrators MAY choose to disable probes of newly
+      allocated addresses.  The server transmits the DHCPOFFER message
+      to the client, using the BOOTP relay agent if necessary.
+
+   Message         Use
+   -------         ---
+
+   DHCPDISCOVER -  Client broadcast to locate available servers.
+
+   DHCPOFFER    -  Server to client in response to DHCPDISCOVER with
+                   offer of configuration parameters.
+
+   DHCPREQUEST  -  Client message to servers either (a) requesting
+                   offered parameters from one server and implicitly
+                   declining offers from all others, (b) confirming
+                   correctness of previously allocated address after,
+                   e.g., system reboot, or (c) extending the lease on a
+                   particular network address.
+
+   DHCPACK      -  Server to client with configuration parameters,
+                   including committed network address.
+
+   DHCPNAK      -  Server to client indicating client's notion of network
+                   address is incorrect (e.g., client has moved to new
+                   subnet) or client's lease as expired
+
+   DHCPDECLINE  -  Client to server indicating network address is already
+                   in use.
+
+   DHCPRELEASE  -  Client to server relinquishing network address and
+                   cancelling remaining lease.
+
+   DHCPINFORM   -  Client to server, asking only for local configuration
+                   parameters; client already has externally configured
+                   network address.
+
+                          Table 2:  DHCP messages
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 14]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+                Server          Client          Server
+            (not selected)                    (selected)
+
+                  v               v               v
+                  |               |               |
+                  |     Begins initialization     |
+                  |               |               |
+                  | _____________/|\____________  |
+                  |/DHCPDISCOVER | DHCPDISCOVER  \|
+                  |               |               |
+              Determines          |          Determines
+             configuration        |         configuration
+                  |               |               |
+                  |\             |  ____________/ |
+                  | \________    | /DHCPOFFER     |
+                  | DHCPOFFER\   |/               |
+                  |           \  |                |
+                  |       Collects replies        |
+                  |             \|                |
+                  |     Selects configuration     |
+                  |               |               |
+                  | _____________/|\____________  |
+                  |/ DHCPREQUEST  |  DHCPREQUEST\ |
+                  |               |               |
+                  |               |     Commits configuration
+                  |               |               |
+                  |               | _____________/|
+                  |               |/ DHCPACK      |
+                  |               |               |
+                  |    Initialization complete    |
+                  |               |               |
+                  .               .               .
+                  .               .               .
+                  |               |               |
+                  |      Graceful shutdown        |
+                  |               |               |
+                  |               |\ ____________ |
+                  |               | DHCPRELEASE  \|
+                  |               |               |
+                  |               |        Discards lease
+                  |               |               |
+                  v               v               v
+     Figure 3: Timeline diagram of messages exchanged between DHCP
+               client and servers when allocating a new network address
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 15]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+  3. The client receives one or more DHCPOFFER messages from one or more
+     servers.  The client may choose to wait for multiple responses.
+     The client chooses one server from which to request configuration
+     parameters, based on the configuration parameters offered in the
+     DHCPOFFER messages.  The client broadcasts a DHCPREQUEST message
+     that MUST include the 'server identifier' option to indicate which
+     server it has selected, and that MAY include other options
+     specifying desired configuration values.  The 'requested IP
+     address' option MUST be set to the value of 'yiaddr' in the
+     DHCPOFFER message from the server.  This DHCPREQUEST message is
+     broadcast and relayed through DHCP/BOOTP relay agents.  To help
+     ensure that any BOOTP relay agents forward the DHCPREQUEST message
+     to the same set of DHCP servers that received the original
+     DHCPDISCOVER message, the DHCPREQUEST message MUST use the same
+     value in the DHCP message header's 'secs' field and be sent to the
+     same IP broadcast address as the original DHCPDISCOVER message.
+     The client times out and retransmits the DHCPDISCOVER message if
+     the client receives no DHCPOFFER messages.
+
+  4. The servers receive the DHCPREQUEST broadcast from the client.
+     Those servers not selected by the DHCPREQUEST message use the
+     message as notification that the client has declined that server's
+     offer.  The server selected in the DHCPREQUEST message commits the
+     binding for the client to persistent storage and responds with a
+     DHCPACK message containing the configuration parameters for the
+     requesting client.  The combination of 'client identifier' or
+     'chaddr' and assigned network address constitute a unique
+     identifier for the client's lease and are used by both the client
+     and server to identify a lease referred to in any DHCP messages.
+     Any configuration parameters in the DHCPACK message SHOULD NOT
+     conflict with those in the earlier DHCPOFFER message to which the
+     client is responding.  The server SHOULD NOT check the offered
+     network address at this point. The 'yiaddr' field in the DHCPACK
+     messages is filled in with the selected network address.
+
+     If the selected server is unable to satisfy the DHCPREQUEST message
+     (e.g., the requested network address has been allocated), the
+     server SHOULD respond with a DHCPNAK message.
+
+     A server MAY choose to mark addresses offered to clients in
+     DHCPOFFER messages as unavailable.  The server SHOULD mark an
+     address offered to a client in a DHCPOFFER message as available if
+     the server receives no DHCPREQUEST message from that client.
+
+  5. The client receives the DHCPACK message with configuration
+     parameters.  The client SHOULD perform a final check on the
+     parameters (e.g., ARP for allocated network address), and notes the
+     duration of the lease specified in the DHCPACK message.  At this
+
+
+
+Droms                       Standards Track                    [Page 16]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+     point, the client is configured.  If the client detects that the
+     address is already in use (e.g., through the use of ARP), the
+     client MUST send a DHCPDECLINE message to the server and restarts
+     the configuration process.  The client SHOULD wait a minimum of ten
+     seconds before restarting the configuration process to avoid
+     excessive network traffic in case of looping.
+
+     If the client receives a DHCPNAK message, the client restarts the
+     configuration process.
+
+     The client times out and retransmits the DHCPREQUEST message if the
+     client receives neither a DHCPACK or a DHCPNAK message.  The client
+     retransmits the DHCPREQUEST according to the retransmission
+     algorithm in section 4.1.  The client should choose to retransmit
+     the DHCPREQUEST enough times to give adequate probability of
+     contacting the server without causing the client (and the user of
+     that client) to wait overly long before giving up; e.g., a client
+     retransmitting as described in section 4.1 might retransmit the
+     DHCPREQUEST message four times, for a total delay of 60 seconds,
+     before restarting the initialization procedure.  If the client
+     receives neither a DHCPACK or a DHCPNAK message after employing the
+     retransmission algorithm, the client reverts to INIT state and
+     restarts the initialization process.  The client SHOULD notify the
+     user that the initialization process has failed and is restarting.
+
+  6. The client may choose to relinquish its lease on a network address
+     by sending a DHCPRELEASE message to the server.  The client
+     identifies the lease to be released with its 'client identifier',
+     or 'chaddr' and network address in the DHCPRELEASE message. If the
+     client used a 'client identifier' when it obtained the lease, it
+     MUST use the same 'client identifier' in the DHCPRELEASE message.
+
+3.2 Client-server interaction - reusing a previously allocated network
+    address
+
+   If a client remembers and wishes to reuse a previously allocated
+   network address, a client may choose to omit some of the steps
+   described in the previous section.  The timeline diagram in figure 4
+   shows the timing relationships in a typical client-server interaction
+   for a client reusing a previously allocated network address.
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 17]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   1. The client broadcasts a DHCPREQUEST message on its local subnet.
+      The message includes the client's network address in the
+      'requested IP address' option. As the client has not received its
+      network address, it MUST NOT fill in the 'ciaddr' field. BOOTP
+      relay agents pass the message on to DHCP servers not on the same
+      subnet.  If the client used a 'client identifier' to obtain its
+      address, the client MUST use the same 'client identifier' in the
+      DHCPREQUEST message.
+
+   2. Servers with knowledge of the client's configuration parameters
+      respond with a DHCPACK message to the client.  Servers SHOULD NOT
+      check that the client's network address is already in use; the
+      client may respond to ICMP Echo Request messages at this point.
+
+                Server          Client          Server
+
+                  v               v               v
+                  |                |               |
+                  |              Begins            |
+                  |          initialization        |
+                  |                |               |
+                  |                /|\             |
+                  |   _________ __/ | \__________  |
+                  | /DHCPREQU EST  |  DHCPREQUEST\ |
+                  |/               |              \|
+                  |                |               |
+               Locates             |            Locates
+            configuration          |         configuration
+                  |                |               |
+                  |\               |              /|
+                  | \              |  ___________/ |
+                  |  \             | /  DHCPACK    |
+                  |   \ _______    |/              |
+                  |     DHCPACK\   |               |
+                  |          Initialization        |
+                  |             complete           |
+                  |               \|               |
+                  |                |               |
+                  |           (Subsequent          |
+                  |             DHCPACKS           |
+                  |             ignored)           |
+                  |                |               |
+                  |                |               |
+                  v                v               v
+
+     Figure 4: Timeline diagram of messages exchanged between DHCP
+               client and servers when reusing a previously allocated
+               network address
+
+
+
+Droms                       Standards Track                    [Page 18]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      If the client's request is invalid (e.g., the client has moved
+      to a new subnet), servers SHOULD respond with a DHCPNAK message to
+      the client. Servers SHOULD NOT respond if their information is not
+      guaranteed to be accurate.  For example, a server that identifies a
+      request for an expired binding that is owned by another server SHOULD
+      NOT respond with a DHCPNAK unless the servers are using an explicit
+      mechanism to maintain coherency among the servers.
+
+      If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
+      the same subnet as the server.  The server MUST
+      broadcast the DHCPNAK message to the 0xffffffff broadcast address
+      because the client may not have a correct network address or subnet
+      mask, and the client may not be answering ARP requests.
+      Otherwise, the server MUST send the DHCPNAK message to the IP
+      address of the BOOTP relay agent, as recorded in 'giaddr'.  The
+      relay agent will, in turn, forward the message directly to the
+      client's hardware address, so that the DHCPNAK can be delivered even
+      if the client has moved to a new network.
+
+   3. The client receives the DHCPACK message with configuration
+      parameters.  The client performs a final check on the parameters
+      (as in section 3.1), and notes the duration of the lease specified
+      in the DHCPACK message.  The specific lease is implicitly identified
+      by the 'client identifier' or 'chaddr' and the network address.  At
+      this point, the client is configured.
+
+      If the client detects that the IP address in the DHCPACK message
+      is already in use, the client MUST send a DHCPDECLINE message to the
+      server and restarts the configuration process by requesting a
+      new network address.  This action corresponds to the client
+      moving to the INIT state in the DHCP state diagram, which is
+      described in section 4.4.
+
+      If the client receives a DHCPNAK message, it cannot reuse its
+      remembered network address.  It must instead request a new
+      address by restarting the configuration process, this time
+      using the (non-abbreviated) procedure described in section
+      3.1.  This action also corresponds to the client moving to
+      the INIT state in the DHCP state diagram.
+
+      The client times out and retransmits the DHCPREQUEST message if
+      the client receives neither a DHCPACK nor a DHCPNAK message.  The
+      client retransmits the DHCPREQUEST according to the retransmission
+      algorithm in section 4.1.  The client should choose to retransmit
+      the DHCPREQUEST enough times to give adequate probability of
+      contacting the server without causing the client (and the user of
+      that client) to wait overly long before giving up; e.g., a client
+      retransmitting as described in section 4.1 might retransmit the
+
+
+
+Droms                       Standards Track                    [Page 19]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      DHCPREQUEST message four times, for a total delay of 60 seconds,
+      before restarting the initialization procedure.  If the client
+      receives neither a DHCPACK or a DHCPNAK message after employing
+      the retransmission algorithm, the client MAY choose to use the
+      previously allocated network address and configuration parameters
+      for the remainder of the unexpired lease.  This corresponds to
+      moving to BOUND state in the client state transition diagram shown
+      in figure 5.
+
+   4. The client may choose to relinquish its lease on a network
+      address by sending a DHCPRELEASE message to the server.  The
+      client identifies the lease to be released with its
+      'client identifier', or 'chaddr' and network address in the
+      DHCPRELEASE message.
+
+      Note that in this case, where the client retains its network
+      address locally, the client will not normally relinquish its
+      lease during a graceful shutdown.  Only in the case where the
+      client explicitly needs to relinquish its lease, e.g., the client
+      is about to be moved to a different subnet, will the client send
+      a DHCPRELEASE message.
+
+3.3 Interpretation and representation of time values
+
+   A client acquires a lease for a network address for a fixed period of
+   time (which may be infinite).  Throughout the protocol, times are to
+   be represented in units of seconds.  The time value of 0xffffffff is
+   reserved to represent "infinity".
+
+   As clients and servers may not have synchronized clocks, times are
+   represented in DHCP messages as relative times, to be interpreted
+   with respect to the client's local clock.  Representing relative
+   times in units of seconds in an unsigned 32 bit word gives a range of
+   relative times from 0 to approximately 100 years, which is sufficient
+   for the relative times to be measured using DHCP.
+
+   The algorithm for lease duration interpretation given in the previous
+   paragraph assumes that client and server clocks are stable relative
+   to each other.  If there is drift between the two clocks, the server
+   may consider the lease expired before the client does.  To
+   compensate, the server may return a shorter lease duration to the
+   client than the server commits to its local database of client
+   information.
+
+3.4 Obtaining parameters with externally configured network address
+
+   If a client has obtained a network address through some other means
+   (e.g., manual configuration), it may use a DHCPINFORM request message
+
+
+
+Droms                       Standards Track                    [Page 20]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   to obtain other local configuration parameters.  Servers receiving a
+   DHCPINFORM message construct a DHCPACK message with any local
+   configuration parameters appropriate for the client without:
+   allocating a new address, checking for an existing binding, filling
+   in 'yiaddr' or including lease time parameters.  The servers SHOULD
+   unicast the DHCPACK reply to the address given in the 'ciaddr' field
+   of the DHCPINFORM message.
+
+   The server SHOULD check the network address in a DHCPINFORM message
+   for consistency, but MUST NOT check for an existing lease.  The
+   server forms a DHCPACK message containing the configuration
+   parameters for the requesting client and sends the DHCPACK message
+   directly to the client.
+
+3.5 Client parameters in DHCP
+
+   Not all clients require initialization of all parameters listed in
+   Appendix A.  Two techniques are used to reduce the number of
+   parameters transmitted from the server to the client.  First, most of
+   the parameters have defaults defined in the Host Requirements RFCs;
+   if the client receives no parameters from the server that override
+   the defaults, a client uses those default values.  Second, in its
+   initial DHCPDISCOVER or DHCPREQUEST message, a client may provide the
+   server with a list of specific parameters the client is interested
+   in.  If the client includes a list of parameters in a DHCPDISCOVER
+   message, it MUST include that list in any subsequent DHCPREQUEST
+   messages.
+
+   The client SHOULD include the 'maximum DHCP message size' option to
+   let the server know how large the server may make its DHCP messages.
+   The parameters returned to a client may still exceed the space
+   allocated to options in a DHCP message.  In this case, two additional
+   options flags (which must appear in the 'options' field of the
+   message) indicate that the 'file' and 'sname' fields are to be used
+   for options.
+
+   The client can inform the server which configuration parameters the
+   client is interested in by including the 'parameter request list'
+   option.  The data portion of this option explicitly lists the options
+   requested by tag number.
+
+   In addition, the client may suggest values for the network address
+   and lease time in the DHCPDISCOVER message.  The client may include
+   the 'requested IP address' option to suggest that a particular IP
+   address be assigned, and may include the 'IP address lease time'
+   option to suggest the lease time it would like.  Other options
+   representing "hints" at configuration parameters are allowed in a
+   DHCPDISCOVER or DHCPREQUEST message.  However, additional options may
+
+
+
+Droms                       Standards Track                    [Page 21]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   be ignored by servers, and multiple servers may, therefore, not
+   return identical values for some options.  The 'requested IP address'
+   option is to be filled in only in a DHCPREQUEST message when the
+   client is verifying network parameters obtained previously. The
+   client fills in the 'ciaddr' field only when correctly configured
+   with an IP address in BOUND, RENEWING or REBINDING state.
+
+   If a server receives a DHCPREQUEST message with an invalid 'requested
+   IP address', the server SHOULD respond to the client with a DHCPNAK
+   message and may choose to report the problem to the system
+   administrator.  The server may include an error message in the
+   'message' option.
+
+3.6 Use of DHCP in clients with multiple interfaces
+
+   A client with multiple network interfaces must use DHCP through each
+   interface independently to obtain configuration information
+   parameters for those separate interfaces.
+
+3.7 When clients should use DHCP
+
+   A client SHOULD use DHCP to reacquire or verify its IP address and
+   network parameters whenever the local network parameters may have
+   changed; e.g., at system boot time or after a disconnection from the
+   local network, as the local network configuration may change without
+   the client's or user's knowledge.
+
+   If a client has knowledge of a previous network address and is unable
+   to contact a local DHCP server, the client may continue to use the
+   previous network address until the lease for that address expires.
+   If the lease expires before the client can contact a DHCP server, the
+   client must immediately discontinue use of the previous network
+   address and may inform local users of the problem.
+
+4. Specification of the DHCP client-server protocol
+
+   In this section, we assume that a DHCP server has a block of network
+   addresses from which it can satisfy requests for new addresses.  Each
+   server also maintains a database of allocated addresses and leases in
+   local permanent storage.
+
+4.1 Constructing and sending DHCP messages
+
+   DHCP clients and servers both construct DHCP messages by filling in
+   fields in the fixed format section of the message and appending
+   tagged data items in the variable length option area.  The options
+   area includes first a four-octet 'magic cookie' (which was described
+   in section 3), followed by the options.  The last option must always
+
+
+
+Droms                       Standards Track                    [Page 22]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   be the 'end' option.
+
+   DHCP uses UDP as its transport protocol.  DHCP messages from a client
+   to a server are sent to the 'DHCP server' port (67), and DHCP
+   messages from a server to a client are sent to the 'DHCP client' port
+   (68). A server with multiple network address (e.g., a multi-homed
+   host) MAY use any of its network addresses in outgoing DHCP messages.
+
+   The 'server identifier' field is used both to identify a DHCP server
+   in a DHCP message and as a destination address from clients to
+   servers.  A server with multiple network addresses MUST be prepared
+   to to accept any of its network addresses as identifying that server
+   in a DHCP message.  To accommodate potentially incomplete network
+   connectivity, a server MUST choose an address as a 'server
+   identifier' that, to the best of the server's knowledge, is reachable
+   from the client.  For example, if the DHCP server and the DHCP client
+   are connected to the same subnet (i.e., the 'giaddr' field in the
+   message from the client is zero), the server SHOULD select the IP
+   address the server is using for communication on that subnet as the
+   'server identifier'.  If the server is using multiple IP addresses on
+   that subnet, any such address may be used.  If the server has
+   received a message through a DHCP relay agent, the server SHOULD
+   choose an address from the interface on which the message was
+   recieved as the 'server identifier' (unless the server has other,
+   better information on which to make its choice).  DHCP clients MUST
+   use the IP address provided in the 'server identifier' option for any
+   unicast requests to the DHCP server.
+
+   DHCP messages broadcast by a client prior to that client obtaining
+   its IP address must have the source address field in the IP header
+   set to 0.
+
+   If the 'giaddr' field in a DHCP message from a client is non-zero,
+   the server sends any return messages to the 'DHCP server' port on the
+   BOOTP relay agent whose address appears in 'giaddr'. If the 'giaddr'
+   field is zero and the 'ciaddr' field is nonzero, then the server
+   unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
+   If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+   set, then the server broadcasts DHCPOFFER and DHCPACK messages to
+   0xffffffff. If the broadcast bit is not set and 'giaddr' is zero and
+   'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
+   messages to the client's hardware address and 'yiaddr' address.  In
+   all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
+   messages to 0xffffffff.
+
+   If the options in a DHCP message extend into the 'sname' and 'file'
+   fields, the 'option overload' option MUST appear in the 'options'
+   field, with value 1, 2 or 3, as specified in RFC 1533.  If the
+
+
+
+Droms                       Standards Track                    [Page 23]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   'option overload' option is present in the 'options' field, the
+   options in the 'options' field MUST be terminated by an 'end' option,
+   and MAY contain one or more 'pad' options to fill the options field.
+   The options in the 'sname' and 'file' fields (if in use as indicated
+   by the 'options overload' option) MUST begin with the first octet of
+   the field, MUST be terminated by an 'end' option, and MUST be
+   followed by 'pad' options to fill the remainder of the field.  Any
+   individual option in the 'options', 'sname' and 'file' fields MUST be
+   entirely contained in that field.  The options in the 'options' field
+   MUST be interpreted first, so that any 'option overload' options may
+   be interpreted.  The 'file' field MUST be interpreted next (if the
+   'option overload' option indicates that the 'file' field contains
+   DHCP options), followed by the 'sname' field.
+
+   The values to be passed in an 'option' tag may be too long to fit in
+   the 255 octets available to a single option (e.g., a list of routers
+   in a 'router' option [21]).  Options may appear only once, unless
+   otherwise specified in the options document.  The client concatenates
+   the values of multiple instances of the same option into a single
+   parameter list for configuration.
+
+   DHCP clients are responsible for all message retransmission.  The
+   client MUST adopt a retransmission strategy that incorporates a
+   randomized exponential backoff algorithm to determine the delay
+   between retransmissions.  The delay between retransmissions SHOULD be
+   chosen to allow sufficient time for replies from the server to be
+   delivered based on the characteristics of the internetwork between
+   the client and the server.  For example, in a 10Mb/sec Ethernet
+   internetwork, the delay before the first retransmission SHOULD be 4
+   seconds randomized by the value of a uniform random number chosen
+   from the range -1 to +1.  Clients with clocks that provide resolution
+   granularity of less than one second may choose a non-integer
+   randomization value.  The delay before the next retransmission SHOULD
+   be 8 seconds randomized by the value of a uniform number chosen from
+   the range -1 to +1.  The retransmission delay SHOULD be doubled with
+   subsequent retransmissions up to a maximum of 64 seconds.  The client
+   MAY provide an indication of retransmission attempts to the user as
+   an indication of the progress of the configuration process.
+
+   The 'xid' field is used by the client to match incoming DHCP messages
+   with pending requests.  A DHCP client MUST choose 'xid's in such a
+   way as to minimize the chance of using an 'xid' identical to one used
+   by another client. For example, a client may choose a different,
+   random initial 'xid' each time the client is rebooted, and
+   subsequently use sequential 'xid's until the next reboot.  Selecting
+   a new 'xid' for each retransmission is an implementation decision.  A
+   client may choose to reuse the same 'xid' or select a new 'xid' for
+   each retransmitted message.
+
+
+
+Droms                       Standards Track                    [Page 24]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Normally, DHCP servers and BOOTP relay agents attempt to deliver
+   DHCPOFFER, DHCPACK and DHCPNAK messages directly to the client using
+   uicast delivery.  The IP destination address (in the IP header) is
+   set to the DHCP 'yiaddr' address and the link-layer destination
+   address is set to the DHCP 'chaddr' address.  Unfortunately, some
+   client implementations are unable to receive such unicast IP
+   datagrams until the implementation has been configured with a valid
+   IP address (leading to a deadlock in which the client's IP address
+   cannot be delivered until the client has been configured with an IP
+   address).
+
+   A client that cannot receive unicast IP datagrams until its protocol
+   software has been configured with an IP address SHOULD set the
+   BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
+   DHCPREQUEST messages that client sends.  The BROADCAST bit will
+   provide a hint to the DHCP server and BOOTP relay agent to broadcast
+   any messages to the client on the client's subnet.  A client that can
+   receive unicast IP datagrams before its protocol software has been
+   configured SHOULD clear the BROADCAST bit to 0.  The BOOTP
+   clarifications document discusses the ramifications of the use of the
+   BROADCAST bit [21].
+
+   A server or relay agent sending or relaying a DHCP message directly
+   to a DHCP client (i.e., not to a relay agent specified in the
+   'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags'
+   field.  If this bit is set to 1, the DHCP message SHOULD be sent as
+   an IP broadcast using an IP broadcast address (preferably 0xffffffff)
+   as the IP destination address and the link-layer broadcast address as
+   the link-layer destination address.  If the BROADCAST bit is cleared
+   to 0, the message SHOULD be sent as an IP unicast to the IP address
+   specified in the 'yiaddr' field and the link-layer address specified
+   in the 'chaddr' field.  If unicasting is not possible, the message
+   MAY be sent as an IP broadcast using an IP broadcast address
+   (preferably 0xffffffff) as the IP destination address and the link-
+   layer broadcast address as the link-layer destination address.
+
+4.2 DHCP server administrative controls
+
+   DHCP servers are not required to respond to every DHCPDISCOVER and
+   DHCPREQUEST message they receive.  For example, a network
+   administrator, to retain stringent control over the clients attached
+   to the network, may choose to configure DHCP servers to respond only
+   to clients that have been previously registered through some external
+   mechanism.  The DHCP specification describes only the interactions
+   between clients and servers when the clients and servers choose to
+   interact; it is beyond the scope of the DHCP specification to
+   describe all of the administrative controls that system
+   administrators might want to use.  Specific DHCP server
+
+
+
+Droms                       Standards Track                    [Page 25]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   implementations may incorporate any controls or policies desired by a
+   network administrator.
+
+   In some environments, a DHCP server will have to consider the values
+   of the vendor class options included in DHCPDISCOVER or DHCPREQUEST
+   messages when determining the correct parameters for a particular
+   client.
+
+   A DHCP server needs to use some unique identifier to associate a
+   client with its lease.  The client MAY choose to explicitly provide
+   the identifier through the 'client identifier' option.  If the client
+   supplies a 'client identifier', the client MUST use the same 'client
+   identifier' in all subsequent messages, and the server MUST use that
+   identifier to identify the client.  If the client does not provide a
+   'client identifier' option, the server MUST use the contents of the
+   'chaddr' field to identify the client. It is crucial for a DHCP
+   client to use an identifier unique within the subnet to which the
+   client is attached in the 'client identifier' option.  Use of
+   'chaddr' as the client's unique identifier may cause unexpected
+   results, as that identifier may be associated with a hardware
+   interface that could be moved to a new client.  Some sites may choose
+   to use a manufacturer's serial number as the 'client identifier', to
+   avoid unexpected changes in a clients network address due to transfer
+   of hardware interfaces among computers.  Sites may also choose to use
+   a DNS name as the 'client identifier', causing address leases to be
+   associated with the DNS name rather than a specific hardware box.
+
+   DHCP clients are free to use any strategy in selecting a DHCP server
+   among those from which the client receives a DHCPOFFER message.  The
+   client implementation of DHCP SHOULD provide a mechanism for the user
+   to select directly the 'vendor class identifier' values.
+
+4.3 DHCP server behavior
+
+   A DHCP server processes incoming DHCP messages from a client based on
+   the current state of the binding for that client.  A DHCP server can
+   receive the following messages from a client:
+
+      o DHCPDISCOVER
+
+      o DHCPREQUEST
+
+      o DHCPDECLINE
+
+      o DHCPRELEASE
+
+      o DHCPINFORM
+
+
+
+
+Droms                       Standards Track                    [Page 26]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Table 3 gives the use of the fields and options in a DHCP message by
+   a server.  The remainder of this section describes the action of the
+   DHCP server for each possible incoming message.
+
+4.3.1 DHCPDISCOVER message
+
+   When a server receives a DHCPDISCOVER message from a client, the
+   server chooses a network address for the requesting client.  If no
+   address is available, the server may choose to report the problem to
+   the system administrator. If an address is available, the new address
+   SHOULD be chosen as follows:
+
+      o The client's current address as recorded in the client's current
+        binding, ELSE
+
+      o The client's previous address as recorded in the client's (now
+        expired or released) binding, if that address is in the server's
+        pool of available addresses and not already allocated, ELSE
+
+      o The address requested in the 'Requested IP Address' option, if that
+        address is valid and not already allocated, ELSE
+
+      o A new address allocated from the server's pool of available
+        addresses; the address is selected based on the subnet from which
+        the message was received (if 'giaddr' is 0) or on the address of
+        the relay agent that forwarded the message ('giaddr' when not 0).
+
+   As described in section 4.2, a server MAY, for administrative
+   reasons, assign an address other than the one requested, or may
+   refuse to allocate an address to a particular client even though free
+   addresses are available.
+
+   Note that, in some network architectures (e.g., internets with more
+   than one IP subnet assigned to a physical network segment), it may be
+   the case that the DHCP client should be assigned an address from a
+   different subnet than the address recorded in 'giaddr'.  Thus, DHCP
+   does not require that the client be assigned as address from the
+   subnet in 'giaddr'.  A server is free to choose some other subnet,
+   and it is beyond the scope of the DHCP specification to describe ways
+   in which the assigned IP address might be chosen.
+
+   While not required for correct operation of DHCP, the server SHOULD
+   NOT reuse the selected network address before the client responds to
+   the server's DHCPOFFER message.  The server may choose to record the
+   address as offered to the client.
+
+   The server must also choose an expiration time for the lease, as
+   follows:
+
+
+
+Droms                       Standards Track                    [Page 27]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   o IF the client has not requested a specific lease in the
+     DHCPDISCOVER message and the client already has an assigned network
+     address, the server returns the lease expiration time previously
+     assigned to that address (note that the client must explicitly
+     request a specific lease to extend the expiration time on a
+     previously assigned address), ELSE
+
+   o IF the client has not requested a specific lease in the
+     DHCPDISCOVER message and the client does not have an assigned
+     network address, the server assigns a locally configured default
+     lease time, ELSE
+
+   o IF the client has requested a specific lease in the DHCPDISCOVER
+     message (regardless of whether the client has an assigned network
+     address), the server may choose either to return the requested
+     lease (if the lease is acceptable to local policy) or select
+     another lease.
+
+Field      DHCPOFFER            DHCPACK             DHCPNAK
+-----      ---------            -------             -------
+'op'       BOOTREPLY            BOOTREPLY           BOOTREPLY
+'htype'    (From "Assigned Numbers" RFC)
+'hlen'     (Hardware address length in octets)
+'hops'     0                    0                   0
+'xid'      'xid' from client    'xid' from client   'xid' from client
+           DHCPDISCOVER         DHCPREQUEST         DHCPREQUEST
+           message              message             message
+'secs'     0                    0                   0
+'ciaddr'   0                    'ciaddr' from       0
+                                DHCPREQUEST or 0
+'yiaddr'   IP address offered   IP address          0
+           to client            assigned to client
+'siaddr'   IP address of next   IP address of next  0
+           bootstrap server     bootstrap server
+'flags'    'flags' from         'flags' from        'flags' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'giaddr'   'giaddr' from        'giaddr' from       'giaddr' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'chaddr'   'chaddr' from        'chaddr' from       'chaddr' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'sname'    Server host name     Server host name    (unused)
+           or options           or options
+'file'     Client boot file     Client boot file    (unused)
+           name or options      name or options
+'options'  options              options
+
+
+
+Droms                       Standards Track                    [Page 28]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Option                    DHCPOFFER    DHCPACK            DHCPNAK
+------                    ---------    -------            -------
+Requested IP address      MUST NOT     MUST NOT           MUST NOT
+IP address lease time     MUST         MUST (DHCPREQUEST) MUST NOT
+                                       MUST NOT (DHCPINFORM)
+Use 'file'/'sname' fields MAY          MAY                MUST NOT
+DHCP message type         DHCPOFFER    DHCPACK            DHCPNAK
+Parameter request list    MUST NOT     MUST NOT           MUST NOT
+Message                   SHOULD       SHOULD             SHOULD
+Client identifier         MUST NOT     MUST NOT           MAY
+Vendor class identifier   MAY          MAY                MAY
+Server identifier         MUST         MUST               MUST
+Maximum message size      MUST NOT     MUST NOT           MUST NOT
+All others                MAY          MAY                MUST NOT
+
+           Table 3:  Fields and options used by DHCP servers
+
+   Once the network address and lease have been determined, the server
+   constructs a DHCPOFFER message with the offered configuration
+   parameters.  It is important for all DHCP servers to return the same
+   parameters (with the possible exception of a newly allocated network
+   address) to ensure predictable client behavior regardless of which
+   server the client selects.  The configuration parameters MUST be
+   selected by applying the following rules in the order given below.
+   The network administrator is responsible for configuring multiple
+   DHCP servers to ensure uniform responses from those servers.  The
+   server MUST return to the client:
+
+   o The client's network address, as determined by the rules given
+     earlier in this section,
+
+   o The expiration time for the client's lease, as determined by the
+     rules given earlier in this section,
+
+   o Parameters requested by the client, according to the following
+     rules:
+
+        -- IF the server has been explicitly configured with a default
+           value for the parameter, the server MUST include that value
+           in an appropriate option in the 'option' field, ELSE
+
+        -- IF the server recognizes the parameter as a parameter
+           defined in the Host Requirements Document, the server MUST
+           include the default value for that parameter as given in the
+           Host Requirements Document in an appropriate option in the
+           'option' field, ELSE
+
+        -- The server MUST NOT return a value for that parameter,
+
+
+
+Droms                       Standards Track                    [Page 29]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+     The server MUST supply as many of the requested parameters as
+     possible and MUST omit any parameters it cannot provide.  The
+     server MUST include each requested parameter only once unless
+     explicitly allowed in the DHCP Options and BOOTP Vendor
+     Extensions document.
+
+   o Any parameters from the existing binding that differ from the Host
+     Requirements Document defaults,
+
+   o Any parameters specific to this client (as identified by
+     the contents of 'chaddr' or 'client identifier' in the DHCPDISCOVER
+     or DHCPREQUEST message), e.g., as configured by the network
+     administrator,
+
+   o Any parameters specific to this client's class (as identified
+     by the contents of the 'vendor class identifier'
+     option in the DHCPDISCOVER or DHCPREQUEST message),
+     e.g., as configured by the network administrator; the parameters
+     MUST be identified by an exact match between the client's vendor
+     class identifiers and the client's classes identified in the
+     server,
+
+   o Parameters with non-default values on the client's subnet.
+
+   The server MAY choose to return the 'vendor class identifier' used to
+   determine the parameters in the DHCPOFFER message to assist the
+   client in selecting which DHCPOFFER to accept.  The server inserts
+   the 'xid' field from the DHCPDISCOVER message into the 'xid' field of
+   the DHCPOFFER message and sends the DHCPOFFER message to the
+   requesting client.
+
+4.3.2 DHCPREQUEST message
+
+   A DHCPREQUEST message may come from a client responding to a
+   DHCPOFFER message from a server, from a client verifying a previously
+   allocated IP address or from a client extending the lease on a
+   network address.  If the DHCPREQUEST message contains a 'server
+   identifier' option, the message is in response to a DHCPOFFER
+   message.  Otherwise, the message is a request to verify or extend an
+   existing lease.  If the client uses a 'client identifier' in a
+   DHCPREQUEST message, it MUST use that same 'client identifier' in all
+   subsequent messages. If the client included a list of requested
+   parameters in a DHCPDISCOVER message, it MUST include that list in
+   all subsequent messages.
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 30]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Any configuration parameters in the DHCPACK message SHOULD NOT
+   conflict with those in the earlier DHCPOFFER message to which the
+   client is responding.  The client SHOULD use the parameters in the
+   DHCPACK message for configuration.
+
+   Clients send DHCPREQUEST messages as follows:
+
+   o DHCPREQUEST generated during SELECTING state:
+
+      Client inserts the address of the selected server in 'server
+      identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
+      filled in with the yiaddr value from the chosen DHCPOFFER.
+
+      Note that the client may choose to collect several DHCPOFFER
+      messages and select the "best" offer.  The client indicates its
+      selection by identifying the offering server in the DHCPREQUEST
+      message.  If the client receives no acceptable offers, the client
+      may choose to try another DHCPDISCOVER message.  Therefore, the
+      servers may not receive a specific DHCPREQUEST from which they can
+      decide whether or not the client has accepted the offer.  Because
+      the servers have not committed any network address assignments on
+      the basis of a DHCPOFFER, servers are free to reuse offered
+      network addresses in response to subsequent requests.  As an
+      implementation detail, servers SHOULD NOT reuse offered addresses
+      and may use an implementation-specific timeout mechanism to decide
+      when to reuse an offered address.
+
+   o DHCPREQUEST generated during INIT-REBOOT state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST be filled in with client's notion of its previously
+      assigned address. 'ciaddr' MUST be zero. The client is seeking to
+      verify a previously allocated, cached configuration. Server SHOULD
+      send a DHCPNAK message to the client if the 'requested IP address'
+      is incorrect, or is on the wrong network.
+
+      Determining whether a client in the INIT-REBOOT state is on the
+      correct network is done by examining the contents of 'giaddr', the
+      'requested IP address' option, and a database lookup. If the DHCP
+      server detects that the client is on the wrong net (i.e., the
+      result of applying the local subnet mask or remote subnet mask (if
+      'giaddr' is not zero) to 'requested IP address' option value
+      doesn't match reality), then the server SHOULD send a DHCPNAK
+      message to the client.
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 31]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      If the network is correct, then the DHCP server should check if
+      the client's notion of its IP address is correct. If not, then the
+      server SHOULD send a DHCPNAK message to the client. If the DHCP
+      server has no record of this client, then it MUST remain silent,
+      and MAY output a warning to the network administrator. This
+      behavior is necessary for peaceful coexistence of non-
+      communicating DHCP servers on the same wire.
+
+      If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
+      the same subnet as the server.  The server MUST broadcast the
+      DHCPNAK message to the 0xffffffff broadcast address because the
+      client may not have a correct network address or subnet mask, and
+      the client may not be answering ARP requests.
+
+      If 'giaddr' is set in the DHCPREQUEST message, the client is on a
+      different subnet.  The server MUST set the broadcast bit in the
+      DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
+      client, because the client may not have a correct network address
+      or subnet mask, and the client may not be answering ARP requests.
+
+   o DHCPREQUEST generated during RENEWING state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST NOT be filled in, 'ciaddr' MUST be filled in with
+      client's IP address. In this situation, the client is completely
+      configured, and is trying to extend its lease. This message will
+      be unicast, so no relay agents will be involved in its
+      transmission.  Because 'giaddr' is therefore not filled in, the
+      DHCP server will trust the value in 'ciaddr', and use it when
+      replying to the client.
+
+      A client MAY choose to renew or extend its lease prior to T1.  The
+      server may choose not to extend the lease (as a policy decision by
+      the network administrator), but should return a DHCPACK message
+      regardless.
+
+   o DHCPREQUEST generated during REBINDING state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST NOT be filled in, 'ciaddr' MUST be filled in with
+      client's IP address. In this situation, the client is completely
+      configured, and is trying to extend its lease. This message MUST
+      be broadcast to the 0xffffffff IP broadcast address.  The DHCP
+      server SHOULD check 'ciaddr' for correctness before replying to
+      the DHCPREQUEST.
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 32]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      The DHCPREQUEST from a REBINDING client is intended to accommodate
+      sites that have multiple DHCP servers and a mechanism for
+      maintaining consistency among leases managed by multiple servers.
+      A DHCP server MAY extend a client's lease only if it has local
+      administrative authority to do so.
+
+4.3.3 DHCPDECLINE message
+
+   If the server receives a DHCPDECLINE message, the client has
+   discovered through some other means that the suggested network
+   address is already in use.  The server MUST mark the network address
+   as not available and SHOULD notify the local system administrator of
+   a possible configuration problem.
+
+4.3.4 DHCPRELEASE message
+
+   Upon receipt of a DHCPRELEASE message, the server marks the network
+   address as not allocated.  The server SHOULD retain a record of the
+   client's initialization parameters for possible reuse in response to
+   subsequent requests from the client.
+
+4.3.5 DHCPINFORM message
+
+   The server responds to a DHCPINFORM message by sending a DHCPACK
+   message directly to the address given in the 'ciaddr' field of the
+   DHCPINFORM message.  The server MUST NOT send a lease expiration time
+   to the client and SHOULD NOT fill in 'yiaddr'.  The server includes
+   other parameters in the DHCPACK message as defined in section 4.3.1.
+
+4.3.6 Client messages
+
+   Table 4 details the differences between messages from clients in
+   various states.
+
+   ---------------------------------------------------------------------
+   |              |INIT-REBOOT  |SELECTING    |RENEWING     |REBINDING |
+   ---------------------------------------------------------------------
+   |broad/unicast |broadcast    |broadcast    |unicast      |broadcast |
+   |server-ip     |MUST NOT     |MUST         |MUST NOT     |MUST NOT  |
+   |requested-ip  |MUST         |MUST         |MUST NOT     |MUST NOT  |
+   |ciaddr        |zero         |zero         |IP address   |IP address|
+   ---------------------------------------------------------------------
+
+              Table 4: Client messages from different states
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 33]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+4.4 DHCP client behavior
+
+   Figure 5 gives a state-transition diagram for a DHCP client.  A
+   client can receive the following messages from a server:
+
+         o DHCPOFFER
+
+         o DHCPACK
+
+         o DHCPNAK
+
+   The DHCPINFORM message is not shown in figure 5.  A client simply
+   sends the DHCPINFORM and waits for DHCPACK messages.  Once the client
+   has selected its parameters, it has completed the configuration
+   process.
+
+   Table 5 gives the use of the fields and options in a DHCP message by
+   a client.  The remainder of this section describes the action of the
+   DHCP client for each possible incoming message.  The description in
+   the following section corresponds to the full configuration procedure
+   previously described in section 3.1, and the text in the subsequent
+   section corresponds to the abbreviated configuration procedure
+   described in section 3.2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 34]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+ --------                               -------
+|        | +-------------------------->|       |<-------------------+
+| INIT-  | |     +-------------------->| INIT  |                    |
+| REBOOT |DHCPNAK/         +---------->|       |<---+               |
+|        |Restart|         |            -------     |               |
+ --------  |  DHCPNAK/     |               |                        |
+    |      Discard offer   |      -/Send DHCPDISCOVER               |
+-/Send DHCPREQUEST         |               |                        |
+    |      |     |      DHCPACK            v        |               |
+ -----------     |   (not accept.)/   -----------   |               |
+|           |    |  Send DHCPDECLINE |           |                  |
+| REBOOTING |    |         |         | SELECTING |<----+            |
+|           |    |        /          |           |     |DHCPOFFER/  |
+ -----------     |       /            -----------   |  |Collect     |
+    |            |      /                  |   |       |  replies   |
+DHCPACK/         |     /  +----------------+   +-------+            |
+Record lease, set|    |   v   Select offer/                         |
+timers T1, T2   ------------  send DHCPREQUEST      |               |
+    |   +----->|            |             DHCPNAK, Lease expired/   |
+    |   |      | REQUESTING |                  Halt network         |
+    DHCPOFFER/ |            |                       |               |
+    Discard     ------------                        |               |
+    |   |        |        |                   -----------           |
+    |   +--------+     DHCPACK/              |           |          |
+    |              Record lease, set    -----| REBINDING |          |
+    |                timers T1, T2     /     |           |          |
+    |                     |        DHCPACK/   -----------           |
+    |                     v     Record lease, set   ^               |
+    +----------------> -------      /timers T1,T2   |               |
+               +----->|       |<---+                |               |
+               |      | BOUND |<---+                |               |
+  DHCPOFFER, DHCPACK, |       |    |            T2 expires/   DHCPNAK/
+   DHCPNAK/Discard     -------     |             Broadcast  Halt network
+               |       | |         |            DHCPREQUEST         |
+               +-------+ |        DHCPACK/          |               |
+                    T1 expires/   Record lease, set |               |
+                 Send DHCPREQUEST timers T1, T2     |               |
+                 to leasing server |                |               |
+                         |   ----------             |               |
+                         |  |          |------------+               |
+                         +->| RENEWING |                            |
+                            |          |----------------------------+
+                             ----------
+          Figure 5:  State-transition diagram for DHCP clients
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 35]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+4.4.1 Initialization and allocation of network address
+
+   The client begins in INIT state and forms a DHCPDISCOVER message.
+   The client SHOULD wait a random time between one and ten seconds to
+   desynchronize the use of DHCP at startup.  The client sets 'ciaddr'
+   to 0x00000000.  The client MAY request specific parameters by
+   including the 'parameter request list' option.  The client MAY
+   suggest a network address and/or lease time by including the
+   'requested IP address' and 'IP address lease time' options.  The
+   client MUST include its hardware address in the 'chaddr' field, if
+   necessary for delivery of DHCP reply messages.  The client MAY
+   include a different unique identifier in the 'client identifier'
+   option, as discussed in section 4.2.  If the client included a list
+   of requested parameters in a DHCPDISCOVER message, it MUST include
+   that list in all subsequent messages.
+
+   The client generates and records a random transaction identifier and
+   inserts that identifier into the 'xid' field.  The client records its
+   own local time for later use in computing the lease expiration.  The
+   client then broadcasts the DHCPDISCOVER on the local hardware
+   broadcast address to the 0xffffffff IP broadcast address and 'DHCP
+   server' UDP port.
+
+   If the 'xid' of an arriving DHCPOFFER message does not match the
+   'xid' of the most recent DHCPDISCOVER message, the DHCPOFFER message
+   must be silently discarded.  Any arriving DHCPACK messages must be
+   silently discarded.
+
+   The client collects DHCPOFFER messages over a period of time, selects
+   one DHCPOFFER message from the (possibly many) incoming DHCPOFFER
+   messages (e.g., the first DHCPOFFER message or the DHCPOFFER message
+   from the previously used server) and extracts the server address from
+   the 'server identifier' option in the DHCPOFFER message.  The time
+   over which the client collects messages and the mechanism used to
+   select one DHCPOFFER are implementation dependent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 36]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Field      DHCPDISCOVER          DHCPREQUEST           DHCPDECLINE,
+           DHCPINFORM                                  DHCPRELEASE
+-----      ------------          -----------           -----------
+'op'       BOOTREQUEST           BOOTREQUEST           BOOTREQUEST
+'htype'    (From "Assigned Numbers" RFC)
+'hlen'     (Hardware address length in octets)
+'hops'     0                     0                     0
+'xid'      selected by client    'xid' from server     selected by
+                                 DHCPOFFER message     client
+'secs'     0 or seconds since    0 or seconds since    0
+           DHCP process started  DHCP process started
+'flags'    Set 'BROADCAST'       Set 'BROADCAST'       0
+           flag if client        flag if client
+           requires broadcast    requires broadcast
+           reply                 reply
+'ciaddr'   0 (DHCPDISCOVER)      0 or client's         0 (DHCPDECLINE)
+           client's              network address       client's network
+           network address       (BOUND/RENEW/REBIND)  address
+           (DHCPINFORM)                                (DHCPRELEASE)
+'yiaddr'   0                     0                     0
+'siaddr'   0                     0                     0
+'giaddr'   0                     0                     0
+'chaddr'   client's hardware     client's hardware     client's hardware
+           address               address               address
+'sname'    options, if           options, if           (unused)
+           indicated in          indicated in
+           'sname/file'          'sname/file'
+           option; otherwise     option; otherwise
+           unused                unused
+'file'     options, if           options, if           (unused)
+           indicated in          indicated in
+           'sname/file'          'sname/file'
+           option; otherwise     option; otherwise
+           unused                unused
+'options'  options               options               (unused)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 37]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Option                     DHCPDISCOVER  DHCPREQUEST      DHCPDECLINE,
+                           DHCPINFORM                     DHCPRELEASE
+------                     ------------  -----------      -----------
+Requested IP address       MAY           MUST (in         MUST
+                           (DISCOVER)    SELECTING or     (DHCPDECLINE),
+                           MUST NOT      INIT-REBOOT)     MUST NOT
+                           (INFORM)      MUST NOT (in     (DHCPRELEASE)
+                                         BOUND or
+                                         RENEWING)
+IP address lease time      MAY           MAY              MUST NOT
+                           (DISCOVER)
+                           MUST NOT
+                           (INFORM)
+Use 'file'/'sname' fields  MAY           MAY              MAY
+DHCP message type          DHCPDISCOVER/ DHCPREQUEST      DHCPDECLINE/
+                           DHCPINFORM                     DHCPRELEASE
+Client identifier          MAY           MAY              MAY
+Vendor class identifier    MAY           MAY              MUST NOT
+Server identifier          MUST NOT      MUST (after      MUST
+                                         SELECTING)
+                                         MUST NOT (after
+                                         INIT-REBOOT,
+                                         BOUND, RENEWING
+                                         or REBINDING)
+Parameter request list     MAY           MAY              MUST NOT
+Maximum message size       MAY           MAY              MUST NOT
+Message                    SHOULD NOT    SHOULD NOT       SHOULD
+Site-specific              MAY           MAY              MUST NOT
+All others                 MAY           MAY              MUST NOT
+
+             Table 5:  Fields and options used by DHCP clients
+
+   If the parameters are acceptable, the client records the address of
+   the server that supplied the parameters from the 'server identifier'
+   field and sends that address in the 'server identifier' field of a
+   DHCPREQUEST broadcast message.  Once the DHCPACK message from the
+   server arrives, the client is initialized and moves to BOUND state.
+   The DHCPREQUEST message contains the same 'xid' as the DHCPOFFER
+   message.  The client records the lease expiration time as the sum of
+   the time at which the original request was sent and the duration of
+   the lease from the DHCPACK message.    The client SHOULD perform a
+   check on the suggested address to ensure that the address is not
+   already in use.  For example, if the client is on a network that
+   supports ARP, the client may issue an ARP request for the suggested
+   request.  When broadcasting an ARP request for the suggested address,
+   the client must fill in its own hardware address as the sender's
+   hardware address, and 0 as the sender's IP address, to avoid
+   confusing ARP caches in other hosts on the same subnet.  If the
+
+
+
+Droms                       Standards Track                    [Page 38]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   network address appears to be in use, the client MUST send a
+   DHCPDECLINE message to the server. The client SHOULD broadcast an ARP
+   reply to announce the client's new IP address and clear any outdated
+   ARP cache entries in hosts on the client's subnet.
+
+4.4.2 Initialization with known network address
+
+   The client begins in INIT-REBOOT state and sends a DHCPREQUEST
+   message.  The client MUST insert its known network address as a
+   'requested IP address' option in the DHCPREQUEST message.  The client
+   may request specific configuration parameters by including the
+   'parameter request list' option.  The client generates and records a
+   random transaction identifier and inserts that identifier into the
+   'xid' field.  The client records its own local time for later use in
+   computing the lease expiration.  The client MUST NOT include a
+   'server identifier' in the DHCPREQUEST message.  The client then
+   broadcasts the DHCPREQUEST on the local hardware broadcast address to
+   the 'DHCP server' UDP port.
+
+   Once a DHCPACK message with an 'xid' field matching that in the
+   client's DHCPREQUEST message arrives from any server, the client is
+   initialized and moves to BOUND state.  The client records the lease
+   expiration time as the sum of the time at which the DHCPREQUEST
+   message was sent and the duration of the lease from the DHCPACK
+   message.
+
+4.4.3 Initialization with an externally assigned network address
+
+   The client sends a DHCPINFORM message. The client may request
+   specific configuration parameters by including the 'parameter request
+   list' option. The client generates and records a random transaction
+   identifier and inserts that identifier into the 'xid' field. The
+   client places its own network address in the 'ciaddr' field. The
+   client SHOULD NOT request lease time parameters.
+
+   The client then unicasts the DHCPINFORM to the DHCP server if it
+   knows the server's address, otherwise it broadcasts the message to
+   the limited (all 1s) broadcast address.  DHCPINFORM messages MUST be
+   directed to the 'DHCP server' UDP port.
+
+   Once a DHCPACK message with an 'xid' field matching that in the
+   client's DHCPINFORM message arrives from any server, the client is
+   initialized.
+
+   If the client does not receive a DHCPACK within a reasonable period
+   of time (60 seconds or 4 tries if using timeout suggested in section
+   4.1), then it SHOULD display a message informing the user of the
+   problem, and then SHOULD begin network processing using suitable
+
+
+
+Droms                       Standards Track                    [Page 39]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   defaults as per Appendix A.
+
+4.4.4 Use of broadcast and unicast
+
+   The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM
+   messages, unless the client knows the address of a DHCP server.  The
+   client unicasts DHCPRELEASE messages to the server.  Because the
+   client is declining the use of the IP address supplied by the server,
+   the client broadcasts DHCPDECLINE messages.
+
+   When the DHCP client knows the address of a DHCP server, in either
+   INIT or REBOOTING state, the client may use that address in the
+   DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address.
+   The client may also use unicast to send DHCPINFORM messages to a
+   known DHCP server.  If the client receives no response to DHCP
+   messages sent to the IP address of a known DHCP server, the DHCP
+   client reverts to using the IP broadcast address.
+
+4.4.5 Reacquisition and expiration
+
+   The client maintains two times, T1 and T2, that specify the times at
+   which the client tries to extend its lease on its network address.
+   T1 is the time at which the client enters the RENEWING state and
+   attempts to contact the server that originally issued the client's
+   network address.  T2 is the time at which the client enters the
+   REBINDING state and attempts to contact any server. T1 MUST be
+   earlier than T2, which, in turn, MUST be earlier than the time at
+   which the client's lease will expire.
+
+   To avoid the need for synchronized clocks, T1 and T2 are expressed in
+   options as relative times [2].
+
+   At time T1 the client moves to RENEWING state and sends (via unicast)
+   a DHCPREQUEST message to the server to extend its lease.  The client
+   sets the 'ciaddr' field in the DHCPREQUEST to its current network
+   address. The client records the local time at which the DHCPREQUEST
+   message is sent for computation of the lease expiration time.  The
+   client MUST NOT include a 'server identifier' in the DHCPREQUEST
+   message.
+
+   Any DHCPACK messages that arrive with an 'xid' that does not match
+   the 'xid' of the client's DHCPREQUEST message are silently discarded.
+   When the client receives a DHCPACK from the server, the client
+   computes the lease expiration time as the sum of the time at which
+   the client sent the DHCPREQUEST message and the duration of the lease
+   in the DHCPACK message.  The client has successfully reacquired its
+   network address, returns to BOUND state and may continue network
+   processing.
+
+
+
+Droms                       Standards Track                    [Page 40]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   If no DHCPACK arrives before time T2, the client moves to REBINDING
+   state and sends (via broadcast) a DHCPREQUEST message to extend its
+   lease.  The client sets the 'ciaddr' field in the DHCPREQUEST to its
+   current network address.  The client MUST NOT include a 'server
+   identifier' in the DHCPREQUEST message.
+
+   Times T1 and T2 are configurable by the server through options.  T1
+   defaults to (0.5 * duration_of_lease).  T2 defaults to (0.875 *
+   duration_of_lease).  Times T1 and T2 SHOULD be chosen with some
+   random "fuzz" around a fixed value, to avoid synchronization of
+   client reacquisition.
+
+   A client MAY choose to renew or extend its lease prior to T1.  The
+   server MAY choose to extend the client's lease according to policy
+   set by the network administrator.  The server SHOULD return T1 and
+   T2, and their values SHOULD be adjusted from their original values to
+   take account of the time remaining on the lease.
+
+   In both RENEWING and REBINDING states, if the client receives no
+   response to its DHCPREQUEST message, the client SHOULD wait one-half
+   of the remaining time until T2 (in RENEWING state) and one-half of
+   the remaining lease time (in REBINDING state), down to a minimum of
+   60 seconds, before retransmitting the DHCPREQUEST message.
+
+   If the lease expires before the client receives a DHCPACK, the client
+   moves to INIT state, MUST immediately stop any other network
+   processing and requests network initialization parameters as if the
+   client were uninitialized.  If the client then receives a DHCPACK
+   allocating that client its previous network address, the client
+   SHOULD continue network processing.  If the client is given a new
+   network address, it MUST NOT continue using the previous network
+   address and SHOULD notify the local users of the problem.
+
+4.4.6 DHCPRELEASE
+
+   If the client no longer requires use of its assigned network address
+   (e.g., the client is gracefully shut down), the client sends a
+   DHCPRELEASE message to the server.  Note that the correct operation
+   of DHCP does not depend on the transmission of DHCPRELEASE messages.
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 41]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+5. Acknowledgments
+
+   The author thanks the many (and too numerous to mention!) members of
+   the DHC WG for their tireless and ongoing efforts in the development
+   of DHCP and this document.
+
+   The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
+   Mendonca in organizing DHCP interoperability testing sessions are
+   gratefully acknowledged.
+
+   The development of this document was supported in part by grants from
+   the Corporation for National Research Initiatives (CNRI), Bucknell
+   University and Sun Microsystems.
+
+6. References
+
+   [1] Acetta, M., "Resource Location Protocol", RFC 887, CMU, December
+       1983.
+
+   [2] Alexander, S., and R. Droms, "DHCP Options and BOOTP Vendor
+       Extensions", RFC 1533, Lachman Technology, Inc., Bucknell
+       University, October 1993.
+
+   [3] Braden, R., Editor, "Requirements for Internet Hosts --
+       Communication Layers", STD 3, RFC 1122, USC/Information Sciences
+       Institute, October 1989.
+
+   [4] Braden, R., Editor, "Requirements for Internet Hosts --
+       Application and Support, STD 3, RFC 1123, USC/Information
+       Sciences Institute, October 1989.
+
+   [5] Brownell, D, "Dynamic Reverse Address Resolution Protocol
+       (DRARP)", Work in Progress.
+
+   [6] Comer, D., and R. Droms, "Uniform Access to Internet Directory
+       Services", Proc. of ACM SIGCOMM '90 (Special issue of Computer
+       Communications Review), 20(4):50--59, 1990.
+
+   [7] Croft, B., and J. Gilmore, "Bootstrap Protocol (BOOTP)", RFC 951,
+       Stanford and SUN Microsystems, September 1985.
+
+   [8] Deering, S., "ICMP Router Discovery Messages", RFC 1256, Xerox
+       PARC, September 1991.
+
+   [9] Droms, D., "Interoperation between DHCP and BOOTP", RFC 1534,
+       Bucknell University, October 1993.
+
+
+
+
+
+Droms                       Standards Track                    [Page 42]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   [10] Finlayson, R., Mann, T., Mogul, J., and M. Theimer, "A Reverse
+        Address Resolution Protocol", RFC 903, Stanford, June 1984.
+
+   [11] Gray C., and D. Cheriton, "Leases: An Efficient Fault-Tolerant
+        Mechanism for Distributed File Cache Consistency", In Proc. of
+        the Twelfth ACM Symposium on Operating Systems Design, 1989.
+
+   [12] Mockapetris, P., "Domain Names -- Concepts and Facilities", STD
+        13, RFC 1034, USC/Information Sciences Institute, November 1987.
+
+   [13] Mockapetris, P., "Domain Names -- Implementation and
+        Specification", STD 13, RFC 1035, USC/Information Sciences
+        Institute, November 1987.
+
+   [14] Mogul J., and S. Deering, "Path MTU Discovery", RFC 1191,
+        November 1990.
+
+   [15] Morgan, R., "Dynamic IP Address Assignment for Ethernet Attached
+        Hosts", Work in Progress.
+
+   [16] Postel, J., "Internet Control Message Protocol", STD 5, RFC 792,
+        USC/Information Sciences Institute, September 1981.
+
+   [17] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
+        USC/Information Sciences Institute, August 1993.
+
+   [18] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+        USC/Information Sciences Institute, October 1994.
+
+   [19] Jeffrey Schiller and Mark Rosenstein. A Protocol for the Dynamic
+        Assignment of IP Addresses for use on an Ethernet. (Available
+        from the Athena Project, MIT), 1989.
+
+   [20] Sollins, K., "The TFTP Protocol (Revision 2)",  RFC 783, NIC,
+        June 1981.
+
+   [21] Wimer, W., "Clarifications and Extensions for the Bootstrap
+        Protocol", RFC 1542, Carnegie Mellon University, October 1993.
+
+7. Security Considerations
+
+   DHCP is built directly on UDP and IP which are as yet inherently
+   insecure.  Furthermore, DHCP is generally intended to make
+   maintenance of remote and/or diskless hosts easier.  While perhaps
+   not impossible, configuring such hosts with passwords or keys may be
+   difficult and inconvenient.  Therefore, DHCP in its current form is
+   quite insecure.
+
+
+
+
+Droms                       Standards Track                    [Page 43]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Unauthorized DHCP servers may be easily set up.  Such servers can
+   then send false and potentially disruptive information to clients
+   such as incorrect or duplicate IP addresses, incorrect routing
+   information (including spoof routers, etc.), incorrect domain
+   nameserver addresses (such as spoof nameservers), and so on.
+   Clearly, once this seed information is in place, an attacker can
+   further compromise affected systems.
+
+   Malicious DHCP clients could masquerade as legitimate clients and
+   retrieve information intended for those legitimate clients.  Where
+   dynamic allocation of resources is used, a malicious client could
+   claim all resources for itself, thereby denying resources to
+   legitimate clients.
+
+8. Author's Address
+
+      Ralph Droms
+      Computer Science Department
+      323 Dana Engineering
+      Bucknell University
+      Lewisburg, PA 17837
+
+      Phone: (717) 524-1145
+      EMail: droms@bucknell.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 44]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+A. Host Configuration Parameters
+
+   IP-layer_parameters,_per_host:_
+
+   Be a router                     on/off                 HRC 3.1
+   Non-local source routing        on/off                 HRC 3.3.5
+   Policy filters for
+   non-local source routing        (list)                 HRC 3.3.5
+   Maximum reassembly size         integer                HRC 3.3.2
+   Default TTL                     integer                HRC 3.2.1.7
+   PMTU aging timeout              integer                MTU 6.6
+   MTU plateau table               (list)                 MTU 7
+   IP-layer_parameters,_per_interface:_
+   IP address                      (address)              HRC 3.3.1.6
+   Subnet mask                     (address mask)         HRC 3.3.1.6
+   MTU                             integer                HRC 3.3.3
+   All-subnets-MTU                 on/off                 HRC 3.3.3
+   Broadcast address flavor        0x00000000/0xffffffff  HRC 3.3.6
+   Perform mask discovery          on/off                 HRC 3.2.2.9
+   Be a mask supplier              on/off                 HRC 3.2.2.9
+   Perform router discovery        on/off                 RD 5.1
+   Router solicitation address     (address)              RD 5.1
+   Default routers, list of:
+           router address          (address)              HRC 3.3.1.6
+           preference level        integer                HRC 3.3.1.6
+   Static routes, list of:
+           destination             (host/subnet/net)      HRC 3.3.1.2
+           destination mask        (address mask)         HRC 3.3.1.2
+           type-of-service         integer                HRC 3.3.1.2
+           first-hop router        (address)              HRC 3.3.1.2
+           ignore redirects        on/off                 HRC 3.3.1.2
+           PMTU                    integer                MTU 6.6
+           perform PMTU discovery  on/off                 MTU 6.6
+
+   Link-layer_parameters,_per_interface:_
+   Trailers                       on/off                 HRC 2.3.1
+   ARP cache timeout              integer                HRC 2.3.2.1
+   Ethernet encapsulation         (RFC 894/RFC 1042)     HRC 2.3.3
+
+   TCP_parameters,_per_host:_
+   TTL                            integer                HRC 4.2.2.19
+   Keep-alive interval            integer                HRC 4.2.3.6
+   Keep-alive data size           0/1                    HRC 4.2.3.6
+
+Key:
+
+   MTU = Path MTU Discovery (RFC 1191, Proposed Standard)
+   RD = Router Discovery (RFC 1256, Proposed Standard)
+
+
+
+Droms                       Standards Track                    [Page 45]
+\f
diff --git a/doc/rfc2132.txt b/doc/rfc2132.txt
new file mode 100644 (file)
index 0000000..e9c4f4b
--- /dev/null
@@ -0,0 +1,1907 @@
+
+
+
+
+
+
+Network Working Group                                       S. Alexander
+Request for Comments: 2132                        Silicon Graphics, Inc.
+Obsoletes: 1533                                                 R. Droms
+Category: Standards Track                            Bucknell University
+                                                              March 1997
+
+                DHCP Options and BOOTP Vendor Extensions
+
+Status of this memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   The Dynamic Host Configuration Protocol (DHCP) [1] provides a
+   framework for passing configuration information to hosts on a TCP/IP
+   network.  Configuration parameters and other control information are
+   carried in tagged data items that are stored in the 'options' field
+   of the DHCP message.  The data items themselves are also called
+   "options."
+
+   This document specifies the current set of DHCP options.  Future
+   options will be specified in separate RFCs.  The current list of
+   valid options is also available in ftp://ftp.isi.edu/in-
+   notes/iana/assignments [22].
+
+   All of the vendor information extensions defined in RFC 1497 [2] may
+   be used as DHCP options.  The definitions given in RFC 1497 are
+   included in this document, which supersedes RFC 1497.  All of the
+   DHCP options defined in this document, except for those specific to
+   DHCP as defined in section 9, may be used as BOOTP vendor information
+   extensions.
+
+Table of Contents
+
+    1.  Introduction ..............................................  2
+    2.  BOOTP Extension/DHCP Option Field Format ..................  4
+    3.  RFC 1497 Vendor Extensions ................................  5
+    4.  IP Layer Parameters per Host .............................. 11
+    5.  IP Layer Parameters per Interface ........................  13
+    6.  Link Layer Parameters per Interface ....................... 16
+    7.  TCP Parameters ............................................ 17
+    8.  Application and Service Parameters ........................ 18
+    9.  DHCP Extensions ........................................... 25
+
+
+
+Alexander & Droms           Standards Track                     [Page 1]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   10.  Defining new extensions ................................... 31
+   11.  Acknowledgements .......................................... 31
+   12.  References ................................................ 32
+   13.  Security Considerations ................................... 33
+   14.  Authors' Addresses ........................................ 34
+
+1. Introduction
+
+   This document specifies options for use with both the Dynamic Host
+   Configuration Protocol and the Bootstrap Protocol.
+
+   The full description of DHCP packet formats may be found in the DHCP
+   specification document [1], and the full description of BOOTP packet
+   formats may be found in the BOOTP specification document [3].  This
+   document defines the format of information in the last field of DHCP
+   packets ('options') and of BOOTP packets ('vend').  The remainder of
+   this section defines a generalized use of this area for giving
+   information useful to a wide class of machines, operating systems and
+   configurations. Sites with a single DHCP or BOOTP server that is
+   shared among heterogeneous clients may choose to define other, site-
+   specific formats for the use of the 'options' field.
+
+   Section 2 of this memo describes the formats of DHCP options and
+   BOOTP vendor extensions.  Section 3 describes options defined in
+   previous documents for use with BOOTP (all may also be used with
+   DHCP).  Sections 4-8 define new options intended for use with both
+   DHCP and BOOTP. Section 9 defines options used only in DHCP.
+
+   References further describing most of the options defined in sections
+   2-6 can be found in section 12.  The use of the options defined in
+   section 9 is described in the DHCP specification [1].
+
+   Information on registering new options is contained in section 10.
+
+   This document updates the definition of DHCP/BOOTP options that
+   appears in RFC1533.  The classing mechanism has been extended to
+   include vendor classes as described in section 8.4 and 9.13.  The new
+   procedure for defining new DHCP/BOOTP options in described in section
+   10.  Several new options, including NIS+ domain and servers, Mobile
+   IP home agent, SMTP server, TFTP server and Bootfile server, have
+   been added.  Text giving definitions used throughout the document has
+   been added in section 1.1.  Text emphasizing the need for uniqueness
+   of client-identifiers has been added to section 9.14.
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 2]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+1.1 Requirements
+
+   Throughout this document, the words that are used to define the
+   significance of particular requirements are capitalized.  These words
+   are:
+
+      o "MUST"
+
+       This word or the adjective "REQUIRED" means that the item is an
+       absolute requirement of this specification.
+
+      o "MUST NOT"
+
+       This phrase means that the item is an absolute prohibition of
+       this specification.
+
+      o "SHOULD"
+
+       This word or the adjective "RECOMMENDED" means that there may
+       exist valid reasons in particular circumstances to ignore this
+       item, but the full implications should be understood and the case
+       carefully weighed before choosing a different course.
+
+      o "SHOULD NOT"
+
+       This phrase means that there may exist valid reasons in
+       particular circumstances when the listed behavior is acceptable
+       or even useful, but the full implications should be understood
+       and the case carefully weighed before implementing any behavior
+       described with this label.
+
+      o "MAY"
+
+       This word or the adjective "OPTIONAL" means that this item is
+       truly optional.  One vendor may choose to include the item
+       because a particular marketplace requires it or because it
+       enhances the product, for example; another vendor may omit the
+       same item.
+
+1.2 Terminology
+
+   This document uses the following terms:
+
+      o "DHCP client"
+
+       A DHCP client or "client" is an Internet host using DHCP to
+       obtain configuration parameters such as a network address.
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 3]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+      o "DHCP server"
+
+       A DHCP server of "server"is an Internet host that returns
+       configuration parameters to DHCP clients.
+
+      o "binding"
+
+       A binding is a collection of configuration parameters, including
+       at least an IP address, associated with or "bound to" a DHCP
+       client.  Bindings are managed by DHCP servers.
+
+2. BOOTP Extension/DHCP Option Field Format
+
+
+   DHCP options have the same format as the BOOTP 'vendor extensions'
+   defined in RFC 1497 [2].  Options may be fixed length or variable
+   length.  All options begin with a tag octet, which uniquely
+   identifies the option.  Fixed-length options without data consist of
+   only a tag octet.  Only options 0 and 255 are fixed length.  All
+   other options are variable-length with a length octet following the
+   tag octet.  The value of the length octet does not include the two
+   octets specifying the tag and length.  The length octet is followed
+   by "length" octets of data.  Options containing NVT ASCII data SHOULD
+   NOT include a trailing NULL; however, the receiver of such options
+   MUST be prepared to delete trailing nulls if they exist.  The
+   receiver MUST NOT require that a trailing null be included in the
+   data.  In the case of some variable-length options the length field
+   is a constant but must still be specified.
+
+   Any options defined subsequent to this document MUST contain a length
+   octet even if the length is fixed or zero.
+
+   All multi-octet quantities are in network byte-order.
+
+   When used with BOOTP, the first four octets of the vendor information
+   field have been assigned to the "magic cookie" (as suggested in RFC
+   951).  This field identifies the mode in which the succeeding data is
+   to be interpreted.  The value of the magic cookie is the 4 octet
+   dotted decimal 99.130.83.99 (or hexadecimal number 63.82.53.63) in
+   network byte order.
+
+   All of the "vendor extensions" defined in RFC 1497 are also DHCP
+   options.
+
+   Option codes 128 to 254 (decimal) are reserved for site-specific
+   options.
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 4]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   Except for the options in section 9, all options may be used with
+   either DHCP or BOOTP.
+
+   Many of these options have their default values specified in other
+   documents.  In particular, RFC 1122 [4] specifies default values for
+   most IP and TCP configuration parameters.
+
+   Many options supply one or more 32-bit IP address.  Use of IP
+   addresses rather than fully-qualified Domain Names (FQDNs) may make
+   future renumbering of IP hosts more difficult.  Use of these
+   addresses is discouraged at sites that may require renumbering.
+
+3. RFC 1497 Vendor Extensions
+
+   This section lists the vendor extensions as defined in RFC 1497.
+   They are defined here for completeness.
+
+3.1. Pad Option
+
+   The pad option can be used to cause subsequent fields to align on
+   word boundaries.
+
+   The code for the pad option is 0, and its length is 1 octet.
+
+    Code
+   +-----+
+   |  0  |
+   +-----+
+
+3.2. End Option
+
+   The end option marks the end of valid information in the vendor
+   field.  Subsequent octets should be filled with pad options.
+
+   The code for the end option is 255, and its length is 1 octet.
+
+    Code
+   +-----+
+   | 255 |
+   +-----+
+
+3.3. Subnet Mask
+
+   The subnet mask option specifies the client's subnet mask as per RFC
+   950 [5].
+
+   If both the subnet mask and the router option are specified in a DHCP
+   reply, the subnet mask option MUST be first.
+
+
+
+Alexander & Droms           Standards Track                     [Page 5]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the subnet mask option is 1, and its length is 4 octets.
+
+    Code   Len        Subnet Mask
+   +-----+-----+-----+-----+-----+-----+
+   |  1  |  4  |  m1 |  m2 |  m3 |  m4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.4. Time Offset
+
+   The time offset field specifies the offset of the client's subnet in
+   seconds from Coordinated Universal Time (UTC).  The offset is
+   expressed as a two's complement 32-bit integer.  A positive offset
+   indicates a location east of the zero meridian and a negative offset
+   indicates a location west of the zero meridian.
+
+   The code for the time offset option is 2, and its length is 4 octets.
+
+    Code   Len        Time Offset
+   +-----+-----+-----+-----+-----+-----+
+   |  2  |  4  |  n1 |  n2 |  n3 |  n4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.5. Router Option
+
+   The router option specifies a list of IP addresses for routers on the
+   client's subnet.  Routers SHOULD be listed in order of preference.
+
+   The code for the router option is 3.  The minimum length for the
+   router option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  3  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.6. Time Server Option
+
+   The time server option specifies a list of RFC 868 [6] time servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the time server option is 4.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 6]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  4  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.7. Name Server Option
+
+   The name server option specifies a list of IEN 116 [7] name servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the name server option is 5.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  5  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.8. Domain Name Server Option
+
+   The domain name server option specifies a list of Domain Name System
+   (STD 13, RFC 1035 [8]) name servers available to the client.  Servers
+   SHOULD be listed in order of preference.
+
+   The code for the domain name server option is 6.  The minimum length
+   for this option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  6  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.9. Log Server Option
+
+   The log server option specifies a list of MIT-LCS UDP log servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the log server option is 7.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  7  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+
+
+Alexander & Droms           Standards Track                     [Page 7]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+3.10. Cookie Server Option
+
+   The cookie server option specifies a list of RFC 865 [9] cookie
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for the log server option is 8.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  8  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.11. LPR Server Option
+
+   The LPR server option specifies a list of RFC 1179 [10] line printer
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for the LPR server option is 9.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  9  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.12. Impress Server Option
+
+   The Impress server option specifies a list of Imagen Impress servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the Impress server option is 10.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  10 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.13. Resource Location Server Option
+
+   This option specifies a list of RFC 887 [11] Resource Location
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+
+
+Alexander & Droms           Standards Track                     [Page 8]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 11.  The minimum length for this option
+   is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  11 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.14. Host Name Option
+
+   This option specifies the name of the client.  The name may or may
+   not be qualified with the local domain name (see section 3.17 for the
+   preferred way to retrieve the domain name).  See RFC 1035 for
+   character set restrictions.
+
+   The code for this option is 12, and its minimum length is 1.
+
+    Code   Len                 Host Name
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  12 |  n  |  h1 |  h2 |  h3 |  h4 |  h5 |  h6 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.15. Boot File Size Option
+
+   This option specifies the length in 512-octet blocks of the default
+   boot image for the client.  The file length is specified as an
+   unsigned 16-bit integer.
+
+   The code for this option is 13, and its length is 2.
+
+    Code   Len   File Size
+   +-----+-----+-----+-----+
+   |  13 |  2  |  l1 |  l2 |
+   +-----+-----+-----+-----+
+
+3.16. Merit Dump File
+
+   This option specifies the path-name of a file to which the client's
+   core image should be dumped in the event the client crashes.  The
+   path is formatted as a character string consisting of characters from
+   the NVT ASCII character set.
+
+   The code for this option is 14.  Its minimum length is 1.
+
+    Code   Len      Dump File Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  14 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+
+
+Alexander & Droms           Standards Track                     [Page 9]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+3.17. Domain Name
+
+   This option specifies the domain name that client should use when
+   resolving hostnames via the Domain Name System.
+
+   The code for this option is 15.  Its minimum length is 1.
+
+    Code   Len        Domain Name
+   +-----+-----+-----+-----+-----+-----+--
+   |  15 |  n  |  d1 |  d2 |  d3 |  d4 |  ...
+   +-----+-----+-----+-----+-----+-----+--
+
+3.18. Swap Server
+
+   This specifies the IP address of the client's swap server.
+
+   The code for this option is 16 and its length is 4.
+
+    Code   Len    Swap Server Address
+   +-----+-----+-----+-----+-----+-----+
+   |  16 |  n  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.19. Root Path
+
+   This option specifies the path-name that contains the client's root
+   disk.  The path is formatted as a character string consisting of
+   characters from the NVT ASCII character set.
+
+   The code for this option is 17.  Its minimum length is 1.
+
+    Code   Len      Root Disk Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  17 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+3.20. Extensions Path
+
+   A string to specify a file, retrievable via TFTP, which contains
+   information which can be interpreted in the same way as the 64-octet
+   vendor-extension field within the BOOTP response, with the following
+   exceptions:
+
+          - the length of the file is unconstrained;
+          - all references to Tag 18 (i.e., instances of the
+            BOOTP Extensions Path field) within the file are
+            ignored.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 10]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 18.  Its minimum length is 1.
+
+    Code   Len      Extensions Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  18 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+4. IP Layer Parameters per Host
+
+   This section details the options that affect the operation of the IP
+   layer on a per-host basis.
+
+4.1. IP Forwarding Enable/Disable Option
+
+   This option specifies whether the client should configure its IP
+   layer for packet forwarding.  A value of 0 means disable IP
+   forwarding, and a value of 1 means enable IP forwarding.
+
+   The code for this option is 19, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  19 |  1  | 0/1 |
+   +-----+-----+-----+
+
+4.2. Non-Local Source Routing Enable/Disable Option
+
+   This option specifies whether the client should configure its IP
+   layer to allow forwarding of datagrams with non-local source routes
+   (see Section 3.3.5 of [4] for a discussion of this topic).  A value
+   of 0 means disallow forwarding of such datagrams, and a value of 1
+   means allow forwarding.
+
+   The code for this option is 20, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  20 |  1  | 0/1 |
+   +-----+-----+-----+
+
+4.3. Policy Filter Option
+
+   This option specifies policy filters for non-local source routing.
+   The filters consist of a list of IP addresses and masks which specify
+   destination/mask pairs with which to filter incoming source routes.
+
+   Any source routed datagram whose next-hop address does not match one
+   of the filters should be discarded by the client.
+
+
+
+Alexander & Droms           Standards Track                    [Page 11]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   See [4] for further information.
+
+   The code for this option is 21.  The minimum length of this option is
+   8, and the length MUST be a multiple of 8.
+
+    Code   Len         Address 1                  Mask 1
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  21 |  n  |  a1 |  a2 |  a3 |  a4 |  m1 |  m2 |  m3 |  m4 |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+           Address 2                  Mask 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  a1 |  a2 |  a3 |  a4 |  m1 |  m2 |  m3 |  m4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+4.4. Maximum Datagram Reassembly Size
+
+   This option specifies the maximum size datagram that the client
+   should be prepared to reassemble.  The size is specified as a 16-bit
+   unsigned integer.  The minimum value legal value is 576.
+
+   The code for this option is 22, and its length is 2.
+
+    Code   Len      Size
+   +-----+-----+-----+-----+
+   |  22 |  2  |  s1 |  s2 |
+   +-----+-----+-----+-----+
+
+4.5. Default IP Time-to-live
+
+   This option specifies the default time-to-live that the client should
+   use on outgoing datagrams.  The TTL is specified as an octet with a
+   value between 1 and 255.
+
+   The code for this option is 23, and its length is 1.
+
+    Code   Len   TTL
+   +-----+-----+-----+
+   |  23 |  1  | ttl |
+   +-----+-----+-----+
+
+4.6. Path MTU Aging Timeout Option
+
+   This option specifies the timeout (in seconds) to use when aging Path
+   MTU values discovered by the mechanism defined in RFC 1191 [12].  The
+   timeout is specified as a 32-bit unsigned integer.
+
+   The code for this option is 24, and its length is 4.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 12]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len           Timeout
+   +-----+-----+-----+-----+-----+-----+
+   |  24 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+4.7. Path MTU Plateau Table Option
+
+   This option specifies a table of MTU sizes to use when performing
+   Path MTU Discovery as defined in RFC 1191.  The table is formatted as
+   a list of 16-bit unsigned integers, ordered from smallest to largest.
+   The minimum MTU value cannot be smaller than 68.
+
+   The code for this option is 25.  Its minimum length is 2, and the
+   length MUST be a multiple of 2.
+
+    Code   Len     Size 1      Size 2
+   +-----+-----+-----+-----+-----+-----+---
+   |  25 |  n  |  s1 |  s2 |  s1 |  s2 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+5. IP Layer Parameters per Interface
+
+   This section details the options that affect the operation of the IP
+   layer on a per-interface basis.  It is expected that a client can
+   issue multiple requests, one per interface, in order to configure
+   interfaces with their specific parameters.
+
+5.1. Interface MTU Option
+
+   This option specifies the MTU to use on this interface.  The MTU is
+   specified as a 16-bit unsigned integer.  The minimum legal value for
+   the MTU is 68.
+
+   The code for this option is 26, and its length is 2.
+
+    Code   Len      MTU
+   +-----+-----+-----+-----+
+   |  26 |  2  |  m1 |  m2 |
+   +-----+-----+-----+-----+
+
+5.2. All Subnets are Local Option
+
+   This option specifies whether or not the client may assume that all
+   subnets of the IP network to which the client is connected use the
+   same MTU as the subnet of that network to which the client is
+   directly connected.  A value of 1 indicates that all subnets share
+   the same MTU.  A value of 0 means that the client should assume that
+   some subnets of the directly connected network may have smaller MTUs.
+
+
+
+Alexander & Droms           Standards Track                    [Page 13]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 27, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  27 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.3. Broadcast Address Option
+
+   This option specifies the broadcast address in use on the client's
+   subnet.  Legal values for broadcast addresses are specified in
+   section 3.2.1.3 of [4].
+
+   The code for this option is 28, and its length is 4.
+
+    Code   Len     Broadcast Address
+   +-----+-----+-----+-----+-----+-----+
+   |  28 |  4  |  b1 |  b2 |  b3 |  b4 |
+   +-----+-----+-----+-----+-----+-----+
+
+5.4. Perform Mask Discovery Option
+
+   This option specifies whether or not the client should perform subnet
+   mask discovery using ICMP.  A value of 0 indicates that the client
+   should not perform mask discovery.  A value of 1 means that the
+   client should perform mask discovery.
+
+   The code for this option is 29, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  29 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.5. Mask Supplier Option
+
+   This option specifies whether or not the client should respond to
+   subnet mask requests using ICMP.  A value of 0 indicates that the
+   client should not respond.  A value of 1 means that the client should
+   respond.
+
+   The code for this option is 30, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  30 |  1  | 0/1 |
+   +-----+-----+-----+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 14]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+5.6. Perform Router Discovery Option
+
+   This option specifies whether or not the client should solicit
+   routers using the Router Discovery mechanism defined in RFC 1256
+   [13].  A value of 0 indicates that the client should not perform
+   router discovery.  A value of 1 means that the client should perform
+   router discovery.
+
+   The code for this option is 31, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  31 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.7. Router Solicitation Address Option
+
+   This option specifies the address to which the client should transmit
+   router solicitation requests.
+
+   The code for this option is 32, and its length is 4.
+
+    Code   Len            Address
+   +-----+-----+-----+-----+-----+-----+
+   |  32 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+5.8. Static Route Option
+
+   This option specifies a list of static routes that the client should
+   install in its routing cache.  If multiple routes to the same
+   destination are specified, they are listed in descending order of
+   priority.
+
+   The routes consist of a list of IP address pairs.  The first address
+   is the destination address, and the second address is the router for
+   the destination.
+
+   The default route (0.0.0.0) is an illegal destination for a static
+   route.  See section 3.5 for information about the router option.
+
+   The code for this option is 33.  The minimum length of this option is
+   8, and the length MUST be a multiple of 8.
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 15]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Destination 1           Router 1
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  33 |  n  |  d1 |  d2 |  d3 |  d4 |  r1 |  r2 |  r3 |  r4 |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+           Destination 2           Router 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  d1 |  d2 |  d3 |  d4 |  r1 |  r2 |  r3 |  r4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+6. Link Layer Parameters per Interface
+
+   This section lists the options that affect the operation of the data
+   link layer on a per-interface basis.
+
+6.1. Trailer Encapsulation Option
+
+   This option specifies whether or not the client should negotiate the
+   use of trailers (RFC 893 [14]) when using the ARP protocol.  A value
+   of 0 indicates that the client should not attempt to use trailers.  A
+   value of 1 means that the client should attempt to use trailers.
+
+   The code for this option is 34, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  34 |  1  | 0/1 |
+   +-----+-----+-----+
+
+6.2. ARP Cache Timeout Option
+
+   This option specifies the timeout in seconds for ARP cache entries.
+   The time is specified as a 32-bit unsigned integer.
+
+   The code for this option is 35, and its length is 4.
+
+    Code   Len           Time
+   +-----+-----+-----+-----+-----+-----+
+   |  35 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+6.3. Ethernet Encapsulation Option
+
+   This option specifies whether or not the client should use Ethernet
+   Version 2 (RFC 894 [15]) or IEEE 802.3 (RFC 1042 [16]) encapsulation
+   if the interface is an Ethernet.  A value of 0 indicates that the
+   client should use RFC 894 encapsulation.  A value of 1 means that the
+   client should use RFC 1042 encapsulation.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 16]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 36, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  36 |  1  | 0/1 |
+   +-----+-----+-----+
+
+7. TCP Parameters
+
+   This section lists the options that affect the operation of the TCP
+   layer on a per-interface basis.
+
+7.1. TCP Default TTL Option
+
+   This option specifies the default TTL that the client should use when
+   sending TCP segments.  The value is represented as an 8-bit unsigned
+   integer.  The minimum value is 1.
+
+   The code for this option is 37, and its length is 1.
+
+    Code   Len   TTL
+   +-----+-----+-----+
+   |  37 |  1  |  n  |
+   +-----+-----+-----+
+
+7.2. TCP Keepalive Interval Option
+
+   This option specifies the interval (in seconds) that the client TCP
+   should wait before sending a keepalive message on a TCP connection.
+   The time is specified as a 32-bit unsigned integer.  A value of zero
+   indicates that the client should not generate keepalive messages on
+   connections unless specifically requested by an application.
+
+   The code for this option is 38, and its length is 4.
+
+    Code   Len           Time
+   +-----+-----+-----+-----+-----+-----+
+   |  38 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+7.3. TCP Keepalive Garbage Option
+
+   This option specifies the whether or not the client should send TCP
+   keepalive messages with a octet of garbage for compatibility with
+   older implementations.  A value of 0 indicates that a garbage octet
+   should not be sent. A value of 1 indicates that a garbage octet
+   should be sent.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 17]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 39, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  39 |  1  | 0/1 |
+   +-----+-----+-----+
+
+8. Application and Service Parameters
+
+   This section details some miscellaneous options used to configure
+   miscellaneous applications and services.
+
+8.1. Network Information Service Domain Option
+
+   This option specifies the name of the client's NIS [17] domain.  The
+   domain is formatted as a character string consisting of characters
+   from the NVT ASCII character set.
+
+   The code for this option is 40.  Its minimum length is 1.
+
+    Code   Len      NIS Domain Name
+   +-----+-----+-----+-----+-----+-----+---
+   |  40 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+8.2. Network Information Servers Option
+
+   This option specifies a list of IP addresses indicating NIS servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 41.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  41 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.3. Network Time Protocol Servers Option
+
+   This option specifies a list of IP addresses indicating NTP [18]
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for this option is 42.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 18]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  42 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.4. Vendor Specific Information
+
+   This option is used by clients and servers to exchange vendor-
+   specific information.  The information is an opaque object of n
+   octets, presumably interpreted by vendor-specific code on the clients
+   and servers.  The definition of this information is vendor specific.
+   The vendor is indicated in the vendor class identifier option.
+   Servers not equipped to interpret the vendor-specific information
+   sent by a client MUST ignore it (although it may be reported).
+   Clients which do not receive desired vendor-specific information
+   SHOULD make an attempt to operate without it, although they may do so
+   (and announce they are doing so) in a degraded mode.
+
+   If a vendor potentially encodes more than one item of information in
+   this option, then the vendor SHOULD encode the option using
+   "Encapsulated vendor-specific options" as described below:
+
+   The Encapsulated vendor-specific options field SHOULD be encoded as a
+   sequence of code/length/value fields of identical syntax to the DHCP
+   options field with the following exceptions:
+
+      1) There SHOULD NOT be a "magic cookie" field in the encapsulated
+         vendor-specific extensions field.
+
+      2) Codes other than 0 or 255 MAY be redefined by the vendor within
+         the encapsulated vendor-specific extensions field, but SHOULD
+         conform to the tag-length-value syntax defined in section 2.
+
+      3) Code 255 (END), if present, signifies the end of the
+         encapsulated vendor extensions, not the end of the vendor
+         extensions field. If no code 255 is present, then the end of
+         the enclosing vendor-specific information field is taken as the
+         end of the encapsulated vendor-specific extensions field.
+
+   The code for this option is 43 and its minimum length is 1.
+
+   Code   Len   Vendor-specific information
+   +-----+-----+-----+-----+---
+   |  43 |  n  |  i1 |  i2 | ...
+   +-----+-----+-----+-----+---
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 19]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   When encapsulated vendor-specific extensions are used, the
+   information bytes 1-n have the following format:
+
+    Code   Len   Data item        Code   Len   Data item       Code
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  T1 |  n  |  d1 |  d2 | ... |  T2 |  n  |  D1 |  D2 | ... | ... |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+
+8.5. NetBIOS over TCP/IP Name Server Option
+
+   The NetBIOS name server (NBNS) option specifies a list of RFC
+   1001/1002 [19] [20] NBNS name servers listed in order of preference.
+
+   The code for this option is 44.  The minimum length of the option is
+   4 octets, and the length must always be a multiple of 4.
+
+    Code   Len           Address 1              Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+   |  44 |  n  |  a1 |  a2 |  a3 |  a4 |  b1 |  b2 |  b3 |  b4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+
+8.6. NetBIOS over TCP/IP Datagram Distribution Server Option
+
+   The NetBIOS datagram distribution server (NBDD) option specifies a
+   list of RFC 1001/1002 NBDD servers listed in order of preference. The
+   code for this option is 45.  The minimum length of the option is 4
+   octets, and the length must always be a multiple of 4.
+
+    Code   Len           Address 1              Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+   |  45 |  n  |  a1 |  a2 |  a3 |  a4 |  b1 |  b2 |  b3 |  b4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+
+8.7. NetBIOS over TCP/IP Node Type Option
+
+   The NetBIOS node type option allows NetBIOS over TCP/IP clients which
+   are configurable to be configured as described in RFC 1001/1002.  The
+   value is specified as a single octet which identifies the client type
+   as follows:
+
+      Value         Node Type
+      -----         ---------
+      0x1           B-node
+      0x2           P-node
+      0x4           M-node
+      0x8           H-node
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 20]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   In the above chart, the notation '0x' indicates a number in base-16
+   (hexadecimal).
+
+   The code for this option is 46.  The length of this option is always
+   1.
+
+    Code   Len  Node Type
+   +-----+-----+-----------+
+   |  46 |  1  | see above |
+   +-----+-----+-----------+
+
+8.8. NetBIOS over TCP/IP Scope Option
+
+   The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+   parameter for the client as specified in RFC 1001/1002. See [19],
+   [20], and [8] for character-set restrictions.
+
+   The code for this option is 47.  The minimum length of this option is
+   1.
+
+    Code   Len       NetBIOS Scope
+   +-----+-----+-----+-----+-----+-----+----
+   |  47 |  n  |  s1 |  s2 |  s3 |  s4 | ...
+   +-----+-----+-----+-----+-----+-----+----
+
+8.9. X Window System Font Server Option
+
+   This option specifies a list of X Window System [21] Font servers
+   available to the client. Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 48.  The minimum length of this option is
+   4 octets, and the length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  48 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |   ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+8.10. X Window System Display Manager Option
+
+   This option specifies a list of IP addresses of systems that are
+   running the X Window System Display Manager and are available to the
+   client.
+
+   Addresses SHOULD be listed in order of preference.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 21]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the this option is 49. The minimum length of this option
+   is 4, and the length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  49 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |   ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+8.11. Network Information Service+ Domain Option
+
+   This option specifies the name of the client's NIS+ [17] domain.  The
+   domain is formatted as a character string consisting of characters
+   from the NVT ASCII character set.
+
+   The code for this option is 64.  Its minimum length is 1.
+
+    Code   Len      NIS Client Domain Name
+   +-----+-----+-----+-----+-----+-----+---
+   |  64 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+8.12. Network Information Service+ Servers Option
+
+   This option specifies a list of IP addresses indicating NIS+ servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 65.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  65 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.13. Mobile IP Home Agent option
+
+   This option specifies a list of IP addresses indicating mobile IP
+   home agents available to the client.  Agents SHOULD be listed in
+   order of preference.
+
+   The code for this option is 68.  Its minimum length is 0 (indicating
+   no home agents are available) and the length MUST be a multiple of 4.
+   It is expected that the usual length will be four octets, containing
+   a single home agent's address.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 22]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code Len    Home Agent Addresses (zero or more)
+   +-----+-----+-----+-----+-----+-----+--
+   | 68  |  n  | a1  | a2  | a3  | a4  | ...
+   +-----+-----+-----+-----+-----+-----+--
+
+8.14. Simple Mail Transport Protocol (SMTP) Server Option
+
+   The SMTP server option specifies a list of SMTP servers available to
+   the client.  Servers SHOULD be listed in order of preference.
+
+   The code for the SMTP server option is 69.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 69  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.15. Post Office Protocol (POP3) Server Option
+
+   The POP3 server option specifies a list of POP3 available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the POP3 server option is 70.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 70  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.16. Network News Transport Protocol (NNTP) Server Option
+
+   The NNTP server option specifies a list of NNTP available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the NNTP server option is 71. The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 71  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 23]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+8.17. Default World Wide Web (WWW) Server Option
+
+   The WWW server option specifies a list of WWW available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the WWW server option is 72.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 72  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.18. Default Finger Server Option
+
+   The Finger server option specifies a list of Finger available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the Finger server option is 73.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 73  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.19. Default Internet Relay Chat (IRC) Server Option
+
+   The IRC server option specifies a list of IRC available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the IRC server option is 74.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 74  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.20. StreetTalk Server Option
+
+   The StreetTalk server option specifies a list of StreetTalk servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 24]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the StreetTalk server option is 75.  The minimum length
+   for this option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 75  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.21. StreetTalk Directory Assistance (STDA) Server Option
+
+   The StreetTalk Directory Assistance (STDA) server option specifies a
+   list of STDA servers available to the client.  Servers SHOULD be
+   listed in order of preference.
+
+   The code for the StreetTalk Directory Assistance server option is 76.
+   The minimum length for this option is 4 octets, and the length MUST
+   always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 76  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+9. DHCP Extensions
+
+   This section details the options that are specific to DHCP.
+
+9.1. Requested IP Address
+
+   This option is used in a client request (DHCPDISCOVER) to allow the
+   client to request that a particular IP address be assigned.
+
+   The code for this option is 50, and its length is 4.
+
+    Code   Len          Address
+   +-----+-----+-----+-----+-----+-----+
+   |  50 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.2. IP Address Lease Time
+
+   This option is used in a client request (DHCPDISCOVER or DHCPREQUEST)
+   to allow the client to request a lease time for the IP address.  In a
+   server reply (DHCPOFFER), a DHCP server uses this option to specify
+   the lease time it is willing to offer.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 25]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The time is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 51, and its length is 4.
+
+    Code   Len         Lease Time
+   +-----+-----+-----+-----+-----+-----+
+   |  51 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.3. Option Overload
+
+   This option is used to indicate that the DHCP 'sname' or 'file'
+   fields are being overloaded by using them to carry DHCP options. A
+   DHCP server inserts this option if the returned parameters will
+   exceed the usual space allotted for options.
+
+   If this option is present, the client interprets the specified
+   additional fields after it concludes interpretation of the standard
+   option fields.
+
+   The code for this option is 52, and its length is 1.  Legal values
+   for this option are:
+
+           Value   Meaning
+           -----   --------
+             1     the 'file' field is used to hold options
+             2     the 'sname' field is used to hold options
+             3     both fields are used to hold options
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  52 |  1  |1/2/3|
+   +-----+-----+-----+
+
+9.4 TFTP server name
+
+   This option is used to identify a TFTP server when the 'sname' field
+   in the DHCP header has been used for DHCP options.
+
+   The code for this option is 66, and its minimum length is 1.
+
+       Code  Len   TFTP server
+      +-----+-----+-----+-----+-----+---
+      | 66  |  n  |  c1 |  c2 |  c3 | ...
+      +-----+-----+-----+-----+-----+---
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 26]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+9.5 Bootfile name
+
+   This option is used to identify a bootfile when the 'file' field in
+   the DHCP header has been used for DHCP options.
+
+   The code for this option is 67, and its minimum length is 1.
+
+       Code  Len   Bootfile name
+      +-----+-----+-----+-----+-----+---
+      | 67  |  n  |  c1 |  c2 |  c3 | ...
+      +-----+-----+-----+-----+-----+---
+
+9.6. DHCP Message Type
+
+   This option is used to convey the type of the DHCP message.  The code
+   for this option is 53, and its length is 1.  Legal values for this
+   option are:
+
+           Value   Message Type
+           -----   ------------
+             1     DHCPDISCOVER
+             2     DHCPOFFER
+             3     DHCPREQUEST
+             4     DHCPDECLINE
+             5     DHCPACK
+             6     DHCPNAK
+             7     DHCPRELEASE
+             8     DHCPINFORM
+
+    Code   Len  Type
+   +-----+-----+-----+
+   |  53 |  1  | 1-9 |
+   +-----+-----+-----+
+
+9.7. Server Identifier
+
+   This option is used in DHCPOFFER and DHCPREQUEST messages, and may
+   optionally be included in the DHCPACK and DHCPNAK messages.  DHCP
+   servers include this option in the DHCPOFFER in order to allow the
+   client to distinguish between lease offers.  DHCP clients use the
+   contents of the 'server identifier' field as the destination address
+   for any DHCP messages unicast to the DHCP server.  DHCP clients also
+   indicate which of several lease offers is being accepted by including
+   this option in a DHCPREQUEST message.
+
+   The identifier is the IP address of the selected server.
+
+   The code for this option is 54, and its length is 4.
+
+
+
+Alexander & Droms           Standards Track                    [Page 27]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len            Address
+   +-----+-----+-----+-----+-----+-----+
+   |  54 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.8. Parameter Request List
+
+   This option is used by a DHCP client to request values for specified
+   configuration parameters.  The list of requested parameters is
+   specified as n octets, where each octet is a valid DHCP option code
+   as defined in this document.
+
+   The client MAY list the options in order of preference.  The DHCP
+   server is not required to return the options in the requested order,
+   but MUST try to insert the requested options in the order requested
+   by the client.
+
+   The code for this option is 55.  Its minimum length is 1.
+
+    Code   Len   Option Codes
+   +-----+-----+-----+-----+---
+   |  55 |  n  |  c1 |  c2 | ...
+   +-----+-----+-----+-----+---
+
+9.9. Message
+
+   This option is used by a DHCP server to provide an error message to a
+   DHCP client in a DHCPNAK message in the event of a failure. A client
+   may use this option in a DHCPDECLINE message to indicate the why the
+   client declined the offered parameters.  The message consists of n
+   octets of NVT ASCII text, which the client may display on an
+   available output device.
+
+   The code for this option is 56 and its minimum length is 1.
+
+    Code   Len     Text
+   +-----+-----+-----+-----+---
+   |  56 |  n  |  c1 |  c2 | ...
+   +-----+-----+-----+-----+---
+
+9.10. Maximum DHCP Message Size
+
+   This option specifies the maximum length DHCP message that it is
+   willing to accept.  The length is specified as an unsigned 16-bit
+   integer.  A client may use the maximum DHCP message size option in
+   DHCPDISCOVER or DHCPREQUEST messages, but should not use the option
+   in DHCPDECLINE messages.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 28]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 57, and its length is 2.  The minimum
+   legal value is 576 octets.
+
+    Code   Len     Length
+   +-----+-----+-----+-----+
+   |  57 |  2  |  l1 |  l2 |
+   +-----+-----+-----+-----+
+
+9.11. Renewal (T1) Time Value
+
+   This option specifies the time interval from address assignment until
+   the client transitions to the RENEWING state.
+
+   The value is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 58, and its length is 4.
+
+    Code   Len         T1 Interval
+   +-----+-----+-----+-----+-----+-----+
+   |  58 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.12. Rebinding (T2) Time Value
+
+   This option specifies the time interval from address assignment until
+   the client transitions to the REBINDING state.
+
+   The value is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 59, and its length is 4.
+
+    Code   Len         T2 Interval
+   +-----+-----+-----+-----+-----+-----+
+   |  59 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.13. Vendor class identifier
+
+   This option is used by DHCP clients to optionally identify the vendor
+   type and configuration of a DHCP client.  The information is a string
+   of n octets, interpreted by servers.  Vendors may choose to define
+   specific vendor class identifiers to convey particular configuration
+   or other identification information about a client.  For example, the
+   identifier may encode the client's hardware configuration.  Servers
+   not equipped to interpret the class-specific information sent by a
+   client MUST ignore it (although it may be reported). Servers that
+
+
+
+Alexander & Droms           Standards Track                    [Page 29]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   respond SHOULD only use option 43 to return the vendor-specific
+   information to the client.
+
+   The code for this option is 60, and its minimum length is 1.
+
+   Code   Len   Vendor class Identifier
+   +-----+-----+-----+-----+---
+   |  60 |  n  |  i1 |  i2 | ...
+   +-----+-----+-----+-----+---
+
+9.14. Client-identifier
+
+   This option is used by DHCP clients to specify their unique
+   identifier.  DHCP servers use this value to index their database of
+   address bindings.  This value is expected to be unique for all
+   clients in an administrative domain.
+
+   Identifiers SHOULD be treated as opaque objects by DHCP servers.
+
+   The client identifier MAY consist of type-value pairs similar to the
+   'htype'/'chaddr' fields defined in [3]. For instance, it MAY consist
+   of a hardware type and hardware address. In this case the type field
+   SHOULD be one of the ARP hardware types defined in STD2 [22].  A
+   hardware type of 0 (zero) should be used when the value field
+   contains an identifier other than a hardware address (e.g. a fully
+   qualified domain name).
+
+   For correct identification of clients, each client's client-
+   identifier MUST be unique among the client-identifiers used on the
+   subnet to which the client is attached.  Vendors and system
+   administrators are responsible for choosing client-identifiers that
+   meet this requirement for uniqueness.
+
+   The code for this option is 61, and its minimum length is 2.
+
+   Code   Len   Type  Client-Identifier
+   +-----+-----+-----+-----+-----+---
+   |  61 |  n  |  t1 |  i1 |  i2 | ...
+   +-----+-----+-----+-----+-----+---
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 30]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+10. Defining new extensions
+
+   The author of a new DHCP option will follow these steps to obtain
+   acceptance of the option as a part of the DHCP Internet Standard:
+
+   1. The author devises the new option.
+   2. The author requests a number for the new option from IANA by
+      contacting:
+      Internet Assigned Numbers Authority (IANA)
+      USC/Information Sciences Institute
+      4676 Admiralty Way
+      Marina del Rey, California  90292-6695
+
+      or by email as: iana@iana.org
+
+   3. The author documents the new option, using the newly obtained
+      option number, as an Internet Draft.
+   4. The author submits the Internet Draft for review through the IETF
+      standards process as defined in "Internet Official Protocol
+      Standards" (STD 1).  The new option will be submitted for eventual
+      acceptance as an Internet Standard.
+   5. The new option progresses through the IETF standards process; the
+      new option will be reviewed by the Dynamic Host Configuration
+      Working Group (if that group still exists), or as an Internet
+      Draft not submitted by an IETF working group.
+   6. If the new option fails to gain acceptance as an Internet
+      Standard, the assigned option number will be returned to IANA for
+      reassignment.
+
+      This procedure for defining new extensions will ensure that:
+
+      * allocation of new option numbers is coordinated from a single
+        authority,
+      * new options are reviewed for technical correctness and
+        appropriateness, and
+      * documentation for new options is complete and published.
+
+11. Acknowledgements
+
+   The author thanks the many (and too numerous to mention!) members of
+   the DHC WG for their tireless and ongoing efforts in the development
+   of DHCP and this document.
+
+   The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
+   Mendonca in organizing DHCP interoperability testing sessions are
+   gratefully acknowledged.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 31]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The development of this document was supported in part by grants from
+   the Corporation for National Research Initiatives (CNRI), Bucknell
+   University and Sun Microsystems.
+
+12. References
+
+   [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
+       Bucknell University, March 1997.
+
+   [2] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
+       USC/Information Sciences Institute, August 1993.
+
+   [3] Croft, W., and J. Gilmore, "Bootstrap Protocol", RFC 951,
+       Stanford University and Sun Microsystems, September 1985.
+
+   [4] Braden, R., Editor, "Requirements for Internet Hosts -
+       Communication Layers", STD 3, RFC 1122, USC/Information Sciences
+       Institute, October 1989.
+
+   [5] Mogul, J., and J. Postel, "Internet Standard Subnetting
+       Procedure", STD 5, RFC 950, USC/Information Sciences Institute,
+       August 1985.
+
+   [6] Postel, J., and K. Harrenstien, "Time Protocol", STD 26, RFC
+       868, USC/Information Sciences Institute, SRI, May 1983.
+
+   [7] Postel, J., "Name Server", IEN 116, USC/Information Sciences
+       Institute, August 1979.
+
+   [8] Mockapetris, P., "Domain Names - Implementation and
+       Specification", STD 13, RFC 1035, USC/Information Sciences
+       Institute, November 1987.
+
+   [9] Postel, J., "Quote of the Day Protocol", STD 23, RFC 865,
+       USC/Information Sciences Institute, May 1983.
+
+   [10] McLaughlin, L., "Line Printer Daemon Protocol", RFC 1179, The
+        Wollongong Group, August 1990.
+
+   [11] Accetta, M., "Resource Location Protocol", RFC 887, CMU,
+        December 1983.
+
+   [12] Mogul, J. and S. Deering, "Path MTU Discovery", RFC 1191,
+        DECWRL,  Stanford University, November 1990.
+
+   [13] Deering, S., "ICMP Router Discovery Messages", RFC 1256,
+        Xerox PARC, September 1991.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 32]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   [14] Leffler, S. and M. Karels, "Trailer Encapsulations", RFC 893,
+        U. C. Berkeley, April 1984.
+
+   [15] Hornig, C., "Standard for the Transmission of IP Datagrams over
+        Ethernet Networks", RFC 894, Symbolics, April 1984.
+
+   [16] Postel, J. and J. Reynolds, "Standard for the Transmission of
+        IP Datagrams Over IEEE 802 Networks", RFC 1042,  USC/Information
+        Sciences Institute, February 1988.
+
+   [17] Sun Microsystems, "System and Network Administration", March
+        1990.
+
+   [18] Mills, D., "Internet Time Synchronization: The Network Time
+        Protocol", RFC 1305, UDEL, March 1992.
+
+   [19] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
+        on a TCP/UDP transport: Concepts and Methods", STD 19, RFC 1001,
+        March 1987.
+
+   [20] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
+        on a TCP/UDP transport: Detailed Specifications", STD 19, RFC
+        1002, March 1987.
+
+   [21] Scheifler, R., "FYI On the X Window System", FYI 6, RFC 1198,
+        MIT Laboratory for Computer Science, January 1991.
+
+   [22] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+        USC/Information Sciences Institute, July 1992.
+
+13. Security Considerations
+
+   Security issues are not discussed in this memo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 33]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+14. Authors' Addresses
+
+   Steve Alexander
+   Silicon Graphics, Inc.
+   2011 N. Shoreline Boulevard
+   Mailstop 510
+   Mountain View, CA 94043-1389
+
+   Phone: (415) 933-6172
+   EMail: sca@engr.sgi.com
+
+
+   Ralph Droms
+   Bucknell University
+   Lewisburg, PA 17837
+
+   Phone: (717) 524-1145
+   EMail: droms@bucknell.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 34]
+\f
diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644 (file)
index 0000000..6173fea
--- /dev/null
@@ -0,0 +1,511 @@
+Service hierarchy
+=================
+
+Service                net.connman
+Interface      net.connman.Service
+Object path    [variable prefix]/{service0,service1,...}
+
+Methods                dict GetProperties()  [deprecated]
+
+                       Returns properties for the service object. See
+                       the properties section for available properties.
+
+                       Usage of this method is highly discouraged. Use
+                       the Manager.GetServices() method instead.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       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.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+               void ClearProperty(string name)
+
+                       Clears the value of the specified property.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+               void Connect()
+
+                       Connect this service. It will attempt to connect
+                       WiFi, WiMAX or Bluetooth services.
+
+                       For Ethernet devices this method can only be used
+                       if it has previously been disconnected. Otherwise
+                       the plugging of a cable will trigger connecting
+                       automatically. If no cable is plugged in this method
+                       will fail.
+
+                       This method call will only return in case of an
+                       error or when the service is fully connected. So
+                       setting a longer D-Bus timeout might be a really
+                       good idea.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void Disconnect()
+
+                       Disconnect this service. If the service is not
+                       connected an error message will be generated.
+
+                       On Ethernet devices this will disconnect the IP
+                       details from the service. It will not magically
+                       unplug the cable. When no cable is plugged in this
+                       method will fail.
+
+                       This method can also be used to abort a previous
+                       connectiong attempt via the Connect method.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void Remove()
+
+                       A successfully connected service with Favorite=true
+                       can be removed this way. If it is connected, it will
+                       be automatically disconnected first.
+
+                       If the service requires a passphrase it will be
+                       cleared and forgotten when removing.
+
+                       This is similar to setting the Favorite property
+                       to false, but that is currently not supported.
+
+                       In the case a connection attempt failed and the
+                       service is in the State=failure, this method can
+                       also be used to reset the service.
+
+                       Calling this method on Ethernet devices will cause
+                       an error message. It is not possible to remove these
+                       kind of devices.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void MoveBefore(object service)
+
+                       If a service has been used before, this allows a
+                       reorder of the favorite services.
+
+                       The target service object must be part of this
+                       profile. Moving between profiles is not supported.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void MoveAfter(object service)
+
+                       If a service has been used before, this allows a
+                       reorder of the favorite services.
+
+                       The target service object must be part of this
+                       profile. Moving between profiles is not supported.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void ResetCounters()  [experimental]
+
+                       Reset the counter statistics.
+
+                       Possible Errors: None
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State [readonly]
+
+                       The service state information.
+
+                       Valid states are "idle", "failure", "association",
+                       "configuration", "ready" and "online".
+
+                       The "ready" state signals a successfully
+                       connected device. "online" signals that an
+                       Internet connection is available and has been
+                       verified.
+
+               string Error [readonly]
+
+                       The service error status details.
+
+                       When error occur during connection or disconnection
+                       the detailed information is represented in this
+                       property to help the user interface to present the
+                       user with alternate options.
+
+                       This property is only valid when the service is in
+                       the "failure" state. Otherwise it might be empty or
+                       not present at all.
+
+                       Current defined error code is "dhcp-failed".
+
+               string Name [readonly]
+
+                       The service name (for example "Wireless" etc.)
+
+                       This name can be used for directly displaying it in
+                       the application. It has pure informational purpose
+                       and no attempt should be made to translate it.
+
+                       For Ethernet devices and hidden WiFi networks this
+                       property is not present.
+
+               string Type [readonly]
+
+                       The service type (for example "ethernet", "wifi" etc.)
+
+                       This information should only be used to determine
+                       advanced properties or showing the correct icon
+                       to the user.
+
+                       Together with a missing Name property, this can
+                       be used to identify hidden WiFi networks.
+
+               array{string} Security [readonly]
+
+                       If the service type is WiFi, then this property is
+                       present and contains the list of security methods
+                       or key management settings.
+
+                       Possible values are "none", "wep", "psk", "ieee8021x"
+                       and also "wps".
+
+                       This property might be only present for WiFi
+                       services.
+
+               string BSSID [readonly]
+
+                       If the service type is WiFi, then this property
+                       indicates the BSSID of the service.
+
+               uint32 MaxRate [readonly]
+
+                       If the service type is WiFi, then this property
+                       indicates the Maximum speed(bps) of the service.
+
+               uint16 Frequency [readonly]
+
+                       If the service type is WiFi, then this property
+                       indicates the frequency band(MHz) of the service.
+
+               string EncryptionMode [readonly]
+
+                       If the service type is WiFi, then this property
+                       indicates the key encryption mode.
+
+                       Possible values are "none", "wep", "tkip", "aes"
+                       and "mixed".
+
+                       This property might be only present for WiFi
+                       services.
+
+               uint8 Strength [readonly]
+
+                       Indicates the signal strength of the service. This
+                       is a normalized value between 0 and 100.
+
+                       This property will not be present for Ethernet
+                       devices.
+
+               boolean Favorite [readonly]
+
+                       Will be true if a cable is plugged in or the user
+                       selected and successfully connected to this service.
+
+                       This value is automatically changed and to revert
+                       it back to false the Remove() method needs to be
+                       used.
+
+               boolean Immutable [readonly]
+
+                       This value will be set to true if the service is
+                       configured externally via a configuration file.
+
+                       The only valid operation are Connect() and of
+                       course Disconnect(). The Remove() method will
+                       result in an error.
+
+               boolean AutoConnect [readwrite]
+
+                       If set to true, this service will auto-connect
+                       when no other connection is available.
+
+                       The service won't auto-connect while roaming.
+
+                       For favorite services it is possible to change
+                       this value to prevent or permit automatic
+                       connection attempts.
+
+               boolean Roaming [readonly]
+
+                       This property indicates if this service is roaming.
+
+                       In the case of Cellular services this normally
+                       indicates connections to a foreign provider when
+                       traveling abroad.
+
+               array{string} Nameservers [readonly]
+
+                       The list of currently active nameservers for this
+                       service. If the server is not in READY or ONLINE
+                       state than this list will be empty.
+
+                       Global nameservers are automatically added to this
+                       list. The array represents a sorted list of the
+                       current nameservers. The first one has the highest
+                       priority and is used by default.
+
+                       When using DHCP this array represents the nameservers
+                       provided by the network. In case of manual settings,
+                       the ones from Nameservers.Configuration are used.
+
+               array{string} Nameservers.Configuration [readwrite]
+
+                       The list of manually configured domain name
+                       servers. Some cellular networks don't provide
+                       correct name servers and this allows for an
+                       override.
+
+                       This array is sorted by priority and the first
+                       entry in the list represents the nameserver with
+                       the highest priority.
+
+                       When using manual configuration and no global
+                       nameservers are configured, then it is useful
+                       to configure this setting.
+
+                       Changes to the domain name servers can be done
+                       at any time. It will not cause a disconnect of
+                       the service. However there might be small window
+                       where name resolution might fail.
+
+               array{string} Timeservers [readonly]
+
+                       The list of currently active timeservers for this
+                       service. If the server is not in READY or ONLINE
+                       state than this list will be empty.
+
+               array{string} Timeservers.Configuration [readwrite]
+
+                       The list of manually configured time servers.
+
+                       The first entry in the list represents the
+                       timeserver with the highest priority.
+
+                       When using manual configuration this setting
+                       is useful to override all the other timeserver
+                       settings. This is service specific, hence only
+                       the values for the default service are used.
+
+                       Changes to this property will result in restart
+                       of NTP query.
+
+               array{string} Domains [readonly]
+
+                       The list of currently used search domains taken
+                       from Domains.Configurations if set, otherwise a
+                       domain name if provided by DHCP or VPNs.
+
+               array{string} Domains.Configuration [readwrite]
+
+                       The list of manually configured search domains.
+
+               dict IPv4 [readonly]
+
+                       string Method [readonly]
+
+                               Possible values are "dhcp", "manual"
+                               and "off".
+
+                               The value "fixed" indicates an IP address
+                               that can not be modified. For example
+                               cellular networks return fixed information.
+
+                       string Address [readonly]
+
+                               The current configured IPv4 address.
+
+                       string Netmask [readonly]
+
+                               The current configured IPv4 netmask.
+
+                       string Gateway [readonly]
+
+                               The current configured IPv4 gateway.
+
+               dict IPv4.Configuration [readwrite]
+
+                       Same values as IPv4 property. The IPv4 represents
+                       the actual system configuration while this allows
+                       user configuration.
+
+                       Changing these settings will cause a state change
+                       of the service. The service will become unavailable
+                       until the new configuration has been successfully
+                       installed.
+
+               dict IPv6 [readonly]
+
+                       string Method [readonly]
+
+                               Possible values are "auto", "manual", "6to4"
+                               and "off".
+
+                               The value "fixed" indicates an IP address
+                               that can not be modified. For example
+                               cellular networks return fixed information.
+                               The value "6to4" is returned if 6to4 tunnel
+                               is created by connman. The tunnel can only be
+                               created if method was set to "auto" by the
+                               user. User cannot set the method to "6to4".
+
+                       string Address [readonly]
+
+                               The current configured IPv6 address.
+
+                       uint8 PrefixLength [readonly]
+
+                               The prefix length of the IPv6 address.
+
+                       string Gateway [readonly]
+
+                               The current configured IPv6 gateway.
+
+                       string Privacy [readonly]
+
+                               Enable or disable IPv6 privacy extension
+                               that is described in RFC 4941. The value
+                               has only meaning if Method is set to "auto".
+
+                               Value "disabled" means that privacy extension
+                               is disabled and normal autoconf addresses are
+                               used.
+
+                               Value "enabled" means that privacy extension is
+                               enabled and system prefers to use public
+                               addresses over temporary addresses.
+
+                               Value "prefered" means that privacy extension is
+                               enabled and system prefers temporary addresses
+                               over public addresses.
+
+                               Default value is "disabled".
+
+               dict IPv6.Configuration [readwrite]
+
+                       Same values as IPv6 property. The IPv6 represents
+                       the actual system configuration while this allows
+                       user configuration.
+
+                       Changing these settings will cause a state change
+                       of the service. The service will become unavailable
+                       until the new configuration has been successfully
+                       installed.
+
+               dict Proxy [readonly]
+
+                       string Method [readonly]
+
+                               Possible values are "direct", "auto" and
+                               "manual".
+
+                               In case of "auto" method, the URL file can be
+                               provided unless you want to let DHCP/WPAD
+                               auto-discover to be tried. In such case if DHCP
+                               and WPAD auto-discover methods fails then
+                               method will be "direct".
+
+                               In case of "direct" no additional information
+                               are provided. For the "manual" method the
+                               Servers have to be set, Excludes is optional.
+
+                       string URL [readonly]
+
+                               Automatic proxy configuration URL. Used by
+                               "auto" method.
+
+                       array{string} Servers [readonly]
+
+                               Used when "manual" method is set.
+
+                               List of proxy URIs. The URI without a protocol
+                               will be interpreted as the generic proxy URI.
+                               All others will target a specific protocol and
+                               only once.
+
+                               Example for generic proxy server entry would
+                               be like this: "server.example.com:911".
+
+                       array{string} Excludes [readonly]
+
+                               Used when "manual" method is set.
+
+                               List of hosts which can be accessed directly.
+
+               dict Proxy.Configuration [readwrite]
+
+                       Same values as Proxy property. The Proxy represents
+                       the actual system configuration while this allows
+                       user configuration.
+
+                       If "auto" method is set with an empty URL, then
+                       DHCP/WPAD auto-discover will be tried. Otherwise the
+                       specified URL will be used.
+
+               dict Provider [readonly]
+
+                       string Host [readonly]
+
+                               VPN host IP.
+
+                       string Domain [readonly]
+
+                               VPN Domain.
+
+                       string Name [readonly]
+
+                               VPN provider Name.
+
+                       string Type [readonly]
+
+                               VPN provider type.
+
+               dict Ethernet [readonly]
+
+                       string Method [readonly]
+
+                               Possible values are "auto" and "manual".
+
+                       string Interface [readonly]
+
+                               Interface name (for example eth0).
+
+                       string Address [readonly]
+
+                               Ethernet device address (MAC address).
+
+                       uint16 MTU [readonly]
+
+                               The Ethernet MTU (default is 1500).
+
+                       uint16 Speed [readonly]
+
+                               Selected speed of the line.
+
+                               This information might not always be
+                               available.
+
+                       string Duplex [readonly]
+
+                               Selected duplex settings of the line.
+
+                               Possible values are "half" and "full".
+
+                               This information might not always be
+                               available.
diff --git a/doc/session-api.txt b/doc/session-api.txt
new file mode 100644 (file)
index 0000000..e19c6bf
--- /dev/null
@@ -0,0 +1,315 @@
+Service                unique name
+Interface      net.connman.Notification
+Object path    freely definable
+
+Methods                void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the session. A counter can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       session, because when this method gets called it has
+                       already been unregistered.
+
+               void Update(dict settings)
+
+                       Sends an update of changed settings. Only settings
+                       that are changed will be included.
+
+                       Initially on every session creation this method is
+                       called once to inform about the current settings.
+
+
+Service                net.connman
+Interface      net.connman.Session
+Object path    variable
+
+Methods                void Destroy()
+
+                       Close the current session. This is similar to
+                       DestroySession method on the manager interface. It
+                       is just provided for convenience depending on how
+                       the application wants to track the session.
+
+               void Connect()
+
+                       If not connected, then attempt to connect this
+                       session.
+
+                       The usage of this method depends a little bit on
+                       the model of the application. Some application
+                       should not try to call Connect on any session at
+                       all. They should just monitor if it becomes online
+                       or gets back offline.
+
+                       Others might require an active connection right now.
+                       So for example email notification should only check
+                       for new emails when a connection is available. However
+                       if the user presses the button for get email or wants
+                       to send an email it should request to get online with
+                       this method.
+
+                       Depending on the bearer settings the current service
+                       is used or a new service will be connected.
+
+                       This method returns immediately after it has been
+                       called. The application is informed through the update
+                       notification about the state of the session.
+
+                       It is also not guaranteed that a session stays online
+                       after this method call. It can be taken offline at any
+                       time. This might happen because of idle timeouts or
+                       other reasons.
+
+                       It is safe to call this method multiple times. The
+                       actual usage will be sorted out for the application.
+
+               void Disconnect()
+
+                       This method indicates that the current session does
+                       not need a connection anymore.
+
+                       This method returns immediately. The application is
+                       informed through the update notification about the
+                       state of the session.
+
+               void Change(string name, variant value)
+
+                       Change the value of certain settings. Not all
+                       settings can be changed. Normally this should not
+                       be needed or an extra session should be created.
+                       However in some cases it makes sense to change
+                       a value and trigger different behavior.
+
+                       A change of a setting will cause an update notification
+                       to be sent. Some changes might cause the session to
+                       be moved to offline state.
+
+Settings       string  Bearer [readonly]
+
+                       This indicates the current bearer that is used
+                       for this session. Or an empty string if no bearer
+                       if available.
+
+               string ConnectionType [readwrite]
+
+                       This is used to indicate which connection is requested
+                       from the session. The state of the session will be
+                       updated accordingly. Values can be nothing, 'local' or
+                       'internet'.
+                       'local' means the session requests to be connected,
+                       but does not require specifically to be online.
+                       Therefore State property will be set to 'connected' if
+                       underlying service gets ready and/or online.
+                       'online' means the session requests to be connected,
+                       and online. State property will never get 'connected'
+                       but instead will switch to 'online' if underlying
+                       service gets online.
+                       No value means the session requests any kind of
+                       connection and the state will be updated on all steps,
+                       'connected' and 'online'. This is the default value.
+
+               boolean State [readonly]
+
+                       This indicates if the connection is disconnected,
+                       connected or online. It is updated according to the
+                       selected ConnectionType. The session will not be
+                       in a useful shape (i.e.: providing a network connection
+                       to the owner) until its State gets updated to connected
+                       and/or online.
+
+                       This maps to the useful port of the  service state.
+                       And it is only valid for the selected bearer
+                       configuration. Otherwise it will be reported as
+                       disconnected even if connected services are present.
+
+                       In addition the State settings notification might
+                       not happen right away. Notifications of this state
+                       can be delayed based on the speed of the bearer. It
+                       is done to avoid congestion on bearers like cellular
+                       etc.
+
+               boolean Priority [readwrite]
+
+                       This allows a session to mark itself as priority or
+                       not. In general application are not allowed to make
+                       themselves more important than others.
+
+                       The priority handling is done internally by usage
+                       and first come, first serve order. By default this
+                       settings is of course false.
+
+                       Internally there can be different priorities for
+                       different application, but these are defined by a
+                       configuration file and not via this interface.
+
+                       An application that calls the method to connect
+                       a session is preferred over other sessions. This
+                       priority value is more for application that want to
+                       push themselves up in the asychronization notification
+                       queue once a bearer becomes online.
+
+                       This actual priority order also depends on the
+                       allowed bearers and other factors. This is setting
+                       is just a little indicator of one application being
+                       notified before another one.
+
+                       For example a streaming session should set the
+                       priority value. As soon as realtime data is involved
+                       then this should be set. An email client should not
+                       set this value.
+
+               string Name [readonly]
+
+                       The Service name to which the system is connected.
+                       It should only be used for displaying it in the UI
+                       and not for getting hold on session object.
+
+               array{string} AllowedBearers [readwrite]
+
+                       A list of bearers that can be used for this session.
+                       In general this list should be empty to indicate that
+                       any bearer is acceptable.
+
+                       The order of the entries in AllowedBearers matters.
+                       The services are sorted in the order of the bearer
+                       entries in this list.
+
+                       Also "*" matches any bearer. This is usefull to prefer
+                       certain bearers such as Wifi with a fallback to any
+                       other available bearer.
+
+               dict IPv4 [readonly]
+
+                       Current IPv4 configuration.
+
+               dict IPv6 [readonly]
+
+                       Current IPv6 configuration.
+
+               boolean AvoidHandover [readwrite]
+
+                       By default this setting is false. It can be used
+                       to indicate that a handover is currently not a good
+                       idea. However no connection is guaranteed. So a
+                       handover can happen anyway. This is just an indication
+                       that the application would like to avoid it right now.
+
+                       It is a bad idea to always enable this settings and
+                       actually it will be reset after a while to avoid
+                       congestion.
+
+                       Main use case it is for application that are currently
+                       doing a specific tasks that it prefers to finish
+                       before allowing handovers again. An example would
+                       be synchronization.
+
+                       Never the less application needs to be aware that
+                       handovers can happen at any time even if this is
+                       set to true.
+
+               boolean StayConnected [readwrite]
+
+                       This disables the idle timeout for this session. There
+                       is no guarantee and this should be used with care.
+
+                       If the system is not online yet this value is ignored.
+
+               uint32  PeriodicConnect [readwrite]
+
+                       Indicate that a periodic connection attempt every
+                       n minutes should be made. The minutes value is a
+                       suggestion when to connection. There is no guarantee
+                       that it will be made or will succeed.
+
+                       A value of 0 disable this feature.
+
+                       This feature is interesting for applications that
+                       wanna check status on a regular interval. And instead
+                       of the application waking up and trying to connect,
+                       this can be centralized nicely and multiple wakeups
+                       avoided in case no connection is available.
+
+                       An example application would be an email client that
+                       wants to check for new emails every 10 minutes.
+
+                       On purpose the smallest setting is 1 minute here since
+                       waking up more often and trying to set up a connection
+                       seems rather pointless use case.
+
+                       If an interval step has passed this can be nicely
+                       rescheduled when any connection matching the bearer
+                       settings becomes available becomes available. Using
+                       this feature it is also easy to avoid congestion.
+
+               uint32  IdleTimeout [readwrite]
+
+                       If the system is idle for given period then it should
+                       go offline.
+
+                       If the timeout is 0, this feature is disabled. If
+                       different values are provided by several session object
+                       the longest interval is taken as timeout value.
+
+               boolean EmergencyCall [readwrite]
+
+                       Boolean representing the emergency mode of the
+                       modem. The Emergency is true if an emergency call or
+                       related operation is currently active.
+
+                       If the emergency application sets this setting to true
+                       all other session will be informed about the emergency
+                       situation with setting it also to true. Only the
+                       emergency application can set back to false.
+
+                       As long the EmergencyCall is true no new session can
+                       be created.
+
+                       Only one application is supposed to write this setting
+                       and therefore it will be protected by additional
+                       PolicyKit rule so that only the emergency application
+                       can write.
+
+                       The emergency application is expected to call Connect()
+                       after setting this setting true. If the emergency
+                       situation is over the application should call
+                       Disconnect() and also set the EmergencyCall to false
+                       afterward.
+
+                       Note only services matching the AllowedBearers rule
+                       will be considered.
+
+               string  RoamingPolicy [readwrite]
+
+                       The allowed roaming behavior.
+
+                       Valid policies are "national", "international",
+                       "default", "always" and "forbidden".
+
+                       "national" allows roaming within a country.
+                       "international" allows roaming in a country and
+                       between countries.
+
+                       "default" is used to tell the Session to use
+                       the global roaming setting.
+
+                       "always" will overwrite the default "forbidden"
+                       value which is useful for emergency application.
+                       This value will be protected by additional PolicyKit
+                       rule.
+
+                       Default value is "forbidden".
+
+               string  Interface [readonly]
+
+                       Interface name used by the service object to connect.
+                       This name can be used for SO_BINDTODEVICE in the
+                       application.
+
+               uint32  SessionMarker [readonly]
+
+                       The unique session marker can be used to mark all
+                       traffic from the application with the SO_MARK
+                       socket option. In combination of the EmergencyCall
+                       this allows ConnMan to setup the firewall rules
+                       that only traffic from the emergency application
+                       are transmitted.
diff --git a/doc/session-overview.txt b/doc/session-overview.txt
new file mode 100644 (file)
index 0000000..82e3f89
--- /dev/null
@@ -0,0 +1,141 @@
+Session API
+***********
+
+
+Connection management algorithm basics
+======================================
+
+When a session is created, a sorted list of services is added to the
+session. The services are filtered and stable sorted according
+following rules:
+
+ - AllowedBearers (filter and sort)
+ - RoamingPolicy (filter and sort)
+
+A stable sorting algorithm maintains the relative order.
+
+If a service is removed or added all sessions are updated according
+the above rules.
+
+There are three triggers which lead to evaluate the connect
+algorithm:
+
+ - Session.Connect()
+ - PeriodicConnect
+ - Offline
+
+Connect algorithm:
+
+              Session.Connect()     Offline
+              PeriodicConnect        |
+                       | Yes  +------+-------+  No
+                       +------+StayConnected?+------ Do nothing
+                       |      +--------------+
+Session.Change() ------+
+                       |
+                +------+-------+
+          +-----+EmergencyCall?+-----+
+       Yes|     +--------------+     |No
+          |                          |
+        Connect to            +--------------+
+        first available   +---+AvoidHandover?+---+
+        Service           |   +--------------+   |
+                       Yes|                      |No
+                 +----------------+              |
+             +---+In service_list +---+          |
+          Yes|   |and online?     |   |No        |
+             |   +----------------+   |          |
+             |                        |          |
+         Take that one                Take first in
+                                      the service list
+
+There are two triggers which lead to evaluate the disconnect
+algorithm
+
+ - Session.Disconnect()
+ - IdleTimeout
+
+Disconnect algorithm:
+
+  Session.Disconnect()
+  IdleTimeout
+       |
+       +--- Session.Change()
+       |
++-----------------+    Yes
+|service not used +-------------+
+|by other session?|             |
++------.----------+             |
+       |No                      |
+       |                        |
+    Service.Disconnect()   Do nothing
+
+
+Session States and Transitions
+==============================
+
+There are three main strategies for state changes.
+
+ - Free Ride
+ - Connect
+ - Disconnect
+
+The initial state for all new sessions is Free Ride.
+
+The Free Ride state means that a session will go online if a matching
+service goes online without calling Service.Connect() itself. The idea
+behind this is that a session doesn't request a connection for itself
+instead waits until another session actively requires to go online.
+This is comparable to piggy-backing.
+
+When a session is in the Connect state ConnMan tries to find a
+matching service (see Connect algorithm) and then decides either to
+connect the service or delay the request. ConnMan is allowed to
+connect to the service or to delay it, e.g. group PeriodicConnects
+together. The session will leave the Connect state when the service
+goes offline unless StayConnected is True. It will enter the Free Ride
+mode again.
+
+When the application calls Disconnect() the session enters the
+Disconnect state and stays there until the application calls Connect()
+again.
+
+
+                 State Change to offline & StayConnected = True
+                              +------+
+                              |      v
++-----------+                +---------+ -- Disconnect() --> +------------+
+| Free Ride |-- Connect() -->| Connect |                     | Disconnect |
++-----------+                +---------+  <-- Connect() ---  +------------+
+      |  ^                         |                               ^
+      |  +------------------------ +                               |
+      |   State Change to offline & StayConnected = False          |
+      |                                                            |
+      |                                                            |
+      +----------------------- Disconnect() -----------------------+
+
+Note: this documents the current behavior it is likely to change in near
+future.
+
+
+Additional Information on Settings
+==================================
+
+PeriodicConnect and IdleTimeout
+-------------------------------
+
+If an application wants to go online periodically (e.g. checking for
+new mails) then the application should use PeriodicConnect instead of
+calling Session.Connect() periodically. There is no need for the
+application to maintain timers. ConnMan is also able to try to combine
+several PeriodicConnect calls into one. Applications should not rely on a
+very precise periodic connect. Apart from merging periodic connect
+timeouts there is also the problem that no service might be available
+at that point and ConnMan will defer the connect call.
+
+The IdleTimeout tells ConnMan when a link is idle for given period it
+is okay to disonnect.
+
+PeriodicConnect and IdleTimeout should only consired as hints. ConnMan
+will try to meet them but there is no garantee for doing so. For
+example global settings have precedence over session settings.
diff --git a/doc/technology-api.txt b/doc/technology-api.txt
new file mode 100644 (file)
index 0000000..6429776
--- /dev/null
@@ -0,0 +1,95 @@
+Technology hierarchy
+====================
+
+Service                net.connman
+Interface      net.connman.Technology
+Object path    [variable prefix]/{technology0,technology1,...}
+
+Methods                dict GetProperties()  [deprecated]
+
+                       Returns properties for the technology object. See
+                       the properties section for available properties.
+
+                       Usage of this method is highly discouraged. Use
+                       the Manager.GetTechnologies() method instead.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       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.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.InvalidProperty
+
+               void Scan()
+
+                       Trigger a scan for this specific technology. The
+                       method call will return when a scan has been
+                       finished and results are available. So setting
+                       a longer D-Bus timeout might be a really good
+                       idea.
+
+                       Results will be signaled via the ServicesChanged
+                       signal from the manager interface.
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Powered [readwrite]
+
+                       Boolean representing the power state of the
+                       technology. False means that the technology is
+                       off (and is available RF-Killed) while True means
+                       that the technology is enabled.
+
+               boolean Connected [readonly]
+
+                       Boolean representing if a technolgy is connected.
+
+                       This is just a convience property for allowing the
+                       UI to easily show if this technolgy has an active
+                       connection or not.
+
+                       If this property is True it means that at least one
+                       service of this technology is in ready state.
+
+               string Name [readonly]
+
+                       Name of this technology.
+
+               string Type [readonly]
+
+                       The technology type (for example "ethernet" etc.)
+
+                       This information should only be used to determine
+                       advanced properties or showing the correct icon
+                       to the user.
+
+               boolean Tethering [readwrite]
+
+                       This option allows to enable or disable the support
+                       for tethering. When tethering is enabled then the
+                       default service is bridged to all clients connected
+                       through the technology.
+
+               string TetheringIdentifier [readwrite]
+
+                       The tethering broadcasted identifier.
+
+                       This property is only valid for the WiFi technology,
+                       and is then mapped to the WiFi AP SSID clients will
+                       have to join in order to gain internet connectivity.
+
+               string TetheringPassphrase [readwrite]
+
+                       The tethering connection passphrase.
+
+                       This property is only valid for the WiFi technology,
+                       and is then mapped to the WPA pre-shared key clients
+                       will have to use in order to establish a connection.
diff --git a/doc/version.xml.in b/doc/version.xml.in
new file mode 100644 (file)
index 0000000..d78bda9
--- /dev/null
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644 (file)
index 0000000..0a8a27c
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+                                                       void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending);
+
+typedef enum {
+       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef enum {
+       G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
+       G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
+       G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+} GDBusSecurityFlags;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+} GDBusArgInfo;
+
+typedef struct {
+       const char *name;
+       GDBusMethodFunction function;
+       GDBusMethodFlags flags;
+       unsigned int privilege;
+       const GDBusArgInfo *in_args;
+       const GDBusArgInfo *out_args;
+} GDBusMethodTable;
+
+typedef struct {
+       const char *name;
+       GDBusSignalFlags flags;
+       const GDBusArgInfo *args;
+} GDBusSignalTable;
+
+typedef struct {
+       const char *name;
+       const char *type;
+       GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+typedef struct {
+       unsigned int privilege;
+       const char *action;
+       GDBusSecurityFlags flags;
+       GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args, \
+       .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+                                       __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+                                       __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644 (file)
index 0000000..cff326f
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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.
+ *
+ *  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 <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT  0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+       guint id;
+       DBusTimeout *timeout;
+};
+
+struct watch_info {
+       guint id;
+       DBusWatch *watch;
+       DBusConnection *conn;
+};
+
+struct disconnect_data {
+       GDBusWatchFunction function;
+       void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct disconnect_data *dc_data = data;
+
+       error("Got disconnected from the system message bus");
+
+       dc_data->function(conn, dc_data->user_data);
+
+       dbus_connection_unref(conn);
+
+       return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+       DBusConnection *conn = data;
+
+       dbus_connection_ref(conn);
+
+       /* Dispatch messages */
+       while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+       dbus_connection_unref(conn);
+
+       return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+                                               DBusDispatchStatus status)
+{
+       if (status == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct watch_info *info = data;
+       unsigned int flags = 0;
+       DBusDispatchStatus status;
+
+       dbus_connection_ref(info->conn);
+
+       if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
+       if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+       if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+       if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+       dbus_watch_handle(info->watch, flags);
+
+       status = dbus_connection_get_dispatch_status(info->conn);
+       queue_dispatch(info->conn, status);
+
+       dbus_connection_unref(info->conn);
+
+       return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+       struct watch_info *info = data;
+
+       if (info->id > 0) {
+               g_source_remove(info->id);
+               info->id = 0;
+       }
+
+       dbus_connection_unref(info->conn);
+
+       g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       DBusConnection *conn = data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR;
+       GIOChannel *chan;
+       struct watch_info *info;
+       unsigned int flags;
+       int fd;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       info = g_new0(struct watch_info, 1);
+
+       fd = dbus_watch_get_unix_fd(watch);
+       chan = g_io_channel_unix_new(fd);
+
+       info->watch = watch;
+       info->conn = dbus_connection_ref(conn);
+
+       dbus_watch_set_data(watch, info, watch_info_free);
+
+       flags = dbus_watch_get_flags(watch);
+
+       if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+       if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+       info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+       g_io_channel_unref(chan);
+
+       return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       if (dbus_watch_get_enabled(watch))
+               return;
+
+       /* will trigger watch_info_free() */
+       dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+       /* Because we just exit on OOM, enable/disable is
+        * no different from add/remove */
+       if (dbus_watch_get_enabled(watch))
+               add_watch(watch, data);
+       else
+               remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+       struct timeout_handler *handler = data;
+
+       handler->id = 0;
+
+       /* if not enabled should not be polled by the main loop */
+       if (!dbus_timeout_get_enabled(handler->timeout))
+               return FALSE;
+
+       dbus_timeout_handle(handler->timeout);
+
+       return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+       struct timeout_handler *handler = data;
+
+       if (handler->id > 0) {
+               g_source_remove(handler->id);
+               handler->id = 0;
+       }
+
+       g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       int interval = dbus_timeout_get_interval(timeout);
+       struct timeout_handler *handler;
+
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       handler = g_new0(struct timeout_handler, 1);
+
+       handler->timeout = timeout;
+
+       dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+       handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+                                                               handler);
+
+       return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+       /* will trigger timeout_handler_free() */
+       dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+       if (dbus_timeout_get_enabled(timeout))
+               add_timeout(timeout, data);
+       else
+               remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+                                       DBusDispatchStatus status, void *data)
+{
+       if (!dbus_connection_get_is_connected(conn))
+               return;
+
+       queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+       dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+                                               watch_toggled, conn, NULL);
+
+       dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+                                               timeout_toggled, NULL, NULL);
+
+       dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+                                                               NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+                                               DBusError *error)
+{
+       gboolean result;
+       DBusDispatchStatus status;
+
+       if (name != NULL) {
+               result = g_dbus_request_name(conn, name, error);
+
+               if (error != NULL) {
+                       if (dbus_error_is_set(error) == TRUE)
+                               return FALSE;
+               }
+
+               if (result == FALSE)
+                       return FALSE;
+       }
+
+       setup_dbus_with_main_loop(conn);
+
+       status = dbus_connection_get_dispatch_status(conn);
+       queue_dispatch(conn, status);
+
+       return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get_private(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error)
+{
+       int result;
+
+       result = dbus_bus_request_name(connection, name,
+                                       DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return FALSE;
+       }
+
+       if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               if (error != NULL)
+                       dbus_set_error(error, name, "Name already in use");
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy)
+{
+       struct disconnect_data *dc_data;
+
+       dc_data = g_new0(struct disconnect_data, 1);
+
+       dc_data->function = function;
+       dc_data->user_data = user_data;
+
+       dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+       if (g_dbus_add_signal_watch(connection, NULL, NULL,
+                               DBUS_INTERFACE_LOCAL, "Disconnected",
+                               disconnected_signal, dc_data, g_free) == 0) {
+               error("Failed to add watch for D-Bus Disconnected signal");
+               g_free(dc_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644 (file)
index 0000000..900e7ab
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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.
+ *
+ *  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 <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct generic_data {
+       unsigned int refcount;
+       GSList *interfaces;
+       char *introspect;
+};
+
+struct interface_data {
+       char *name;
+       const GDBusMethodTable *methods;
+       const GDBusSignalTable *signals;
+       const GDBusPropertyTable *properties;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+       GDBusPendingReply pending;
+       DBusMessage *message;
+       const GDBusMethodTable *method;
+       void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+                                               const char *direction)
+{
+       for (; args && args->name; args++) {
+               g_string_append_printf(gstr,
+                                       "\t\t\t<arg name=\"%s\" type=\"%s\"",
+                                       args->name, args->signature);
+
+               if (direction)
+                       g_string_append_printf(gstr,
+                                       " direction=\"%s\"/>\n", direction);
+               else
+                       g_string_append_printf(gstr, "/>\n");
+
+       }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
+
+       for (method = iface->methods; method && method->name; method++) {
+               gboolean deprecated = method->flags &
+                                               G_DBUS_METHOD_FLAG_DEPRECATED;
+               gboolean noreply = method->flags &
+                                               G_DBUS_METHOD_FLAG_NOREPLY;
+
+               if (!deprecated && !noreply &&
+                               !(method->in_args && method->in_args->name) &&
+                               !(method->out_args && method->out_args->name))
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+                                                               method->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+                                                               method->name);
+                       print_arguments(gstr, method->in_args, "in");
+                       print_arguments(gstr, method->out_args, "out");
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       if (noreply)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</method>\n");
+               }
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               gboolean deprecated = signal->flags &
+                                               G_DBUS_SIGNAL_FLAG_DEPRECATED;
+
+               if (!deprecated && !(signal->args && signal->args->name))
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+                                                               signal->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+                                                               signal->name);
+                       print_arguments(gstr, signal->args, NULL);
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</signal>\n");
+               }
+       }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+                               struct generic_data *data, const char *path)
+{
+       GSList *list;
+       GString *gstr;
+       char **children;
+       int i;
+
+       g_free(data->introspect);
+
+       gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+       g_string_append_printf(gstr, "<node>\n");
+
+       for (list = data->interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+
+               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+                                                               iface->name);
+
+               generate_interface_xml(gstr, iface);
+
+               g_string_append_printf(gstr, "\t</interface>\n");
+       }
+
+       if (!dbus_connection_list_registered(conn, path, &children))
+               goto done;
+
+       for (i = 0; children[i]; i++)
+               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+                                                               children[i]);
+
+       dbus_free_string_array(children);
+
+done:
+       g_string_append_printf(gstr, "</node>\n");
+
+       data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessage *reply;
+
+       if (data->introspect == NULL)
+               generate_introspection_xml(connection, data,
+                                               dbus_message_get_path(message));
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+                       DBusMessage *message, const GDBusMethodTable *method,
+                                                       void *iface_user_data)
+{
+       DBusMessage *reply;
+
+       reply = method->function(connection, message, iface_user_data);
+
+       if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+               if (reply != NULL)
+                       dbus_message_unref(reply);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (reply == NULL)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_connection_send(connection, reply, NULL);
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               process_message(connection, secdata->message,
+                               secdata->method, secdata->iface_user_data);
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+               DBusMessage *reply;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               reply = g_dbus_create_error_valist(secdata->message,
+                                                       name, format, args);
+               if (reply != NULL) {
+                       dbus_connection_send(connection, reply, NULL);
+                       dbus_message_unref(reply);
+               }
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+       va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+struct builtin_security_data {
+       DBusConnection *conn;
+       GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+       struct builtin_security_data *data = user_data;
+
+       if (authorized == TRUE)
+               g_dbus_pending_success(data->conn, data->pending);
+       else
+               g_dbus_pending_error(data->conn, data->pending,
+                                               DBUS_ERROR_AUTH_FAILED, NULL);
+
+       g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending)
+{
+       struct builtin_security_data *data;
+
+       data = g_new0(struct builtin_security_data, 1);
+       data->conn = conn;
+       data->pending = pending;
+
+       if (polkit_check_authorization(conn, action, interaction,
+                               builtin_security_result, data, 30000) < 0)
+               g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+                       const GDBusMethodTable *method, void *iface_user_data)
+{
+       const GDBusSecurityTable *security;
+
+       for (security = security_table; security && security->privilege;
+                                                               security++) {
+               struct security_data *secdata;
+               gboolean interaction;
+
+               if (security->privilege != method->privilege)
+                       continue;
+
+               secdata = g_new(struct security_data, 1);
+               secdata->pending = next_pending++;
+               secdata->message = dbus_message_ref(msg);
+               secdata->method = method;
+               secdata->iface_user_data = iface_user_data;
+
+               pending_security = g_slist_prepend(pending_security, secdata);
+
+               if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+                       interaction = TRUE;
+               else
+                       interaction = FALSE;
+
+               if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+                                                       security->function)
+                       security->function(conn, security->action,
+                                               interaction, secdata->pending);
+               else
+                       builtin_security_function(conn, security->action,
+                                               interaction, secdata->pending);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+
+       g_free(data->introspect);
+       g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+                                               const char *name)
+{
+       GSList *list;
+
+       if (name == NULL)
+               return NULL;
+
+       for (list = interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+               if (!strcmp(name, iface->name))
+                       return iface;
+       }
+
+       return NULL;
+}
+
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+                                                       DBusMessage *message)
+{
+       const char *sig = dbus_message_get_signature(message);
+       const char *p = NULL;
+
+       for (; args && args->signature && *sig; args++) {
+               p = args->signature;
+
+               for (; *sig && *p; sig++, p++) {
+                       if (*p != *sig)
+                               return FALSE;
+               }
+       }
+
+       if (*sig || (p && *p) || (args && args->signature))
+               return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+       struct generic_data *data = NULL;
+       char *parent_path, *slash;
+
+       parent_path = g_strdup(child_path);
+       slash = strrchr(parent_path, '/');
+       if (slash == NULL)
+               goto done;
+
+       if (slash == parent_path && parent_path[1] != '\0')
+               parent_path[1] = '\0';
+       else
+               *slash = '\0';
+
+       if (!strlen(parent_path))
+               goto done;
+
+       if (dbus_connection_get_object_path_data(conn, parent_path,
+                                                       (void *) &data) == FALSE) {
+               goto done;
+       }
+
+       invalidate_parent_data(conn, parent_path);
+
+       if (data == NULL)
+               goto done;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+done:
+       g_free(parent_path);
+}
+
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+                                                       const char *path)
+{
+       struct generic_data *data;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == TRUE) {
+               if (data != NULL) {
+                       data->refcount++;
+                       return data;
+               }
+       }
+
+       data = g_new0(struct generic_data, 1);
+       data->refcount = 1;
+
+       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       if (!dbus_connection_register_object_path(connection, path,
+                                               &generic_table, data)) {
+               g_free(data->introspect);
+               g_free(data);
+               return NULL;
+       }
+
+       invalidate_parent_data(connection, path);
+
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+                       introspect_methods, NULL, NULL, data, NULL);
+
+       return data;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
+       if (iface == NULL)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       return TRUE;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+       struct generic_data *data = NULL;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return;
+
+       if (data == NULL)
+               return;
+
+       data->refcount--;
+
+       if (data->refcount > 0)
+               return;
+
+       remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
+       invalidate_parent_data(connection, path);
+
+       dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+                               const char *interface, const char *name,
+                               const GDBusArgInfo **args)
+{
+       struct generic_data *data = NULL;
+       struct interface_data *iface;
+       const GDBusSignalTable *signal;
+
+       *args = NULL;
+       if (!dbus_connection_get_object_path_data(conn, path,
+                                       (void *) &data) || data == NULL) {
+               error("dbus_connection_emit_signal: path %s isn't registered",
+                               path);
+               return FALSE;
+       }
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL) {
+               error("dbus_connection_emit_signal: %s does not implement %s",
+                               path, interface);
+               return FALSE;
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strcmp(signal->name, name)) {
+                       *args = signal->args;
+                       return TRUE;
+               }
+       }
+
+       error("No signal named %s on interface %s", name, interface);
+       return FALSE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int first,
+                                               va_list var_args)
+{
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const GDBusArgInfo *args;
+
+       if (!check_signal(conn, path, interface, name, &args))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, first, var_args);
+       if (!ret)
+               goto fail;
+
+       if (g_dbus_args_have_signature(args, signal) == FALSE) {
+               error("%s.%s: expected signature'%s' but got '%s'",
+                               interface, name, args, signature);
+               ret = FALSE;
+               goto fail;
+       }
+
+       ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy)
+{
+       struct generic_data *data;
+
+       data = object_path_ref(connection, path);
+       if (data == NULL)
+               return FALSE;
+
+       if (find_interface(data->interfaces, name)) {
+               object_path_unref(connection, path);
+               return FALSE;
+       }
+
+       add_interface(data, name, methods, signals,
+                       properties, user_data, destroy);
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name)
+{
+       struct generic_data *data = NULL;
+
+       if (path == NULL)
+               return FALSE;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return FALSE;
+
+       if (data == NULL)
+               return FALSE;
+
+       if (remove_interface(data, name) == FALSE)
+               return FALSE;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       object_path_unref(connection, path);
+
+       return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+       if (security_table != NULL)
+               return FALSE;
+
+       security_table = security;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+       security_table = NULL;
+
+       return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args)
+{
+       char str[1024];
+
+       vsnprintf(str, sizeof(str), format, args);
+
+       return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, format);
+
+       reply = g_dbus_create_error_valist(message, name, format, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, type);
+
+       reply = g_dbus_create_reply_valist(message, type, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+       dbus_bool_t result;
+
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+               dbus_message_set_no_reply(message, TRUE);
+
+       result = dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+
+       return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return FALSE;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = g_dbus_send_reply_valist(connection, message, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args)
+{
+       return emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644 (file)
index 0000000..9e95fa3
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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.
+ *
+ *  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 <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+                                       const char *key, const char *str)
+{
+       DBusMessageIter dict, entry, value;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       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_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_STRING_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       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);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+                               const char *action, dbus_uint32_t flags)
+{
+       const char *busname = dbus_bus_get_unique_name(conn);
+       const char *kind = "system-bus-name";
+       const char *cancel = "";
+       DBusMessageIter subject;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                                       NULL, &subject);
+       dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+       add_dict_with_string_value(&subject, "name", busname);
+       dbus_message_iter_close_container(iter, &subject);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+       add_empty_string_dict(iter);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+       DBusMessageIter result;
+       dbus_bool_t authorized, challenge;
+
+       dbus_message_iter_recurse(iter, &result);
+
+       dbus_message_iter_get_basic(&result, &authorized);
+       dbus_message_iter_get_basic(&result, &challenge);
+
+       return authorized;
+}
+
+struct authorization_data {
+       void (*function) (dbus_bool_t authorized, void *user_data);
+       void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+       struct authorization_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       dbus_bool_t authorized = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+
+       authorized = parse_result(&iter);
+
+done:
+       if (data->function != NULL)
+               data->function(authorized, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout)
+{
+       struct authorization_data *data;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+       dbus_uint32_t flags = 0x00000000;
+
+       if (conn == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+                               AUTHORITY_INTF, "CheckAuthorization");
+       if (msg == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       if (interaction == TRUE)
+               flags |= 0x00000001;
+
+       if (action == NULL)
+               action = "org.freedesktop.policykit.exec";
+
+       dbus_message_iter_init_append(msg, &iter);
+       add_arguments(conn, &iter, action, flags);
+
+       if (dbus_connection_send_with_reply(conn, msg,
+                                               &call, timeout) == FALSE) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, authorization_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(msg);
+
+       return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644 (file)
index 0000000..d749176
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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.
+ *
+ *  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 <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+       DBusConnection *conn;
+       DBusPendingCall *call;
+       char *name;
+       const char *owner;
+       guint id;
+       struct filter_callback *callback;
+};
+
+struct filter_callback {
+       GDBusWatchFunction conn_func;
+       GDBusWatchFunction disc_func;
+       GDBusSignalFunction signal_func;
+       GDBusDestroyFunction destroy_func;
+       struct service_data *data;
+       void *user_data;
+       guint id;
+};
+
+struct filter_data {
+       DBusConnection *connection;
+       DBusHandleMessageFunction handle_func;
+       char *name;
+       char *owner;
+       char *path;
+       char *interface;
+       char *member;
+       char *argument;
+       GSList *callbacks;
+       GSList *processed;
+       guint name_watch;
+       gboolean lock;
+       gboolean registered;
+};
+
+static struct filter_data *filter_data_find(DBusConnection *connection,
+                                                       const char *name,
+                                                       const char *owner,
+                                                       const char *path,
+                                                       const char *interface,
+                                                       const char *member,
+                                                       const char *argument)
+{
+       GSList *current;
+
+       for (current = listeners;
+                       current != NULL; current = current->next) {
+               struct filter_data *data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (name && data->name &&
+                               g_str_equal(name, data->name) == FALSE)
+                       continue;
+
+               if (owner && data->owner &&
+                               g_str_equal(owner, data->owner) == FALSE)
+                       continue;
+
+               if (path && data->path &&
+                               g_str_equal(path, data->path) == FALSE)
+                       continue;
+
+               if (interface && data->interface &&
+                               g_str_equal(interface, data->interface) == FALSE)
+                       continue;
+
+               if (member && data->member &&
+                               g_str_equal(member, data->member) == FALSE)
+                       continue;
+
+               if (argument && data->argument &&
+                               g_str_equal(argument, data->argument) == FALSE)
+                       continue;
+
+               return data;
+       }
+
+       return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+       const char *sender;
+       int offset;
+
+       offset = snprintf(rule, size, "type='signal'");
+       sender = data->name ? : data->owner;
+
+       if (sender)
+               offset += snprintf(rule + offset, size - offset,
+                               ",sender='%s'", sender);
+       if (data->path)
+               offset += snprintf(rule + offset, size - offset,
+                               ",path='%s'", data->path);
+       if (data->interface)
+               offset += snprintf(rule + offset, size - offset,
+                               ",interface='%s'", data->interface);
+       if (data->member)
+               offset += snprintf(rule + offset, size - offset,
+                               ",member='%s'", data->member);
+       if (data->argument)
+               snprintf(rule + offset, size - offset,
+                               ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+                               DBusHandleMessageFunction filter)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+       dbus_error_init(&err);
+
+       dbus_bus_add_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Adding match rule \"%s\" failed: %s", rule,
+                               err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       data->handle_func = filter;
+       data->registered = TRUE;
+
+       return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+
+       dbus_error_init(&err);
+
+       dbus_bus_remove_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Removing owner match rule for %s failed: %s",
+                               rule, err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+                                       DBusHandleMessageFunction filter,
+                                       const char *sender,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *member,
+                                       const char *argument)
+{
+       struct filter_data *data;
+       const char *name = NULL, *owner = NULL;
+
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+               if (!dbus_connection_add_filter(connection,
+                                       message_filter, NULL, NULL)) {
+                       error("dbus_connection_add_filter() failed");
+                       return NULL;
+               }
+       }
+
+       if (sender == NULL)
+               goto proceed;
+
+       if (sender[0] == ':')
+               owner = sender;
+       else
+               name = sender;
+
+proceed:
+       data = filter_data_find(connection, name, owner, path, interface,
+                                       member, argument);
+       if (data)
+               return data;
+
+       data = g_new0(struct filter_data, 1);
+
+       data->connection = dbus_connection_ref(connection);
+       data->name = name ? g_strdup(name) : NULL;
+       data->owner = owner ? g_strdup(owner) : NULL;
+       data->path = g_strdup(path);
+       data->interface = g_strdup(interface);
+       data->member = g_strdup(member);
+       data->argument = g_strdup(argument);
+
+       if (!add_match(data, filter)) {
+               g_free(data);
+               return NULL;
+       }
+
+       listeners = g_slist_append(listeners, data);
+
+       return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+                                               struct filter_data *data,
+                                               guint id)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+       for (l = data->processed; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(data->callbacks);
+       g_dbus_remove_watch(data->connection, data->name_watch);
+       g_free(data->name);
+       g_free(data->owner);
+       g_free(data->path);
+       g_free(data->interface);
+       g_free(data->member);
+       g_free(data->argument);
+       dbus_connection_unref(data->connection);
+       g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->disc_func)
+                       cb->disc_func(data->connection, cb->user_data);
+               if (cb->destroy_func)
+                       cb->destroy_func(cb->user_data);
+               g_free(cb);
+       }
+
+       filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+                                               struct filter_data *data,
+                                               GDBusWatchFunction connect,
+                                               GDBusWatchFunction disconnect,
+                                               GDBusSignalFunction signal,
+                                               GDBusDestroyFunction destroy,
+                                               void *user_data)
+{
+       struct filter_callback *cb = NULL;
+
+       cb = g_new0(struct filter_callback, 1);
+
+       cb->conn_func = connect;
+       cb->disc_func = disconnect;
+       cb->signal_func = signal;
+       cb->destroy_func = destroy;
+       cb->user_data = user_data;
+       cb->id = ++listener_id;
+
+       if (data->lock)
+               data->processed = g_slist_append(data->processed, cb);
+       else
+               data->callbacks = g_slist_append(data->callbacks, cb);
+
+       return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+       struct filter_callback *callback = data->callback;
+
+       dbus_connection_unref(data->conn);
+
+       if (data->call)
+               dbus_pending_call_unref(data->call);
+
+       if (data->id)
+               g_source_remove(data->id);
+
+       g_free(data->name);
+       g_free(data);
+
+       callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+                                               struct filter_callback *cb)
+{
+       DBusConnection *connection;
+
+       data->callbacks = g_slist_remove(data->callbacks, cb);
+       data->processed = g_slist_remove(data->processed, cb);
+
+       /* Cancel pending operations */
+       if (cb->data) {
+               if (cb->data->call)
+                       dbus_pending_call_cancel(cb->data->call);
+               service_data_free(cb->data);
+       }
+
+       if (cb->destroy_func)
+               cb->destroy_func(cb->user_data);
+
+       g_free(cb);
+
+       /* Don't remove the filter if other callbacks exist or data is lock
+        * processing callbacks */
+       if (data->callbacks || data->lock)
+               return TRUE;
+
+       if (data->registered && !remove_match(data))
+               return FALSE;
+
+       connection = dbus_connection_ref(data->connection);
+       listeners = g_slist_remove(listeners, data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                                               NULL) == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       filter_data_free(data);
+       dbus_connection_unref(connection);
+
+       return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (cb->signal_func && !cb->signal_func(connection, message,
+                                                       cb->user_data)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               g_free(data->owner);
+               data->owner = g_strdup(owner);
+       }
+}
+
+static const char *check_name_cache(const char *name)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               return data->owner;
+       }
+
+       return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+       char *name, *old, *new;
+
+       if (!dbus_message_get_args(message, NULL,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &old,
+                               DBUS_TYPE_STRING, &new,
+                               DBUS_TYPE_INVALID)) {
+               error("Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       update_name_cache(name, new);
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (*new == '\0') {
+                       if (cb->disc_func)
+                               cb->disc_func(connection, cb->user_data);
+               } else {
+                       if (cb->conn_func)
+                               cb->conn_func(connection, cb->user_data);
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               /* Only auto remove if it is a bus name watch */
+               if (data->argument[0] == ':' &&
+                               (cb->conn_func == NULL || cb->disc_func == NULL)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data;
+       const char *sender, *path, *iface, *member, *arg = NULL;
+
+       /* Only filter signals */
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       sender = dbus_message_get_sender(message);
+       path = dbus_message_get_path(message);
+       iface = dbus_message_get_interface(message);
+       member = dbus_message_get_member(message);
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+       /* Sender is always bus name */
+       data = filter_data_find(connection, NULL, sender, path, iface, member,
+                                       arg);
+       if (data == NULL) {
+               error("Got %s.%s signal which has no listeners", iface, member);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (data->handle_func) {
+               data->lock = TRUE;
+
+               data->handle_func(connection, message, data);
+
+               data->callbacks = data->processed;
+               data->processed = NULL;
+               data->lock = FALSE;
+       }
+
+       if (data->callbacks)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       remove_match(data);
+
+       listeners = g_slist_remove(listeners, data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                                               NULL) == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       filter_data_free(data);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+       struct service_data *data = user_data;
+       struct filter_callback *cb = data->callback;
+
+       update_name_cache(data->name, data->owner);
+       if (cb->conn_func)
+               cb->conn_func(data->conn, cb->user_data);
+
+       service_data_free(data);
+
+       return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+       struct service_data *data = user_data;
+       DBusMessage *reply;
+       DBusError err;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply))
+               goto fail;
+
+       if (dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_STRING, &data->owner,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto fail;
+
+       update_service(data);
+
+       goto done;
+
+fail:
+       error("%s", err.message);
+       dbus_error_free(&err);
+       service_data_free(data);
+done:
+       dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+                                       const char *name,
+                                       struct filter_callback *callback)
+{
+       DBusMessage *message;
+       struct service_data *data;
+
+       data = g_try_malloc0(sizeof(*data));
+       if (data == NULL) {
+               error("Can't allocate data structure");
+               return;
+       }
+
+       data->conn = dbus_connection_ref(connection);
+       data->name = g_strdup(name);
+       data->callback = callback;
+       callback->data = data;
+
+       data->owner = check_name_cache(name);
+       if (data->owner != NULL) {
+               data->id = g_idle_add(update_service, data);
+               return;
+       }
+
+       message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+       if (message == NULL) {
+               error("Can't allocate new message");
+               g_free(data);
+               return;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                                       &data->call, -1) == FALSE) {
+               error("Failed to execute method call");
+               g_free(data);
+               goto done;
+       }
+
+       if (data->call == NULL) {
+               error("D-Bus connection not available");
+               g_free(data);
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       if (name == NULL)
+               return 0;
+
+       data = filter_data_get(connection, service_filter, NULL, NULL,
+                               DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+                               name);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (connect)
+               check_service(connection, name, cb);
+
+       return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction func,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       return g_dbus_add_service_watch(connection, name, NULL, func,
+                                                       user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       data = filter_data_get(connection, signal_filter, sender, path,
+                               interface, member, NULL);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (data->name != NULL && data->name_watch == 0)
+               data->name_watch = g_dbus_add_service_watch(connection,
+                                                       data->name, NULL,
+                                                       NULL, NULL, NULL);
+
+       return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+       GSList *ldata;
+
+       if (id == 0)
+               return FALSE;
+
+       for (ldata = listeners; ldata; ldata = ldata->next) {
+               data = ldata->data;
+
+               cb = filter_data_find_callback(data, id);
+               if (cb) {
+                       filter_data_remove_callback(data, cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+       struct filter_data *data;
+
+       while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+                                       NULL, NULL))) {
+               listeners = g_slist_remove(listeners, data);
+               filter_data_call_and_free(data);
+       }
+
+       dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/gdhcp/client.c b/gdhcp/client.c
new file mode 100644 (file)
index 0000000..5331c77
--- /dev/null
@@ -0,0 +1,2689 @@
+/*
+ *
+ *  DHCP client library with GLib integration
+ *
+ *  Copyright (C) 2009-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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <netpacket/packet.h>
+#include <netinet/if_ether.h>
+#include <net/ethernet.h>
+
+#include <linux/if.h>
+#include <linux/filter.h>
+
+#include <glib.h>
+
+#include "gdhcp.h"
+#include "common.h"
+#include "ipv4ll.h"
+
+#define DISCOVER_TIMEOUT 3
+#define DISCOVER_RETRIES 10
+
+#define REQUEST_TIMEOUT 3
+#define REQUEST_RETRIES 5
+
+typedef enum _listen_mode {
+       L_NONE,
+       L2,
+       L3,
+       L_ARP,
+} ListenMode;
+
+typedef enum _dhcp_client_state {
+       INIT_SELECTING,
+       REQUESTING,
+       BOUND,
+       RENEWING,
+       REBINDING,
+       RELEASED,
+       IPV4LL_PROBE,
+       IPV4LL_ANNOUNCE,
+       IPV4LL_MONITOR,
+       IPV4LL_DEFEND,
+       INFORMATION_REQ,
+       SOLICITATION,
+       REQUEST,
+       RENEW,
+       REBIND,
+       RELEASE,
+} ClientState;
+
+struct _GDHCPClient {
+       int ref_count;
+       GDHCPType type;
+       ClientState state;
+       int ifindex;
+       char *interface;
+       uint8_t mac_address[6];
+       uint32_t xid;
+       uint32_t server_ip;
+       uint32_t requested_ip;
+       char *assigned_ip;
+       time_t start;
+       uint32_t lease_seconds;
+       ListenMode listen_mode;
+       int listener_sockfd;
+       uint8_t retry_times;
+       uint8_t ack_retry_times;
+       uint8_t conflicts;
+       guint timeout;
+       guint listener_watch;
+       GIOChannel *listener_channel;
+       GList *require_list;
+       GList *request_list;
+       GHashTable *code_value_hash;
+       GHashTable *send_value_hash;
+       GDHCPClientEventFunc lease_available_cb;
+       gpointer lease_available_data;
+       GDHCPClientEventFunc ipv4ll_available_cb;
+       gpointer ipv4ll_available_data;
+       GDHCPClientEventFunc no_lease_cb;
+       gpointer no_lease_data;
+       GDHCPClientEventFunc lease_lost_cb;
+       gpointer lease_lost_data;
+       GDHCPClientEventFunc ipv4ll_lost_cb;
+       gpointer ipv4ll_lost_data;
+       GDHCPClientEventFunc address_conflict_cb;
+       gpointer address_conflict_data;
+       GDHCPDebugFunc debug_func;
+       gpointer debug_data;
+       GDHCPClientEventFunc information_req_cb;
+       gpointer information_req_data;
+       GDHCPClientEventFunc solicitation_cb;
+       gpointer solicitation_data;
+       GDHCPClientEventFunc advertise_cb;
+       gpointer advertise_data;
+       GDHCPClientEventFunc request_cb;
+       gpointer request_data;
+       GDHCPClientEventFunc renew_cb;
+       gpointer renew_data;
+       GDHCPClientEventFunc rebind_cb;
+       gpointer rebind_data;
+       GDHCPClientEventFunc release_cb;
+       gpointer release_data;
+       char *last_address;
+       unsigned char *duid;
+       int duid_len;
+       unsigned char *server_duid;
+       int server_duid_len;
+       uint16_t status_code;
+       uint32_t iaid;
+       uint32_t T1, T2;
+       struct in6_addr ia_na;
+       struct in6_addr ia_ta;
+       time_t last_renew;
+       time_t last_rebind;
+       time_t expire;
+#if defined TIZEN_EXT
+       gboolean init_reboot;
+#endif
+};
+
+static inline void debug(GDHCPClient *client, const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (client->debug_func == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               client->debug_func(str, client->debug_data);
+
+       va_end(ap);
+}
+
+/* Initialize the packet with the proper defaults */
+static void init_packet(GDHCPClient *dhcp_client, gpointer pkt, char type)
+{
+       if (dhcp_client->type == G_DHCP_IPV6)
+               dhcpv6_init_header(pkt, type);
+       else {
+               struct dhcp_packet *packet = pkt;
+
+               dhcp_init_header(packet, type);
+               memcpy(packet->chaddr, dhcp_client->mac_address, 6);
+       }
+}
+
+static void add_request_options(GDHCPClient *dhcp_client,
+                               struct dhcp_packet *packet)
+{
+       int len = 0;
+       GList *list;
+       uint8_t code;
+       int end = dhcp_end_option(packet->options);
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint8_t) GPOINTER_TO_INT(list->data);
+
+               packet->options[end + OPT_DATA + len] = code;
+               len++;
+       }
+
+       if (len) {
+               packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+               packet->options[end + OPT_LEN] = len;
+               packet->options[end + OPT_DATA + len] = DHCP_END;
+       }
+}
+
+struct hash_params {
+       unsigned char *buf;
+       int max_buf;
+       unsigned char **ptr_buf;
+};
+
+static void add_dhcpv6_binary_option(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       uint8_t *option = value;
+       uint16_t len;
+       struct hash_params *params = user_data;
+
+       /* option[0][1] contains option code */
+       len = option[2] << 8 | option[3];
+
+       if ((*params->ptr_buf + len + 2 + 2) > (params->buf + params->max_buf))
+               return;
+
+       memcpy(*params->ptr_buf, option, len + 2 + 2);
+       (*params->ptr_buf) += len + 2 + 2;
+}
+
+static void add_dhcpv6_send_options(GDHCPClient *dhcp_client,
+                               unsigned char *buf, int max_buf,
+                               unsigned char **ptr_buf)
+{
+       struct hash_params params = {
+               .buf = buf,
+               .max_buf = max_buf,
+               .ptr_buf = ptr_buf
+       };
+
+       if (dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       g_hash_table_foreach(dhcp_client->send_value_hash,
+                               add_dhcpv6_binary_option, &params);
+
+       *ptr_buf = *params.ptr_buf;
+}
+
+static void copy_option(uint8_t *buf, uint16_t code, uint16_t len,
+                       uint8_t *msg)
+{
+       buf[0] = code >> 8;
+       buf[1] = code & 0xff;
+       buf[2] = len >> 8;
+       buf[3] = len & 0xff;
+       if (len > 0 && msg != NULL)
+               memcpy(&buf[4], msg, len);
+}
+
+static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
+                               struct dhcpv6_packet *packet,
+                               unsigned char *buf, int max_buf,
+                               unsigned char **ptr_buf)
+{
+       GList *list;
+       uint16_t code;
+       int len;
+
+       if (dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint16_t) GPOINTER_TO_INT(list->data);
+
+               switch (code) {
+               case G_DHCPV6_CLIENTID:
+                       if (dhcp_client->duid == NULL)
+                               return;
+
+                       len = 2 + 2 + dhcp_client->duid_len;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing client id option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_CLIENTID,
+                               dhcp_client->duid_len, dhcp_client->duid);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_SERVERID:
+                       if (dhcp_client->server_duid == NULL)
+                               return;
+
+                       len = 2 + 2 + dhcp_client->server_duid_len;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing server id option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_SERVERID,
+                               dhcp_client->server_duid_len,
+                               dhcp_client->server_duid);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_RAPID_COMMIT:
+                       len = 2 + 2;
+                       if ((*ptr_buf + len) > (buf + max_buf)) {
+                               debug(dhcp_client, "Too long dhcpv6 message "
+                                       "when writing rapid commit option");
+                               return;
+                       }
+
+                       copy_option(*ptr_buf, G_DHCPV6_RAPID_COMMIT, 0, 0);
+                       (*ptr_buf) += len;
+                       break;
+
+               case G_DHCPV6_ORO:
+                       break;
+
+               case G_DHCPV6_DNS_SERVERS:
+                       break;
+
+               case G_DHCPV6_SNTP_SERVERS:
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
+{
+       uint8_t *option = value;
+       struct dhcp_packet *packet = user_data;
+
+       dhcp_add_binary_option(packet, option);
+}
+
+static void add_send_options(GDHCPClient *dhcp_client,
+                               struct dhcp_packet *packet)
+{
+       g_hash_table_foreach(dhcp_client->send_value_hash,
+                               add_binary_option, packet);
+}
+
+/*
+ * Return an RFC 951- and 2131-complaint BOOTP 'secs' value that
+ * represents the number of seconds elapsed from the start of
+ * attempting DHCP to satisfy some DHCP servers that allow for an
+ * "authoritative" reply before responding.
+ */
+static uint16_t dhcp_attempt_secs(GDHCPClient *dhcp_client)
+{
+       return htons(MIN(time(NULL) - dhcp_client->start, UINT16_MAX));
+}
+
+static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
+{
+       struct dhcp_packet packet;
+
+       debug(dhcp_client, "sending DHCP discover request");
+
+       init_packet(dhcp_client, &packet, DHCPDISCOVER);
+
+       packet.xid = dhcp_client->xid;
+       packet.secs = dhcp_attempt_secs(dhcp_client);
+
+       if (requested)
+               dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
+
+       /* Explicitly saying that we want RFC-compliant packets helps
+        * some buggy DHCP servers to NOT send bigger packets */
+       dhcp_add_simple_option(&packet, DHCP_MAX_SIZE, htons(576));
+
+       add_request_options(dhcp_client, &packet);
+
+       add_send_options(dhcp_client, &packet);
+
+       return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+                                       INADDR_BROADCAST, SERVER_PORT,
+                                       MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_select(GDHCPClient *dhcp_client)
+{
+       struct dhcp_packet packet;
+
+       debug(dhcp_client, "sending DHCP select request");
+
+       init_packet(dhcp_client, &packet, DHCPREQUEST);
+
+       packet.xid = dhcp_client->xid;
+       packet.secs = dhcp_attempt_secs(dhcp_client);
+
+       dhcp_add_simple_option(&packet, DHCP_REQUESTED_IP,
+                                       dhcp_client->requested_ip);
+#if defined TIZEN_EXT
+       if (dhcp_client->init_reboot != TRUE)
+#endif
+       dhcp_add_simple_option(&packet, DHCP_SERVER_ID, dhcp_client->server_ip);
+
+       add_request_options(dhcp_client, &packet);
+
+       add_send_options(dhcp_client, &packet);
+
+       return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+                                       INADDR_BROADCAST, SERVER_PORT,
+                                       MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_renew(GDHCPClient *dhcp_client)
+{
+       struct dhcp_packet packet;
+
+       debug(dhcp_client, "sending DHCP renew request");
+
+       init_packet(dhcp_client , &packet, DHCPREQUEST);
+       packet.xid = dhcp_client->xid;
+       packet.ciaddr = dhcp_client->requested_ip;
+
+       add_request_options(dhcp_client, &packet);
+
+       add_send_options(dhcp_client, &packet);
+
+       return dhcp_send_kernel_packet(&packet,
+               dhcp_client->requested_ip, CLIENT_PORT,
+               dhcp_client->server_ip, SERVER_PORT);
+}
+
+static int send_rebound(GDHCPClient *dhcp_client)
+{
+       struct dhcp_packet packet;
+
+       debug(dhcp_client, "sending DHCP rebound request");
+
+       init_packet(dhcp_client , &packet, DHCPREQUEST);
+       packet.xid = dhcp_client->xid;
+       packet.ciaddr = dhcp_client->requested_ip;
+
+       add_request_options(dhcp_client, &packet);
+
+       add_send_options(dhcp_client, &packet);
+
+       return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+                                       INADDR_BROADCAST, SERVER_PORT,
+                                       MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_release(GDHCPClient *dhcp_client,
+                       uint32_t server, uint32_t ciaddr)
+{
+       struct dhcp_packet packet;
+
+       debug(dhcp_client, "sending DHCP release request");
+
+       init_packet(dhcp_client, &packet, DHCPRELEASE);
+       packet.xid = rand();
+       packet.ciaddr = ciaddr;
+
+       dhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
+
+       return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
+                                               server, SERVER_PORT);
+}
+
+static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
+static int switch_listening_mode(GDHCPClient *dhcp_client,
+                                       ListenMode listen_mode);
+
+static gboolean send_probe_packet(gpointer dhcp_data)
+{
+       GDHCPClient *dhcp_client;
+       guint timeout;
+
+       dhcp_client = dhcp_data;
+       /* if requested_ip is not valid, pick a new address*/
+       if (dhcp_client->requested_ip == 0) {
+               debug(dhcp_client, "pick a new random address");
+               dhcp_client->requested_ip = ipv4ll_random_ip(0);
+       }
+
+       debug(dhcp_client, "sending IPV4LL probe request");
+
+       if (dhcp_client->retry_times == 1) {
+               dhcp_client->state = IPV4LL_PROBE;
+               switch_listening_mode(dhcp_client, L_ARP);
+       }
+       ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
+                       dhcp_client->requested_ip, dhcp_client->ifindex);
+
+       if (dhcp_client->retry_times < PROBE_NUM) {
+               /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
+               timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
+               timeout += PROBE_MIN*1000;
+       } else
+               timeout = (ANNOUNCE_WAIT * 1000);
+
+       dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
+                                                timeout,
+                                                ipv4ll_probe_timeout,
+                                                dhcp_client,
+                                                NULL);
+       return FALSE;
+}
+
+static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
+static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
+
+static gboolean send_announce_packet(gpointer dhcp_data)
+{
+       GDHCPClient *dhcp_client;
+
+       dhcp_client = dhcp_data;
+
+       debug(dhcp_client, "sending IPV4LL announce request");
+
+       ipv4ll_send_arp_packet(dhcp_client->mac_address,
+                               dhcp_client->requested_ip,
+                               dhcp_client->requested_ip,
+                               dhcp_client->ifindex);
+
+       if (dhcp_client->timeout > 0)
+               g_source_remove(dhcp_client->timeout);
+       dhcp_client->timeout = 0;
+
+       if (dhcp_client->state == IPV4LL_DEFEND) {
+               dhcp_client->timeout =
+                       g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                               DEFEND_INTERVAL,
+                                               ipv4ll_defend_timeout,
+                                               dhcp_client,
+                                               NULL);
+               return TRUE;
+       } else
+               dhcp_client->timeout =
+                       g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                               ANNOUNCE_INTERVAL,
+                                               ipv4ll_announce_timeout,
+                                               dhcp_client,
+                                               NULL);
+       return TRUE;
+}
+
+static void get_interface_mac_address(int index, uint8_t *mac_address)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               perror("Open socket error");
+               return;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               perror("Get interface name error");
+               goto done;
+       }
+
+       err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+       if (err < 0) {
+               perror("Get mac address error");
+               goto done;
+       }
+
+       memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
+
+done:
+       close(sk);
+}
+
+int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
+                       unsigned char **duid, int *duid_len)
+{
+       time_t duid_time;
+
+       switch (duid_type) {
+       case G_DHCPV6_DUID_LLT:
+               *duid_len = 2 + 2 + 4 + ETH_ALEN;
+               *duid = g_try_malloc(*duid_len);
+               if (*duid == NULL)
+                       return -ENOMEM;
+
+               (*duid)[0] = 0;
+               (*duid)[1] = 1;
+               get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
+               (*duid)[2] = 0;
+               (*duid)[3] = type;
+               duid_time = time(NULL) - DUID_TIME_EPOCH;
+               (*duid)[4] = duid_time >> 24;
+               (*duid)[5] = duid_time >> 16;
+               (*duid)[6] = duid_time >> 8;
+               (*duid)[7] = duid_time & 0xff;
+               break;
+       case G_DHCPV6_DUID_EN:
+               return -EINVAL;
+       case G_DHCPV6_DUID_LL:
+               *duid_len = 2 + 2 + ETH_ALEN;
+               *duid = g_try_malloc(*duid_len);
+               if (*duid == NULL)
+                       return -ENOMEM;
+
+               (*duid)[0] = 0;
+               (*duid)[1] = 3;
+               get_interface_mac_address(index, &(*duid)[2 + 2]);
+               (*duid)[2] = 0;
+               (*duid)[3] = type;
+               break;
+       }
+
+       return 0;
+}
+
+int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
+                       int duid_len)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return -EINVAL;
+
+       g_free(dhcp_client->duid);
+
+       dhcp_client->duid = duid;
+       dhcp_client->duid_len = duid_len;
+
+       return 0;
+}
+
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return 0;
+
+       return dhcp_client->iaid;
+}
+
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+                               unsigned char *iaid)
+{
+       uint8_t buf[6];
+
+       get_interface_mac_address(index, buf);
+
+       memcpy(iaid, &buf[2], 4);
+       dhcp_client->iaid = iaid[0] << 24 |
+                       iaid[1] << 16 | iaid[2] << 8 | iaid[3];
+}
+
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+                               uint32_t *T1, uint32_t *T2,
+                               time_t *last_renew, time_t *last_rebind,
+                               time_t *expire)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return -EINVAL;
+
+       if (T1 != NULL)
+               *T1 = dhcp_client->T1;
+
+       if (T2 != NULL)
+               *T2 = dhcp_client->T2;
+
+       if (last_renew != NULL)
+               *last_renew = dhcp_client->last_renew;
+
+       if (last_rebind != NULL)
+               *last_rebind = dhcp_client->last_rebind;
+
+       if (expire != NULL)
+               *expire = dhcp_client->expire;
+
+       return 0;
+}
+
+static uint8_t *create_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
+                               uint16_t len)
+{
+       buf[0] = 0;
+       buf[1] = G_DHCPV6_IAADDR;
+       buf[2] = 0;
+       buf[3] = len;
+       memcpy(&buf[4], &dhcp_client->ia_na, 16);
+       memset(&buf[20], 0, 4); /* preferred */
+       memset(&buf[24], 0, 4); /* valid */
+       return buf;
+}
+
+static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
+{
+       uint32_t iaid;
+
+       iaid = g_dhcpv6_client_get_iaid(dhcp_client);
+       if (iaid == 0) {
+               g_dhcpv6_client_create_iaid(dhcp_client, index, buf);
+               return;
+       }
+
+       buf[0] = iaid >> 24;
+       buf[1] = iaid >> 16;
+       buf[2] = iaid >> 8;
+       buf[3] = iaid;
+}
+
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+                       int code, uint32_t *T1, uint32_t *T2,
+                       gboolean add_iaaddr)
+{
+       if (code == G_DHCPV6_IA_TA) {
+               uint8_t ia_options[4];
+
+               put_iaid(dhcp_client, index, ia_options);
+
+               g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_TA);
+               g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_TA,
+                                       ia_options, sizeof(ia_options));
+
+       } else if (code == G_DHCPV6_IA_NA) {
+
+               g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
+
+               if (add_iaaddr == TRUE) {
+#define IAADDR_LEN (16+4+4)
+                       uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
+
+                       put_iaid(dhcp_client, index, ia_options);
+
+                       if (T1 != NULL) {
+                               ia_options[4] = *T1 >> 24;
+                               ia_options[5] = *T1 >> 16;
+                               ia_options[6] = *T1 >> 8;
+                               ia_options[7] = *T1;
+                       } else
+                               memset(&ia_options[4], 0x00, 4);
+
+                       if (T2 != NULL) {
+                               ia_options[8] = *T2 >> 24;
+                               ia_options[9] = *T2 >> 16;
+                               ia_options[10] = *T2 >> 8;
+                               ia_options[11] = *T2;
+                       } else
+                               memset(&ia_options[8], 0x00, 4);
+
+                       create_iaaddr(dhcp_client, &ia_options[12],
+                                       IAADDR_LEN);
+
+                       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+                                       ia_options, sizeof(ia_options));
+               } else {
+                       uint8_t ia_options[4+4+4];
+
+                       put_iaid(dhcp_client, index, ia_options);
+
+                       memset(&ia_options[4], 0x00, 4); /* T1 (4 bytes) */
+                       memset(&ia_options[8], 0x00, 4); /* T2 (4 bytes) */
+
+                       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+                                       ia_options, sizeof(ia_options));
+               }
+
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...)
+{
+       va_list va;
+       int i, j, len = sizeof(uint16_t) * args;
+       uint8_t *values;
+
+       values = g_try_malloc(len);
+       if (values == NULL)
+               return -ENOMEM;
+
+       va_start(va, args);
+       for (i = 0, j = 0; i < args; i++) {
+               uint16_t value = va_arg(va, int);
+               values[j++] = value >> 8;
+               values[j++] = value & 0xff;
+       }
+       va_end(va);
+
+       g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_ORO, values, len);
+
+       return 0;
+}
+
+static int send_dhcpv6_msg(GDHCPClient *dhcp_client, int type, char *msg)
+{
+       struct dhcpv6_packet *packet;
+       uint8_t buf[MAX_DHCPV6_PKT_SIZE];
+       unsigned char *ptr;
+       int ret, max_buf;
+
+       memset(buf, 0, sizeof(buf));
+       packet = (struct dhcpv6_packet *)&buf[0];
+       ptr = buf + sizeof(struct dhcpv6_packet);
+
+       debug(dhcp_client, "sending DHCPv6 %s message", msg);
+
+       init_packet(dhcp_client, packet, type);
+
+       dhcp_client->xid = packet->transaction_id[0] << 16 |
+                       packet->transaction_id[1] << 8 |
+                       packet->transaction_id[2];
+
+       max_buf = MAX_DHCPV6_PKT_SIZE - sizeof(struct dhcpv6_packet);
+
+       add_dhcpv6_request_options(dhcp_client, packet, buf, max_buf, &ptr);
+
+       add_dhcpv6_send_options(dhcp_client, buf, max_buf, &ptr);
+
+       ret = dhcpv6_send_packet(dhcp_client->ifindex, packet, ptr - buf);
+
+       debug(dhcp_client, "sent %d pkt %p len %d", ret, packet, ptr - buf);
+       return ret;
+}
+
+static int send_solicitation(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit");
+}
+
+static int send_dhcpv6_request(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
+}
+
+static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
+}
+
+static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
+}
+
+static int send_dhcpv6_release(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release");
+}
+
+static int send_information_req(GDHCPClient *dhcp_client)
+{
+       return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
+                               "information-req");
+}
+
+static void remove_value(gpointer data, gpointer user_data)
+{
+       char *value = data;
+       g_free(value);
+}
+
+static void remove_option_value(gpointer data)
+{
+       GList *option_value = data;
+
+       g_list_foreach(option_value, remove_value, NULL);
+}
+
+GDHCPClient *g_dhcp_client_new(GDHCPType type,
+                       int ifindex, GDHCPClientError *error)
+{
+       GDHCPClient *dhcp_client;
+
+       if (ifindex < 0) {
+               *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
+               return NULL;
+       }
+
+       dhcp_client = g_try_new0(GDHCPClient, 1);
+       if (dhcp_client == NULL) {
+               *error = G_DHCP_CLIENT_ERROR_NOMEM;
+               return NULL;
+       }
+
+       dhcp_client->interface = get_interface_name(ifindex);
+       if (dhcp_client->interface == NULL) {
+               *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
+               goto error;
+       }
+
+       if (interface_is_up(ifindex) == FALSE) {
+               *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
+               goto error;
+       }
+
+       get_interface_mac_address(ifindex, dhcp_client->mac_address);
+
+       dhcp_client->listener_sockfd = -1;
+       dhcp_client->listener_channel = NULL;
+       dhcp_client->listen_mode = L_NONE;
+       dhcp_client->ref_count = 1;
+       dhcp_client->type = type;
+       dhcp_client->ifindex = ifindex;
+       dhcp_client->lease_available_cb = NULL;
+       dhcp_client->ipv4ll_available_cb = NULL;
+       dhcp_client->no_lease_cb = NULL;
+       dhcp_client->lease_lost_cb = NULL;
+       dhcp_client->ipv4ll_lost_cb = NULL;
+       dhcp_client->address_conflict_cb = NULL;
+       dhcp_client->listener_watch = 0;
+       dhcp_client->retry_times = 0;
+       dhcp_client->ack_retry_times = 0;
+       dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
+                               g_direct_equal, NULL, remove_option_value);
+       dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
+                               g_direct_equal, NULL, g_free);
+       dhcp_client->request_list = NULL;
+       dhcp_client->require_list = NULL;
+       dhcp_client->duid = NULL;
+       dhcp_client->duid_len = 0;
+       dhcp_client->last_renew = dhcp_client->last_rebind = time(NULL);
+       dhcp_client->expire = 0;
+
+       *error = G_DHCP_CLIENT_ERROR_NONE;
+
+       return dhcp_client;
+
+error:
+       g_free(dhcp_client->interface);
+       g_free(dhcp_client);
+       return NULL;
+}
+
+#define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
+
+static int dhcp_l2_socket(int ifindex)
+{
+       int fd;
+       struct sockaddr_ll sock;
+
+       /*
+        * Comment:
+        *
+        *      I've selected not to see LL header, so BPF doesn't see it, too.
+        *      The filter may also pass non-IP and non-ARP packets, but we do
+        *      a more complete check when receiving the message in userspace.
+        *
+        * and filter shamelessly stolen from:
+        *
+        *      http://www.flamewarmaster.de/software/dhcpclient/
+        *
+        * There are a few other interesting ideas on that page (look under
+        * "Motivation").  Use of netlink events is most interesting.  Think
+        * of various network servers listening for events and reconfiguring.
+        * That would obsolete sending HUP signals and/or make use of restarts.
+        *
+        * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
+        * License: GPL v2.
+        *
+        * TODO: make conditional?
+        */
+       static const struct sock_filter filter_instr[] = {
+               /* check for udp */
+               BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+               /* L5, L1, is UDP? */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
+               /* ugly check for arp on ethernet-like and IPv4 */
+               BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
+               /* skip IP header */
+               BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
+               /* check udp source and destination ports */
+               BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
+               /* L3, L4 */
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
+               /* returns */
+               BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
+               BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
+       };
+
+       static const struct sock_fprog filter_prog = {
+               .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+               /* casting const away: */
+               .filter = (struct sock_filter *) filter_instr,
+       };
+
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
+       if (fd < 0)
+               return fd;
+
+       if (SERVER_PORT == 67 && CLIENT_PORT == 68)
+               /* Use only if standard ports are in use */
+               setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
+                                                       sizeof(filter_prog));
+
+       memset(&sock, 0, sizeof(sock));
+       sock.sll_family = AF_PACKET;
+       sock.sll_protocol = htons(ETH_P_IP);
+       sock.sll_ifindex = ifindex;
+
+       if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
+               close(fd);
+               return -errno;
+       }
+
+       return fd;
+}
+
+static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
+{
+       if (packet->ip.protocol != IPPROTO_UDP)
+               return FALSE;
+
+       if (packet->ip.version != IPVERSION)
+               return FALSE;
+
+       if (packet->ip.ihl != sizeof(packet->ip) >> 2)
+               return FALSE;
+
+       if (packet->udp.dest != htons(CLIENT_PORT))
+               return FALSE;
+
+       if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
+               return FALSE;
+
+       return TRUE;
+}
+
+static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
+{
+       int bytes;
+       struct ip_udp_dhcp_packet packet;
+       uint16_t check;
+
+       memset(&packet, 0, sizeof(packet));
+
+       bytes = read(fd, &packet, sizeof(packet));
+       if (bytes < 0)
+               return -1;
+
+       if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
+               return -1;
+
+       if (bytes < ntohs(packet.ip.tot_len))
+               /* packet is bigger than sizeof(packet), we did partial read */
+               return -1;
+
+       /* ignore any extra garbage bytes */
+       bytes = ntohs(packet.ip.tot_len);
+
+       if (sanity_check(&packet, bytes) == FALSE)
+               return -1;
+
+       check = packet.ip.check;
+       packet.ip.check = 0;
+       if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
+               return -1;
+
+       /* verify UDP checksum. IP header has to be modified for this */
+       memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
+       /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
+       packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
+       check = packet.udp.check;
+       packet.udp.check = 0;
+       if (check && check != dhcp_checksum(&packet, bytes))
+               return -1;
+
+       memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
+                                                       sizeof(packet.udp)));
+
+       if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
+               return -1;
+
+       return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+}
+
+static void ipv4ll_start(GDHCPClient *dhcp_client)
+{
+       guint timeout;
+       int seed;
+
+       if (dhcp_client->timeout > 0) {
+               g_source_remove(dhcp_client->timeout);
+               dhcp_client->timeout = 0;
+       }
+
+       switch_listening_mode(dhcp_client, L_NONE);
+       dhcp_client->type = G_DHCP_IPV4LL;
+       dhcp_client->retry_times = 0;
+       dhcp_client->requested_ip = 0;
+
+       /*try to start with a based mac address ip*/
+       seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
+       dhcp_client->requested_ip = ipv4ll_random_ip(seed);
+
+       /*first wait a random delay to avoid storm of arp request on boot*/
+       timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
+
+       dhcp_client->retry_times++;
+       dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
+                                               timeout,
+                                               send_probe_packet,
+                                               dhcp_client,
+                                               NULL);
+}
+
+static void ipv4ll_stop(GDHCPClient *dhcp_client)
+{
+
+       switch_listening_mode(dhcp_client, L_NONE);
+
+       if (dhcp_client->timeout > 0)
+               g_source_remove(dhcp_client->timeout);
+
+       if (dhcp_client->listener_watch > 0) {
+               g_source_remove(dhcp_client->listener_watch);
+               dhcp_client->listener_watch = 0;
+       }
+
+       dhcp_client->state = IPV4LL_PROBE;
+       dhcp_client->retry_times = 0;
+       dhcp_client->requested_ip = 0;
+
+       g_free(dhcp_client->assigned_ip);
+       dhcp_client->assigned_ip = NULL;
+}
+
+static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
+{
+       int bytes;
+       struct ether_arp arp;
+       uint32_t ip_requested;
+       int source_conflict;
+       int target_conflict;
+
+       memset(&arp, 0, sizeof(arp));
+       bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
+       if (bytes < 0)
+               return bytes;
+
+       if (arp.arp_op != htons(ARPOP_REPLY) &&
+                       arp.arp_op != htons(ARPOP_REQUEST))
+               return -EINVAL;
+
+       ip_requested = ntohl(dhcp_client->requested_ip);
+       source_conflict = !memcmp(arp.arp_spa, &ip_requested,
+                                               sizeof(ip_requested));
+
+       target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
+                               sizeof(ip_requested));
+
+       if (!source_conflict && !target_conflict)
+               return 0;
+
+       dhcp_client->conflicts++;
+
+       debug(dhcp_client, "IPV4LL conflict detected");
+
+       if (dhcp_client->state == IPV4LL_MONITOR) {
+               if (!source_conflict)
+                       return 0;
+               dhcp_client->state = IPV4LL_DEFEND;
+               debug(dhcp_client, "DEFEND mode conflicts : %d",
+                       dhcp_client->conflicts);
+               /*Try to defend with a single announce*/
+               send_announce_packet(dhcp_client);
+               return 0;
+       }
+
+       if (dhcp_client->state == IPV4LL_DEFEND) {
+               if (!source_conflict)
+                       return 0;
+               else if (dhcp_client->ipv4ll_lost_cb != NULL)
+                       dhcp_client->ipv4ll_lost_cb(dhcp_client,
+                                               dhcp_client->ipv4ll_lost_data);
+       }
+
+       ipv4ll_stop(dhcp_client);
+
+       if (dhcp_client->conflicts < MAX_CONFLICTS) {
+               /*restart whole state machine*/
+               dhcp_client->retry_times++;
+               dhcp_client->timeout =
+                       g_timeout_add_full(G_PRIORITY_HIGH,
+                                       ipv4ll_random_delay_ms(PROBE_WAIT),
+                                       send_probe_packet,
+                                       dhcp_client,
+                                       NULL);
+       }
+       /* Here we got a lot of conflicts, RFC3927 states that we have
+        * to wait RATE_LIMIT_INTERVAL before retrying,
+        * but we just report failure.
+        */
+       else if (dhcp_client->no_lease_cb != NULL)
+                       dhcp_client->no_lease_cb(dhcp_client,
+                                               dhcp_client->no_lease_data);
+
+       return 0;
+}
+
+static gboolean check_package_owner(GDHCPClient *dhcp_client, gpointer pkt)
+{
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               struct dhcpv6_packet *packet6 = pkt;
+               uint32_t xid;
+
+               if (packet6 == NULL)
+                       return FALSE;
+
+               xid = packet6->transaction_id[0] << 16 |
+                       packet6->transaction_id[1] << 8 |
+                       packet6->transaction_id[2];
+
+               if (xid != dhcp_client->xid)
+                       return FALSE;
+       } else {
+               struct dhcp_packet *packet = pkt;
+
+               if (packet->xid != dhcp_client->xid)
+                       return FALSE;
+
+               if (packet->hlen != 6)
+                       return FALSE;
+
+               if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void start_request(GDHCPClient *dhcp_client);
+
+static gboolean request_timeout(gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+
+       debug(dhcp_client, "request timeout (retries %d)",
+                                       dhcp_client->retry_times);
+
+       dhcp_client->retry_times++;
+
+       start_request(dhcp_client);
+
+       return FALSE;
+}
+
+static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data);
+
+static int switch_listening_mode(GDHCPClient *dhcp_client,
+                                       ListenMode listen_mode)
+{
+       GIOChannel *listener_channel;
+       int listener_sockfd;
+
+       if (dhcp_client->listen_mode == listen_mode)
+               return 0;
+
+       debug(dhcp_client, "switch listening mode (%d ==> %d)",
+                               dhcp_client->listen_mode, listen_mode);
+
+       if (dhcp_client->listen_mode != L_NONE) {
+               if (dhcp_client->listener_watch > 0)
+                       g_source_remove(dhcp_client->listener_watch);
+               dhcp_client->listener_channel = NULL;
+               dhcp_client->listen_mode = L_NONE;
+               dhcp_client->listener_sockfd = -1;
+               dhcp_client->listener_watch = 0;
+       }
+
+       if (listen_mode == L_NONE)
+               return 0;
+
+       if (listen_mode == L2)
+               listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
+       else if (listen_mode == L3) {
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       listener_sockfd = dhcp_l3_socket(DHCPV6_CLIENT_PORT,
+                                                       dhcp_client->interface,
+                                                       AF_INET6);
+               else
+                       listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
+                                                       dhcp_client->interface,
+                                                       AF_INET);
+       } else if (listen_mode == L_ARP)
+               listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
+       else
+               return -EIO;
+
+       if (listener_sockfd < 0)
+               return -EIO;
+
+       listener_channel = g_io_channel_unix_new(listener_sockfd);
+       if (listener_channel == NULL) {
+               /* Failed to create listener channel */
+               close(listener_sockfd);
+               return -EIO;
+       }
+
+       dhcp_client->listen_mode = listen_mode;
+       dhcp_client->listener_sockfd = listener_sockfd;
+       dhcp_client->listener_channel = listener_channel;
+
+       g_io_channel_set_close_on_unref(listener_channel, TRUE);
+       dhcp_client->listener_watch =
+                       g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
+                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               listener_event, dhcp_client,
+                                                               NULL);
+       g_io_channel_unref(dhcp_client->listener_channel);
+
+       return 0;
+}
+
+static void start_request(GDHCPClient *dhcp_client)
+{
+       debug(dhcp_client, "start request (retries %d)",
+                                       dhcp_client->retry_times);
+
+       if (dhcp_client->retry_times == REQUEST_RETRIES) {
+               dhcp_client->state = INIT_SELECTING;
+               ipv4ll_start(dhcp_client);
+
+               return;
+       }
+
+       if (dhcp_client->retry_times == 0) {
+               dhcp_client->state = REQUESTING;
+               switch_listening_mode(dhcp_client, L2);
+       }
+
+       send_select(dhcp_client);
+
+       dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                                       REQUEST_TIMEOUT,
+                                                       request_timeout,
+                                                       dhcp_client,
+                                                       NULL);
+}
+
+static uint32_t get_lease(struct dhcp_packet *packet)
+{
+       uint8_t *option_u8;
+       uint32_t lease_seconds;
+
+       option_u8 = dhcp_get_option(packet, DHCP_LEASE_TIME);
+       if (option_u8 == NULL)
+               return 3600;
+
+       lease_seconds = dhcp_get_unaligned((uint32_t *) option_u8);
+       lease_seconds = ntohl(lease_seconds);
+       /* paranoia: must not be prone to overflows */
+       lease_seconds &= 0x0fffffff;
+       if (lease_seconds < 10)
+               lease_seconds = 10;
+
+       return lease_seconds;
+}
+
+static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
+{
+       debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
+
+       if (dhcp_client->timeout > 0) {
+               g_source_remove(dhcp_client->timeout);
+               dhcp_client->timeout = 0;
+       }
+
+       dhcp_client->retry_times = retry_times;
+       dhcp_client->requested_ip = 0;
+       switch_listening_mode(dhcp_client, L2);
+
+       g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
+}
+
+static gboolean start_rebound_timeout(gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+
+       debug(dhcp_client, "start rebound timeout");
+
+       switch_listening_mode(dhcp_client, L2);
+
+       dhcp_client->lease_seconds >>= 1;
+
+       /* We need to have enough time to receive ACK package*/
+       if (dhcp_client->lease_seconds <= 6) {
+
+               /* ip need to be cleared */
+               if (dhcp_client->lease_lost_cb != NULL)
+                       dhcp_client->lease_lost_cb(dhcp_client,
+                                       dhcp_client->lease_lost_data);
+
+               restart_dhcp(dhcp_client, 0);
+       } else {
+               send_rebound(dhcp_client);
+
+               dhcp_client->timeout =
+                               g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                               dhcp_client->lease_seconds >> 1,
+                                                       start_rebound_timeout,
+                                                               dhcp_client,
+                                                               NULL);
+       }
+
+       return FALSE;
+}
+
+static void start_rebound(GDHCPClient *dhcp_client)
+{
+       debug(dhcp_client, "start rebound");
+
+       dhcp_client->state = REBINDING;
+
+       dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                               dhcp_client->lease_seconds >> 1,
+                                                       start_rebound_timeout,
+                                                               dhcp_client,
+                                                               NULL);
+}
+
+static gboolean start_renew_timeout(gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+
+       debug(dhcp_client, "start renew timeout");
+
+       dhcp_client->state = RENEWING;
+
+       dhcp_client->lease_seconds >>= 1;
+
+       switch_listening_mode(dhcp_client, L3);
+       if (dhcp_client->lease_seconds <= 60)
+               start_rebound(dhcp_client);
+       else {
+               send_renew(dhcp_client);
+
+               if (dhcp_client->timeout > 0)
+                       g_source_remove(dhcp_client->timeout);
+
+               dhcp_client->timeout =
+                               g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                               dhcp_client->lease_seconds >> 1,
+                                                       start_renew_timeout,
+                                                               dhcp_client,
+                                                               NULL);
+       }
+
+       return FALSE;
+}
+
+static void start_bound(GDHCPClient *dhcp_client)
+{
+       debug(dhcp_client, "start bound");
+
+       dhcp_client->state = BOUND;
+
+       if (dhcp_client->timeout > 0)
+               g_source_remove(dhcp_client->timeout);
+
+       dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                       dhcp_client->lease_seconds >> 1,
+                                       start_renew_timeout, dhcp_client,
+                                                       NULL);
+}
+
+static gboolean restart_dhcp_timeout(gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+
+       debug(dhcp_client, "restart DHCP timeout");
+
+       dhcp_client->ack_retry_times++;
+
+       restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
+
+       return FALSE;
+}
+
+static char *get_ip(uint32_t ip)
+{
+       struct in_addr addr;
+
+       addr.s_addr = ip;
+
+       return g_strdup(inet_ntoa(addr));
+}
+
+/* get a rough idea of how long an option will be */
+static const uint8_t len_of_option_as_string[] = {
+       [OPTION_IP] = sizeof("255.255.255.255 "),
+       [OPTION_STRING] = 1,
+       [OPTION_U8] = sizeof("255 "),
+       [OPTION_U16] = sizeof("65535 "),
+       [OPTION_U32] = sizeof("4294967295 "),
+};
+
+static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
+{
+       return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
+}
+
+/* Create "opt_value1 option_value2 ..." string */
+static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
+{
+       unsigned upper_length;
+       int len, optlen;
+       char *dest, *ret;
+
+       len = option[OPT_LEN - OPT_DATA];
+       type &= OPTION_TYPE_MASK;
+       optlen = dhcp_option_lengths[type];
+       if (optlen == 0)
+               return NULL;
+       upper_length = len_of_option_as_string[type] *
+                       ((unsigned)len / (unsigned)optlen);
+       dest = ret = malloc(upper_length + 1);
+       if (ret == NULL)
+               return NULL;
+
+       while (len >= optlen) {
+               switch (type) {
+               case OPTION_IP:
+                       dest += sprint_nip(dest, "", option);
+                       break;
+               case OPTION_U16: {
+                       uint16_t val_u16 = dhcp_get_unaligned(
+                                               (uint16_t *) option);
+                       dest += sprintf(dest, "%u", ntohs(val_u16));
+                       break;
+               }
+               case OPTION_U32: {
+                       uint32_t val_u32 = dhcp_get_unaligned(
+                                               (uint32_t *) option);
+                       dest += sprintf(dest, type == OPTION_U32 ? "%lu" :
+                                       "%ld", (unsigned long) ntohl(val_u32));
+                       break;
+               }
+               case OPTION_STRING:
+                       memcpy(dest, option, len);
+                       dest[len] = '\0';
+                       return ret;
+               default:
+                       break;
+               }
+               option += optlen;
+               len -= optlen;
+               if (len <= 0)
+                       break;
+               *dest++ = ' ';
+               *dest = '\0';
+       }
+
+       return ret;
+}
+
+static GList *get_option_value_list(char *value, GDHCPOptionType type)
+{
+       char *pos = value;
+       GList *list = NULL;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (type == OPTION_STRING)
+               return g_list_append(list, g_strdup(value));
+
+       while ((pos = strchr(pos, ' ')) != NULL) {
+               *pos = '\0';
+
+               list = g_list_append(list, g_strdup(value));
+
+               value = ++pos;
+       }
+
+       list = g_list_append(list, g_strdup(value));
+
+       return list;
+}
+
+static inline uint32_t get_uint32(unsigned char *value)
+{
+       return value[0] << 24 | value[1] << 16 |
+               value[2] << 8 | value[3];
+}
+
+static inline uint16_t get_uint16(unsigned char *value)
+{
+       return value[0] << 8 | value[1];
+}
+
+static GList *get_addresses(GDHCPClient *dhcp_client,
+                               int code, int len,
+                               unsigned char *value,
+                               uint16_t *status)
+{
+       GList *list = NULL;
+       struct in6_addr addr;
+       uint32_t iaid, T1 = 0, T2 = 0, preferred = 0, valid = 0;
+       uint16_t option_len, option_code, st = 0, max_len;
+       int addr_count = 0, i, pos;
+       uint8_t *option;
+       char *str;
+
+       if (value == NULL || len < 4)
+               return NULL;
+
+       iaid = get_uint32(&value[0]);
+       if (dhcp_client->iaid != iaid)
+               return NULL;
+
+       if (code == G_DHCPV6_IA_NA) {
+               T1 = get_uint32(&value[4]);
+               T2 = get_uint32(&value[8]);
+
+               if (T1 > T2)
+                       /* RFC 3315, 22.4 */
+                       return NULL;
+
+               pos = 12;
+       } else
+               pos = 4;
+
+       if (len <= pos)
+               return NULL;
+
+       max_len = len - pos;
+
+       /* We have more sub-options in this packet. */
+       do {
+               option = dhcpv6_get_sub_option(&value[pos], max_len,
+                                       &option_code, &option_len);
+
+               debug(dhcp_client, "pos %d option %p code %d len %d",
+                       pos, option, option_code, option_len);
+
+               if (option == NULL)
+                       break;
+
+               if (pos >= max_len)
+                       break;
+
+               switch (option_code) {
+               case G_DHCPV6_IAADDR:
+                       i = 0;
+                       memcpy(&addr, &option[0], sizeof(addr));
+                       i += sizeof(addr);
+                       preferred = get_uint32(&option[i]);
+                       i += 4;
+                       valid = get_uint32(&option[i]);
+
+                       addr_count++;
+                       break;
+
+               case G_DHCPV6_STATUS_CODE:
+                       st = get_uint16(&option[0]);
+                       debug(dhcp_client, "error code %d", st);
+                       if (option_len > 2) {
+                               str = g_strndup((gchar *)&option[2],
+                                               option_len - 2);
+                               debug(dhcp_client, "error text: %s", str);
+                               g_free(str);
+                       }
+
+                       *status = st;
+                       break;
+               }
+
+               pos += 2 + 2 + option_len;
+
+       } while (option != NULL);
+
+       if (addr_count > 0 && st == 0) {
+               /* We only support one address atm */
+               char str[INET6_ADDRSTRLEN + 1];
+
+               if (preferred > valid)
+                       /* RFC 3315, 22.6 */
+                       return NULL;
+
+               dhcp_client->T1 = T1;
+               dhcp_client->T2 = T2;
+
+               inet_ntop(AF_INET6, &addr, str, INET6_ADDRSTRLEN);
+               debug(dhcp_client, "count %d addr %s T1 %u T2 %u",
+                       addr_count, str, T1, T2);
+
+               list = g_list_append(list, g_strdup(str));
+
+               if (code == G_DHCPV6_IA_NA)
+                       memcpy(&dhcp_client->ia_na, &addr,
+                                               sizeof(struct in6_addr));
+               else
+                       memcpy(&dhcp_client->ia_ta, &addr,
+                                               sizeof(struct in6_addr));
+
+               g_dhcpv6_client_set_expire(dhcp_client, valid);
+       }
+
+       return list;
+}
+
+static GList *get_dhcpv6_option_value_list(GDHCPClient *dhcp_client,
+                                       int code, int len,
+                                       unsigned char *value,
+                                       uint16_t *status)
+{
+       GList *list = NULL;
+       char *str;
+       int i;
+
+       if (value == NULL)
+               return NULL;
+
+       switch (code) {
+       case G_DHCPV6_DNS_SERVERS:      /* RFC 3646, chapter 3 */
+       case G_DHCPV6_SNTP_SERVERS:     /* RFC 4075, chapter 4 */
+               if (len % 16) {
+                       debug(dhcp_client,
+                               "%s server list length (%d) is invalid",
+                               code == G_DHCPV6_DNS_SERVERS ? "DNS" : "SNTP",
+                               len);
+                       return NULL;
+               }
+               for (i = 0; i < len; i += 16) {
+
+                       str = g_try_malloc0(INET6_ADDRSTRLEN+1);
+                       if (str == NULL)
+                               return list;
+
+                       if (inet_ntop(AF_INET6, &value[i], str,
+                                       INET6_ADDRSTRLEN) == NULL)
+                               g_free(str);
+                       else
+                               list = g_list_append(list, str);
+               }
+               break;
+
+       case G_DHCPV6_IA_NA:            /* RFC 3315, chapter 22.4 */
+       case G_DHCPV6_IA_TA:            /* RFC 3315, chapter 22.5 */
+               list = get_addresses(dhcp_client, code, len, value, status);
+               break;
+
+       default:
+               break;
+       }
+
+       return list;
+}
+
+static void get_dhcpv6_request(GDHCPClient *dhcp_client,
+                               struct dhcpv6_packet *packet,
+                               uint16_t pkt_len, uint16_t *status)
+{
+       GList *list, *value_list;
+       uint8_t *option;
+       uint16_t code;
+       uint16_t option_len;
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint16_t) GPOINTER_TO_INT(list->data);
+
+               option = dhcpv6_get_option(packet, pkt_len, code, &option_len,
+                                               NULL);
+               if (option == NULL) {
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+                       continue;
+               }
+
+               value_list = get_dhcpv6_option_value_list(dhcp_client, code,
+                                               option_len, option, status);
+
+               debug(dhcp_client, "code %d %p len %d list %p", code, option,
+                       option_len, value_list);
+
+               if (value_list == NULL)
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+               else
+                       g_hash_table_insert(dhcp_client->code_value_hash,
+                               GINT_TO_POINTER((int) code), value_list);
+       }
+}
+
+static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
+{
+       GDHCPOptionType type;
+       GList *list, *value_list;
+       char *option_value;
+       uint8_t *option;
+       uint8_t code;
+
+       for (list = dhcp_client->request_list; list; list = list->next) {
+               code = (uint8_t) GPOINTER_TO_INT(list->data);
+
+               option = dhcp_get_option(packet, code);
+               if (option == NULL) {
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+                       continue;
+               }
+
+               type =  dhcp_get_code_type(code);
+
+               option_value = malloc_option_value_string(option, type);
+               if (option_value == NULL)
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+
+               value_list = get_option_value_list(option_value, type);
+
+               g_free(option_value);
+
+               if (value_list == NULL)
+                       g_hash_table_remove(dhcp_client->code_value_hash,
+                                               GINT_TO_POINTER((int) code));
+               else
+                       g_hash_table_insert(dhcp_client->code_value_hash,
+                               GINT_TO_POINTER((int) code), value_list);
+       }
+}
+
+static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+       struct dhcp_packet packet;
+       struct dhcpv6_packet *packet6 = NULL;
+       uint8_t *message_type = NULL, *client_id = NULL, *option_u8,
+               *server_id = NULL;
+       uint16_t option_len = 0, status = 0;
+       gpointer pkt;
+       unsigned char buf[MAX_DHCPV6_PKT_SIZE];
+       uint16_t pkt_len = 0;
+       int count;
+       int re;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               dhcp_client->listener_watch = 0;
+               return FALSE;
+       }
+
+       if (dhcp_client->listen_mode == L_NONE)
+               return FALSE;
+
+       pkt = &packet;
+
+       if (dhcp_client->listen_mode == L2)
+               re = dhcp_recv_l2_packet(&packet,
+                                       dhcp_client->listener_sockfd);
+       else if (dhcp_client->listen_mode == L3) {
+               if (dhcp_client->type == G_DHCP_IPV6) {
+                       re = dhcpv6_recv_l3_packet(&packet6, buf, sizeof(buf),
+                                               dhcp_client->listener_sockfd);
+                       pkt_len = re;
+                       pkt = packet6;
+               } else
+                       re = dhcp_recv_l3_packet(&packet,
+                                               dhcp_client->listener_sockfd);
+       } else if (dhcp_client->listen_mode == L_ARP) {
+               ipv4ll_recv_arp_packet(dhcp_client);
+               return TRUE;
+       }
+       else
+               re = -EIO;
+
+       if (re < 0)
+               return TRUE;
+
+       if (check_package_owner(dhcp_client, pkt) == FALSE)
+               return TRUE;
+
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               if (packet6 == NULL)
+                       return TRUE;
+
+               count = 0;
+               client_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_CLIENTID, &option_len, &count);
+
+               if (client_id == NULL || count == 0 || option_len == 0 ||
+                               memcmp(dhcp_client->duid, client_id,
+                                       dhcp_client->duid_len) != 0) {
+                       debug(dhcp_client,
+                               "client duid error, discarding msg %p/%d/%d",
+                               client_id, option_len, count);
+                       return TRUE;
+               }
+
+               option_u8 = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_STATUS_CODE, &option_len, NULL);
+               if (option_u8 != 0 && option_len > 0) {
+                       status = option_u8[0]<<8 | option_u8[1];
+                       if (status != 0) {
+                               debug(dhcp_client, "error code %d", status);
+                               if (option_len > 2) {
+                                       gchar *txt = g_strndup(
+                                               (gchar *)&option_u8[2],
+                                               option_len - 2);
+                                       debug(dhcp_client, "error text: %s",
+                                               txt);
+                                       g_free(txt);
+                               }
+                       }
+                       dhcp_client->status_code = status;
+               } else
+                       dhcp_client->status_code = 0;
+
+       } else {
+               message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+               if (message_type == NULL)
+                       return TRUE;
+       }
+
+       if (message_type == NULL && client_id == NULL)
+               /* No message type / client id option, ignore package */
+               return TRUE;
+
+       debug(dhcp_client, "received DHCP packet (current state %d)",
+                                                       dhcp_client->state);
+
+       switch (dhcp_client->state) {
+       case INIT_SELECTING:
+               if (*message_type != DHCPOFFER)
+                       return TRUE;
+
+               g_source_remove(dhcp_client->timeout);
+               dhcp_client->timeout = 0;
+               dhcp_client->retry_times = 0;
+
+               option_u8 = dhcp_get_option(&packet, DHCP_SERVER_ID);
+               dhcp_client->server_ip =
+                               dhcp_get_unaligned((uint32_t *) option_u8);
+               dhcp_client->requested_ip = packet.yiaddr;
+
+               dhcp_client->state = REQUESTING;
+
+               start_request(dhcp_client);
+
+               return TRUE;
+       case REQUESTING:
+       case RENEWING:
+       case REBINDING:
+               if (*message_type == DHCPACK) {
+                       dhcp_client->retry_times = 0;
+
+                       if (dhcp_client->timeout > 0)
+                               g_source_remove(dhcp_client->timeout);
+                       dhcp_client->timeout = 0;
+
+                       dhcp_client->lease_seconds = get_lease(&packet);
+
+#if defined TIZEN_EXT
+                       debug(dhcp_client, "lease %d secs", dhcp_client->lease_seconds);
+#endif
+                       get_request(dhcp_client, &packet);
+
+                       switch_listening_mode(dhcp_client, L_NONE);
+
+                       g_free(dhcp_client->assigned_ip);
+                       dhcp_client->assigned_ip = get_ip(packet.yiaddr);
+
+                       /* Address should be set up here */
+                       if (dhcp_client->lease_available_cb != NULL)
+                               dhcp_client->lease_available_cb(dhcp_client,
+                                       dhcp_client->lease_available_data);
+
+                       start_bound(dhcp_client);
+               } else if (*message_type == DHCPNAK) {
+                       dhcp_client->retry_times = 0;
+
+                       if (dhcp_client->timeout > 0)
+                               g_source_remove(dhcp_client->timeout);
+
+#if defined TIZEN_EXT
+                       g_dhcp_client_set_address_known(dhcp_client, FALSE);
+#endif
+                       dhcp_client->timeout = g_timeout_add_seconds_full(
+                                                       G_PRIORITY_HIGH, 3,
+                                                       restart_dhcp_timeout,
+                                                       dhcp_client,
+                                                       NULL);
+               }
+
+               break;
+       case SOLICITATION:
+               if (dhcp_client->type != G_DHCP_IPV6)
+                       return TRUE;
+
+               if (packet6->message != DHCPV6_REPLY &&
+                               packet6->message != DHCPV6_ADVERTISE)
+                       return TRUE;
+
+               count = 0;
+               server_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_SERVERID, &option_len, &count);
+               if (server_id == NULL || count != 1 || option_len == 0) {
+                       /* RFC 3315, 15.10 */
+                       debug(dhcp_client,
+                               "server duid error, discarding msg %p/%d/%d",
+                               server_id, option_len, count);
+                       return TRUE;
+               }
+               dhcp_client->server_duid = g_try_malloc(option_len);
+               if (dhcp_client->server_duid == NULL)
+                       return TRUE;
+               memcpy(dhcp_client->server_duid, server_id, option_len);
+               dhcp_client->server_duid_len = option_len;
+
+               if (packet6->message == DHCPV6_REPLY) {
+                       uint8_t *rapid_commit;
+                       count = 0;
+                       option_len = 0;
+                       rapid_commit = dhcpv6_get_option(packet6, pkt_len,
+                                                       G_DHCPV6_RAPID_COMMIT,
+                                                       &option_len, &count);
+                       if (rapid_commit == NULL || option_len == 0 ||
+                                                               count != 1)
+                               /* RFC 3315, 17.1.4 */
+                               return TRUE;
+               }
+
+               switch_listening_mode(dhcp_client, L_NONE);
+
+               if (dhcp_client->status_code == 0)
+                       get_dhcpv6_request(dhcp_client, packet6, pkt_len,
+                                       &dhcp_client->status_code);
+
+               if (packet6->message == DHCPV6_ADVERTISE) {
+                       if (dhcp_client->advertise_cb != NULL)
+                               dhcp_client->advertise_cb(dhcp_client,
+                                               dhcp_client->advertise_data);
+                       return TRUE;
+               }
+
+               if (dhcp_client->solicitation_cb != NULL) {
+                       /*
+                        * The dhcp_client might not be valid after the
+                        * callback call so just return immediately.
+                        */
+                       dhcp_client->solicitation_cb(dhcp_client,
+                                       dhcp_client->solicitation_data);
+                       return TRUE;
+               }
+               break;
+       case INFORMATION_REQ:
+       case REQUEST:
+       case RENEW:
+       case REBIND:
+       case RELEASE:
+               if (dhcp_client->type != G_DHCP_IPV6)
+                       return TRUE;
+
+               if (packet6->message != DHCPV6_REPLY)
+                       return TRUE;
+
+               count = 0;
+               option_len = 0;
+               server_id = dhcpv6_get_option(packet6, pkt_len,
+                               G_DHCPV6_SERVERID, &option_len, &count);
+               if (server_id == NULL || count != 1 || option_len == 0 ||
+                               (dhcp_client->server_duid_len > 0 &&
+                               memcmp(dhcp_client->server_duid, server_id,
+                                       dhcp_client->server_duid_len) != 0)) {
+                       /* RFC 3315, 15.10 */
+                       debug(dhcp_client,
+                               "server duid error, discarding msg %p/%d/%d",
+                               server_id, option_len, count);
+                       return TRUE;
+               }
+
+               switch_listening_mode(dhcp_client, L_NONE);
+
+               dhcp_client->status_code = 0;
+               get_dhcpv6_request(dhcp_client, packet6, pkt_len,
+                                               &dhcp_client->status_code);
+
+               if (dhcp_client->information_req_cb != NULL) {
+                       /*
+                        * The dhcp_client might not be valid after the
+                        * callback call so just return immediately.
+                        */
+                       dhcp_client->information_req_cb(dhcp_client,
+                                       dhcp_client->information_req_data);
+                       return TRUE;
+               }
+               if (dhcp_client->request_cb != NULL) {
+                       dhcp_client->request_cb(dhcp_client,
+                                       dhcp_client->request_data);
+                       return TRUE;
+               }
+               if (dhcp_client->renew_cb != NULL) {
+                       dhcp_client->renew_cb(dhcp_client,
+                                       dhcp_client->renew_data);
+                       return TRUE;
+               }
+               if (dhcp_client->rebind_cb != NULL) {
+                       dhcp_client->rebind_cb(dhcp_client,
+                                       dhcp_client->rebind_data);
+                       return TRUE;
+               }
+               if (dhcp_client->release_cb != NULL) {
+                       dhcp_client->release_cb(dhcp_client,
+                                       dhcp_client->release_data);
+                       return TRUE;
+               }
+               break;
+       default:
+               break;
+       }
+
+       debug(dhcp_client, "processed DHCP packet (new state %d)",
+                                                       dhcp_client->state);
+
+       return TRUE;
+}
+
+static gboolean discover_timeout(gpointer user_data)
+{
+       GDHCPClient *dhcp_client = user_data;
+
+       dhcp_client->retry_times++;
+
+       /*
+        * We do not send the REQUESTED IP option if we are retrying because
+        * if the server is non-authoritative it will ignore the request if the
+        * option is present.
+        */
+       g_dhcp_client_start(dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
+{
+       GDHCPClient *dhcp_client = dhcp_data;
+
+       debug(dhcp_client, "back to MONITOR mode");
+
+       dhcp_client->conflicts = 0;
+       dhcp_client->state = IPV4LL_MONITOR;
+
+       return FALSE;
+}
+
+static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
+{
+       GDHCPClient *dhcp_client = dhcp_data;
+       uint32_t ip;
+
+       debug(dhcp_client, "request timeout (retries %d)",
+              dhcp_client->retry_times);
+
+       if (dhcp_client->retry_times != ANNOUNCE_NUM){
+               dhcp_client->retry_times++;
+               send_announce_packet(dhcp_client);
+               return FALSE;
+       }
+
+       ip = htonl(dhcp_client->requested_ip);
+       debug(dhcp_client, "switching to monitor mode");
+       dhcp_client->state = IPV4LL_MONITOR;
+       dhcp_client->assigned_ip = get_ip(ip);
+
+       if (dhcp_client->ipv4ll_available_cb != NULL)
+               dhcp_client->ipv4ll_available_cb(dhcp_client,
+                                       dhcp_client->ipv4ll_available_data);
+       dhcp_client->conflicts = 0;
+
+       return FALSE;
+}
+
+static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
+{
+
+       GDHCPClient *dhcp_client = dhcp_data;
+
+       debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
+              dhcp_client->retry_times);
+
+       if (dhcp_client->retry_times == PROBE_NUM) {
+               dhcp_client->state = IPV4LL_ANNOUNCE;
+               dhcp_client->retry_times = 0;
+
+               dhcp_client->retry_times++;
+               send_announce_packet(dhcp_client);
+               return FALSE;
+       }
+       dhcp_client->retry_times++;
+       send_probe_packet(dhcp_client);
+
+       return FALSE;
+}
+
+int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
+{
+       int re;
+       uint32_t addr;
+
+       if (dhcp_client->type == G_DHCP_IPV6) {
+               if (dhcp_client->information_req_cb) {
+                       dhcp_client->state = INFORMATION_REQ;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_information_req(dhcp_client);
+
+               } else if (dhcp_client->solicitation_cb) {
+                       dhcp_client->state = SOLICITATION;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_solicitation(dhcp_client);
+
+               } else if (dhcp_client->request_cb) {
+                       dhcp_client->state = REQUEST;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_request(dhcp_client);
+
+               } else if (dhcp_client->renew_cb) {
+                       dhcp_client->state = RENEW;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_renew(dhcp_client);
+
+               } else if (dhcp_client->rebind_cb) {
+                       dhcp_client->state = REBIND;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_rebind(dhcp_client);
+
+               } else if (dhcp_client->release_cb) {
+                       dhcp_client->state = RENEW;
+                       re = switch_listening_mode(dhcp_client, L3);
+                       if (re != 0) {
+                               switch_listening_mode(dhcp_client, L_NONE);
+                               dhcp_client->state = 0;
+                               return re;
+                       }
+                       send_dhcpv6_release(dhcp_client);
+               }
+
+               return 0;
+       }
+
+       if (dhcp_client->retry_times == DISCOVER_RETRIES) {
+               ipv4ll_start(dhcp_client);
+               return 0;
+       }
+
+       if (dhcp_client->retry_times == 0) {
+               g_free(dhcp_client->assigned_ip);
+               dhcp_client->assigned_ip = NULL;
+
+               dhcp_client->state = INIT_SELECTING;
+               re = switch_listening_mode(dhcp_client, L2);
+               if (re != 0)
+                       return re;
+
+               dhcp_client->xid = rand();
+               dhcp_client->start = time(NULL);
+       }
+
+       if (last_address == NULL) {
+               addr = 0;
+       } else {
+               addr = inet_addr(last_address);
+               if (addr == 0xFFFFFFFF) {
+                       addr = 0;
+               } else {
+                       g_free(dhcp_client->last_address);
+                       dhcp_client->last_address = g_strdup(last_address);
+               }
+       }
+#if defined TIZEN_EXT
+       if (dhcp_client->init_reboot == TRUE) {
+               dhcp_client->requested_ip = addr;
+
+               start_request(dhcp_client);
+
+               return 0;
+       }
+#endif
+       send_discover(dhcp_client, addr);
+
+       dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+                                                       DISCOVER_TIMEOUT,
+                                                       discover_timeout,
+                                                       dhcp_client,
+                                                       NULL);
+       return 0;
+}
+
+void g_dhcp_client_stop(GDHCPClient *dhcp_client)
+{
+       switch_listening_mode(dhcp_client, L_NONE);
+
+       if (dhcp_client->state == BOUND ||
+                       dhcp_client->state == RENEWING ||
+                               dhcp_client->state == REBINDING)
+               send_release(dhcp_client, dhcp_client->server_ip,
+                                       dhcp_client->requested_ip);
+
+       if (dhcp_client->timeout > 0) {
+               g_source_remove(dhcp_client->timeout);
+               dhcp_client->timeout = 0;
+       }
+
+       if (dhcp_client->listener_watch > 0) {
+               g_source_remove(dhcp_client->listener_watch);
+               dhcp_client->listener_watch = 0;
+       }
+
+       dhcp_client->listener_channel = NULL;
+
+       dhcp_client->retry_times = 0;
+       dhcp_client->ack_retry_times = 0;
+
+       dhcp_client->requested_ip = 0;
+       dhcp_client->state = RELEASED;
+       dhcp_client->lease_seconds = 0;
+}
+
+GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
+                                       unsigned char option_code)
+{
+       return g_hash_table_lookup(dhcp_client->code_value_hash,
+                                       GINT_TO_POINTER((int) option_code));
+}
+
+void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
+                                       GDHCPClientEvent event,
+                                       GDHCPClientEventFunc func,
+                                                       gpointer data)
+{
+       switch (event) {
+       case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
+               dhcp_client->lease_available_cb = func;
+               dhcp_client->lease_available_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       return;
+               dhcp_client->ipv4ll_available_cb = func;
+               dhcp_client->ipv4ll_available_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_NO_LEASE:
+               dhcp_client->no_lease_cb = func;
+               dhcp_client->no_lease_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_LEASE_LOST:
+               dhcp_client->lease_lost_cb = func;
+               dhcp_client->lease_lost_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
+               if (dhcp_client->type == G_DHCP_IPV6)
+                       return;
+               dhcp_client->ipv4ll_lost_cb = func;
+               dhcp_client->ipv4ll_lost_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
+               dhcp_client->address_conflict_cb = func;
+               dhcp_client->address_conflict_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_INFORMATION_REQ:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->information_req_cb = func;
+               dhcp_client->information_req_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_SOLICITATION:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->solicitation_cb = func;
+               dhcp_client->solicitation_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_ADVERTISE:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->advertise_cb = func;
+               dhcp_client->advertise_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_REQUEST:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->request_cb = func;
+               dhcp_client->request_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_RENEW:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->renew_cb = func;
+               dhcp_client->renew_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_REBIND:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->rebind_cb = func;
+               dhcp_client->rebind_data = data;
+               return;
+       case G_DHCP_CLIENT_EVENT_RELEASE:
+               if (dhcp_client->type == G_DHCP_IPV4)
+                       return;
+               dhcp_client->release_cb = func;
+               dhcp_client->release_data = data;
+               return;
+       }
+}
+
+int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
+{
+       return dhcp_client->ifindex;
+}
+
+char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
+{
+       return g_strdup(dhcp_client->assigned_ip);
+}
+
+char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
+{
+       GList *option = NULL;
+
+       if (dhcp_client->type == G_DHCP_IPV6)
+               return NULL;
+
+       switch (dhcp_client->state) {
+       case IPV4LL_DEFEND:
+       case IPV4LL_MONITOR:
+               return g_strdup("255.255.0.0");
+       case BOUND:
+       case RENEWING:
+       case REBINDING:
+               option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
+               if (option != NULL)
+                       return g_strdup(option->data);
+       case INIT_SELECTING:
+       case REQUESTING:
+       case RELEASED:
+       case IPV4LL_PROBE:
+       case IPV4LL_ANNOUNCE:
+       case INFORMATION_REQ:
+       case SOLICITATION:
+       case REQUEST:
+       case RENEW:
+       case REBIND:
+       case RELEASE:
+               break;
+       }
+       return NULL;
+}
+
+GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
+                                               unsigned int option_code)
+{
+       if (g_list_find(dhcp_client->request_list,
+                       GINT_TO_POINTER((int) option_code)) == NULL)
+               dhcp_client->request_list = g_list_prepend(
+                                       dhcp_client->request_list,
+                                       (GINT_TO_POINTER((int) option_code)));
+
+       return G_DHCP_CLIENT_ERROR_NONE;
+}
+
+void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client)
+{
+       g_list_free(dhcp_client->request_list);
+       dhcp_client->request_list = NULL;
+}
+
+void g_dhcp_client_clear_values(GDHCPClient *dhcp_client)
+{
+       g_hash_table_remove_all(dhcp_client->send_value_hash);
+}
+
+static uint8_t *alloc_dhcp_option(int code, const uint8_t *data, unsigned size)
+{
+       uint8_t *storage;
+
+       storage = g_try_malloc(size + OPT_DATA);
+       if (storage == NULL)
+               return NULL;
+
+       storage[OPT_CODE] = code;
+       storage[OPT_LEN] = size;
+       memcpy(&storage[OPT_DATA], data, size);
+
+       return storage;
+}
+
+static uint8_t *alloc_dhcp_data_option(int code, const uint8_t *data, unsigned size)
+{
+       return alloc_dhcp_option(code, data, MIN(size, 255));
+}
+
+static uint8_t *alloc_dhcp_string_option(int code, const char *str)
+{
+       return alloc_dhcp_data_option(code, (const uint8_t *)str, strlen(str));
+}
+
+GDHCPClientError g_dhcp_client_set_id(GDHCPClient *dhcp_client)
+{
+       const unsigned maclen = 6;
+       const unsigned idlen = maclen + 1;
+       const uint8_t option_code = G_DHCP_CLIENT_ID;
+       uint8_t idbuf[idlen];
+       uint8_t *data_option;
+
+       idbuf[0] = ARPHRD_ETHER;
+
+       memcpy(&idbuf[1], dhcp_client->mac_address, maclen);
+
+       data_option = alloc_dhcp_data_option(option_code, idbuf, idlen);
+       if (data_option == NULL)
+               return G_DHCP_CLIENT_ERROR_NOMEM;
+
+       g_hash_table_insert(dhcp_client->send_value_hash,
+               GINT_TO_POINTER((int) option_code), data_option);
+
+       return G_DHCP_CLIENT_ERROR_NONE;
+}
+
+/* Now only support send hostname */
+GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
+               unsigned char option_code, const char *option_value)
+{
+       uint8_t *binary_option;
+
+       if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
+               binary_option = alloc_dhcp_string_option(option_code,
+                                                       option_value);
+               if (binary_option == NULL)
+                       return G_DHCP_CLIENT_ERROR_NOMEM;
+
+               g_hash_table_insert(dhcp_client->send_value_hash,
+                       GINT_TO_POINTER((int) option_code), binary_option);
+       }
+
+       return G_DHCP_CLIENT_ERROR_NONE;
+}
+
+static uint8_t *alloc_dhcpv6_option(uint16_t code, uint8_t *option,
+                               uint16_t len)
+{
+       uint8_t *storage;
+
+       storage = g_malloc(2 + 2 + len);
+       if (storage == NULL)
+               return NULL;
+
+       storage[0] = code >> 8;
+       storage[1] = code & 0xff;
+       storage[2] = len >> 8;
+       storage[3] = len & 0xff;
+       memcpy(storage + 2 + 2, option, len);
+
+       return storage;
+}
+
+void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client,
+                                       uint16_t option_code,
+                                       uint8_t *option_value,
+                                       uint16_t option_len)
+{
+       if (option_value != NULL) {
+               uint8_t *binary_option;
+
+               debug(dhcp_client, "setting option %d to %p len %d",
+                       option_code, option_value, option_len);
+
+               binary_option = alloc_dhcpv6_option(option_code, option_value,
+                                               option_len);
+               if (binary_option != NULL)
+                       g_hash_table_insert(dhcp_client->send_value_hash,
+                                       GINT_TO_POINTER((int) option_code),
+                                       binary_option);
+       }
+}
+
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->last_renew = time(NULL);
+}
+
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->last_rebind = time(NULL);
+}
+
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return;
+
+       dhcp_client->expire = time(NULL) + timeout;
+}
+
+uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+               return 0;
+
+       return dhcp_client->status_code;
+}
+
+GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&dhcp_client->ref_count, 1);
+
+       return dhcp_client;
+}
+
+void g_dhcp_client_unref(GDHCPClient *dhcp_client)
+{
+       if (dhcp_client == NULL)
+               return;
+
+       if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
+               return;
+
+       g_dhcp_client_stop(dhcp_client);
+
+       g_free(dhcp_client->interface);
+       g_free(dhcp_client->assigned_ip);
+       g_free(dhcp_client->last_address);
+       g_free(dhcp_client->duid);
+       g_free(dhcp_client->server_duid);
+
+       g_list_free(dhcp_client->request_list);
+       g_list_free(dhcp_client->require_list);
+
+       g_hash_table_destroy(dhcp_client->code_value_hash);
+       g_hash_table_destroy(dhcp_client->send_value_hash);
+
+       g_free(dhcp_client);
+}
+
+void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
+                               GDHCPDebugFunc func, gpointer user_data)
+{
+       if (dhcp_client == NULL)
+               return;
+
+       dhcp_client->debug_func = func;
+       dhcp_client->debug_data = user_data;
+}
+#if defined TIZEN_EXT
+void g_dhcp_client_set_address_known(GDHCPClient *dhcp_client, gboolean known)
+{
+       /* DHCPREQUEST during INIT-REBOOT state (rfc2131)
+        * 4.4.3 Initialization with known network address
+        * 4.3.2 DHCPREQUEST generated during INIT-REBOOT state
+        */
+       debug(dhcp_client, "known network address (%d)", known);
+
+       if (dhcp_client->init_reboot == known)
+               return;
+
+       dhcp_client->init_reboot = known;
+}
+#endif
diff --git a/gdhcp/common.c b/gdhcp/common.c
new file mode 100644 (file)
index 0000000..8d5c284
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ *  DHCP library with GLib integration
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "gdhcp.h"
+#include "common.h"
+
+static const DHCPOption client_options[] = {
+       { OPTION_IP,                    0x01 }, /* subnet-mask */
+       { OPTION_IP | OPTION_LIST,      0x03 }, /* routers */
+       { OPTION_IP | OPTION_LIST,      0x06 }, /* domain-name-servers */
+       { OPTION_STRING,                0x0c }, /* hostname */
+       { OPTION_STRING,                0x0f }, /* domain-name */
+       { OPTION_IP | OPTION_LIST,      0x2a }, /* ntp-servers */
+       { OPTION_U32,                   0x33 }, /* dhcp-lease-time */
+       /* Options below will not be exposed to user */
+       { OPTION_IP,                    0x32 }, /* requested-ip */
+       { OPTION_U8,                    0x35 }, /* message-type */
+       { OPTION_U32,                   0x36 }, /* server-id */
+       { OPTION_U16,                   0x39 }, /* max-size */
+       { OPTION_STRING,                0x3c }, /* vendor */
+       { OPTION_STRING,                0x3d }, /* client-id */
+       { OPTION_STRING,                0xfc }, /* UNOFFICIAL proxy-pac */
+       { OPTION_UNKNOWN,               0x00 },
+};
+
+GDHCPOptionType dhcp_get_code_type(uint8_t code)
+{
+       int i;
+
+       for (i = 0; client_options[i].code; i++) {
+               if (client_options[i].code == code)
+                       return client_options[i].type;
+       }
+
+       return OPTION_UNKNOWN;
+}
+
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
+{
+       int len, rem;
+       uint8_t *optionptr;
+       uint8_t overload = 0;
+
+       /* option bytes: [code][len][data1][data2]..[dataLEN] */
+       optionptr = packet->options;
+       rem = sizeof(packet->options);
+
+       while (1) {
+               if (rem <= 0)
+                       /* Bad packet, malformed option field */
+                       return NULL;
+
+               if (optionptr[OPT_CODE] == DHCP_PADDING) {
+                       rem--;
+                       optionptr++;
+
+                       continue;
+               }
+
+               if (optionptr[OPT_CODE] == DHCP_END) {
+                       if (overload & FILE_FIELD) {
+                               overload &= ~FILE_FIELD;
+
+                               optionptr = packet->file;
+                               rem = sizeof(packet->file);
+
+                               continue;
+                       } else if (overload & SNAME_FIELD) {
+                               overload &= ~SNAME_FIELD;
+
+                               optionptr = packet->sname;
+                               rem = sizeof(packet->sname);
+
+                               continue;
+                       }
+
+                       break;
+               }
+
+               len = 2 + optionptr[OPT_LEN];
+
+               rem -= len;
+               if (rem < 0)
+                       continue; /* complain and return NULL */
+
+               if (optionptr[OPT_CODE] == code)
+                       return optionptr + OPT_DATA;
+
+               if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
+                       overload |= optionptr[OPT_DATA];
+
+               optionptr += len;
+       }
+
+       return NULL;
+}
+
+int dhcp_end_option(uint8_t *optionptr)
+{
+       int i = 0;
+
+       while (optionptr[i] != DHCP_END) {
+               if (optionptr[i] != DHCP_PADDING)
+                       i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
+
+               i++;
+       }
+
+       return i;
+}
+
+uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
+                       int code, uint16_t *option_len, int *option_count)
+{
+       int rem, count = 0;
+       uint8_t *optionptr, *found = NULL;
+       uint16_t opt_code, opt_len, len;
+
+       optionptr = packet->options;
+       rem = pkt_len - 1 - 3;
+
+       if (rem <= 0)
+               goto bad_packet;
+
+       while (1) {
+               opt_code = optionptr[0] << 8 | optionptr[1];
+               opt_len = len = optionptr[2] << 8 | optionptr[3];
+               len += 2 + 2; /* skip code and len */
+
+               if (len < 4)
+                       goto bad_packet;
+
+               rem -= len;
+               if (rem < 0)
+                       break;
+
+               if (opt_code == code) {
+                       if (option_len != NULL)
+                               *option_len = opt_len;
+                       if (rem == 0)
+                               found = NULL;
+                       else
+                               found = optionptr + 2 + 2;
+                       count++;
+               }
+
+               if (rem == 0)
+                       break;
+
+               optionptr += len;
+       }
+
+       if (option_count != NULL)
+               *option_count = count;
+
+       return found;
+
+bad_packet:
+       if (option_len != NULL)
+               *option_len = 0;
+       if (option_count != NULL)
+               *option_count = 0;
+       return NULL;
+}
+
+uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
+                       uint16_t *option_code, uint16_t *option_len)
+{
+       int rem;
+       uint16_t code, len;
+
+       rem = max_len - 2 - 2;
+
+       if (rem <= 0)
+               /* Bad option */
+               return NULL;
+
+       code = option[0] << 8 | option[1];
+       len = option[2] << 8 | option[3];
+
+       rem -= len;
+       if (rem < 0)
+               return NULL;
+
+       *option_code = code;
+       *option_len = len;
+
+       return &option[4];
+}
+
+/*
+ * Add an option (supplied in binary form) to the options.
+ * Option format: [code][len][data1][data2]..[dataLEN]
+ */
+void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
+{
+       unsigned len;
+       uint8_t *optionptr = packet->options;
+       unsigned end = dhcp_end_option(optionptr);
+
+       len = OPT_DATA + addopt[OPT_LEN];
+
+       /* end position + (option code/length + addopt length) + end option */
+       if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE)
+               /* option did not fit into the packet */
+               return;
+
+       memcpy(optionptr + end, addopt, len);
+
+       optionptr[end + len] = DHCP_END;
+}
+
+/*
+ * Add an option (supplied in binary form) to the options.
+ * Option format: [code][len][data1][data2]..[dataLEN]
+ */
+void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
+                               uint16_t *pkt_len, uint8_t *addopt)
+{
+       unsigned len;
+       uint8_t *optionptr = packet->options;
+
+       len = 2 + 2 + (addopt[2] << 8 | addopt[3]);
+
+       /* end position + (option code/length + addopt length) */
+       if (*pkt_len + len >= max_len)
+               /* option did not fit into the packet */
+               return;
+
+       memcpy(optionptr + *pkt_len, addopt, len);
+       *pkt_len += len;
+}
+
+void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
+                                                       uint32_t data)
+{
+       uint8_t option[6], len;
+       GDHCPOptionType type = dhcp_get_code_type(code);
+
+       if (type == OPTION_UNKNOWN)
+               return;
+
+       option[OPT_CODE] = code;
+
+       len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
+       option[OPT_LEN] = len;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+       data <<= 8 * (4 - len);
+#endif
+
+       dhcp_put_unaligned(data, (uint32_t *)(option + OPT_DATA));
+       dhcp_add_binary_option(packet, option);
+
+       return;
+}
+
+void dhcp_init_header(struct dhcp_packet *packet, char type)
+{
+       memset(packet, 0, sizeof(*packet));
+
+       packet->op = BOOTREQUEST;
+
+       switch (type) {
+       case DHCPOFFER:
+       case DHCPACK:
+       case DHCPNAK:
+               packet->op = BOOTREPLY;
+       }
+
+       packet->htype = 1;
+       packet->hlen = 6;
+       packet->cookie = htonl(DHCP_MAGIC);
+       packet->options[0] = DHCP_END;
+
+       dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
+}
+
+void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type)
+{
+       int id;
+
+       memset(packet, 0, sizeof(*packet));
+
+       packet->message = type;
+
+       id = random();
+
+       packet->transaction_id[0] = (id >> 16) & 0xff;
+       packet->transaction_id[1] = (id >> 8) & 0xff;
+       packet->transaction_id[2] = id & 0xff;
+}
+
+static gboolean check_vendor(uint8_t  *option_vendor, const char *vendor)
+{
+       uint8_t vendor_length = sizeof(vendor) - 1;
+
+       if (option_vendor[OPT_LEN - OPT_DATA] != vendor_length)
+               return FALSE;
+
+       if (memcmp(option_vendor, vendor, vendor_length) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void check_broken_vendor(struct dhcp_packet *packet)
+{
+       uint8_t *vendor;
+
+       if (packet->op != BOOTREQUEST)
+               return;
+
+       vendor = dhcp_get_option(packet, DHCP_VENDOR);
+       if (vendor == NULL)
+               return;
+
+       if (check_vendor(vendor, "MSFT 98") == TRUE)
+               packet->flags |= htons(BROADCAST_FLAG);
+}
+
+int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
+{
+       int n;
+
+       memset(packet, 0, sizeof(*packet));
+
+       n = read(fd, packet, sizeof(*packet));
+       if (n < 0)
+               return -errno;
+
+       if (packet->cookie != htonl(DHCP_MAGIC))
+               return -EPROTO;
+
+       check_broken_vendor(packet);
+
+       return n;
+}
+
+int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
+                       int buf_len, int fd)
+{
+       int n;
+
+       n = read(fd, buf, buf_len);
+       if (n < 0)
+               return -errno;
+
+       *packet = (struct dhcpv6_packet *)buf;
+
+       return n;
+}
+
+/* TODO: Use glib checksum */
+uint16_t dhcp_checksum(void *addr, int count)
+{
+       /*
+        * Compute Internet Checksum for "count" bytes
+        * beginning at location "addr".
+        */
+       int32_t sum = 0;
+       uint16_t *source = (uint16_t *) addr;
+
+       while (count > 1)  {
+               /*  This is the inner loop */
+               sum += *source++;
+               count -= 2;
+       }
+
+       /*  Add left-over byte, if any */
+       if (count > 0) {
+               /* Make sure that the left-over byte is added correctly both
+                * with little and big endian hosts */
+               uint16_t tmp = 0;
+               *(uint8_t *) &tmp = *(uint8_t *) source;
+               sum += tmp;
+       }
+       /*  Fold 32-bit sum to 16 bits */
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return ~sum;
+}
+
+#define IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT \
+       { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0x1,0,0x2 } } } /* ff02::1:2 */
+static const struct in6_addr in6addr_all_dhcp_relay_agents_and_servers_mc =
+       IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT;
+
+/* from netinet/in.h */
+struct in6_pktinfo {
+       struct in6_addr ipi6_addr;  /* src/dst IPv6 address */
+       unsigned int ipi6_ifindex;  /* send/recv interface index */
+};
+
+int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
+{
+       struct msghdr m;
+       struct iovec v;
+       struct in6_pktinfo *pktinfo;
+       struct cmsghdr *cmsg;
+       int fd, ret;
+       struct sockaddr_in6 dst;
+       void *control_buf;
+       size_t control_buf_len;
+
+       fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       if (fd < 0)
+               return -errno;
+
+       memset(&dst, 0, sizeof(dst));
+       dst.sin6_family = AF_INET6;
+       dst.sin6_port = htons(DHCPV6_SERVER_PORT);
+
+       dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;
+
+       control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+       control_buf = g_try_malloc0(control_buf_len);
+       if (control_buf == NULL) {
+               close(fd);
+               return -ENOMEM;
+       }
+
+       memset(&m, 0, sizeof(m));
+       memset(&v, 0, sizeof(v));
+
+       m.msg_name = &dst;
+       m.msg_namelen = sizeof(dst);
+
+       v.iov_base = (char *)dhcp_pkt;
+       v.iov_len = len;
+       m.msg_iov = &v;
+       m.msg_iovlen = 1;
+
+       m.msg_control = control_buf;
+       m.msg_controllen = control_buf_len;
+       cmsg = CMSG_FIRSTHDR(&m);
+       cmsg->cmsg_level = IPPROTO_IPV6;
+       cmsg->cmsg_type = IPV6_PKTINFO;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+
+       pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+       memset(pktinfo, 0, sizeof(*pktinfo));
+       pktinfo->ipi6_ifindex = index;
+       m.msg_controllen = cmsg->cmsg_len;
+
+       ret = sendmsg(fd, &m, 0);
+       if (ret < 0)
+               perror("DHCPv6 msg send failed");
+
+       g_free(control_buf);
+       close(fd);
+
+       return ret;
+}
+
+int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
+               uint32_t source_ip, int source_port, uint32_t dest_ip,
+                       int dest_port, const uint8_t *dest_arp, int ifindex)
+{
+       struct sockaddr_ll dest;
+       struct ip_udp_dhcp_packet packet;
+       int fd, n;
+
+       enum {
+               IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) -
+                                               EXTEND_FOR_BUGGY_SERVERS,
+               UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE -
+                               offsetof(struct ip_udp_dhcp_packet, udp),
+       };
+
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
+       if (fd < 0)
+               return -errno;
+
+       memset(&dest, 0, sizeof(dest));
+       memset(&packet, 0, sizeof(packet));
+       packet.data = *dhcp_pkt;
+
+       dest.sll_family = AF_PACKET;
+       dest.sll_protocol = htons(ETH_P_IP);
+       dest.sll_ifindex = ifindex;
+       dest.sll_halen = 6;
+       memcpy(dest.sll_addr, dest_arp, 6);
+       if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
+               close(fd);
+               return -errno;
+       }
+
+       packet.ip.protocol = IPPROTO_UDP;
+       packet.ip.saddr = source_ip;
+       packet.ip.daddr = dest_ip;
+       packet.udp.source = htons(source_port);
+       packet.udp.dest = htons(dest_port);
+       /* size, excluding IP header: */
+       packet.udp.len = htons(UPD_DHCP_SIZE);
+       /* for UDP checksumming, ip.len is set to UDP packet len */
+       packet.ip.tot_len = packet.udp.len;
+       packet.udp.check = dhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
+       /* but for sending, it is set to IP packet len */
+       packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
+       packet.ip.ihl = sizeof(packet.ip) >> 2;
+       packet.ip.version = IPVERSION;
+       packet.ip.ttl = IPDEFTTL;
+       packet.ip.check = dhcp_checksum(&packet.ip, sizeof(packet.ip));
+
+       /*
+        * Currently we send full-sized DHCP packets (zero padded).
+        * If you need to change this: last byte of the packet is
+        * packet.data.options[dhcp_end_option(packet.data.options)]
+        */
+       n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
+                       (struct sockaddr *) &dest, sizeof(dest));
+       close(fd);
+
+       if (n < 0)
+               return -errno;
+
+       return n;
+}
+
+int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
+                               uint32_t source_ip, int source_port,
+                               uint32_t dest_ip, int dest_port)
+{
+       struct sockaddr_in client;
+       int fd, n, opt = 1;
+
+       enum {
+               DHCP_SIZE = sizeof(struct dhcp_packet) -
+                                       EXTEND_FOR_BUGGY_SERVERS,
+       };
+
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       if (fd < 0)
+               return -errno;
+
+       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+       memset(&client, 0, sizeof(client));
+       client.sin_family = AF_INET;
+       client.sin_port = htons(source_port);
+       client.sin_addr.s_addr = source_ip;
+       if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
+               close(fd);
+               return -errno;
+       }
+
+       memset(&client, 0, sizeof(client));
+       client.sin_family = AF_INET;
+       client.sin_port = htons(dest_port);
+       client.sin_addr.s_addr = dest_ip;
+       if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
+               close(fd);
+               return -errno;
+       }
+
+       n = write(fd, dhcp_pkt, DHCP_SIZE);
+
+       close(fd);
+
+       if (n < 0)
+               return -errno;
+
+       return n;
+}
+
+int dhcp_l3_socket(int port, const char *interface, int family)
+{
+       int fd, opt = 1, len;
+       struct sockaddr_in addr4;
+       struct sockaddr_in6 addr6;
+       struct sockaddr *addr;
+
+       fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+       if (fd < 0)
+               return -errno;
+
+       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+       if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
+                               interface, strlen(interface) + 1) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       if (family == AF_INET) {
+               memset(&addr4, 0, sizeof(addr4));
+               addr4.sin_family = family;
+               addr4.sin_port = htons(port);
+               addr = (struct sockaddr *)&addr4;
+               len = sizeof(addr4);
+       } else if (family == AF_INET6) {
+               memset(&addr6, 0, sizeof(addr6));
+               addr6.sin6_family = family;
+               addr6.sin6_port = htons(port);
+               addr = (struct sockaddr *)&addr6;
+               len = sizeof(addr6);
+       } else {
+               close(fd);
+               return -EINVAL;
+       }
+
+       if (bind(fd, addr, len) != 0) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+char *get_interface_name(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               perror("Open socket error");
+               return NULL;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               perror("Get interface name error");
+               close(sk);
+               return NULL;
+       }
+
+       close(sk);
+
+       return g_strdup(ifr.ifr_name);
+}
+
+gboolean interface_is_up(int index)
+{
+       int sk, err;
+       struct ifreq ifr;
+       gboolean ret = FALSE;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               perror("Open socket error");
+               return FALSE;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               perror("Get interface name error");
+               goto done;
+       }
+
+       err = ioctl(sk, SIOCGIFFLAGS, &ifr);
+       if (err < 0) {
+               perror("Get interface flags error");
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP)
+               ret = TRUE;
+
+done:
+       close(sk);
+
+       return ret;
+}
diff --git a/gdhcp/common.h b/gdhcp/common.h
new file mode 100644 (file)
index 0000000..13f49d8
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ *
+ *  DHCP client library with GLib integration
+ *
+ *  Copyright (C) 2009-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
+ *
+ */
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+#include <glib.h>
+
+#include "gdhcp.h"
+
+#define dhcp_get_unaligned(ptr)                        \
+({                                             \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (void *) (ptr);                \
+       __p->__v;                               \
+})
+
+#define dhcp_put_unaligned(val, ptr)           \
+do {                                           \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (void *) (ptr);                \
+       __p->__v = (val);                       \
+} while (0)
+
+#define CLIENT_PORT 68
+#define SERVER_PORT 67
+
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+#define MAX_DHCPV6_PKT_SIZE 1500
+
+#define EXTEND_FOR_BUGGY_SERVERS 80
+
+static const uint8_t MAC_BCAST_ADDR[ETH_ALEN] __attribute__((aligned(2))) = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const uint8_t MAC_ANY_ADDR[ETH_ALEN] __attribute__((aligned(2))) = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* DHCP packet */
+#define DHCP_MAGIC              0x63825363
+#define DHCP_OPTIONS_BUFSIZE    308
+#define BOOTREQUEST             1
+#define BOOTREPLY               2
+
+#define BROADCAST_FLAG         0x8000
+
+/* See RFC 2131 */
+struct dhcp_packet {
+       uint8_t op;
+       uint8_t htype;
+       uint8_t hlen;
+       uint8_t hops;
+       uint32_t xid;
+       uint16_t secs;
+       uint16_t flags;
+       uint32_t ciaddr;
+       uint32_t yiaddr;
+       uint32_t siaddr_nip;
+       uint32_t gateway_nip;
+       uint8_t chaddr[16];
+       uint8_t sname[64];
+       uint8_t file[128];
+       uint32_t cookie;
+       uint8_t options[DHCP_OPTIONS_BUFSIZE + EXTEND_FOR_BUGGY_SERVERS];
+} __attribute__((packed));
+
+struct ip_udp_dhcp_packet {
+       struct iphdr ip;
+       struct udphdr udp;
+       struct dhcp_packet data;
+} __attribute__((packed));
+
+/* See RFC 3315 */
+struct dhcpv6_packet {
+       uint8_t message;
+       uint8_t transaction_id[3];
+       uint8_t options[];
+} __attribute__((packed));
+
+
+/* See RFC 2132 */
+#define DHCP_PADDING           0x00
+#define DHCP_SUBNET            0x01
+#define DHCP_ROUTER            0x03
+#define DHCP_TIME_SERVER       0x04
+#define DHCP_NAME_SERVER       0x05
+#define DHCP_DNS_SERVER                0x06
+#define DHCP_HOST_NAME         0x0c
+#define DHCP_DOMAIN_NAME       0x0f
+#define DHCP_NTP_SERVER                0x2a
+#define DHCP_REQUESTED_IP      0x32
+#define DHCP_LEASE_TIME                0x33
+#define DHCP_OPTION_OVERLOAD   0x34
+#define DHCP_MESSAGE_TYPE      0x35
+#define DHCP_SERVER_ID         0x36
+#define DHCP_PARAM_REQ         0x37
+#define DHCP_ERR_MESSAGE       0x38
+#define DHCP_MAX_SIZE          0x39
+#define DHCP_VENDOR            0x3c
+#define DHCP_CLIENT_ID         0x3d
+#define DHCP_END               0xff
+
+#define OPT_CODE               0
+#define OPT_LEN                        1
+#define OPT_DATA               2
+#define OPTION_FIELD           0
+#define FILE_FIELD             1
+#define SNAME_FIELD            2
+
+/* DHCP_MESSAGE_TYPE values */
+#define DHCPDISCOVER           1
+#define DHCPOFFER              2
+#define DHCPREQUEST            3
+#define DHCPDECLINE            4
+#define DHCPACK                        5
+#define DHCPNAK                        6
+#define DHCPRELEASE            7
+#define DHCPINFORM             8
+#define DHCP_MINTYPE DHCPDISCOVER
+#define DHCP_MAXTYPE DHCPINFORM
+
+/* Message types for DHCPv6, RFC 3315 sec 5.3 */
+#define DHCPV6_SOLICIT         1
+#define DHCPV6_ADVERTISE       2
+#define DHCPV6_REQUEST         3
+#define DHCPV6_CONFIRM         4
+#define DHCPV6_RENEW           5
+#define DHCPV6_REBIND          6
+#define DHCPV6_REPLY           7
+#define DHCPV6_RELEASE         8
+#define DHCPV6_DECLINE         9
+#define DHCPV6_RECONFIGURE     10
+#define DHCPV6_INFORMATION_REQ 11
+
+/*
+ * DUID time starts 2000-01-01.
+ */
+#define DUID_TIME_EPOCH 946684800
+
+typedef enum {
+       OPTION_UNKNOWN,
+       OPTION_IP,
+       OPTION_STRING,
+       OPTION_U8,
+       OPTION_U16,
+       OPTION_U32,
+       OPTION_TYPE_MASK = 0x0f,
+       OPTION_LIST = 0x10,
+} GDHCPOptionType;
+
+typedef struct dhcp_option {
+       GDHCPOptionType type;
+       uint8_t code;
+} DHCPOption;
+
+/* Length of the option types in binary form */
+static const uint8_t dhcp_option_lengths[] = {
+       [OPTION_IP]     = 4,
+       [OPTION_STRING] = 1,
+       [OPTION_U8]     = 1,
+       [OPTION_U16]    = 2,
+       [OPTION_U32]    = 4,
+};
+
+uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
+uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
+                       int code, uint16_t *option_len, int *option_count);
+uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
+                       uint16_t *code, uint16_t *option_len);
+int dhcp_end_option(uint8_t *optionptr);
+void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt);
+void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
+                               uint16_t *pkt_len, uint8_t *addopt);
+void dhcp_add_simple_option(struct dhcp_packet *packet,
+                               uint8_t code, uint32_t data);
+GDHCPOptionType dhcp_get_code_type(uint8_t code);
+GDHCPOptionType dhcpv6_get_code_type(uint16_t code);
+
+uint16_t dhcp_checksum(void *addr, int count);
+
+void dhcp_init_header(struct dhcp_packet *packet, char type);
+void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type);
+
+int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
+                       uint32_t source_ip, int source_port,
+                       uint32_t dest_ip, int dest_port,
+                       const uint8_t *dest_arp, int ifindex);
+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);
+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,
+                       int buf_len, int fd);
+int dhcp_l3_socket_send(int index, int port, int family);
+
+char *get_interface_name(int index);
+gboolean interface_is_up(int index);
diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
new file mode 100644 (file)
index 0000000..718feaf
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ *
+ *  DHCP library with GLib integration
+ *
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_DHCP_H
+#define __G_DHCP_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* DHCP Client part*/
+struct _GDHCPClient;
+
+typedef struct _GDHCPClient GDHCPClient;
+
+typedef enum {
+       G_DHCP_CLIENT_ERROR_NONE,
+       G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE,
+       G_DHCP_CLIENT_ERROR_INTERFACE_IN_USE,
+       G_DHCP_CLIENT_ERROR_INTERFACE_DOWN,
+       G_DHCP_CLIENT_ERROR_NOMEM,
+       G_DHCP_CLIENT_ERROR_INVALID_INDEX,
+       G_DHCP_CLIENT_ERROR_INVALID_OPTION
+} GDHCPClientError;
+
+typedef enum {
+       G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
+       G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE,
+       G_DHCP_CLIENT_EVENT_NO_LEASE,
+       G_DHCP_CLIENT_EVENT_LEASE_LOST,
+       G_DHCP_CLIENT_EVENT_IPV4LL_LOST,
+       G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT,
+       G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
+       G_DHCP_CLIENT_EVENT_SOLICITATION,
+       G_DHCP_CLIENT_EVENT_ADVERTISE,
+       G_DHCP_CLIENT_EVENT_REQUEST,
+       G_DHCP_CLIENT_EVENT_RENEW,
+       G_DHCP_CLIENT_EVENT_REBIND,
+       G_DHCP_CLIENT_EVENT_RELEASE,
+} GDHCPClientEvent;
+
+typedef enum {
+       G_DHCP_IPV4,
+       G_DHCP_IPV6,
+       G_DHCP_IPV4LL,
+} GDHCPType;
+
+#define G_DHCP_SUBNET          0x01
+#define G_DHCP_ROUTER          0x03
+#define G_DHCP_TIME_SERVER     0x04
+#define G_DHCP_DNS_SERVER      0x06
+#define G_DHCP_DOMAIN_NAME     0x0f
+#define G_DHCP_HOST_NAME       0x0c
+#define G_DHCP_NTP_SERVER      0x2a
+#define G_DHCP_CLIENT_ID       0x3d
+
+#define G_DHCPV6_CLIENTID      1
+#define G_DHCPV6_SERVERID      2
+#define G_DHCPV6_IA_NA         3
+#define G_DHCPV6_IA_TA         4
+#define G_DHCPV6_IAADDR                5
+#define G_DHCPV6_ORO           6
+#define G_DHCPV6_STATUS_CODE   13
+#define G_DHCPV6_RAPID_COMMIT  14
+#define G_DHCPV6_DNS_SERVERS   23
+#define G_DHCPV6_SNTP_SERVERS  31
+
+#define G_DHCPV6_ERROR_SUCCESS 0
+#define G_DHCPV6_ERROR_FAILURE 1
+#define G_DHCPV6_ERROR_NO_ADDR 2
+#define G_DHCPV6_ERROR_BINDING 3
+#define G_DHCPV6_ERROR_LINK    4
+#define G_DHCPV6_ERROR_MCAST   5
+
+typedef enum {
+       G_DHCPV6_DUID_LLT = 1,
+       G_DHCPV6_DUID_EN  = 2,
+       G_DHCPV6_DUID_LL  = 3,
+} GDHCPDuidType;
+
+typedef void (*GDHCPClientEventFunc) (GDHCPClient *client, gpointer user_data);
+
+typedef void (*GDHCPDebugFunc)(const char *str, gpointer user_data);
+
+GDHCPClient *g_dhcp_client_new(GDHCPType type, int index,
+                                               GDHCPClientError *error);
+
+int g_dhcp_client_start(GDHCPClient *client, const char *last_address);
+void g_dhcp_client_stop(GDHCPClient *client);
+
+GDHCPClient *g_dhcp_client_ref(GDHCPClient *client);
+void g_dhcp_client_unref(GDHCPClient *client);
+
+void g_dhcp_client_register_event(GDHCPClient *client,
+                                       GDHCPClientEvent event,
+                                       GDHCPClientEventFunc func,
+                                       gpointer user_data);
+
+GDHCPClientError g_dhcp_client_set_request(GDHCPClient *client,
+                                               unsigned int option_code);
+void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client);
+void g_dhcp_client_clear_values(GDHCPClient *dhcp_client);
+GDHCPClientError g_dhcp_client_set_id(GDHCPClient *client);
+GDHCPClientError g_dhcp_client_set_send(GDHCPClient *client,
+                                               unsigned char option_code,
+                                               const char *option_value);
+
+char *g_dhcp_client_get_address(GDHCPClient *client);
+char *g_dhcp_client_get_netmask(GDHCPClient *client);
+GList *g_dhcp_client_get_option(GDHCPClient *client,
+                                               unsigned char option_code);
+int g_dhcp_client_get_index(GDHCPClient *client);
+
+void g_dhcp_client_set_debug(GDHCPClient *client,
+                               GDHCPDebugFunc func, gpointer user_data);
+int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
+                       unsigned char **duid, int *duid_len);
+int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
+                       int duid_len);
+void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client, uint16_t option_code,
+                       uint8_t *option_value, uint16_t option_len);
+uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client);
+int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...);
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+                               unsigned char *iaid);
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+                               uint32_t *T1, uint32_t *T2,
+                               time_t *last_renew, time_t *last_rebind,
+                               time_t *expire);
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client);
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+               int code, uint32_t *T1, uint32_t *T2, gboolean add_iaaddr);
+void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client);
+void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout);
+
+/* DHCP Server */
+typedef enum {
+       G_DHCP_SERVER_ERROR_NONE,
+       G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE,
+       G_DHCP_SERVER_ERROR_INTERFACE_IN_USE,
+       G_DHCP_SERVER_ERROR_INTERFACE_DOWN,
+       G_DHCP_SERVER_ERROR_NOMEM,
+       G_DHCP_SERVER_ERROR_INVALID_INDEX,
+       G_DHCP_SERVER_ERROR_INVALID_OPTION,
+       G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID
+} GDHCPServerError;
+
+typedef void (*GDHCPSaveLeaseFunc) (unsigned char *mac,
+                       unsigned int nip, unsigned int expire);
+struct _GDHCPServer;
+
+typedef struct _GDHCPServer GDHCPServer;
+
+GDHCPServer *g_dhcp_server_new(GDHCPType type,
+               int ifindex, GDHCPServerError *error);
+int g_dhcp_server_start(GDHCPServer *server);
+void g_dhcp_server_stop(GDHCPServer *server);
+
+GDHCPServer *g_dhcp_server_ref(GDHCPServer *server);
+void g_dhcp_server_unref(GDHCPServer *server);
+
+int g_dhcp_server_set_option(GDHCPServer *server,
+               unsigned char option_code, const char *option_value);
+int g_dhcp_server_set_ip_range(GDHCPServer *server,
+               const char *start_ip, const char *end_ip);
+void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
+                               unsigned char *mac, unsigned int lease_ip);
+void g_dhcp_server_set_debug(GDHCPServer *server,
+                               GDHCPDebugFunc func, gpointer user_data);
+void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server,
+                                               unsigned int lease_time);
+void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
+                               GDHCPSaveLeaseFunc func, gpointer user_data);
+#if defined TIZEN_EXT
+void g_dhcp_client_set_address_known(GDHCPClient *client, gboolean known);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_DHCP_H */
diff --git a/gdhcp/ipv4ll.c b/gdhcp/ipv4ll.c
new file mode 100644 (file)
index 0000000..4c09572
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  IPV4 Local Link library with GLib integration
+ *
+ *  Copyright (C) 2009-2010  Aldebaran Robotics. 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
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include "ipv4ll.h"
+
+/**
+ * Return a random link local IP
+ */
+uint32_t ipv4ll_random_ip(int seed)
+{
+       unsigned tmp;
+
+       if (seed)
+               srand(seed);
+       else {
+               struct timeval tv;
+               gettimeofday(&tv, NULL);
+               srand(tv.tv_usec);
+       }
+       do {
+               tmp = rand();
+               tmp = tmp & IN_CLASSB_HOST;
+       } while (tmp > (IN_CLASSB_HOST - 0x0200));
+       return ((LINKLOCAL_ADDR + 0x0100) + tmp);
+}
+
+/**
+ * Return a random delay in range of zero to secs*1000
+ */
+guint ipv4ll_random_delay_ms(guint secs)
+{
+       struct timeval tv;
+       guint tmp;
+
+       gettimeofday(&tv, NULL);
+       srand(tv.tv_usec);
+       tmp = rand();
+       return tmp % (secs * 1000);
+}
+
+int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
+                   uint32_t target_ip, int ifindex)
+{
+       struct sockaddr_ll dest;
+       struct ether_arp p;
+       uint32_t ip_source;
+       uint32_t ip_target;
+       int fd, n;
+
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP));
+       if (fd < 0)
+               return -errno;
+
+       memset(&dest, 0, sizeof(dest));
+       memset(&p, 0, sizeof(p));
+
+       dest.sll_family = AF_PACKET;
+       dest.sll_protocol = htons(ETH_P_ARP);
+       dest.sll_ifindex = ifindex;
+       dest.sll_halen = ETH_ALEN;
+       memset(dest.sll_addr, 0xFF, ETH_ALEN);
+       if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
+               close(fd);
+               return -errno;
+       }
+
+       ip_source = htonl(source_ip);
+       ip_target = htonl(target_ip);
+       p.arp_hrd = htons(ARPHRD_ETHER);
+       p.arp_pro = htons(ETHERTYPE_IP);
+       p.arp_hln = ETH_ALEN;
+       p.arp_pln = 4;
+       p.arp_op = htons(ARPOP_REQUEST);
+
+       memcpy(&p.arp_sha, source_eth, ETH_ALEN);
+       memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa));
+       memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa));
+
+       n = sendto(fd, &p, sizeof(p), 0,
+              (struct sockaddr*) &dest, sizeof(dest));
+       if (n < 0)
+               return -errno;
+
+       close(fd);
+
+       return n;
+}
+
+int ipv4ll_arp_socket(int ifindex)
+{
+       int fd;
+       struct sockaddr_ll sock;
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP));
+       if (fd < 0)
+               return fd;
+
+       sock.sll_family = AF_PACKET;
+       sock.sll_protocol = htons(ETH_P_ARP);
+       sock.sll_ifindex = ifindex;
+
+       if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
+               close(fd);
+               return -errno;
+       }
+
+       return fd;
+}
diff --git a/gdhcp/ipv4ll.h b/gdhcp/ipv4ll.h
new file mode 100644 (file)
index 0000000..aaac33e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  IPV4 Local Link library with GLib integration
+ *
+ *  Copyright (C) 2009-2010  Aldebaran Robotics. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_IPV4LL_H
+#define __G_IPV4LL_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 169.254.0.0 */
+#define LINKLOCAL_ADDR 0xa9fe0000
+
+/* See RFC 3927 */
+#define PROBE_WAIT          1
+#define PROBE_NUM           3
+#define PROBE_MIN           1
+#define PROBE_MAX           2
+#define ANNOUNCE_WAIT       2
+#define ANNOUNCE_NUM        2
+#define ANNOUNCE_INTERVAL    2
+#define MAX_CONFLICTS      10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL            10
+
+uint32_t ipv4ll_random_ip(int seed);
+guint ipv4ll_random_delay_ms(guint secs);
+int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
+                   uint32_t target_ip, int ifindex);
+int ipv4ll_arp_socket(int ifindex);
+
+#ifdef __cplusplus
+}
+#endif
+#endif     /* !IPV4LL_H_ */
diff --git a/gdhcp/server.c b/gdhcp/server.c
new file mode 100644 (file)
index 0000000..a342985
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ *
+ *  DHCP Server library with GLib integration
+ *
+ *  Copyright (C) 2009-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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include <linux/if.h>
+#include <linux/filter.h>
+
+#include <glib.h>
+
+#include "common.h"
+
+/* 8 hours */
+#define DEFAULT_DHCP_LEASE_SEC (8*60*60)
+
+/* 5 minutes  */
+#define OFFER_TIME (5*60)
+
+struct _GDHCPServer {
+       int ref_count;
+       GDHCPType type;
+       gboolean started;
+       int ifindex;
+       char *interface;
+       uint32_t start_ip;
+       uint32_t end_ip;
+       uint32_t server_nip;
+       uint32_t lease_seconds;
+       int listener_sockfd;
+       guint listener_watch;
+       GIOChannel *listener_channel;
+       GList *lease_list;
+       GHashTable *nip_lease_hash;
+       GHashTable *option_hash; /* Options send to client */
+       GDHCPSaveLeaseFunc save_lease_func;
+       GDHCPDebugFunc debug_func;
+       gpointer debug_data;
+};
+
+struct dhcp_lease {
+       time_t expire;
+       uint32_t lease_nip;
+       uint8_t lease_mac[ETH_ALEN];
+};
+
+static inline void debug(GDHCPServer *server, const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (server->debug_func == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               server->debug_func(str, server->debug_data);
+
+       va_end(ap);
+}
+
+static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server,
+                                               const uint8_t *mac)
+{
+       GList *list;
+
+       for (list = dhcp_server->lease_list; list; list = list->next) {
+               struct dhcp_lease *lease = list->data;
+
+               if (memcmp(lease->lease_mac, mac, ETH_ALEN) == 0)
+                       return lease;
+       }
+
+       return NULL;
+}
+
+static void remove_lease(GDHCPServer *dhcp_server, struct dhcp_lease *lease)
+{
+       dhcp_server->lease_list =
+                       g_list_remove(dhcp_server->lease_list, lease);
+
+       g_hash_table_remove(dhcp_server->nip_lease_hash,
+                               GINT_TO_POINTER((int) lease->lease_nip));
+       g_free(lease);
+}
+
+/* Clear the old lease and create the new one */
+static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
+                               const uint8_t *mac, struct dhcp_lease **lease)
+{
+       struct dhcp_lease *lease_nip, *lease_mac;
+
+       if (yiaddr == 0)
+               return -ENXIO;
+
+       if (ntohl(yiaddr) < dhcp_server->start_ip)
+               return -ENXIO;
+
+       if (ntohl(yiaddr) > dhcp_server->end_ip)
+               return -ENXIO;
+
+       if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
+               return -ENXIO;
+
+       if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
+               return -ENXIO;
+
+       lease_mac = find_lease_by_mac(dhcp_server, mac);
+
+       lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
+                                               GINT_TO_POINTER((int) yiaddr));
+       debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);
+
+       if (lease_nip != NULL) {
+               dhcp_server->lease_list =
+                               g_list_remove(dhcp_server->lease_list,
+                                                               lease_nip);
+               g_hash_table_remove(dhcp_server->nip_lease_hash,
+                                       GINT_TO_POINTER((int) yiaddr));
+
+               if (lease_mac == NULL)
+                       *lease = lease_nip;
+               else if (lease_nip != lease_mac) {
+                       remove_lease(dhcp_server, lease_mac);
+                       *lease = lease_nip;
+               } else
+                       *lease = lease_nip;
+
+               return 0;
+       }
+
+       if (lease_mac != NULL) {
+               dhcp_server->lease_list =
+                               g_list_remove(dhcp_server->lease_list,
+                                                               lease_mac);
+               g_hash_table_remove(dhcp_server->nip_lease_hash,
+                               GINT_TO_POINTER((int) lease_mac->lease_nip));
+               *lease = lease_mac;
+
+               return 0;
+       }
+
+       *lease = g_try_new0(struct dhcp_lease, 1);
+       if (*lease == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static gint compare_expire(gconstpointer a, gconstpointer b)
+{
+       const struct dhcp_lease *lease1 = a;
+       const struct dhcp_lease *lease2 = b;
+
+       return lease2->expire - lease1->expire;
+}
+
+static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t expire,
+                                       const uint8_t *chaddr, uint32_t yiaddr)
+{
+       struct dhcp_lease *lease = NULL;
+       int ret;
+
+       ret = get_lease(dhcp_server, yiaddr, chaddr, &lease);
+       if (ret != 0)
+               return NULL;
+
+       memset(lease, 0, sizeof(*lease));
+
+       memcpy(lease->lease_mac, chaddr, ETH_ALEN);
+       lease->lease_nip = yiaddr;
+
+       if (expire == 0)
+               lease->expire = time(NULL) + dhcp_server->lease_seconds;
+       else
+               lease->expire = expire;
+
+       dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
+                                                       lease, compare_expire);
+
+       g_hash_table_insert(dhcp_server->nip_lease_hash,
+                               GINT_TO_POINTER((int) lease->lease_nip), lease);
+
+       return lease;
+}
+
+static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
+                                                               uint32_t nip)
+{
+       return g_hash_table_lookup(dhcp_server->nip_lease_hash,
+                                               GINT_TO_POINTER((int) nip));
+}
+
+/* Check if the IP is taken; if it is, add it to the lease table */
+static gboolean arp_check(uint32_t nip, const uint8_t *safe_mac)
+{
+       /* TODO: Add ARP checking */
+       return TRUE;
+}
+
+static gboolean is_expired_lease(struct dhcp_lease *lease)
+{
+       if (lease->expire < time(NULL))
+               return TRUE;
+
+       return FALSE;
+}
+
+static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
+                                       const uint8_t *safe_mac)
+{
+       uint32_t ip_addr;
+       struct dhcp_lease *lease;
+       GList *list;
+       ip_addr = dhcp_server->start_ip;
+       for (; ip_addr <= dhcp_server->end_ip; ip_addr++) {
+               /* e.g. 192.168.55.0 */
+               if ((ip_addr & 0xff) == 0)
+                       continue;
+
+               /* e.g. 192.168.55.255 */
+               if ((ip_addr & 0xff) == 0xff)
+                       continue;
+
+               lease = find_lease_by_nip(dhcp_server,
+                               (uint32_t) htonl(ip_addr));
+               if (lease != NULL)
+                       continue;
+
+               if (arp_check(htonl(ip_addr), safe_mac) == TRUE)
+                       return htonl(ip_addr);
+       }
+
+       /* The last lease is the oldest one */
+       list = g_list_last(dhcp_server->lease_list);
+       if (list == NULL)
+               return 0;
+
+       lease = list->data;
+       if (lease == NULL)
+               return 0;
+
+        if (is_expired_lease(lease) == FALSE)
+               return 0;
+
+        if (arp_check(lease->lease_nip, safe_mac) == FALSE)
+               return 0;
+
+       return lease->lease_nip;
+}
+
+static void lease_set_expire(GDHCPServer *dhcp_server,
+                       struct dhcp_lease *lease, uint32_t expire)
+{
+       dhcp_server->lease_list = g_list_remove(dhcp_server->lease_list, lease);
+
+       lease->expire = expire;
+
+       dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
+                                                       lease, compare_expire);
+}
+
+static void destroy_lease_table(GDHCPServer *dhcp_server)
+{
+       GList *list;
+
+       g_hash_table_destroy(dhcp_server->nip_lease_hash);
+
+       dhcp_server->nip_lease_hash = NULL;
+
+       for (list = dhcp_server->lease_list; list; list = list->next) {
+               struct dhcp_lease *lease = list->data;
+
+               g_free(lease);
+       }
+
+       g_list_free(dhcp_server->lease_list);
+
+       dhcp_server->lease_list = NULL;
+}
+static uint32_t get_interface_address(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+       struct sockaddr_in *server_ip;
+       uint32_t ret = 0;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               perror("Open socket error");
+               return 0;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err < 0) {
+               perror("Get interface name error");
+               goto done;
+       }
+
+       err = ioctl(sk, SIOCGIFADDR, &ifr);
+       if (err < 0) {
+               perror("Get ip address error");
+               goto done;
+       }
+
+       server_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+       ret = server_ip->sin_addr.s_addr;
+
+done:
+       close(sk);
+
+       return ret;
+}
+
+GDHCPServer *g_dhcp_server_new(GDHCPType type,
+               int ifindex, GDHCPServerError *error)
+{
+       GDHCPServer *dhcp_server = NULL;
+
+       if (ifindex < 0) {
+               *error = G_DHCP_SERVER_ERROR_INVALID_INDEX;
+               return NULL;
+       }
+
+       dhcp_server = g_try_new0(GDHCPServer, 1);
+       if (dhcp_server == NULL) {
+               *error = G_DHCP_SERVER_ERROR_NOMEM;
+               return NULL;
+       }
+
+       dhcp_server->interface = get_interface_name(ifindex);
+       if (dhcp_server->interface == NULL) {
+               *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
+               goto error;
+       }
+
+       if (interface_is_up(ifindex) == FALSE) {
+               *error = G_DHCP_SERVER_ERROR_INTERFACE_DOWN;
+               goto error;
+       }
+
+       dhcp_server->server_nip = get_interface_address(ifindex);
+       if (dhcp_server->server_nip == 0) {
+               *error = G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID;
+               goto error;
+       }
+
+       dhcp_server->nip_lease_hash = g_hash_table_new_full(g_direct_hash,
+                                               g_direct_equal, NULL, NULL);
+       dhcp_server->option_hash = g_hash_table_new_full(g_direct_hash,
+                                               g_direct_equal, NULL, NULL);
+
+       dhcp_server->started = FALSE;
+
+       /* All the leases have the same fixed lease time,
+        * do not support DHCP_LEASE_TIME option from client.
+        */
+       dhcp_server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
+
+       dhcp_server->type = type;
+       dhcp_server->ref_count = 1;
+       dhcp_server->ifindex = ifindex;
+       dhcp_server->listener_sockfd = -1;
+       dhcp_server->listener_watch = -1;
+       dhcp_server->listener_channel = NULL;
+       dhcp_server->save_lease_func = NULL;
+       dhcp_server->debug_func = NULL;
+       dhcp_server->debug_data = NULL;
+
+       *error = G_DHCP_SERVER_ERROR_NONE;
+
+       return dhcp_server;
+
+error:
+       g_free(dhcp_server->interface);
+       g_free(dhcp_server);
+       return NULL;
+}
+
+
+static uint8_t check_packet_type(struct dhcp_packet *packet)
+{
+       uint8_t *type;
+
+       if (packet->hlen != ETH_ALEN)
+               return 0;
+
+       if (packet->op != BOOTREQUEST)
+               return 0;
+
+       type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
+
+       if (type == NULL)
+               return 0;
+
+       if (*type < DHCP_MINTYPE)
+               return 0;
+
+       if (*type > DHCP_MAXTYPE)
+               return 0;
+
+       return *type;
+}
+
+static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
+                               struct dhcp_packet *client_packet, char type)
+{
+       /* Sets op, htype, hlen, cookie fields
+        * and adds DHCP_MESSAGE_TYPE option */
+       dhcp_init_header(packet, type);
+
+       packet->xid = client_packet->xid;
+       memcpy(packet->chaddr, client_packet->chaddr,
+                               sizeof(client_packet->chaddr));
+       packet->flags = client_packet->flags;
+       packet->gateway_nip = client_packet->gateway_nip;
+       packet->ciaddr = client_packet->ciaddr;
+       dhcp_add_simple_option(packet, DHCP_SERVER_ID, dhcp_server->server_nip);
+}
+
+static void add_option(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *option_value = value;
+       uint8_t option_code = GPOINTER_TO_INT(key);
+       struct in_addr nip;
+       struct dhcp_packet *packet = user_data;
+
+       if (option_value == NULL)
+               return;
+
+       switch (option_code) {
+       case G_DHCP_SUBNET:
+       case G_DHCP_ROUTER:
+       case G_DHCP_DNS_SERVER:
+               if (inet_aton(option_value, &nip) == 0)
+                       return;
+
+               dhcp_add_simple_option(packet, (uint8_t) option_code,
+                                                               nip.s_addr);
+               break;
+       default:
+               return;
+       }
+}
+
+static void add_server_options(GDHCPServer *dhcp_server,
+                               struct dhcp_packet *packet)
+{
+       g_hash_table_foreach(dhcp_server->option_hash,
+                               add_option, packet);
+}
+
+static gboolean check_requested_nip(GDHCPServer *dhcp_server,
+                                       uint32_t requested_nip)
+{
+       struct dhcp_lease *lease;
+
+       if (requested_nip == 0)
+               return FALSE;
+
+       if (ntohl(requested_nip) < dhcp_server->start_ip)
+               return FALSE;
+
+       if (ntohl(requested_nip) > dhcp_server->end_ip)
+               return FALSE;
+
+       lease = find_lease_by_nip(dhcp_server, requested_nip);
+       if (lease == NULL)
+               return TRUE;
+
+       if (is_expired_lease(lease) == FALSE)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void send_packet_to_client(GDHCPServer *dhcp_server,
+                               struct dhcp_packet *dhcp_pkt)
+{
+       const uint8_t *chaddr;
+       uint32_t ciaddr;
+
+       if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
+                               || dhcp_pkt->ciaddr == 0) {
+               debug(dhcp_server, "Broadcasting packet to client");
+               ciaddr = INADDR_BROADCAST;
+               chaddr = MAC_BCAST_ADDR;
+       } else {
+               debug(dhcp_server, "Unicasting packet to client ciaddr");
+               ciaddr = dhcp_pkt->ciaddr;
+               chaddr = dhcp_pkt->chaddr;
+       }
+
+       dhcp_send_raw_packet(dhcp_pkt,
+               dhcp_server->server_nip, SERVER_PORT,
+               ciaddr, CLIENT_PORT, chaddr,
+               dhcp_server->ifindex);
+}
+
+static void send_offer(GDHCPServer *dhcp_server,
+                       struct dhcp_packet *client_packet,
+                               struct dhcp_lease *lease,
+                                       uint32_t requested_nip)
+{
+       struct dhcp_packet packet;
+       struct in_addr addr;
+
+       init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
+
+       if (lease)
+               packet.yiaddr = lease->lease_nip;
+       else if (check_requested_nip(dhcp_server, requested_nip) == TRUE)
+               packet.yiaddr = requested_nip;
+       else
+               packet.yiaddr = find_free_or_expired_nip(
+                               dhcp_server, client_packet->chaddr);
+
+       debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
+
+       if (!packet.yiaddr) {
+               debug(dhcp_server, "Err: Can not found lease and send offer");
+               return;
+       }
+
+       lease = add_lease(dhcp_server, OFFER_TIME,
+                               packet.chaddr, packet.yiaddr);
+       if (lease == NULL) {
+               debug(dhcp_server,
+                               "Err: No free IP addresses. OFFER abandoned");
+               return;
+       }
+
+       dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
+                               htonl(dhcp_server->lease_seconds));
+       add_server_options(dhcp_server, &packet);
+
+       addr.s_addr = packet.yiaddr;
+
+       debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
+       send_packet_to_client(dhcp_server, &packet);
+}
+
+static void save_lease(GDHCPServer *dhcp_server)
+{
+       GList *list;
+
+       if (dhcp_server->save_lease_func == NULL)
+               return;
+
+       for (list = dhcp_server->lease_list; list; list = list->next) {
+               struct dhcp_lease *lease = list->data;
+               dhcp_server->save_lease_func(lease->lease_mac,
+                                       lease->lease_nip, lease->expire);
+       }
+}
+
+static void send_ACK(GDHCPServer *dhcp_server,
+               struct dhcp_packet *client_packet, uint32_t yiaddr)
+{
+       struct dhcp_packet packet;
+       uint32_t lease_time_sec;
+       struct in_addr addr;
+
+       init_packet(dhcp_server, &packet, client_packet, DHCPACK);
+       packet.yiaddr = yiaddr;
+
+       lease_time_sec = dhcp_server->lease_seconds;
+
+       dhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+
+       add_server_options(dhcp_server, &packet);
+
+       addr.s_addr = yiaddr;
+
+       debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
+
+       send_packet_to_client(dhcp_server, &packet);
+
+       add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
+}
+
+static void send_NAK(GDHCPServer *dhcp_server,
+                       struct dhcp_packet *client_packet)
+{
+       struct dhcp_packet packet;
+
+       init_packet(dhcp_server, &packet, client_packet, DHCPNAK);
+
+       debug(dhcp_server, "Sending NAK");
+
+       dhcp_send_raw_packet(&packet,
+                       dhcp_server->server_nip, SERVER_PORT,
+                       INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
+                       dhcp_server->ifindex);
+}
+
+static void send_inform(GDHCPServer *dhcp_server,
+                               struct dhcp_packet *client_packet)
+{
+       struct dhcp_packet packet;
+
+       init_packet(dhcp_server, &packet, client_packet, DHCPACK);
+       add_server_options(dhcp_server, &packet);
+       send_packet_to_client(dhcp_server, &packet);
+}
+
+static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       GDHCPServer *dhcp_server = user_data;
+       struct dhcp_packet packet;
+       struct dhcp_lease *lease;
+       uint32_t requested_nip = 0;
+       uint8_t type, *server_id_option, *request_ip_option;
+       int re;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               dhcp_server->listener_watch = 0;
+               return FALSE;
+       }
+
+       re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
+       if (re < 0)
+               return TRUE;
+
+       type = check_packet_type(&packet);
+       if (type == 0)
+               return TRUE;
+
+       server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
+       if (server_id_option) {
+               uint32_t server_nid = dhcp_get_unaligned(
+                                       (uint32_t *) server_id_option);
+
+               if (server_nid != dhcp_server->server_nip)
+                       return TRUE;
+       }
+
+       request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
+       if (request_ip_option)
+               requested_nip = dhcp_get_unaligned(
+                                       (uint32_t *) request_ip_option);
+
+       lease = find_lease_by_mac(dhcp_server, packet.chaddr);
+
+       switch (type) {
+               case DHCPDISCOVER:
+                       debug(dhcp_server, "Received DISCOVER");
+
+                       send_offer(dhcp_server, &packet, lease, requested_nip);
+               break;
+               case DHCPREQUEST:
+                       debug(dhcp_server, "Received REQUEST NIP %d",
+                                                       requested_nip);
+                       if (requested_nip == 0) {
+                               requested_nip = packet.ciaddr;
+                               if (requested_nip == 0)
+                                       break;
+                       }
+
+                       if (lease && requested_nip == lease->lease_nip) {
+                               debug(dhcp_server, "Sending ACK");
+                               send_ACK(dhcp_server, &packet,
+                                               lease->lease_nip);
+                               break;
+                       }
+
+                       if (server_id_option || lease == NULL) {
+                               debug(dhcp_server, "Sending NAK");
+                               send_NAK(dhcp_server, &packet);
+                       }
+
+               break;
+               case DHCPDECLINE:
+                       debug(dhcp_server, "Received DECLINE");
+
+                       if (server_id_option == NULL)
+                               break;
+
+                       if (request_ip_option == NULL)
+                               break;
+
+                       if (lease == NULL)
+                               break;
+
+                       if (requested_nip == lease->lease_nip)
+                               remove_lease(dhcp_server, lease);
+
+               break;
+               case DHCPRELEASE:
+                       debug(dhcp_server, "Received RELEASE");
+
+                       if (server_id_option == NULL)
+                               break;
+
+                       if (lease == NULL)
+                               break;
+
+                       if (packet.ciaddr == lease->lease_nip)
+                               lease_set_expire(dhcp_server, lease,
+                                                               time(NULL));
+               break;
+               case DHCPINFORM:
+                       debug(dhcp_server, "Received INFORM");
+                       send_inform(dhcp_server, &packet);
+               break;
+       }
+
+       return TRUE;
+}
+
+/* Caller need to load leases before call it */
+int g_dhcp_server_start(GDHCPServer *dhcp_server)
+{
+       GIOChannel *listener_channel;
+       int listener_sockfd;
+
+       if (dhcp_server->started == TRUE)
+               return 0;
+
+       listener_sockfd = dhcp_l3_socket(SERVER_PORT,
+                                       dhcp_server->interface, AF_INET);
+       if (listener_sockfd < 0)
+               return -EIO;
+
+       listener_channel = g_io_channel_unix_new(listener_sockfd);
+       if (listener_channel == NULL) {
+               close(listener_sockfd);
+               return -EIO;
+       }
+
+       dhcp_server->listener_sockfd = listener_sockfd;
+       dhcp_server->listener_channel = listener_channel;
+
+       g_io_channel_set_close_on_unref(listener_channel, TRUE);
+       dhcp_server->listener_watch =
+                       g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
+                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               listener_event, dhcp_server,
+                                                               NULL);
+       g_io_channel_unref(dhcp_server->listener_channel);
+
+       dhcp_server->started = TRUE;
+
+       return 0;
+}
+
+int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
+               unsigned char option_code, const char *option_value)
+{
+       struct in_addr nip;
+
+       if (option_value == NULL)
+               return -EINVAL;
+
+       debug(dhcp_server, "option_code %d option_value %s",
+                                       option_code, option_value);
+       switch (option_code) {
+       case G_DHCP_SUBNET:
+       case G_DHCP_ROUTER:
+       case G_DHCP_DNS_SERVER:
+               if (inet_aton(option_value, &nip) == 0)
+                       return -ENXIO;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       g_hash_table_replace(dhcp_server->option_hash,
+                       GINT_TO_POINTER((int) option_code),
+                                       (gpointer) option_value);
+       return 0;
+}
+
+void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
+                               GDHCPSaveLeaseFunc func, gpointer user_data)
+{
+       if (dhcp_server == NULL)
+               return;
+
+       dhcp_server->save_lease_func = func;
+}
+
+GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
+{
+       if (dhcp_server == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&dhcp_server->ref_count, 1);
+
+       return dhcp_server;
+}
+
+void g_dhcp_server_stop(GDHCPServer *dhcp_server)
+{
+       /* Save leases, before stop; load them before start */
+       save_lease(dhcp_server);
+
+       if (dhcp_server->listener_watch > 0) {
+               g_source_remove(dhcp_server->listener_watch);
+               dhcp_server->listener_watch = 0;
+       }
+
+       dhcp_server->listener_channel = NULL;
+
+       dhcp_server->started = FALSE;
+}
+
+void g_dhcp_server_unref(GDHCPServer *dhcp_server)
+{
+       if (dhcp_server == NULL)
+               return;
+
+       if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1)
+               return;
+
+       g_dhcp_server_stop(dhcp_server);
+
+       g_hash_table_destroy(dhcp_server->option_hash);
+
+       destroy_lease_table(dhcp_server);
+
+       g_free(dhcp_server->interface);
+
+       g_free(dhcp_server);
+}
+
+void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
+                               unsigned char *mac, unsigned int lease_ip)
+{
+       add_lease(dhcp_server, expire, mac, lease_ip);
+}
+
+int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
+               const char *start_ip, const char *end_ip)
+{
+       struct in_addr _host_addr;
+
+       if (inet_aton(start_ip, &_host_addr) == 0)
+               return -ENXIO;
+
+       dhcp_server->start_ip = ntohl(_host_addr.s_addr);
+
+       if (inet_aton(end_ip, &_host_addr) == 0)
+               return -ENXIO;
+
+       dhcp_server->end_ip = ntohl(_host_addr.s_addr);
+
+       return 0;
+}
+
+void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server, unsigned int lease_time)
+{
+       if (dhcp_server == NULL)
+               return;
+
+       dhcp_server->lease_seconds = lease_time;
+}
+
+void g_dhcp_server_set_debug(GDHCPServer *dhcp_server,
+                               GDHCPDebugFunc func, gpointer user_data)
+{
+       if (dhcp_server == NULL)
+               return;
+
+       dhcp_server->debug_func = func;
+       dhcp_server->debug_data = user_data;
+}
diff --git a/gsupplicant/dbus.c b/gsupplicant/dbus.c
new file mode 100644 (file)
index 0000000..0da5fbd
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ *
+ *  WPA supplicant library with GLib integration
+ *
+ *  Copyright (C) 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "dbus.h"
+
+#define TIMEOUT 5000
+
+static DBusConnection *connection;
+
+void supplicant_dbus_setup(DBusConnection *conn)
+{
+       connection = conn;
+}
+
+void supplicant_dbus_array_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_array_function function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) {
+               if (function != NULL)
+                       function(&entry, user_data);
+
+               dbus_message_iter_next(&entry);
+       }
+}
+
+void supplicant_dbus_property_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data)
+{
+       DBusMessageIter dict;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       return;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (key != NULL) {
+                       if (strcmp(key, "Properties") == 0)
+                               supplicant_dbus_property_foreach(&value,
+                                                       function, user_data);
+                       else if (function != NULL)
+                               function(key, &value, user_data);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+struct property_get_data {
+       supplicant_dbus_property_function function;
+       void *user_data;
+};
+
+static void property_get_all_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_get_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       supplicant_dbus_property_foreach(&iter, data->function,
+                                                       data->user_data);
+
+       if (data->function != NULL)
+               data->function(NULL, NULL, data->user_data);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_property_get_all(const char *path, const char *interface,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data)
+{
+       struct property_get_data *data;
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                       DBUS_INTERFACE_PROPERTIES, "GetAll");
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &interface, NULL);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, property_get_all_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static void property_get_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_get_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
+               DBusMessageIter variant;
+
+               dbus_message_iter_recurse(&iter, &variant);
+
+               if (data->function != NULL)
+                       data->function(NULL, &variant, data->user_data);
+       }
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_property_get(const char *path, const char *interface,
+                               const char *method,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data)
+{
+       struct property_get_data *data;
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL || method == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                       DBUS_INTERFACE_PROPERTIES, "Get");
+
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &interface,
+                                       DBUS_TYPE_STRING, &method, NULL);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, property_get_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+struct property_set_data {
+       supplicant_dbus_result_function function;
+       void *user_data;
+};
+
+static void property_set_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_set_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       const char *error;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               error = dbus_message_get_error_name(reply);
+       else
+               error = NULL;
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (data->function != NULL)
+               data->function(error, &iter, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_property_set(const char *path, const char *interface,
+                               const char *key, const char *signature,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data)
+{
+       struct property_set_data *data;
+       DBusMessage *message;
+       DBusMessageIter iter, value;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL)
+               return -EINVAL;
+
+       if (key == NULL || signature == NULL || setup == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                       DBUS_INTERFACE_PROPERTIES, "Set");
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       setup(&value, user_data);
+       dbus_message_iter_close_container(&iter, &value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, property_set_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+struct method_call_data {
+       supplicant_dbus_result_function function;
+       void *user_data;
+};
+
+static void method_call_reply(DBusPendingCall *call, void *user_data)
+{
+       struct method_call_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       const char *error;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               error = dbus_message_get_error_name(reply);
+       else
+               error = NULL;
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (data->function != NULL)
+               data->function(error, &iter, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_method_call(const char *path,
+                               const char *interface, const char *method,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data)
+{
+       struct method_call_data *data;
+       DBusMessage *message;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL || method == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                                       interface, method);
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       if (setup != NULL)
+               setup(&iter, user_data);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, method_call_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter value;
+       const char *signature;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       case DBUS_TYPE_STRING:
+               signature = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_BYTE:
+               signature = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT16:
+               signature = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+       case DBUS_TYPE_INT16:
+               signature = DBUS_TYPE_INT16_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT32:
+               signature = DBUS_TYPE_UINT32_AS_STRING;
+               break;
+       case DBUS_TYPE_INT32:
+               signature = DBUS_TYPE_INT32_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       default:
+               signature = DBUS_TYPE_VARIANT_AS_STRING;
+               break;
+       }
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void supplicant_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_BYTE:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       dbus_message_iter_append_fixed_array(&array, type, val, len);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void supplicant_dbus_property_append_array(DBusMessageIter *iter,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       if (function)
+               function(&array, user_data);
+
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
diff --git a/gsupplicant/dbus.h b/gsupplicant/dbus.h
new file mode 100644 (file)
index 0000000..dfe77fe
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *
+ *  WPA supplicant library with GLib integration
+ *
+ *  Copyright (C) 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
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define SUPPLICANT_SERVICE     "fi.w1.wpa_supplicant1"
+#define SUPPLICANT_INTERFACE   "fi.w1.wpa_supplicant1"
+#define SUPPLICANT_PATH                "/fi/w1/wpa_supplicant1"
+
+typedef void (*supplicant_dbus_array_function) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+typedef void (*supplicant_dbus_property_function) (const char *key,
+                               DBusMessageIter *iter, void *user_data);
+
+typedef void (*supplicant_dbus_setup_function) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+typedef void (*supplicant_dbus_result_function) (const char *error,
+                               DBusMessageIter *iter, void *user_data);
+
+void supplicant_dbus_property_append_array(DBusMessageIter *iter,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data);
+
+void supplicant_dbus_setup(DBusConnection *conn);
+
+void supplicant_dbus_array_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_array_function function,
+                                                       void *user_data);
+
+void supplicant_dbus_property_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_property_get_all(const char *path, const char *interface,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_property_get(const char *path, const char *interface,
+                               const char *method,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_property_set(const char *path, const char *interface,
+                               const char *key, const char *signature,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_method_call(const char *path,
+                               const char *interface, const char *method,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data);
+
+void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val);
+
+void supplicant_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len);
+
+static inline void supplicant_dbus_dict_open(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void supplicant_dbus_dict_close(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_close_container(iter, dict);
+}
+
+static inline void supplicant_dbus_dict_append_basic(DBusMessageIter *dict,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       supplicant_dbus_property_append_basic(&entry, key, type, val);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void
+supplicant_dbus_dict_append_fixed_array(DBusMessageIter *dict,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+       supplicant_dbus_property_append_fixed_array(&entry, key, type, val, len);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void
+supplicant_dbus_dict_append_array(DBusMessageIter *dict,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       supplicant_dbus_property_append_array(&entry, key, type,
+                                               function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
new file mode 100644 (file)
index 0000000..a230312
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *
+ *  WPA supplicant library with GLib integration
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef __G_SUPPLICANT_H
+#define __G_SUPPLICANT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define G_SUPPLICANT_EAP_METHOD_MD5    (1 << 0)
+#define G_SUPPLICANT_EAP_METHOD_TLS    (1 << 1)
+#define G_SUPPLICANT_EAP_METHOD_MSCHAPV2       (1 << 2)
+#define G_SUPPLICANT_EAP_METHOD_PEAP   (1 << 3)
+#define G_SUPPLICANT_EAP_METHOD_TTLS   (1 << 4)
+#define G_SUPPLICANT_EAP_METHOD_GTC    (1 << 5)
+#define G_SUPPLICANT_EAP_METHOD_OTP    (1 << 6)
+#define G_SUPPLICANT_EAP_METHOD_LEAP   (1 << 7)
+#define G_SUPPLICANT_EAP_METHOD_WSC    (1 << 8)
+
+#define G_SUPPLICANT_CAPABILITY_AUTHALG_OPEN   (1 << 0)
+#define G_SUPPLICANT_CAPABILITY_AUTHALG_SHARED (1 << 1)
+#define G_SUPPLICANT_CAPABILITY_AUTHALG_LEAP   (1 << 2)
+
+#define G_SUPPLICANT_CAPABILITY_PROTO_WPA              (1 << 0)
+#define G_SUPPLICANT_CAPABILITY_PROTO_RSN              (1 << 1)
+
+#define G_SUPPLICANT_CAPABILITY_SCAN_ACTIVE    (1 << 0)
+#define G_SUPPLICANT_CAPABILITY_SCAN_PASSIVE   (1 << 1)
+#define G_SUPPLICANT_CAPABILITY_SCAN_SSID              (1 << 2)
+
+#define G_SUPPLICANT_CAPABILITY_MODE_INFRA     (1 << 0)
+#define G_SUPPLICANT_CAPABILITY_MODE_IBSS              (1 << 1)
+#define G_SUPPLICANT_CAPABILITY_MODE_AP                (1 << 2)
+
+#define G_SUPPLICANT_KEYMGMT_NONE              (1 << 0)
+#define G_SUPPLICANT_KEYMGMT_IEEE8021X (1 << 1)
+#define G_SUPPLICANT_KEYMGMT_WPA_NONE  (1 << 2)
+#define G_SUPPLICANT_KEYMGMT_WPA_PSK   (1 << 3)
+#define G_SUPPLICANT_KEYMGMT_WPA_PSK_256       (1 << 4)
+#define G_SUPPLICANT_KEYMGMT_WPA_FT_PSK        (1 << 5)
+#define G_SUPPLICANT_KEYMGMT_WPA_FT_EAP        (1 << 6)
+#define G_SUPPLICANT_KEYMGMT_WPA_EAP   (1 << 7)
+#define G_SUPPLICANT_KEYMGMT_WPA_EAP_256       (1 << 8)
+#define G_SUPPLICANT_KEYMGMT_WPS               (1 << 9)
+
+#define G_SUPPLICANT_PROTO_WPA         (1 << 0)
+#define G_SUPPLICANT_PROTO_RSN         (1 << 1)
+
+#define G_SUPPLICANT_GROUP_WEP40               (1 << 0)
+#define G_SUPPLICANT_GROUP_WEP104              (1 << 1)
+#define G_SUPPLICANT_GROUP_TKIP                (1 << 2)
+#define G_SUPPLICANT_GROUP_CCMP                (1 << 3)
+
+#define G_SUPPLICANT_PAIRWISE_NONE     (1 << 0)
+#define G_SUPPLICANT_PAIRWISE_TKIP     (1 << 1)
+#define G_SUPPLICANT_PAIRWISE_CCMP     (1 << 2)
+
+#define G_SUPPLICANT_WPS_CONFIGURED     (1 << 0)
+#define G_SUPPLICANT_WPS_PBC            (1 << 1)
+#define G_SUPPLICANT_WPS_PIN            (1 << 2)
+#define G_SUPPLICANT_WPS_REGISTRAR      (1 << 3)
+
+typedef enum {
+       G_SUPPLICANT_MODE_UNKNOWN,
+       G_SUPPLICANT_MODE_INFRA,
+       G_SUPPLICANT_MODE_IBSS,
+       G_SUPPLICANT_MODE_MASTER,
+} GSupplicantMode;
+
+typedef enum {
+       G_SUPPLICANT_SECURITY_UNKNOWN,
+       G_SUPPLICANT_SECURITY_NONE,
+       G_SUPPLICANT_SECURITY_WEP,
+       G_SUPPLICANT_SECURITY_PSK,
+       G_SUPPLICANT_SECURITY_IEEE8021X,
+} GSupplicantSecurity;
+
+typedef enum {
+       G_SUPPLICANT_STATE_UNKNOWN,
+       G_SUPPLICANT_STATE_DISCONNECTED,
+       G_SUPPLICANT_STATE_INACTIVE,
+       G_SUPPLICANT_STATE_SCANNING,
+       G_SUPPLICANT_STATE_AUTHENTICATING,
+       G_SUPPLICANT_STATE_ASSOCIATING,
+       G_SUPPLICANT_STATE_ASSOCIATED,
+       G_SUPPLICANT_STATE_4WAY_HANDSHAKE,
+       G_SUPPLICANT_STATE_GROUP_HANDSHAKE,
+       G_SUPPLICANT_STATE_COMPLETED,
+} GSupplicantState;
+
+typedef enum {
+       G_SUPPLICANT_WPS_STATE_UNKNOWN,
+       G_SUPPLICANT_WPS_STATE_SUCCESS,
+       G_SUPPLICANT_WPS_STATE_FAIL,
+} GSupplicantWpsState;
+
+struct _GSupplicantSSID {
+       const void *ssid;
+       unsigned int ssid_len;
+       unsigned int scan_ssid;
+       GSupplicantMode mode;
+       GSupplicantSecurity security;
+       unsigned int protocol;
+       unsigned int pairwise_cipher;
+       unsigned int group_cipher;
+       unsigned int freq;
+       const char *eap;
+       const char *passphrase;
+       const char *identity;
+       const char *ca_cert_path;
+       const char *client_cert_path;
+       const char *private_key_path;
+       const char *private_key_passphrase;
+       const char *phase2_auth;
+       dbus_bool_t use_wps;
+       const char *pin_wps;
+       const char *bgscan;
+};
+
+typedef struct _GSupplicantSSID GSupplicantSSID;
+
+/*
+ * Max number of SSIDs that can be scanned.
+ * In wpa_s 0.7x the limit is 4.
+ * In wps_s 0.8 or later it is 16.
+ * The value is only used if wpa_supplicant does not return any max limit
+ * for number of scannable SSIDs.
+ */
+#define WPAS_MAX_SCAN_SSIDS 4
+
+struct scan_ssid {
+       unsigned char ssid[32];
+       uint8_t ssid_len;
+};
+
+struct _GSupplicantScanParams {
+       GSList *ssids;
+
+       uint8_t num_ssids;
+
+       uint16_t *freqs;
+};
+
+typedef struct _GSupplicantScanParams GSupplicantScanParams;
+
+/* global API */
+typedef void (*GSupplicantCountryCallback) (void *user_data);
+
+int g_supplicant_set_country(const char *alpha2,
+                               GSupplicantCountryCallback callback,
+                                               const void *user_data);
+
+/* Interface API */
+struct _GSupplicantInterface;
+
+typedef struct _GSupplicantInterface GSupplicantInterface;
+
+typedef void (*GSupplicantInterfaceCallback) (int result,
+                                       GSupplicantInterface *interface,
+                                                       void *user_data);
+
+int g_supplicant_interface_create(const char *ifname, const char *driver,
+                                       const char *bridge,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data);
+int g_supplicant_interface_remove(GSupplicantInterface *interface,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data);
+int g_supplicant_interface_scan(GSupplicantInterface *interface,
+                                       GSupplicantScanParams *scan_data,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data);
+
+int g_supplicant_interface_connect(GSupplicantInterface *interface,
+                                       GSupplicantSSID *ssid,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data);
+
+int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data);
+
+int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
+                                                       unsigned int ap_scan);
+
+void g_supplicant_interface_set_data(GSupplicantInterface *interface,
+                                                               void *data);
+void *g_supplicant_interface_get_data(GSupplicantInterface *interface);
+const char *g_supplicant_interface_get_ifname(GSupplicantInterface *interface);
+const char *g_supplicant_interface_get_driver(GSupplicantInterface *interface);
+GSupplicantState g_supplicant_interface_get_state(GSupplicantInterface *interface);
+const char *g_supplicant_interface_get_wps_key(GSupplicantInterface *interface);
+const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
+                                                       unsigned int *ssid_len);
+GSupplicantWpsState g_supplicant_interface_get_wps_state(GSupplicantInterface *interface);
+unsigned int g_supplicant_interface_get_mode(GSupplicantInterface *interface);
+dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface);
+unsigned int g_supplicant_interface_get_max_scan_ssids(
+                                       GSupplicantInterface *interface);
+
+int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interface,
+                                                       dbus_bool_t enable);
+
+/* Network API */
+struct _GSupplicantNetwork;
+
+typedef struct _GSupplicantNetwork GSupplicantNetwork;
+
+GSupplicantInterface *g_supplicant_network_get_interface(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_name(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_identifier(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_path(GSupplicantNetwork *network);
+const void *g_supplicant_network_get_ssid(GSupplicantNetwork *network,
+                                                       unsigned int *ssid_len);
+const char *g_supplicant_network_get_mode(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_security(GSupplicantNetwork *network);
+dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network);
+dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_active(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network);
+dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network);
+
+#if defined TIZEN_EXT
+/*
+ * Description: Network client requires additional wifi specific info
+ */
+const unsigned char *g_supplicant_network_get_bssid(GSupplicantNetwork *network);
+unsigned int g_supplicant_network_get_maxrate(GSupplicantNetwork *network);
+const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network);
+#endif
+
+struct _GSupplicantCallbacks {
+       void (*system_ready) (void);
+       void (*system_killed) (void);
+       void (*interface_added) (GSupplicantInterface *interface);
+       void (*interface_state) (GSupplicantInterface *interface);
+       void (*interface_removed) (GSupplicantInterface *interface);
+       void (*scan_started) (GSupplicantInterface *interface);
+       void (*scan_finished) (GSupplicantInterface *interface);
+       void (*network_added) (GSupplicantNetwork *network);
+       void (*network_removed) (GSupplicantNetwork *network);
+       void (*network_changed) (GSupplicantNetwork *network,
+                                       const char *property);
+       void (*debug) (const char *str);
+};
+
+typedef struct _GSupplicantCallbacks GSupplicantCallbacks;
+
+int g_supplicant_register(const GSupplicantCallbacks *callbacks);
+void g_supplicant_unregister(const GSupplicantCallbacks *callbacks);
+
+static inline
+void g_supplicant_free_scan_params(GSupplicantScanParams *scan_params)
+{
+       g_slist_free_full(scan_params->ssids, g_free);
+       g_free(scan_params->freqs);
+       g_free(scan_params);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_SUPPLICANT_H */
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
new file mode 100644 (file)
index 0000000..f8e1073
--- /dev/null
@@ -0,0 +1,3772 @@
+/*
+ *
+ *  WPA supplicant library with GLib integration
+ *
+ *  Copyright (C) 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "dbus.h"
+#include "gsupplicant.h"
+
+#define TIMEOUT 5000
+
+#define IEEE80211_CAP_ESS      0x0001
+#define IEEE80211_CAP_IBSS     0x0002
+#define IEEE80211_CAP_PRIVACY  0x0010
+
+static DBusConnection *connection;
+
+static const GSupplicantCallbacks *callbacks_pointer;
+
+static dbus_bool_t system_available = FALSE;
+static dbus_bool_t system_ready = FALSE;
+
+static dbus_int32_t debug_level;
+static dbus_bool_t debug_timestamp = FALSE;
+static dbus_bool_t debug_showkeys = FALSE;
+
+static const char *debug_strings[] = {
+       "msgdump", "debug", "info", "warning", "error", NULL
+};
+
+static unsigned int eap_methods;
+
+struct strvalmap {
+       const char *str;
+       unsigned int val;
+};
+
+static struct strvalmap eap_method_map[] = {
+       { "MD5",        G_SUPPLICANT_EAP_METHOD_MD5     },
+       { "TLS",        G_SUPPLICANT_EAP_METHOD_TLS     },
+       { "MSCHAPV2",   G_SUPPLICANT_EAP_METHOD_MSCHAPV2        },
+       { "PEAP",       G_SUPPLICANT_EAP_METHOD_PEAP    },
+       { "TTLS",       G_SUPPLICANT_EAP_METHOD_TTLS    },
+       { "GTC",        G_SUPPLICANT_EAP_METHOD_GTC     },
+       { "OTP",        G_SUPPLICANT_EAP_METHOD_OTP     },
+       { "LEAP",       G_SUPPLICANT_EAP_METHOD_LEAP    },
+       { "WSC",        G_SUPPLICANT_EAP_METHOD_WSC     },
+       { }
+};
+
+static struct strvalmap keymgmt_map[] = {
+       { "none",               G_SUPPLICANT_KEYMGMT_NONE               },
+       { "ieee8021x",          G_SUPPLICANT_KEYMGMT_IEEE8021X  },
+       { "wpa-none",           G_SUPPLICANT_KEYMGMT_WPA_NONE   },
+       { "wpa-psk",            G_SUPPLICANT_KEYMGMT_WPA_PSK    },
+       { "wpa-psk-sha256",     G_SUPPLICANT_KEYMGMT_WPA_PSK_256        },
+       { "wpa-ft-psk",         G_SUPPLICANT_KEYMGMT_WPA_FT_PSK },
+       { "wpa-ft-eap",         G_SUPPLICANT_KEYMGMT_WPA_FT_EAP },
+       { "wpa-eap",            G_SUPPLICANT_KEYMGMT_WPA_EAP    },
+       { "wpa-eap-sha256",     G_SUPPLICANT_KEYMGMT_WPA_EAP_256        },
+       { "wps",                G_SUPPLICANT_KEYMGMT_WPS                },
+       { }
+};
+
+static struct strvalmap authalg_capa_map[] = {
+       { "open",       G_SUPPLICANT_CAPABILITY_AUTHALG_OPEN    },
+       { "shared",     G_SUPPLICANT_CAPABILITY_AUTHALG_SHARED  },
+       { "leap",       G_SUPPLICANT_CAPABILITY_AUTHALG_LEAP    },
+       { }
+};
+
+static struct strvalmap proto_capa_map[] = {
+       { "wpa",        G_SUPPLICANT_CAPABILITY_PROTO_WPA               },
+       { "rsn",        G_SUPPLICANT_CAPABILITY_PROTO_RSN               },
+       { }
+};
+
+static struct strvalmap group_map[] = {
+       { "wep40",      G_SUPPLICANT_GROUP_WEP40        },
+       { "wep104",     G_SUPPLICANT_GROUP_WEP104       },
+       { "tkip",       G_SUPPLICANT_GROUP_TKIP },
+       { "ccmp",       G_SUPPLICANT_GROUP_CCMP },
+       { }
+};
+
+static struct strvalmap pairwise_map[] = {
+       { "none",       G_SUPPLICANT_PAIRWISE_NONE      },
+       { "tkip",       G_SUPPLICANT_PAIRWISE_TKIP      },
+       { "ccmp",       G_SUPPLICANT_PAIRWISE_CCMP      },
+       { }
+};
+
+static struct strvalmap scan_capa_map[] = {
+       { "active",     G_SUPPLICANT_CAPABILITY_SCAN_ACTIVE     },
+       { "passive",    G_SUPPLICANT_CAPABILITY_SCAN_PASSIVE    },
+       { "ssid",       G_SUPPLICANT_CAPABILITY_SCAN_SSID               },
+       { }
+};
+
+static struct strvalmap mode_capa_map[] = {
+       { "infrastructure",     G_SUPPLICANT_CAPABILITY_MODE_INFRA      },
+       { "ad-hoc",             G_SUPPLICANT_CAPABILITY_MODE_IBSS       },
+       { "ap",                 G_SUPPLICANT_CAPABILITY_MODE_AP         },
+       { }
+};
+
+static GHashTable *interface_table;
+static GHashTable *bss_mapping;
+
+struct _GSupplicantWpsCredentials {
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       char *key;
+};
+
+struct _GSupplicantInterface {
+       char *path;
+       char *network_path;
+       unsigned int keymgmt_capa;
+       unsigned int authalg_capa;
+       unsigned int proto_capa;
+       unsigned int group_capa;
+       unsigned int pairwise_capa;
+       unsigned int scan_capa;
+       unsigned int mode_capa;
+       unsigned int max_scan_ssids;
+       dbus_bool_t ready;
+       GSupplicantState state;
+       dbus_bool_t scanning;
+       GSupplicantInterfaceCallback scan_callback;
+       void *scan_data;
+       int apscan;
+       char *ifname;
+       char *driver;
+       char *bridge;
+       struct _GSupplicantWpsCredentials wps_cred;
+       GSupplicantWpsState wps_state;
+       GHashTable *network_table;
+       GHashTable *net_mapping;
+       GHashTable *bss_mapping;
+       void *data;
+};
+
+struct g_supplicant_bss {
+       GSupplicantInterface *interface;
+       char *path;
+       unsigned char bssid[6];
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       dbus_uint16_t frequency;
+       dbus_uint32_t maxrate;
+       dbus_int16_t signal;
+       GSupplicantMode mode;
+       GSupplicantSecurity security;
+       dbus_bool_t rsn_selected;
+       unsigned int wpa_keymgmt;
+       unsigned int wpa_pairwise;
+       unsigned int wpa_group;
+       unsigned int rsn_keymgmt;
+       unsigned int rsn_pairwise;
+       unsigned int rsn_group;
+       unsigned int keymgmt;
+       dbus_bool_t privacy;
+       dbus_bool_t psk;
+       dbus_bool_t ieee8021x;
+       unsigned int wps_capabilities;
+};
+
+struct _GSupplicantNetwork {
+       GSupplicantInterface *interface;
+       char *path;
+       char *group;
+       char *name;
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       dbus_int16_t signal;
+       dbus_uint16_t frequency;
+       struct g_supplicant_bss *best_bss;
+       GSupplicantMode mode;
+       GSupplicantSecurity security;
+       dbus_bool_t wps;
+       unsigned int wps_capabilities;
+       GHashTable *bss_table;
+       GHashTable *config_table;
+};
+
+static inline void debug(const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (callbacks_pointer->debug == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               callbacks_pointer->debug(str);
+
+       va_end(ap);
+}
+
+#define SUPPLICANT_DBG(fmt, arg...) \
+       debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg);
+
+static GSupplicantMode string2mode(const char *mode)
+{
+       if (mode == NULL)
+               return G_SUPPLICANT_MODE_UNKNOWN;
+
+       if (g_str_equal(mode, "infrastructure") == TRUE)
+               return G_SUPPLICANT_MODE_INFRA;
+       else if (g_str_equal(mode, "ad-hoc") == TRUE)
+               return G_SUPPLICANT_MODE_IBSS;
+
+       return G_SUPPLICANT_MODE_UNKNOWN;
+}
+
+static const char *mode2string(GSupplicantMode mode)
+{
+       switch (mode) {
+       case G_SUPPLICANT_MODE_UNKNOWN:
+               break;
+       case G_SUPPLICANT_MODE_INFRA:
+               return "managed";
+       case G_SUPPLICANT_MODE_IBSS:
+               return "adhoc";
+       case G_SUPPLICANT_MODE_MASTER:
+               return "ap";
+       }
+
+       return NULL;
+}
+
+static const char *security2string(GSupplicantSecurity security)
+{
+       switch (security) {
+       case G_SUPPLICANT_SECURITY_UNKNOWN:
+               break;
+       case G_SUPPLICANT_SECURITY_NONE:
+               return "none";
+       case G_SUPPLICANT_SECURITY_WEP:
+               return "wep";
+       case G_SUPPLICANT_SECURITY_PSK:
+               return "psk";
+       case G_SUPPLICANT_SECURITY_IEEE8021X:
+               return "ieee8021x";
+       }
+
+       return NULL;
+}
+
+static GSupplicantState string2state(const char *state)
+{
+       if (state == NULL)
+               return G_SUPPLICANT_STATE_UNKNOWN;
+
+       if (g_str_equal(state, "unknown") == TRUE)
+               return G_SUPPLICANT_STATE_UNKNOWN;
+       else if (g_str_equal(state, "disconnected") == TRUE)
+               return G_SUPPLICANT_STATE_DISCONNECTED;
+       else if (g_str_equal(state, "inactive") == TRUE)
+               return G_SUPPLICANT_STATE_INACTIVE;
+       else if (g_str_equal(state, "scanning") == TRUE)
+               return G_SUPPLICANT_STATE_SCANNING;
+       else if (g_str_equal(state, "authenticating") == TRUE)
+               return G_SUPPLICANT_STATE_AUTHENTICATING;
+       else if (g_str_equal(state, "associating") == TRUE)
+               return G_SUPPLICANT_STATE_ASSOCIATING;
+       else if (g_str_equal(state, "associated") == TRUE)
+               return G_SUPPLICANT_STATE_ASSOCIATED;
+       else if (g_str_equal(state, "group_handshake") == TRUE)
+               return G_SUPPLICANT_STATE_GROUP_HANDSHAKE;
+       else if (g_str_equal(state, "4way_handshake") == TRUE)
+               return G_SUPPLICANT_STATE_4WAY_HANDSHAKE;
+       else if (g_str_equal(state, "completed") == TRUE)
+               return G_SUPPLICANT_STATE_COMPLETED;
+
+       return G_SUPPLICANT_STATE_UNKNOWN;
+}
+
+static void callback_system_ready(void)
+{
+       if (system_ready == TRUE)
+               return;
+
+       system_ready = TRUE;
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->system_ready == NULL)
+               return;
+
+       callbacks_pointer->system_ready();
+}
+
+static void callback_system_killed(void)
+{
+       system_ready = FALSE;
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->system_killed == NULL)
+               return;
+
+       callbacks_pointer->system_killed();
+}
+
+static void callback_interface_added(GSupplicantInterface *interface)
+{
+       SUPPLICANT_DBG("");
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->interface_added == NULL)
+               return;
+
+       callbacks_pointer->interface_added(interface);
+}
+
+static void callback_interface_state(GSupplicantInterface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->interface_state == NULL)
+               return;
+
+       callbacks_pointer->interface_state(interface);
+}
+
+static void callback_interface_removed(GSupplicantInterface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->interface_removed == NULL)
+               return;
+
+       callbacks_pointer->interface_removed(interface);
+}
+
+static void callback_scan_started(GSupplicantInterface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->scan_started == NULL)
+               return;
+
+       callbacks_pointer->scan_started(interface);
+}
+
+static void callback_scan_finished(GSupplicantInterface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->scan_finished == NULL)
+               return;
+
+       callbacks_pointer->scan_finished(interface);
+}
+
+static void callback_network_added(GSupplicantNetwork *network)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_added == NULL)
+               return;
+
+       callbacks_pointer->network_added(network);
+}
+
+static void callback_network_removed(GSupplicantNetwork *network)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_removed == NULL)
+               return;
+
+       callbacks_pointer->network_removed(network);
+}
+
+static void callback_network_changed(GSupplicantNetwork *network,
+                                       const char *property)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_changed == NULL)
+               return;
+
+       callbacks_pointer->network_changed(network, property);
+}
+
+static void remove_interface(gpointer data)
+{
+       GSupplicantInterface *interface = data;
+
+       g_hash_table_destroy(interface->bss_mapping);
+       g_hash_table_destroy(interface->net_mapping);
+       g_hash_table_destroy(interface->network_table);
+
+       if (interface->scan_callback != NULL) {
+               SUPPLICANT_DBG("call interface %p callback %p scanning %d",
+                               interface, interface->scan_callback,
+                               interface->scanning);
+
+               interface->scan_callback(-EIO, interface, interface->scan_data);
+                interface->scan_callback = NULL;
+                interface->scan_data = NULL;
+
+               if (interface->scanning == TRUE) {
+                       interface->scanning = FALSE;
+                       callback_scan_finished(interface);
+               }
+       }
+
+       callback_interface_removed(interface);
+
+       g_free(interface->wps_cred.key);
+       g_free(interface->path);
+       g_free(interface->network_path);
+       g_free(interface->ifname);
+       g_free(interface->driver);
+       g_free(interface->bridge);
+       g_free(interface);
+}
+
+static void remove_network(gpointer data)
+{
+       GSupplicantNetwork *network = data;
+
+       g_hash_table_destroy(network->bss_table);
+
+       callback_network_removed(network);
+
+       g_hash_table_destroy(network->config_table);
+
+       g_free(network->path);
+       g_free(network->group);
+       g_free(network->name);
+       g_free(network);
+}
+
+static void remove_bss(gpointer data)
+{
+       struct g_supplicant_bss *bss = data;
+
+       g_free(bss->path);
+       g_free(bss);
+}
+
+static void debug_strvalmap(const char *label, struct strvalmap *map,
+                                                       unsigned int val)
+{
+       int i;
+
+       for (i = 0; map[i].str != NULL; i++) {
+               if (val & map[i].val)
+                       SUPPLICANT_DBG("%s: %s", label, map[i].str);
+       }
+}
+
+static void interface_capability_keymgmt(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; keymgmt_map[i].str != NULL; i++)
+               if (strcmp(str, keymgmt_map[i].str) == 0) {
+                       interface->keymgmt_capa |= keymgmt_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_authalg(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; authalg_capa_map[i].str != NULL; i++)
+               if (strcmp(str, authalg_capa_map[i].str) == 0) {
+                       interface->authalg_capa |= authalg_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_proto(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; proto_capa_map[i].str != NULL; i++)
+               if (strcmp(str, proto_capa_map[i].str) == 0) {
+                       interface->proto_capa |= proto_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_pairwise(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; pairwise_map[i].str != NULL; i++)
+               if (strcmp(str, pairwise_map[i].str) == 0) {
+                       interface->pairwise_capa |= pairwise_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_group(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; group_map[i].str != NULL; i++)
+               if (strcmp(str, group_map[i].str) == 0) {
+                       interface->group_capa |= group_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_scan(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; scan_capa_map[i].str != NULL; i++)
+               if (strcmp(str, scan_capa_map[i].str) == 0) {
+                       interface->scan_capa |= scan_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_mode(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; mode_capa_map[i].str != NULL; i++)
+               if (strcmp(str, mode_capa_map[i].str) == 0) {
+                       interface->mode_capa |= mode_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (key == NULL)
+               return;
+
+       if (g_strcmp0(key, "KeyMgmt") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_keymgmt, interface);
+       else if (g_strcmp0(key, "AuthAlg") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_authalg, interface);
+       else if (g_strcmp0(key, "Protocol") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_proto, interface);
+       else if (g_strcmp0(key, "Pairwise") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_pairwise, interface);
+       else if (g_strcmp0(key, "Group") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_group, interface);
+       else if (g_strcmp0(key, "Scan") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_scan, interface);
+       else if (g_strcmp0(key, "Modes") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_mode, interface);
+       else if (g_strcmp0(key, "MaxScanSSID") == 0) {
+               dbus_int32_t max_scan_ssid;
+
+               dbus_message_iter_get_basic(iter, &max_scan_ssid);
+               interface->max_scan_ssids = max_scan_ssid;
+
+       } else
+               SUPPLICANT_DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void set_apscan(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int ap_scan = *(unsigned int *)user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &ap_scan);
+}
+
+int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
+                                                       unsigned int ap_scan)
+{
+       return supplicant_dbus_property_set(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface",
+                               "ApScan", DBUS_TYPE_UINT32_AS_STRING,
+                                       set_apscan, NULL, &ap_scan);
+}
+
+void g_supplicant_interface_set_data(GSupplicantInterface *interface,
+                                                               void *data)
+{
+       if (interface == NULL)
+               return;
+
+       interface->data = data;
+}
+
+void *g_supplicant_interface_get_data(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return interface->data;
+}
+
+const char *g_supplicant_interface_get_ifname(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return interface->ifname;
+}
+
+const char *g_supplicant_interface_get_driver(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return interface->driver;
+}
+
+GSupplicantState g_supplicant_interface_get_state(
+                                       GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return G_SUPPLICANT_STATE_UNKNOWN;
+
+       return interface->state;
+}
+
+const char *g_supplicant_interface_get_wps_key(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return (const char *)interface->wps_cred.key;
+}
+
+const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
+                                                       unsigned int *ssid_len)
+{
+       if (ssid_len == NULL)
+               return NULL;
+
+       if (interface == NULL || interface->wps_cred.ssid == NULL) {
+               *ssid_len = 0;
+               return NULL;
+       }
+
+       *ssid_len = interface->wps_cred.ssid_len;
+       return interface->wps_cred.ssid;
+}
+
+GSupplicantWpsState g_supplicant_interface_get_wps_state(
+                                       GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+       return interface->wps_state;
+}
+
+unsigned int g_supplicant_interface_get_mode(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return 0;
+
+       return interface->mode_capa;
+}
+
+unsigned int g_supplicant_interface_get_max_scan_ssids(
+                               GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return 0;
+
+#if !defined TIZEN_EXT
+       if (interface->max_scan_ssids == 0)
+               return WPAS_MAX_SCAN_SSIDS;
+#endif
+
+       return interface->max_scan_ssids;
+}
+
+static void set_network_enabled(DBusMessageIter *iter, void *user_data)
+{
+       dbus_bool_t enable = *(dbus_bool_t *)user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable);
+}
+
+int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interface,
+                                                       dbus_bool_t enable)
+{
+       if (interface == NULL)
+               return -1;
+
+       if (interface->network_path == NULL)
+               return -1;
+
+       SUPPLICANT_DBG(" ");
+       return supplicant_dbus_property_set(interface->network_path,
+                               SUPPLICANT_INTERFACE ".Network",
+                               "Enabled", DBUS_TYPE_BOOLEAN_AS_STRING,
+                               set_network_enabled, NULL, &enable);
+}
+
+dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return FALSE;
+
+       return interface->ready;
+}
+
+GSupplicantInterface *g_supplicant_network_get_interface(
+                                       GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return NULL;
+
+       return network->interface;
+}
+
+const char *g_supplicant_network_get_name(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->name == NULL)
+               return "";
+
+       return network->name;
+}
+
+const char *g_supplicant_network_get_identifier(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->group == NULL)
+               return "";
+
+       return network->group;
+}
+
+const char *g_supplicant_network_get_path(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->path == NULL)
+               return NULL;
+
+       return network->path;
+}
+
+const char *g_supplicant_network_get_mode(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return G_SUPPLICANT_MODE_UNKNOWN;
+
+       return mode2string(network->mode);
+}
+
+const char *g_supplicant_network_get_security(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return G_SUPPLICANT_SECURITY_UNKNOWN;
+
+       return security2string(network->security);
+}
+
+const void *g_supplicant_network_get_ssid(GSupplicantNetwork *network,
+                                               unsigned int *ssid_len)
+{
+       if (network == NULL || network->ssid == NULL) {
+               *ssid_len = 0;
+               return NULL;
+       }
+
+       *ssid_len = network->ssid_len;
+       return network->ssid;
+}
+
+dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return 0;
+
+       return network->signal;
+}
+
+dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return 0;
+
+       return network->frequency;
+}
+
+dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       return network->wps;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_active(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_CONFIGURED)
+               return TRUE;
+
+       return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_pbc(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_PBC)
+               return TRUE;
+
+       return FALSE;
+}
+
+dbus_bool_t g_supplicant_network_is_wps_advertizing(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return FALSE;
+
+       if (network->wps_capabilities & G_SUPPLICANT_WPS_REGISTRAR)
+               return TRUE;
+
+       return FALSE;
+}
+
+#if defined TIZEN_EXT
+/*
+ * Description: Network client requires additional wifi specific info
+ */
+const unsigned char *g_supplicant_network_get_bssid(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->best_bss == NULL)
+               return NULL;
+
+       return (const unsigned char *)network->best_bss->bssid;
+}
+
+unsigned int g_supplicant_network_get_maxrate(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->best_bss == NULL)
+               return 0;
+
+       return network->best_bss->maxrate;
+}
+
+const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network)
+{
+       if (network == NULL || network->best_bss == NULL)
+               return NULL;
+
+       if ((network->best_bss->wpa_pairwise & G_SUPPLICANT_PAIRWISE_CCMP) &&
+           (network->best_bss->wpa_pairwise & G_SUPPLICANT_PAIRWISE_TKIP))
+               return "mixed";
+       else if (network->best_bss->wpa_pairwise & G_SUPPLICANT_PAIRWISE_CCMP)
+               return "aes";
+       else if (network->best_bss->wpa_pairwise & G_SUPPLICANT_PAIRWISE_TKIP)
+               return "tkip";
+       else if (network->best_bss->security == G_SUPPLICANT_SECURITY_WEP)
+               return "wep";
+
+       return "none";
+}
+#endif
+
+static void merge_network(GSupplicantNetwork *network)
+{
+       GString *str;
+       const char *ssid, *mode, *key_mgmt;
+       unsigned int i, ssid_len;
+       char *group;
+
+       ssid = g_hash_table_lookup(network->config_table, "ssid");
+       mode = g_hash_table_lookup(network->config_table, "mode");
+       key_mgmt = g_hash_table_lookup(network->config_table, "key_mgmt");
+
+       SUPPLICANT_DBG("ssid %s mode %s", ssid, mode);
+
+       if (ssid != NULL)
+               ssid_len = strlen(ssid);
+       else
+               ssid_len = 0;
+
+       str = g_string_sized_new((ssid_len * 2) + 24);
+       if (str == NULL)
+               return;
+
+       for (i = 0; i < ssid_len; i++)
+               g_string_append_printf(str, "%02x", ssid[i]);
+
+       if (g_strcmp0(mode, "0") == 0)
+               g_string_append_printf(str, "_managed");
+       else if (g_strcmp0(mode, "1") == 0)
+               g_string_append_printf(str, "_adhoc");
+
+       if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+               g_string_append_printf(str, "_psk");
+
+       group = g_string_free(str, FALSE);
+
+       SUPPLICANT_DBG("%s", group);
+
+       g_free(group);
+
+       g_hash_table_destroy(network->config_table);
+
+       g_free(network->path);
+       g_free(network);
+}
+
+static void network_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantNetwork *network = user_data;
+
+       if (network->interface == NULL)
+               return;
+
+       if (key == NULL) {
+               merge_network(network);
+               return;
+       }
+
+       if (g_strcmp0(key, "Enabled") == 0) {
+               dbus_bool_t enabled = FALSE;
+
+               dbus_message_iter_get_basic(iter, &enabled);
+       } else if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL) {
+                       g_hash_table_replace(network->config_table,
+                                               g_strdup(key), g_strdup(str));
+               }
+       } else
+               SUPPLICANT_DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void interface_network_added(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       const char *path = NULL;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       network = g_hash_table_lookup(interface->net_mapping, path);
+       if (network != NULL)
+               return;
+
+       network = g_try_new0(GSupplicantNetwork, 1);
+       if (network == NULL)
+               return;
+
+       network->interface = interface;
+       network->path = g_strdup(path);
+
+       network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               supplicant_dbus_property_foreach(iter, network_property,
+                                                               network);
+               network_property(NULL, NULL, network);
+               return;
+       }
+
+       supplicant_dbus_property_get_all(path,
+                               SUPPLICANT_INTERFACE ".Network",
+                                               network_property, network);
+}
+
+static void interface_network_removed(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->net_mapping, path);
+       if (network == NULL)
+               return;
+
+       g_hash_table_remove(interface->net_mapping, path);
+}
+
+static char *create_name(unsigned char *ssid, int ssid_len)
+{
+       GString *string;
+       const gchar *remainder, *invalid;
+       int valid_bytes, remaining_bytes;
+
+       if (ssid_len < 1 || ssid[0] == '\0')
+               return g_strdup("");
+
+       string = NULL;
+       remainder = (const gchar *)ssid;
+       remaining_bytes = ssid_len;
+
+       while (remaining_bytes != 0) {
+               if (g_utf8_validate(remainder, remaining_bytes,
+                                       &invalid) == TRUE) {
+                       break;
+               }
+
+               valid_bytes = invalid - remainder;
+
+               if (string == NULL)
+                       string = g_string_sized_new(remaining_bytes);
+
+               g_string_append_len(string, remainder, valid_bytes);
+
+               /* append U+FFFD REPLACEMENT CHARACTER */
+               g_string_append(string, "\357\277\275");
+
+               remaining_bytes -= valid_bytes + 1;
+               remainder = invalid + 1;
+       }
+
+       if (string == NULL)
+               return g_strndup((const gchar *)ssid, ssid_len + 1);
+
+       g_string_append(string, remainder);
+
+       return g_string_free(string, FALSE);
+}
+
+static char *create_group(struct g_supplicant_bss *bss)
+{
+       GString *str;
+       unsigned int i;
+       const char *mode, *security;
+
+       str = g_string_sized_new((bss->ssid_len * 2) + 24);
+       if (str == NULL)
+               return NULL;
+
+       if (bss->ssid_len > 0 && bss->ssid[0] != '\0') {
+               for (i = 0; i < bss->ssid_len; i++)
+                       g_string_append_printf(str, "%02x", bss->ssid[i]);
+       } else
+               g_string_append_printf(str, "hidden");
+
+       mode = mode2string(bss->mode);
+       if (mode != NULL)
+               g_string_append_printf(str, "_%s", mode);
+
+       security = security2string(bss->security);
+       if (security != NULL)
+               g_string_append_printf(str, "_%s", security);
+
+       return g_string_free(str, FALSE);
+}
+
+static void add_or_replace_bss_to_network(struct g_supplicant_bss *bss)
+{
+       GSupplicantInterface *interface = bss->interface;
+       GSupplicantNetwork *network;
+       char *group;
+
+       group = create_group(bss);
+       SUPPLICANT_DBG("New group created: %s", group);
+
+       if (group == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->network_table, group);
+       if (network != NULL) {
+               g_free(group);
+               SUPPLICANT_DBG("Network %s already exist", network->name);
+
+               goto done;
+       }
+
+       network = g_try_new0(GSupplicantNetwork, 1);
+       if (network == NULL) {
+               g_free(group);
+               return;
+       }
+
+       network->interface = interface;
+       if (network->path == NULL)
+               network->path = g_strdup(bss->path);
+       network->group = group;
+       network->name = create_name(bss->ssid, bss->ssid_len);
+       network->mode = bss->mode;
+       network->security = bss->security;
+       network->ssid_len = bss->ssid_len;
+       memcpy(network->ssid, bss->ssid, bss->ssid_len);
+       network->signal = bss->signal;
+       network->frequency = bss->frequency;
+       network->best_bss = bss;
+
+       SUPPLICANT_DBG("New network %s created", network->name);
+
+       network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, remove_bss);
+
+       network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+
+       g_hash_table_replace(interface->network_table,
+                                               network->group, network);
+
+       callback_network_added(network);
+
+done:
+       /* We update network's WPS properties if only bss provides WPS. */
+       if ((bss->keymgmt & G_SUPPLICANT_KEYMGMT_WPS) != 0) {
+               network->wps = TRUE;
+               network->wps_capabilities |= bss->wps_capabilities;
+       }
+
+       if (bss->signal > network->signal) {
+               network->signal = bss->signal;
+               network->best_bss = bss;
+               callback_network_changed(network, "Signal");
+       }
+
+       g_hash_table_replace(interface->bss_mapping, bss->path, network);
+       g_hash_table_replace(network->bss_table, bss->path, bss);
+
+       g_hash_table_replace(bss_mapping, bss->path, interface);
+}
+
+static void bss_rates(DBusMessageIter *iter, void *user_data)
+{
+       struct g_supplicant_bss *bss = user_data;
+       dbus_uint32_t rate = 0;
+
+       dbus_message_iter_get_basic(iter, &rate);
+       if (rate == 0)
+               return;
+
+       if (rate > bss->maxrate)
+               bss->maxrate = rate;
+}
+
+static void bss_keymgmt(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int *keymgmt = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; keymgmt_map[i].str != NULL; i++)
+               if (strcmp(str, keymgmt_map[i].str) == 0) {
+                       SUPPLICANT_DBG("Keymgmt: %s", str);
+                       *keymgmt |= keymgmt_map[i].val;
+                       break;
+               }
+}
+
+static void bss_group(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int *group = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; group_map[i].str != NULL; i++)
+               if (strcmp(str, group_map[i].str) == 0) {
+                       SUPPLICANT_DBG("Group: %s", str);
+                       *group |= group_map[i].val;
+                       break;
+               }
+}
+
+static void bss_pairwise(DBusMessageIter *iter, void *user_data)
+{
+       unsigned int *pairwise = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; pairwise_map[i].str != NULL; i++)
+               if (strcmp(str, pairwise_map[i].str) == 0) {
+                       SUPPLICANT_DBG("Pairwise: %s", str);
+                       *pairwise |= pairwise_map[i].val;
+                       break;
+               }
+}
+
+static void bss_wpa(const char *key, DBusMessageIter *iter,
+                       void *user_data)
+{
+       struct g_supplicant_bss *bss = user_data;
+       unsigned int value = 0;
+
+       SUPPLICANT_DBG("Key: %s", key);
+
+       if (g_strcmp0(key, "KeyMgmt") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_keymgmt, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_keymgmt = value;
+               else
+                       bss->wpa_keymgmt = value;
+       } else if (g_strcmp0(key, "Group") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_group, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_group = value;
+               else
+                       bss->wpa_group = value;
+       } else if (g_strcmp0(key, "Pairwise") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_pairwise, &value);
+
+               if (bss->rsn_selected == TRUE)
+                       bss->rsn_pairwise = value;
+               else
+                       bss->wpa_pairwise = value;
+       }
+}
+
+static unsigned int get_tlv(unsigned char *ie, unsigned int ie_size,
+                                                       unsigned int type)
+{
+       unsigned int len = 0;
+
+       while (len + 4 < ie_size) {
+               unsigned int hi = ie[len];
+               unsigned int lo = ie[len + 1];
+               unsigned int tmp_type = (hi << 8) + lo;
+               unsigned int v_len = 0;
+
+               /* hi and lo are used to recreate an unsigned int
+                * based on 2 8bits length unsigned int. */
+
+               hi = ie[len + 2];
+               lo = ie[len + 3];
+               v_len = (hi << 8) + lo;
+
+               if (tmp_type == type) {
+                       unsigned int ret_value = 0;
+                       unsigned char *value = (unsigned char *)&ret_value;
+
+                       SUPPLICANT_DBG("IE: match type 0x%x", type);
+
+                       /* Verifying length relevance */
+                       if (v_len > sizeof(unsigned int) ||
+                               len + 4 + v_len > ie_size)
+                               break;
+
+                       memcpy(value, ie + len + 4, v_len);
+
+                       SUPPLICANT_DBG("returning 0x%x", ret_value);
+                       return ret_value;
+               }
+
+               len += v_len + 4;
+       }
+
+       SUPPLICANT_DBG("returning 0");
+       return 0;
+}
+
+static void bss_process_ies(DBusMessageIter *iter, void *user_data)
+{
+       struct g_supplicant_bss *bss = user_data;
+       const unsigned char WPS_OUI[] = { 0x00, 0x50, 0xf2, 0x04 };
+       unsigned char *ie, *ie_end;
+       DBusMessageIter array;
+       unsigned int value;
+       int ie_len;
+
+#define WMM_WPA1_WPS_INFO 221
+#define WPS_INFO_MIN_LEN  6
+#define WPS_VERSION_TLV   0x104A
+#define WPS_STATE_TLV     0x1044
+#define WPS_METHODS_TLV   0x1012
+#define WPS_REGISTRAR_TLV 0x1041
+#define WPS_VERSION       0x10
+#define WPS_PBC           0x04
+#define WPS_PIN           0x00
+#define WPS_CONFIGURED    0x02
+
+       dbus_message_iter_recurse(iter, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie == NULL || ie_len < 2)
+               return;
+
+       bss->wps_capabilities = 0;
+       bss->keymgmt = 0;
+
+       for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end;
+                                                       ie += ie[1] + 2) {
+
+               if (ie[0] != WMM_WPA1_WPS_INFO || ie[1] < WPS_INFO_MIN_LEN ||
+                       memcmp(ie+2, WPS_OUI, sizeof(WPS_OUI)) != 0)
+                       continue;
+
+               SUPPLICANT_DBG("IE: match WPS_OUI");
+
+               value = get_tlv(&ie[6], ie[1], WPS_STATE_TLV);
+               if (get_tlv(&ie[6], ie[1], WPS_VERSION_TLV) == WPS_VERSION &&
+                                                               value != 0) {
+                       bss->keymgmt |= G_SUPPLICANT_KEYMGMT_WPS;
+
+                       if (value == WPS_CONFIGURED)
+                               bss->wps_capabilities |=
+                                       G_SUPPLICANT_WPS_CONFIGURED;
+               }
+
+               value = get_tlv(&ie[6], ie[1], WPS_METHODS_TLV);
+               if (value != 0) {
+                       if (GUINT16_FROM_BE(value) == WPS_PBC)
+                               bss->wps_capabilities |= G_SUPPLICANT_WPS_PBC;
+                       if (GUINT16_FROM_BE(value) == WPS_PIN)
+                               bss->wps_capabilities |= G_SUPPLICANT_WPS_PIN;
+               } else
+                       bss->wps_capabilities |=
+                               G_SUPPLICANT_WPS_PBC | G_SUPPLICANT_WPS_PIN;
+
+               /* If the AP sends this it means it's advertizing
+                * as a registrar and the WPS process is launched
+                * on its side */
+               if (get_tlv(&ie[6], ie[1], WPS_REGISTRAR_TLV) != 0)
+                       bss->wps_capabilities |= G_SUPPLICANT_WPS_REGISTRAR;
+
+               SUPPLICANT_DBG("WPS Methods 0x%x", bss->wps_capabilities);
+       }
+}
+
+static void bss_compute_security(struct g_supplicant_bss *bss)
+{
+       /*
+        * Combining RSN and WPA keymgmt
+        * We combine it since parsing IEs might have set something for WPS. */
+       bss->keymgmt |= bss->rsn_keymgmt | bss->wpa_keymgmt;
+
+       bss->ieee8021x = FALSE;
+       bss->psk = FALSE;
+
+       if (bss->keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_EAP |
+                               G_SUPPLICANT_KEYMGMT_WPA_FT_EAP |
+                               G_SUPPLICANT_KEYMGMT_WPA_EAP_256))
+               bss->ieee8021x = TRUE;
+
+       if (bss->keymgmt &
+                       (G_SUPPLICANT_KEYMGMT_WPA_PSK |
+                               G_SUPPLICANT_KEYMGMT_WPA_FT_PSK |
+                               G_SUPPLICANT_KEYMGMT_WPA_PSK_256))
+               bss->psk = TRUE;
+
+       if (bss->ieee8021x == TRUE)
+               bss->security = G_SUPPLICANT_SECURITY_IEEE8021X;
+       else if (bss->psk == TRUE)
+               bss->security = G_SUPPLICANT_SECURITY_PSK;
+       else if (bss->privacy == TRUE)
+               bss->security = G_SUPPLICANT_SECURITY_WEP;
+       else
+               bss->security = G_SUPPLICANT_SECURITY_NONE;
+}
+
+
+static void bss_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct g_supplicant_bss *bss = user_data;
+
+       if (bss->interface == NULL)
+               return;
+
+       SUPPLICANT_DBG("key %s", key);
+
+       if (key == NULL)
+               return;
+
+       if (g_strcmp0(key, "BSSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *addr;
+               int addr_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
+
+               if (addr_len == 6)
+                       memcpy(bss->bssid, addr, addr_len);
+       } else if (g_strcmp0(key, "SSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *ssid;
+               int ssid_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+               if (ssid_len > 0 && ssid_len < 33) {
+                       memcpy(bss->ssid, ssid, ssid_len);
+                       bss->ssid_len = ssid_len;
+               } else {
+                       memset(bss->ssid, 0, sizeof(bss->ssid));
+                       bss->ssid_len = 0;
+               }
+       } else if (g_strcmp0(key, "Capabilities") == 0) {
+               dbus_uint16_t capabilities = 0x0000;
+
+               dbus_message_iter_get_basic(iter, &capabilities);
+
+               if (capabilities & IEEE80211_CAP_ESS)
+                       bss->mode = G_SUPPLICANT_MODE_INFRA;
+               else if (capabilities & IEEE80211_CAP_IBSS)
+                       bss->mode = G_SUPPLICANT_MODE_IBSS;
+
+               if (capabilities & IEEE80211_CAP_PRIVACY)
+                       bss->privacy = TRUE;
+       } else if (g_strcmp0(key, "Mode") == 0) {
+               const char *mode = NULL;
+
+               dbus_message_iter_get_basic(iter, &mode);
+               bss->mode = string2mode(mode);
+       } else if (g_strcmp0(key, "Frequency") == 0) {
+               dbus_uint16_t frequency = 0;
+
+               dbus_message_iter_get_basic(iter, &frequency);
+               bss->frequency = frequency;
+       } else if (g_strcmp0(key, "Signal") == 0) {
+               dbus_int16_t signal = 0;
+
+               dbus_message_iter_get_basic(iter, &signal);
+
+               bss->signal = signal;
+       } else if (g_strcmp0(key, "Level") == 0) {
+               dbus_int32_t level = 0;
+
+               dbus_message_iter_get_basic(iter, &level);
+       } else if (g_strcmp0(key, "Rates") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_rates, bss);
+       } else if (g_strcmp0(key, "MaxRate") == 0) {
+               dbus_uint32_t maxrate = 0;
+
+               dbus_message_iter_get_basic(iter, &maxrate);
+               if (maxrate != 0)
+                       bss->maxrate = maxrate;
+       } else if (g_strcmp0(key, "Privacy") == 0) {
+               dbus_bool_t privacy = FALSE;
+
+               dbus_message_iter_get_basic(iter, &privacy);
+               bss->privacy = privacy;
+       } else if (g_strcmp0(key, "RSN") == 0) {
+               bss->rsn_selected = TRUE;
+
+               supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+       } else if (g_strcmp0(key, "WPA") == 0) {
+               bss->rsn_selected = FALSE;
+
+               supplicant_dbus_property_foreach(iter, bss_wpa, bss);
+       } else if (g_strcmp0(key, "IEs") == 0)
+               bss_process_ies(iter, bss);
+       else
+               SUPPLICANT_DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static struct g_supplicant_bss *interface_bss_added(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       struct g_supplicant_bss *bss;
+       const char *path = NULL;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return NULL;
+
+       if (g_strcmp0(path, "/") == 0)
+               return NULL;
+
+       SUPPLICANT_DBG("%s", path);
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network != NULL) {
+               bss = g_hash_table_lookup(network->bss_table, path);
+               if (bss != NULL)
+                       return NULL;
+       }
+
+       bss = g_try_new0(struct g_supplicant_bss, 1);
+       if (bss == NULL)
+               return NULL;
+
+       bss->interface = interface;
+       bss->path = g_strdup(path);
+
+       return bss;
+}
+
+static void interface_bss_added_with_keys(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       struct g_supplicant_bss *bss;
+
+       SUPPLICANT_DBG("");
+
+       bss = interface_bss_added(iter, user_data);
+       if (bss == NULL)
+               return;
+
+       dbus_message_iter_next(iter);
+
+       if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_INVALID)
+               return;
+
+       supplicant_dbus_property_foreach(iter, bss_property, bss);
+
+       bss_compute_security(bss);
+       add_or_replace_bss_to_network(bss);
+}
+
+static void interface_bss_added_without_keys(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       struct g_supplicant_bss *bss;
+
+       SUPPLICANT_DBG("");
+
+       bss = interface_bss_added(iter, user_data);
+       if (bss == NULL)
+               return;
+
+       supplicant_dbus_property_get_all(bss->path,
+                                       SUPPLICANT_INTERFACE ".BSS",
+                                                       bss_property, bss);
+
+       bss_compute_security(bss);
+       add_or_replace_bss_to_network(bss);
+}
+
+static void update_signal(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       struct g_supplicant_bss *bss = value;
+       GSupplicantNetwork *network = user_data;
+
+       if (bss->signal > network->signal) {
+               network->signal = bss->signal;
+               network->best_bss = bss;
+       }
+}
+
+static void update_network_signal(GSupplicantNetwork *network)
+{
+       if (g_hash_table_size(network->bss_table) <= 1)
+               return;
+
+       g_hash_table_foreach(network->bss_table,
+                               update_signal, network);
+
+       SUPPLICANT_DBG("New network signal %d", network->signal);
+}
+
+static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network == NULL)
+               return;
+
+       g_hash_table_remove(bss_mapping, path);
+
+       g_hash_table_remove(interface->bss_mapping, path);
+       g_hash_table_remove(network->bss_table, path);
+
+       update_network_signal(network);
+
+       if (g_hash_table_size(network->bss_table) == 0)
+               g_hash_table_remove(interface->network_table, network->group);
+}
+
+static void interface_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (interface == NULL)
+               return;
+
+       SUPPLICANT_DBG("%s", key);
+
+       if (key == NULL) {
+               debug_strvalmap("KeyMgmt capability", keymgmt_map,
+                                               interface->keymgmt_capa);
+               debug_strvalmap("AuthAlg capability", authalg_capa_map,
+                                               interface->authalg_capa);
+               debug_strvalmap("Protocol capability", proto_capa_map,
+                                               interface->proto_capa);
+               debug_strvalmap("Pairwise capability", pairwise_map,
+                                               interface->pairwise_capa);
+               debug_strvalmap("Group capability", group_map,
+                                               interface->group_capa);
+               debug_strvalmap("Scan capability", scan_capa_map,
+                                               interface->scan_capa);
+               debug_strvalmap("Mode capability", mode_capa_map,
+                                               interface->mode_capa);
+
+               interface->ready = TRUE;
+               callback_interface_added(interface);
+               return;
+       }
+
+       if (g_strcmp0(key, "Capabilities") == 0) {
+               supplicant_dbus_property_foreach(iter, interface_capability,
+                                                               interface);
+       } else if (g_strcmp0(key, "State") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       if (string2state(str) != interface->state) {
+                               interface->state = string2state(str);
+                               callback_interface_state(interface);
+                       }
+
+               SUPPLICANT_DBG("state %s (%d)", str, interface->state);
+       } else if (g_strcmp0(key, "Scanning") == 0) {
+               dbus_bool_t scanning = FALSE;
+
+               dbus_message_iter_get_basic(iter, &scanning);
+               interface->scanning = scanning;
+
+               if (interface->ready == TRUE) {
+                       if (interface->scanning == TRUE)
+                               callback_scan_started(interface);
+                       else
+                               callback_scan_finished(interface);
+               }
+       } else if (g_strcmp0(key, "ApScan") == 0) {
+               int apscan = 1;
+
+               dbus_message_iter_get_basic(iter, &apscan);
+               interface->apscan = apscan;
+       } else if (g_strcmp0(key, "Ifname") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL) {
+                       g_free(interface->ifname);
+                       interface->ifname = g_strdup(str);
+               }
+       } else if (g_strcmp0(key, "Driver") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL) {
+                       g_free(interface->driver);
+                       interface->driver = g_strdup(str);
+               }
+       } else if (g_strcmp0(key, "BridgeIfname") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL) {
+                       g_free(interface->bridge);
+                       interface->bridge = g_strdup(str);
+               }
+       } else if (g_strcmp0(key, "CurrentBSS") == 0) {
+               interface_bss_added_without_keys(iter, interface);
+       } else if (g_strcmp0(key, "CurrentNetwork") == 0) {
+               interface_network_added(iter, interface);
+       } else if (g_strcmp0(key, "BSSs") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_bss_added_without_keys,
+                                                               interface);
+       } else if (g_strcmp0(key, "Blobs") == 0) {
+               /* Nothing */
+       } else if (g_strcmp0(key, "Networks") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_network_added,
+                                                               interface);
+       } else
+               SUPPLICANT_DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void scan_network_update(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+       GSupplicantNetwork *network;
+       char *path;
+
+       if (iter == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &path);
+
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       /* Update the network details based on scan BSS data */
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network != NULL)
+               callback_network_added(network);
+}
+
+static void scan_bss_data(const char *key, DBusMessageIter *iter,
+                               void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (iter)
+               supplicant_dbus_array_foreach(iter, scan_network_update,
+                                               interface);
+
+       if (interface->scan_callback != NULL)
+               interface->scan_callback(0, interface, interface->scan_data);
+
+       interface->scan_callback = NULL;
+       interface->scan_data = NULL;
+}
+
+static GSupplicantInterface *interface_alloc(const char *path)
+{
+       GSupplicantInterface *interface;
+
+       interface = g_try_new0(GSupplicantInterface, 1);
+       if (interface == NULL)
+               return NULL;
+
+       interface->path = g_strdup(path);
+
+       interface->network_table = g_hash_table_new_full(g_str_hash,
+                                       g_str_equal, NULL, remove_network);
+
+       interface->net_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+       interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       g_hash_table_replace(interface_table, interface->path, interface);
+
+       return interface;
+}
+
+static void interface_added(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantInterface *interface;
+       const char *path = NULL;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface != NULL)
+               return;
+
+       interface = interface_alloc(path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               supplicant_dbus_property_foreach(iter, interface_property,
+                                                               interface);
+               interface_property(NULL, NULL, interface);
+               return;
+       }
+
+       supplicant_dbus_property_get_all(path,
+                                       SUPPLICANT_INTERFACE ".Interface",
+                                               interface_property, interface);
+}
+
+static void interface_removed(DBusMessageIter *iter, void *user_data)
+{
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       g_hash_table_remove(interface_table, path);
+}
+
+static void eap_method(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; eap_method_map[i].str != NULL; i++)
+               if (strcmp(str, eap_method_map[i].str) == 0) {
+                       eap_methods |= eap_method_map[i].val;
+                       break;
+               }
+}
+
+static void service_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       if (key == NULL) {
+               callback_system_ready();
+               return;
+       }
+
+       if (g_strcmp0(key, "DebugLevel") == 0) {
+               const char *str = NULL;
+               int i;
+
+               dbus_message_iter_get_basic(iter, &str);
+               for (i = 0; debug_strings[i] != NULL; i++)
+                       if (g_strcmp0(debug_strings[i], str) == 0) {
+                               debug_level = i;
+                               break;
+                       }
+               SUPPLICANT_DBG("Debug level %d", debug_level);
+       } else if (g_strcmp0(key, "DebugTimestamp") == 0) {
+               dbus_message_iter_get_basic(iter, &debug_timestamp);
+               SUPPLICANT_DBG("Debug timestamp %u", debug_timestamp);
+       } else if (g_strcmp0(key, "DebugShowKeys") == 0) {
+               dbus_message_iter_get_basic(iter, &debug_showkeys);
+               SUPPLICANT_DBG("Debug show keys %u", debug_showkeys);
+       } else if (g_strcmp0(key, "Interfaces") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_added, NULL);
+       } else if (g_strcmp0(key, "EapMethods") == 0) {
+               supplicant_dbus_array_foreach(iter, eap_method, NULL);
+               debug_strvalmap("EAP method", eap_method_map, eap_methods);
+       } else if (g_strcmp0(key, "Country") == 0) {
+               const char *country = NULL;
+
+               dbus_message_iter_get_basic(iter, &country);
+               SUPPLICANT_DBG("Country %s", country);
+       } else
+               SUPPLICANT_DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
+{
+       const char *name = NULL, *old = NULL, *new = NULL;
+
+       SUPPLICANT_DBG("");
+
+       if (g_strcmp0(path, DBUS_PATH_DBUS) != 0)
+               return;
+
+       dbus_message_iter_get_basic(iter, &name);
+       if (name == NULL)
+               return;
+
+       if (g_strcmp0(name, SUPPLICANT_SERVICE) != 0)
+               return;
+
+       dbus_message_iter_next(iter);
+       dbus_message_iter_get_basic(iter, &old);
+       dbus_message_iter_next(iter);
+       dbus_message_iter_get_basic(iter, &new);
+
+       if (old == NULL || new == NULL)
+               return;
+
+       if (strlen(old) > 0 && strlen(new) == 0) {
+               system_available = FALSE;
+               g_hash_table_remove_all(bss_mapping);
+               g_hash_table_remove_all(interface_table);
+               callback_system_killed();
+       }
+
+       if (strlen(new) > 0 && strlen(old) == 0) {
+               system_available = TRUE;
+               supplicant_dbus_property_get_all(SUPPLICANT_PATH,
+                                                       SUPPLICANT_INTERFACE,
+                                                       service_property, NULL);
+       }
+}
+
+static void signal_properties_changed(const char *path, DBusMessageIter *iter)
+{
+       SUPPLICANT_DBG("");
+
+       if (g_strcmp0(path, SUPPLICANT_PATH) != 0)
+               return;
+
+       supplicant_dbus_property_foreach(iter, service_property, NULL);
+}
+
+static void signal_interface_added(const char *path, DBusMessageIter *iter)
+{
+       SUPPLICANT_DBG("path %s %s", path, SUPPLICANT_PATH);
+
+       if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+               interface_added(iter, NULL);
+}
+
+static void signal_interface_removed(const char *path, DBusMessageIter *iter)
+{
+       SUPPLICANT_DBG("");
+
+       if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+               interface_removed(iter, NULL);
+}
+
+static void signal_interface_changed(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, interface_property, interface);
+}
+
+static void signal_scan_done(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       dbus_bool_t success = FALSE;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &success);
+
+       /*
+        * If scan is unsuccessful return -EIO else get the scanned BSSs
+        * and update the network details accordingly
+        */
+       if (success == FALSE) {
+               if (interface->scan_callback != NULL)
+                       interface->scan_callback(-EIO, interface,
+                                               interface->scan_data);
+
+               interface->scan_callback = NULL;
+               interface->scan_data = NULL;
+
+               return;
+       }
+
+       supplicant_dbus_property_get(path, SUPPLICANT_INTERFACE ".Interface",
+                                       "BSSs", scan_bss_data, interface);
+}
+
+static void signal_bss_added(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_bss_added_with_keys(iter, interface);
+}
+
+static void signal_bss_removed(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_bss_removed(iter, interface);
+}
+
+static void signal_network_added(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_network_added(iter, interface);
+}
+
+static void signal_network_removed(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_network_removed(iter, interface);
+}
+
+static void signal_bss_changed(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       GSupplicantNetwork *network;
+       GSupplicantSecurity old_security;
+       struct g_supplicant_bss *bss;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(bss_mapping, path);
+       if (interface == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network == NULL)
+               return;
+
+       bss = g_hash_table_lookup(network->bss_table, path);
+       if (bss == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, bss_property, bss);
+
+       old_security = network->security;
+       bss_compute_security(bss);
+
+       if (old_security != bss->security) {
+               struct g_supplicant_bss *new_bss;
+
+               SUPPLICANT_DBG("New network security for %s", bss->ssid);
+
+               /* Security change policy:
+                * - we first copy the current bss into a new one with
+                * its own pointer (path)
+                * - we remove the current bss related network which will
+                * tell the plugin about such removal. This is done due
+                * to the fact that a security change means a group change
+                * so a complete network change.
+                * (current bss becomes invalid as well)
+                * - we add the new bss: it adds new network and tell the
+                * plugin about it. */
+
+               new_bss = g_try_new0(struct g_supplicant_bss, 1);
+               if (new_bss == NULL)
+                       return;
+
+               memcpy(new_bss, bss, sizeof(struct g_supplicant_bss));
+               new_bss->path = g_strdup(bss->path);
+
+               g_hash_table_remove(interface->network_table, network->group);
+
+               add_or_replace_bss_to_network(new_bss);
+
+               return;
+       }
+
+       if (bss->signal == network->signal)
+               return;
+
+       /*
+        * If the new signal is lower than the SSID signal, we need
+        * to check for the new maximum.
+        */
+       if (bss->signal < network->signal) {
+               if (bss != network->best_bss)
+                       return;
+               network->signal = bss->signal;
+               update_network_signal(network);
+       } else {
+               network->signal = bss->signal;
+               network->best_bss = bss;
+       }
+
+       SUPPLICANT_DBG("New network signal for %s %d dBm", network->ssid, network->signal);
+
+       callback_network_changed(network, "Signal");
+}
+
+static void wps_credentials(const char *key, DBusMessageIter *iter,
+                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (key == NULL)
+               return;
+
+       SUPPLICANT_DBG("key %s", key);
+
+       if (g_strcmp0(key, "Key") == 0) {
+               DBusMessageIter array;
+               unsigned char *key;
+               int key_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &key, &key_len);
+
+               g_free(interface->wps_cred.key);
+               interface->wps_cred.key = g_try_malloc0(
+                                               sizeof(char) * key_len+1);
+
+               if (interface->wps_cred.key == NULL)
+                       return;
+
+               memcpy(interface->wps_cred.key, key, sizeof(char) * key_len);
+
+               SUPPLICANT_DBG("WPS key present");
+       } else if (g_strcmp0(key, "SSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *ssid;
+               int ssid_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+               if (ssid_len > 0 && ssid_len < 33) {
+                       memcpy(interface->wps_cred.ssid, ssid, ssid_len);
+                       interface->wps_cred.ssid_len = ssid_len;
+               } else {
+                       memset(interface->wps_cred.ssid, 0, 32);
+                       interface->wps_cred.ssid_len = 0;
+               }
+       }
+}
+
+static void signal_wps_credentials(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, wps_credentials, interface);
+}
+
+static void wps_event_args(const char *key, DBusMessageIter *iter,
+                       void *user_data)
+{
+       GSupplicantInterface *interface = user_data;
+
+       if (key == NULL || interface == NULL)
+               return;
+
+       SUPPLICANT_DBG("Arg Key %s", key);
+}
+
+static void signal_wps_event(const char *path, DBusMessageIter *iter)
+{
+       GSupplicantInterface *interface;
+       const char *name = NULL;
+
+       SUPPLICANT_DBG("");
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &name);
+
+       SUPPLICANT_DBG("Name: %s", name);
+
+       if (g_strcmp0(name, "success") == 0)
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_SUCCESS;
+       else if (g_strcmp0(name, "failed") == 0)
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_FAIL;
+       else
+               interface->wps_state = G_SUPPLICANT_WPS_STATE_UNKNOWN;
+
+       if (!dbus_message_iter_has_next(iter))
+               return;
+
+       dbus_message_iter_next(iter);
+
+       supplicant_dbus_property_foreach(iter, wps_event_args, interface);
+}
+
+static struct {
+       const char *interface;
+       const char *member;
+       void (*function) (const char *path, DBusMessageIter *iter);
+} signal_map[] = {
+       { DBUS_INTERFACE_DBUS,  "NameOwnerChanged",  signal_name_owner_changed },
+
+       { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
+       { SUPPLICANT_INTERFACE, "InterfaceAdded",    signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceCreated",  signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceRemoved",  signal_interface_removed  },
+
+       { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
+       { SUPPLICANT_INTERFACE ".Interface", "ScanDone",          signal_scan_done         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSAdded",          signal_bss_added         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved",        signal_bss_removed       },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",      signal_network_added     },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved",    signal_network_removed   },
+
+       { SUPPLICANT_INTERFACE ".BSS", "PropertiesChanged", signal_bss_changed   },
+
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Credentials", signal_wps_credentials },
+       { SUPPLICANT_INTERFACE ".Interface.WPS", "Event",       signal_wps_event       },
+
+       { }
+};
+
+static DBusHandlerResult g_supplicant_filter(DBusConnection *conn,
+                                       DBusMessage *message, void *data)
+{
+       DBusMessageIter iter;
+       const char *path;
+       int i;
+
+       path = dbus_message_get_path(message);
+       if (path == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (i = 0; signal_map[i].interface != NULL; i++) {
+               if (dbus_message_has_interface(message,
+                                       signal_map[i].interface) == FALSE)
+                       continue;
+
+               if (dbus_message_has_member(message,
+                                       signal_map[i].member) == FALSE)
+                       continue;
+
+               signal_map[i].function(path, &iter);
+               break;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+struct supplicant_regdom {
+       GSupplicantCountryCallback callback;
+       const void *user_data;
+};
+
+static void country_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+       char *alpha2;
+
+       SUPPLICANT_DBG("Country setting result");
+
+       if (user_data == NULL)
+               return;
+
+       if (error == NULL) {
+               alpha2 = (char *)regdom->user_data;
+       } else {
+               SUPPLICANT_DBG("Country setting failure %s", error);
+               alpha2 = NULL;
+       }
+
+       if (regdom->callback)
+               regdom->callback(alpha2);
+
+       g_free(regdom);
+}
+
+static void country_params(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_regdom *regdom = user_data;
+       const char *country;
+
+       country = regdom->user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &country);
+}
+
+int g_supplicant_set_country(const char *alpha2,
+                               GSupplicantCountryCallback callback,
+                                       const void *user_data)
+{
+       struct supplicant_regdom *regdom;
+
+       SUPPLICANT_DBG("Country setting %s", alpha2);
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       regdom = dbus_malloc0(sizeof(*regdom));
+       if (regdom == NULL)
+               return -ENOMEM;
+
+       regdom->callback = callback;
+       regdom->user_data = user_data;
+
+       return supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
+                                       "Country", DBUS_TYPE_STRING_AS_STRING,
+                                       country_params, country_result,
+                                               regdom);
+}
+
+struct interface_data {
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       void *user_data;
+};
+
+struct interface_create_data {
+       const char *ifname;
+       const char *driver;
+       const char *bridge;
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       void *user_data;
+};
+
+struct interface_connect_data {
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       GSupplicantSSID *ssid;
+       void *user_data;
+};
+
+struct interface_scan_data {
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       GSupplicantScanParams *scan_params;
+       void *user_data;
+};
+
+static void interface_create_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       GSupplicantInterface *interface = data->interface;
+
+       if (key == NULL) {
+               if (data->callback != NULL)
+                       data->callback(0, data->interface, data->user_data);
+
+               dbus_free(data);
+       }
+
+       interface_property(key, iter, interface);
+}
+
+static void interface_create_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       const char *path = NULL;
+       int err;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL) {
+               g_warning("error %s", error);
+               err = -EIO;
+               goto done;
+       }
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (system_available == FALSE) {
+               err = -EFAULT;
+               goto done;
+       }
+
+       data->interface = g_hash_table_lookup(interface_table, path);
+       if (data->interface == NULL) {
+               data->interface = interface_alloc(path);
+               if (data->interface == NULL) {
+                       err = -ENOMEM;
+                       goto done;
+               }
+       }
+
+       err = supplicant_dbus_property_get_all(path,
+                                       SUPPLICANT_INTERFACE ".Interface",
+                                       interface_create_property, data);
+       if (err == 0)
+               return;
+
+done:
+       if (data->callback != NULL)
+               data->callback(err, NULL, data->user_data);
+
+       dbus_free(data);
+}
+
+static void interface_create_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       DBusMessageIter dict;
+
+       SUPPLICANT_DBG("");
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       supplicant_dbus_dict_append_basic(&dict, "Ifname",
+                                       DBUS_TYPE_STRING, &data->ifname);
+
+       if (data->driver != NULL)
+               supplicant_dbus_dict_append_basic(&dict, "Driver",
+                                       DBUS_TYPE_STRING, &data->driver);
+
+       if (data->bridge != NULL)
+               supplicant_dbus_dict_append_basic(&dict, "BridgeIfname",
+                                       DBUS_TYPE_STRING, &data->bridge);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void interface_get_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       GSupplicantInterface *interface;
+       const char *path = NULL;
+       int err;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL) {
+               SUPPLICANT_DBG("Interface not created yet");
+               goto create;
+       }
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL) {
+               err = -ENOENT;
+               goto done;
+       }
+
+       if (data->callback != NULL)
+               data->callback(0, interface, data->user_data);
+
+       dbus_free(data);
+
+       return;
+
+create:
+       if (system_available == FALSE) {
+               err = -EFAULT;
+               goto done;
+       }
+
+       SUPPLICANT_DBG("Creating interface");
+
+       err = supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "CreateInterface",
+                                               interface_create_params,
+                                               interface_create_result, data);
+       if (err == 0)
+               return;
+
+done:
+       if (data->callback != NULL)
+               data->callback(err, NULL, data->user_data);
+
+       dbus_free(data);
+}
+
+static void interface_get_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname);
+}
+
+int g_supplicant_interface_create(const char *ifname, const char *driver,
+                                       const char *bridge,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_create_data *data;
+
+       SUPPLICANT_DBG("ifname %s", ifname);
+
+       if (ifname == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->ifname = ifname;
+       data->driver = driver;
+       data->bridge = bridge;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "GetInterface",
+                                               interface_get_params,
+                                               interface_get_result, data);
+}
+
+static void interface_remove_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+       int err;
+
+       if (error != NULL) {
+               err = -EIO;
+               goto done;
+       }
+
+       if (system_available == FALSE) {
+               err = -EFAULT;
+               goto done;
+       }
+
+       /*
+        * The gsupplicant interface is already freed by the InterfaceRemoved
+        * signal callback. Simply invoke the interface_data callback.
+        */
+       err = 0;
+
+done:
+       if (data->callback != NULL)
+               data->callback(err, NULL, data->user_data);
+
+       dbus_free(data);
+}
+
+
+static void interface_remove_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &data->interface->path);
+}
+
+
+int g_supplicant_interface_remove(GSupplicantInterface *interface,
+                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_data *data;
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "RemoveInterface",
+                                               interface_remove_params,
+                                               interface_remove_result, data);
+}
+
+static void interface_scan_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_scan_data *data = user_data;
+
+       if (error != NULL) {
+               SUPPLICANT_DBG("error %s", error);
+
+               if (data->callback != NULL)
+                       data->callback(-EIO, data->interface, data->user_data);
+       } else {
+               data->interface->scan_callback = data->callback;
+               data->interface->scan_data = data->user_data;
+       }
+
+       if (data != NULL && data->scan_params != NULL)
+               g_supplicant_free_scan_params(data->scan_params);
+
+       dbus_free(data);
+}
+
+static void add_scan_frequency(DBusMessageIter *iter, unsigned int freq)
+{
+       DBusMessageIter data;
+       unsigned int width = 0; /* Not used by wpa_supplicant atm */
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &data);
+
+       dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &freq);
+       dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &width);
+
+       dbus_message_iter_close_container(iter, &data);
+}
+
+static void add_scan_frequencies(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       GSupplicantScanParams *scan_data = user_data;
+       unsigned int freq;
+       int i;
+
+       for (i = 0; i < scan_data->num_ssids; i++) {
+               freq = scan_data->freqs[i];
+               if (!freq)
+                       break;
+
+               add_scan_frequency(iter, freq);
+       }
+}
+
+static void append_ssid(DBusMessageIter *iter,
+                       const void *ssid, unsigned int len)
+{
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                                               &ssid, len);
+       dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_ssids(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantScanParams *scan_data = user_data;
+       GSList *list;
+
+       for (list = scan_data->ssids; list; list = list->next) {
+               struct scan_ssid *scan_ssid = list->data;
+
+               append_ssid(iter, scan_ssid->ssid, scan_ssid->ssid_len);
+       }
+}
+
+static void supplicant_add_scan_frequency(DBusMessageIter *dict,
+               supplicant_dbus_array_function function,
+                                       void *user_data)
+{
+       GSupplicantScanParams *scan_params = user_data;
+       DBusMessageIter entry, value, array;
+       const char *key = "Channels";
+
+       if (scan_params->freqs && scan_params->freqs[0] != 0) {
+               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_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &value);
+
+               dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+
+               if (function)
+                       function(&array, user_data);
+
+               dbus_message_iter_close_container(&value, &array);
+               dbus_message_iter_close_container(&entry, &value);
+               dbus_message_iter_close_container(dict, &entry);
+       }
+}
+
+static void interface_scan_params(DBusMessageIter *iter, void *user_data)
+{
+       DBusMessageIter dict;
+       const char *type = "passive";
+       struct interface_scan_data *data = user_data;
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       if (data && data->scan_params) {
+               type = "active";
+
+               supplicant_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+
+               supplicant_dbus_dict_append_array(&dict, "SSIDs",
+                                               DBUS_TYPE_STRING,
+                                               append_ssids,
+                                               data->scan_params);
+
+               supplicant_add_scan_frequency(&dict, add_scan_frequencies,
+                                               data->scan_params);
+       } else
+               supplicant_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+int g_supplicant_interface_scan(GSupplicantInterface *interface,
+                               GSupplicantScanParams *scan_data,
+                               GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_scan_data *data;
+       int ret;
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       if (interface->scanning == TRUE)
+               return -EALREADY;
+
+       switch (interface->state) {
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+               return -EBUSY;
+       case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISCONNECTED:
+       case G_SUPPLICANT_STATE_INACTIVE:
+       case G_SUPPLICANT_STATE_SCANNING:
+       case G_SUPPLICANT_STATE_COMPLETED:
+               break;
+       }
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->user_data = user_data;
+       data->scan_params = scan_data;
+
+       ret = supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "Scan",
+                       interface_scan_params, interface_scan_result, data);
+
+       if (ret < 0)
+               dbus_free(data);
+
+       return ret;
+}
+
+static int parse_supplicant_error(DBusMessageIter *iter)
+{
+       int err = -ECANCELED;
+       char *key;
+
+       /* If the given passphrase is malformed wpa_s returns
+        * "invalid message format" but this error should be interpreted as
+        * invalid-key.
+        */
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+               dbus_message_iter_get_basic(iter, &key);
+               if (strncmp(key, "psk", 3) == 0 ||
+                               strncmp(key, "wep_key", 7) == 0 ||
+                               strcmp(key, "invalid message format") == 0) {
+                       err = -ENOKEY;
+                       break;
+               }
+               dbus_message_iter_next(iter);
+       }
+
+       return err;
+}
+
+static void interface_select_network_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+       int err;
+
+       SUPPLICANT_DBG("");
+
+       err = 0;
+       if (error != NULL) {
+               SUPPLICANT_DBG("SelectNetwork error %s", error);
+               err = parse_supplicant_error(iter);
+       }
+
+       if (data->callback != NULL)
+               data->callback(err, data->interface, data->user_data);
+
+       g_free(data->ssid);
+       dbus_free(data);
+}
+
+static void interface_select_network_params(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+       GSupplicantInterface *interface = data->interface;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                       &interface->network_path);
+}
+
+static void interface_add_network_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+       GSupplicantInterface *interface = data->interface;
+       const char *path;
+       int err;
+
+       if (error != NULL)
+               goto error;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               goto error;
+
+       SUPPLICANT_DBG("PATH: %s", path);
+
+       g_free(interface->network_path);
+       interface->network_path = g_strdup(path);
+
+       supplicant_dbus_method_call(data->interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "SelectNetwork",
+                       interface_select_network_params,
+                       interface_select_network_result, data);
+
+       return;
+
+error:
+       SUPPLICANT_DBG("AddNetwork error %s", error);
+       err = parse_supplicant_error(iter);
+       if (data->callback != NULL)
+               data->callback(err, data->interface, data->user_data);
+
+       g_free(interface->network_path);
+       interface->network_path = NULL;
+       g_free(data->ssid);
+       g_free(data);
+}
+
+static void add_network_security_wep(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       const char *auth_alg = "OPEN SHARED";
+       const char *key_index = "0";
+
+       supplicant_dbus_dict_append_basic(dict, "auth_alg",
+                                       DBUS_TYPE_STRING, &auth_alg);
+
+       if (ssid->passphrase) {
+               int size = strlen(ssid->passphrase);
+               if (size == 10 || size == 26) {
+                       unsigned char *key = g_try_malloc(13);
+                       char tmp[3];
+                       int i;
+
+                       memset(tmp, 0, sizeof(tmp));
+                       if (key == NULL)
+                               size = 0;
+
+                       for (i = 0; i < size / 2; i++) {
+                               memcpy(tmp, ssid->passphrase + (i * 2), 2);
+                               key[i] = (unsigned char) strtol(tmp, NULL, 16);
+                       }
+
+                       supplicant_dbus_dict_append_fixed_array(dict,
+                                                       "wep_key0",
+                                                       DBUS_TYPE_BYTE,
+                                                       &key, size / 2);
+                       g_free(key);
+               } else if (size == 5 || size == 13) {
+                       unsigned char *key = g_try_malloc(13);
+                       int i;
+
+                       if (key == NULL)
+                               size = 0;
+
+                       for (i = 0; i < size; i++)
+                               key[i] = (unsigned char) ssid->passphrase[i];
+
+                       supplicant_dbus_dict_append_fixed_array(dict,
+                                                               "wep_key0",
+                                                               DBUS_TYPE_BYTE,
+                                                               &key, size);
+                       g_free(key);
+               } else
+                       supplicant_dbus_dict_append_basic(dict,
+                                                       "wep_key0",
+                                                       DBUS_TYPE_STRING,
+                                                       &ssid->passphrase);
+
+               supplicant_dbus_dict_append_basic(dict, "wep_tx_keyidx",
+                                       DBUS_TYPE_STRING, &key_index);
+       }
+}
+
+static dbus_bool_t is_psk_raw_key(const char *psk)
+{
+       int i;
+
+       /* A raw key is always 64 bytes length... */
+       if (strlen(psk) != 64)
+               return FALSE;
+
+       /* ... and its content is in hex representation */
+       for (i = 0; i < 64; i++)
+               if (!isxdigit((unsigned char) psk[i]))
+                       return FALSE;
+
+       return TRUE;
+}
+
+static unsigned char hexchar2bin(char c)
+{
+       if ((c >= '0') && (c <= '9'))
+               return c - '0';
+       else if ((c >= 'A') && (c <= 'F'))
+               return c - 'A' + 10;
+       else if ((c >= 'a') && (c <= 'f'))
+               return c - 'a' + 10;
+       else
+               return c;
+}
+
+static void hexstring2bin(const char *string, unsigned char *data, size_t data_len)
+{
+       size_t i;
+
+       for (i = 0; i < data_len; i++)
+               data[i] = (hexchar2bin(string[i * 2 + 0]) << 4 |
+                          hexchar2bin(string[i * 2 + 1]) << 0);
+}
+
+static void add_network_security_psk(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       if (ssid->passphrase && strlen(ssid->passphrase) > 0) {
+               const char *key = "psk";
+
+               if (is_psk_raw_key(ssid->passphrase) == TRUE) {
+                       unsigned char data[32];
+                       unsigned char *datap = data;
+
+                       /* The above pointer alias is required by D-Bus because
+                        * with D-Bus and GCC, non-heap-allocated arrays cannot
+                        * be passed directly by their base pointer. */
+
+                       hexstring2bin(ssid->passphrase, datap, sizeof(data));
+
+                       supplicant_dbus_dict_append_fixed_array(dict,
+                                                       key, DBUS_TYPE_BYTE,
+                                                       &datap, sizeof(data));
+               } else
+                       supplicant_dbus_dict_append_basic(dict,
+                                                       key, DBUS_TYPE_STRING,
+                                                       &ssid->passphrase);
+       }
+}
+
+static void add_network_security_tls(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       /*
+        * For TLS, we at least need:
+        *              The client certificate
+        *              The client private key file
+        *              The client private key file password
+        *
+        * The Authority certificate is optional.
+        */
+       if (ssid->client_cert_path == NULL)
+               return;
+
+       if (ssid->private_key_path == NULL)
+               return;
+
+       if (ssid->private_key_passphrase == NULL)
+               return;
+
+       if (ssid->ca_cert_path)
+               supplicant_dbus_dict_append_basic(dict, "ca_cert",
+                                       DBUS_TYPE_STRING, &ssid->ca_cert_path);
+
+       supplicant_dbus_dict_append_basic(dict, "private_key",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->private_key_path);
+       supplicant_dbus_dict_append_basic(dict, "private_key_passwd",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->private_key_passphrase);
+       supplicant_dbus_dict_append_basic(dict, "client_cert",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->client_cert_path);
+}
+
+static void add_network_security_peap(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       char *phase2_auth;
+
+       /*
+        * For PEAP/TTLS, we at least need
+        *              The authority certificate
+        *              The 2nd phase authentication method
+        *              The 2nd phase passphrase
+        *
+        * The Client certificate is optional although strongly recommended
+        * When setting it, we need in addition
+        *              The Client private key file
+        *              The Client private key file password
+        */
+       if (ssid->passphrase == NULL)
+               return;
+
+       if (ssid->phase2_auth == NULL)
+               return;
+
+       if (ssid->client_cert_path) {
+               if (ssid->private_key_path == NULL)
+                       return;
+
+               if (ssid->private_key_passphrase == NULL)
+                       return;
+
+               supplicant_dbus_dict_append_basic(dict, "client_cert",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->client_cert_path);
+
+               supplicant_dbus_dict_append_basic(dict, "private_key",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->private_key_path);
+
+               supplicant_dbus_dict_append_basic(dict, "private_key_passwd",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->private_key_passphrase);
+
+       }
+
+       if (g_str_has_prefix(ssid->phase2_auth, "EAP-") == TRUE) {
+               phase2_auth = g_strdup_printf("autheap=%s",
+                                       ssid->phase2_auth + strlen("EAP-"));
+       } else
+               phase2_auth = g_strdup_printf("auth=%s", ssid->phase2_auth);
+
+       supplicant_dbus_dict_append_basic(dict, "password",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->passphrase);
+
+       if (ssid->ca_cert_path)
+               supplicant_dbus_dict_append_basic(dict, "ca_cert",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->ca_cert_path);
+
+       supplicant_dbus_dict_append_basic(dict, "phase2",
+                                               DBUS_TYPE_STRING,
+                                               &phase2_auth);
+
+       g_free(phase2_auth);
+}
+
+static void add_network_security_eap(DBusMessageIter *dict,
+                                       GSupplicantSSID *ssid)
+{
+       char *eap_value;
+
+#if defined TIZEN_EXT
+       if (ssid->eap == NULL)
+#else
+       if (ssid->eap == NULL || ssid->identity == NULL)
+#endif
+               return;
+
+       if (g_strcmp0(ssid->eap, "tls") == 0) {
+               add_network_security_tls(dict, ssid);
+       } else if (g_strcmp0(ssid->eap, "peap") == 0 ||
+                               g_strcmp0(ssid->eap, "ttls") == 0) {
+#if defined TIZEN_EXT
+               if (ssid->identity == NULL)
+                       return;
+#endif
+               add_network_security_peap(dict, ssid);
+#if defined TIZEN_EXT
+       } else if (g_strcmp0(ssid->eap, "sim") == 0 ||
+                       g_strcmp0(ssid->eap, "aka") == 0) {
+#endif
+       } else
+               return;
+
+       eap_value = g_ascii_strup(ssid->eap, -1);
+
+       supplicant_dbus_dict_append_basic(dict, "eap",
+                                               DBUS_TYPE_STRING,
+                                               &eap_value);
+#if defined TIZEN_EXT
+       if (ssid->identity != NULL)
+               supplicant_dbus_dict_append_basic(dict, "identity",
+                                                       DBUS_TYPE_STRING,
+                                                       &ssid->identity);
+#else
+       supplicant_dbus_dict_append_basic(dict, "identity",
+                                               DBUS_TYPE_STRING,
+                                               &ssid->identity);
+#endif
+
+       g_free(eap_value);
+}
+
+static void add_network_security_ciphers(DBusMessageIter *dict,
+                                               GSupplicantSSID *ssid)
+{
+       unsigned int p_cipher, g_cipher, i;
+       char *pairwise, *group;
+       char *pair_ciphers[4];
+       char *group_ciphers[5];
+
+       p_cipher = ssid->pairwise_cipher;
+       g_cipher = ssid->group_cipher;
+
+       if (p_cipher == 0 && g_cipher == 0)
+               return;
+
+       i = 0;
+
+       if (p_cipher & G_SUPPLICANT_PAIRWISE_CCMP)
+               pair_ciphers[i++] = "CCMP";
+
+       if (p_cipher & G_SUPPLICANT_PAIRWISE_TKIP)
+               pair_ciphers[i++] = "TKIP";
+
+       if (p_cipher & G_SUPPLICANT_PAIRWISE_NONE)
+               pair_ciphers[i++] = "NONE";
+
+       pair_ciphers[i] = NULL;
+
+       i = 0;
+
+       if (g_cipher & G_SUPPLICANT_GROUP_CCMP)
+               group_ciphers[i++] = "CCMP";
+
+       if (g_cipher & G_SUPPLICANT_GROUP_TKIP)
+               group_ciphers[i++] = "TKIP";
+
+       if (g_cipher & G_SUPPLICANT_GROUP_WEP104)
+               group_ciphers[i++] = "WEP104";
+
+       if (g_cipher & G_SUPPLICANT_GROUP_WEP40)
+               group_ciphers[i++] = "WEP40";
+
+       group_ciphers[i] = NULL;
+
+       pairwise = g_strjoinv(" ", pair_ciphers);
+       group = g_strjoinv(" ", group_ciphers);
+
+       SUPPLICANT_DBG("cipher %s %s", pairwise, group);
+
+       supplicant_dbus_dict_append_basic(dict, "pairwise",
+                                               DBUS_TYPE_STRING,
+                                               &pairwise);
+       supplicant_dbus_dict_append_basic(dict, "group",
+                                               DBUS_TYPE_STRING,
+                                               &group);
+
+       g_free(pairwise);
+       g_free(group);
+}
+
+static void add_network_security_proto(DBusMessageIter *dict,
+                                               GSupplicantSSID *ssid)
+{
+       unsigned int protocol, i;
+       char *proto;
+       char *protos[3];
+
+       protocol = ssid->protocol;
+
+       if (protocol == 0)
+               return;
+
+       i = 0;
+
+       if (protocol & G_SUPPLICANT_PROTO_RSN)
+               protos[i++] = "RSN";
+
+       if (protocol & G_SUPPLICANT_PROTO_WPA)
+               protos[i++] = "WPA";
+
+       protos[i] = NULL;
+
+       proto = g_strjoinv(" ", protos);
+
+       SUPPLICANT_DBG("proto %s", proto);
+
+       supplicant_dbus_dict_append_basic(dict, "proto",
+                                               DBUS_TYPE_STRING,
+                                               &proto);
+
+       g_free(proto);
+}
+
+static void add_network_security(DBusMessageIter *dict, GSupplicantSSID *ssid)
+{
+       char *key_mgmt;
+
+       switch (ssid->security) {
+       case G_SUPPLICANT_SECURITY_UNKNOWN:
+       case G_SUPPLICANT_SECURITY_NONE:
+       case G_SUPPLICANT_SECURITY_WEP:
+               key_mgmt = "NONE";
+               add_network_security_wep(dict, ssid);
+               add_network_security_ciphers(dict, ssid);
+               break;
+       case G_SUPPLICANT_SECURITY_PSK:
+               key_mgmt = "WPA-PSK";
+               add_network_security_psk(dict, ssid);
+               add_network_security_ciphers(dict, ssid);
+               add_network_security_proto(dict, ssid);
+               break;
+       case G_SUPPLICANT_SECURITY_IEEE8021X:
+               key_mgmt = "WPA-EAP";
+               add_network_security_eap(dict, ssid);
+               add_network_security_ciphers(dict, ssid);
+               add_network_security_proto(dict, ssid);
+               break;
+       }
+
+       supplicant_dbus_dict_append_basic(dict, "key_mgmt",
+                               DBUS_TYPE_STRING, &key_mgmt);
+}
+
+static void add_network_mode(DBusMessageIter *dict, GSupplicantSSID *ssid)
+{
+       dbus_uint32_t mode;
+
+       switch (ssid->mode) {
+       case G_SUPPLICANT_MODE_UNKNOWN:
+       case G_SUPPLICANT_MODE_INFRA:
+               mode = 0;
+               break;
+       case G_SUPPLICANT_MODE_IBSS:
+               mode = 1;
+               break;
+       case G_SUPPLICANT_MODE_MASTER:
+               mode = 2;
+               break;
+       }
+
+       supplicant_dbus_dict_append_basic(dict, "mode",
+                               DBUS_TYPE_UINT32, &mode);
+}
+
+static void interface_add_network_params(DBusMessageIter *iter, void *user_data)
+{
+       DBusMessageIter dict;
+       struct interface_connect_data *data = user_data;
+       GSupplicantSSID *ssid = data->ssid;
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       if (ssid->scan_ssid)
+               supplicant_dbus_dict_append_basic(&dict, "scan_ssid",
+                                        DBUS_TYPE_UINT32, &ssid->scan_ssid);
+
+       if (ssid->freq)
+               supplicant_dbus_dict_append_basic(&dict, "frequency",
+                                        DBUS_TYPE_UINT32, &ssid->freq);
+
+       if (ssid->bgscan != NULL)
+               supplicant_dbus_dict_append_basic(&dict, "bgscan",
+                                       DBUS_TYPE_STRING, &ssid->bgscan);
+
+       add_network_mode(&dict, ssid);
+
+       add_network_security(&dict, ssid);
+
+       supplicant_dbus_dict_append_fixed_array(&dict, "ssid",
+                                       DBUS_TYPE_BYTE, &ssid->ssid,
+                                               ssid->ssid_len);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void interface_wps_start_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+       if (error != NULL)
+               SUPPLICANT_DBG("error: %s", error);
+
+       g_free(data->ssid);
+       dbus_free(data);
+}
+
+static void interface_add_wps_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+       GSupplicantSSID *ssid = data->ssid;
+       const char *role = "enrollee", *type;
+       DBusMessageIter dict;
+
+       SUPPLICANT_DBG("");
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       supplicant_dbus_dict_append_basic(&dict, "Role",
+                                               DBUS_TYPE_STRING, &role);
+
+       type = "pbc";
+       if (ssid->pin_wps != NULL) {
+               type = "pin";
+               supplicant_dbus_dict_append_basic(&dict, "Pin",
+                                       DBUS_TYPE_STRING, &ssid->pin_wps);
+       }
+
+       supplicant_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void wps_start(const char *error, DBusMessageIter *iter, void *user_data)
+{
+       struct interface_connect_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL) {
+               SUPPLICANT_DBG("error: %s", error);
+               g_free(data->ssid);
+               dbus_free(data);
+               return;
+       }
+
+       supplicant_dbus_method_call(data->interface->path,
+                       SUPPLICANT_INTERFACE ".Interface.WPS", "Start",
+                       interface_add_wps_params,
+                       interface_wps_start_result, data);
+}
+
+static void wps_process_credentials(DBusMessageIter *iter, void *user_data)
+{
+       dbus_bool_t credentials = TRUE;
+
+       SUPPLICANT_DBG("");
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &credentials);
+}
+
+
+int g_supplicant_interface_connect(GSupplicantInterface *interface,
+                               GSupplicantSSID *ssid,
+                               GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_connect_data *data;
+       int ret;
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       /* TODO: Check if we're already connected and switch */
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->ssid = ssid;
+       data->user_data = user_data;
+
+       if (ssid->use_wps == TRUE) {
+               g_free(interface->wps_cred.key);
+               memset(&interface->wps_cred, 0,
+                               sizeof(struct _GSupplicantWpsCredentials));
+
+               ret = supplicant_dbus_property_set(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface.WPS",
+                       "ProcessCredentials", DBUS_TYPE_BOOLEAN_AS_STRING,
+                       wps_process_credentials, wps_start, data);
+       } else
+               ret = supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "AddNetwork",
+                       interface_add_network_params,
+                       interface_add_network_result, data);
+
+       if (ret < 0)
+               return ret;
+
+       return -EINPROGRESS;
+}
+
+static void network_remove_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+       int result = 0;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL)
+               result = -EIO;
+
+       if (data->callback != NULL)
+               data->callback(result, data->interface, data->user_data);
+
+       dbus_free(data);
+}
+
+static void network_remove_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+       const char *path = data->interface->network_path;
+
+       SUPPLICANT_DBG("path %s", path);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static int network_remove(struct interface_data *data)
+{
+       GSupplicantInterface *interface = data->interface;
+
+       SUPPLICANT_DBG("");
+
+       return supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "RemoveNetwork",
+                       network_remove_params, network_remove_result, data);
+}
+
+static void interface_disconnect_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_data *data = user_data;
+
+       SUPPLICANT_DBG("");
+
+       if (error != NULL && data->callback != NULL)
+               data->callback(-EIO, data->interface, data->user_data);
+
+       /* If we are disconnecting from previous WPS successful
+        * association. i.e.: it did not went through AddNetwork,
+        * and interface->network_path was never set. */
+       if (data->interface->network_path == NULL) {
+               dbus_free(data);
+               return;
+       }
+
+       network_remove(data);
+}
+
+int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
+                                       GSupplicantInterfaceCallback callback,
+                                                       void *user_data)
+{
+       struct interface_data *data;
+
+       SUPPLICANT_DBG("");
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "Disconnect",
+                               NULL, interface_disconnect_result, data);
+}
+
+
+static const char *g_supplicant_rule0 = "type=signal,"
+                                       "path=" DBUS_PATH_DBUS ","
+                                       "sender=" DBUS_SERVICE_DBUS ","
+                                       "interface=" DBUS_INTERFACE_DBUS ","
+                                       "member=NameOwnerChanged,"
+                                       "arg0=" SUPPLICANT_SERVICE;
+static const char *g_supplicant_rule1 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE;
+static const char *g_supplicant_rule2 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface";
+static const char *g_supplicant_rule3 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface.WPS";
+static const char *g_supplicant_rule4 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".BSS";
+static const char *g_supplicant_rule5 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Network";
+
+static void invoke_introspect_method(void)
+{
+       DBusMessage *message;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE,
+                                       SUPPLICANT_PATH,
+                                       DBUS_INTERFACE_INTROSPECTABLE,
+                                       "Introspect");
+
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+       dbus_connection_send(connection, message, NULL);
+       dbus_message_unref(message);
+}
+
+int g_supplicant_register(const GSupplicantCallbacks *callbacks)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (dbus_connection_add_filter(connection,
+                               g_supplicant_filter, NULL, NULL) == FALSE) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+               return -EIO;
+       }
+
+       callbacks_pointer = callbacks;
+       eap_methods = 0;
+
+       interface_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, remove_interface);
+
+       bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       supplicant_dbus_setup(connection);
+
+       dbus_bus_add_match(connection, g_supplicant_rule0, NULL);
+       dbus_bus_add_match(connection, g_supplicant_rule1, NULL);
+       dbus_bus_add_match(connection, g_supplicant_rule2, NULL);
+       dbus_bus_add_match(connection, g_supplicant_rule3, NULL);
+       dbus_bus_add_match(connection, g_supplicant_rule4, NULL);
+       dbus_bus_add_match(connection, g_supplicant_rule5, NULL);
+       dbus_connection_flush(connection);
+
+       if (dbus_bus_name_has_owner(connection,
+                                       SUPPLICANT_SERVICE, NULL) == TRUE) {
+               system_available = TRUE;
+               supplicant_dbus_property_get_all(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               service_property, NULL);
+       } else
+               invoke_introspect_method();
+
+       return 0;
+}
+
+static void unregister_interface_remove_params(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       const char *path = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &path);
+}
+
+
+static void unregister_remove_interface(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       GSupplicantInterface *interface = value;
+
+       supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                       SUPPLICANT_INTERFACE,
+                                       "RemoveInterface",
+                                       unregister_interface_remove_params,
+                                               NULL, interface->path);
+}
+
+void g_supplicant_unregister(const GSupplicantCallbacks *callbacks)
+{
+       SUPPLICANT_DBG("");
+
+       if (connection != NULL) {
+               dbus_bus_remove_match(connection, g_supplicant_rule5, NULL);
+               dbus_bus_remove_match(connection, g_supplicant_rule4, NULL);
+               dbus_bus_remove_match(connection, g_supplicant_rule3, NULL);
+               dbus_bus_remove_match(connection, g_supplicant_rule2, NULL);
+               dbus_bus_remove_match(connection, g_supplicant_rule1, NULL);
+               dbus_bus_remove_match(connection, g_supplicant_rule0, NULL);
+               dbus_connection_flush(connection);
+
+               dbus_connection_remove_filter(connection,
+                                               g_supplicant_filter, NULL);
+       }
+
+       if (bss_mapping != NULL) {
+               g_hash_table_destroy(bss_mapping);
+               bss_mapping = NULL;
+       }
+
+       if (system_available == TRUE)
+               callback_system_killed();
+
+       if (interface_table != NULL) {
+               g_hash_table_foreach(interface_table,
+                                       unregister_remove_interface, NULL);
+               g_hash_table_destroy(interface_table);
+               interface_table = NULL;
+       }
+
+       if (connection != NULL) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+       }
+
+       callbacks_pointer = NULL;
+       eap_methods = 0;
+}
diff --git a/gweb/giognutls.c b/gweb/giognutls.c
new file mode 100644 (file)
index 0000000..47ff8c4
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ *
+ *  Web service library with GLib integration
+ *
+ *  Copyright (C) 2009-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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <gnutls/gnutls.h>
+
+#include "giognutls.h"
+
+//#define DBG(fmt, arg...)  printf("%s: " fmt "\n" , __func__ , ## arg)
+#define DBG(fmt, arg...)
+
+typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
+typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
+
+struct _GIOGnuTLSChannel {
+       GIOChannel channel;
+       gint fd;
+       gnutls_certificate_credentials_t cred;
+       gnutls_session_t session;
+       gboolean established;
+       gboolean again;
+};
+
+struct _GIOGnuTLSWatch {
+       GSource source;
+       GPollFD pollfd;
+       GIOChannel *channel;
+       GIOCondition condition;
+};
+
+static volatile int global_init_done = 0;
+
+static inline void g_io_gnutls_global_init(void)
+{
+       if (__sync_bool_compare_and_swap(&global_init_done, 0, 1) == TRUE)
+               gnutls_global_init();
+}
+
+static GIOStatus check_handshake(GIOChannel *channel, GError **err)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       int result;
+
+       DBG("channel %p", channel);
+
+       if (gnutls_channel->established == TRUE)
+               return G_IO_STATUS_NORMAL;
+
+again:
+       result = gnutls_handshake(gnutls_channel->session);
+
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
+               goto again;
+       }
+
+       if (result < 0) {
+               g_set_error(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
+               return G_IO_STATUS_ERROR;
+       }
+
+       gnutls_channel->established = TRUE;
+
+       DBG("handshake done");
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
+                               gsize count, gsize *bytes_read, GError **err)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       GIOStatus status;
+       ssize_t result;
+
+       DBG("channel %p count %zu", channel, count);
+
+       *bytes_read = 0;
+
+again:
+       status = check_handshake(channel, err);
+       if (status != G_IO_STATUS_NORMAL)
+               return status;
+
+       result = gnutls_record_recv(gnutls_channel->session, buf, count);
+
+       DBG("result %zd", result);
+
+       if (result == GNUTLS_E_REHANDSHAKE) {
+               gnutls_channel->established = FALSE;
+               goto again;
+       }
+
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
+               goto again;
+       }
+
+       if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+               return G_IO_STATUS_EOF;
+
+       if (result < 0) {
+               g_set_error(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
+               return G_IO_STATUS_ERROR;
+       }
+
+       *bytes_read = result;
+
+       return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
+                               gsize count, gsize *bytes_written, GError **err)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       GIOStatus status;
+       ssize_t result;
+
+       DBG("channel %p count %zu", channel, count);
+
+       *bytes_written = 0;
+
+again:
+       status = check_handshake(channel, err);
+       if (status != G_IO_STATUS_NORMAL)
+               return status;
+
+       result = gnutls_record_send(gnutls_channel->session, buf, count);
+
+       DBG("result %zd", result);
+
+       if (result == GNUTLS_E_REHANDSHAKE) {
+               gnutls_channel->established = FALSE;
+               goto again;
+       }
+
+       if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+               GIOFlags flags = g_io_channel_get_flags(channel);
+
+               if (gnutls_channel->again == TRUE)
+                       return G_IO_STATUS_AGAIN;
+
+               if (flags & G_IO_FLAG_NONBLOCK)
+                       return G_IO_STATUS_AGAIN;
+
+               goto again;
+       }
+
+       if (result < 0) {
+               g_set_error(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
+               return G_IO_STATUS_ERROR;
+        }
+
+       *bytes_written = result;
+
+       return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
+                                               GSeekType type, GError **err)
+{
+       DBG("channel %p", channel);
+
+       g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Not supported");
+       return G_IO_STATUS_ERROR;
+}
+
+static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+
+       DBG("channel %p", channel);
+
+       if (gnutls_channel->established == TRUE)
+               gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
+
+       if (close(gnutls_channel->fd) < 0) {
+               g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                               G_IO_CHANNEL_ERROR_FAILED, "Closing failed");
+               return G_IO_STATUS_ERROR;
+       }
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static void g_io_gnutls_free(GIOChannel *channel)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+
+       DBG("channel %p", channel);
+
+       gnutls_deinit(gnutls_channel->session);
+
+       gnutls_certificate_free_credentials(gnutls_channel->cred);
+
+       g_free(gnutls_channel);
+}
+
+static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
+                                               GIOFlags flags, GError **err)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       glong fcntl_flags = 0;
+
+       DBG("channel %p flags %u", channel, flags);
+
+       if (flags & G_IO_FLAG_NONBLOCK)
+               fcntl_flags |= O_NONBLOCK;
+
+       if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) {
+               g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+                       G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed");
+               return G_IO_STATUS_ERROR;
+       }
+
+       return G_IO_STATUS_NORMAL;
+}
+
+static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       GIOFlags flags = 0;
+       glong fcntl_flags;
+
+       DBG("channel %p", channel);
+
+       fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
+       if (fcntl_flags < 0)
+               return 0;
+
+       if (fcntl_flags & O_NONBLOCK)
+               flags |= G_IO_FLAG_NONBLOCK;
+
+       return flags;
+}
+
+static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
+{
+       DBG("source %p", source);
+
+       *timeout = -1;
+
+       return FALSE;
+}
+
+static gboolean g_io_gnutls_check(GSource *source)
+{
+       GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+       GIOCondition condition = watch->pollfd.revents;
+
+       DBG("source %p condition %u", source, condition);
+
+       if (condition & watch->condition)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
+                                                       gpointer user_data)
+{
+       GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+       GIOFunc func = (GIOFunc) callback;
+       GIOCondition condition = watch->pollfd.revents;
+
+       DBG("source %p condition %u", source, condition);
+
+       if (func == NULL)
+               return FALSE;
+
+       return func(watch->channel, condition & watch->condition, user_data);
+}
+
+static void g_io_gnutls_finalize(GSource *source)
+{
+       GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+
+       DBG("source %p", source);
+
+       g_io_channel_unref(watch->channel);
+}
+
+static GSourceFuncs gnutls_watch_funcs = {
+       g_io_gnutls_prepare,
+       g_io_gnutls_check,
+       g_io_gnutls_dispatch,
+       g_io_gnutls_finalize,
+};
+
+static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
+                                               GIOCondition condition)
+{
+       GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+       GIOGnuTLSWatch *watch;
+       GSource *source;
+
+       DBG("channel %p condition %u", channel, condition);
+
+       source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
+
+       watch = (GIOGnuTLSWatch *) source;
+
+       watch->channel = channel;
+       g_io_channel_ref(channel);
+
+       watch->condition = condition;
+
+       watch->pollfd.fd = gnutls_channel->fd;
+       watch->pollfd.events = condition;
+
+       g_source_add_poll(source, &watch->pollfd);
+
+       return source;
+}
+
+static GIOFuncs gnutls_channel_funcs = {
+       g_io_gnutls_read,
+       g_io_gnutls_write,
+       g_io_gnutls_seek,
+       g_io_gnutls_close,
+       g_io_gnutls_create_watch,
+       g_io_gnutls_free,
+       g_io_gnutls_set_flags,
+       g_io_gnutls_get_flags,
+};
+
+static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
+                                               const void *buf, size_t count)
+{
+       GIOGnuTLSChannel *gnutls_channel = transport_data;
+       ssize_t result;
+
+       DBG("count %zu", count);
+
+       result = write(gnutls_channel->fd, buf, count);
+
+       if (result < 0 && errno == EAGAIN)
+               gnutls_channel->again = TRUE;
+       else
+               gnutls_channel->again = FALSE;
+
+       DBG("result %zd", result);
+
+       return result;
+}
+
+static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
+                                               void *buf, size_t count)
+{
+       GIOGnuTLSChannel *gnutls_channel = transport_data;
+       ssize_t result;
+
+       DBG("count %zu", count);
+
+       result = read(gnutls_channel->fd, buf, count);
+
+       if (result < 0 && errno == EAGAIN)
+               gnutls_channel->again = TRUE;
+       else
+               gnutls_channel->again = FALSE;
+
+       DBG("result %zd", result);
+
+       return result;
+}
+
+gboolean g_io_channel_supports_tls(void)
+{
+       return TRUE;
+}
+
+GIOChannel *g_io_channel_gnutls_new(int fd)
+{
+       GIOGnuTLSChannel *gnutls_channel;
+       GIOChannel *channel;
+       int err;
+
+       DBG("");
+
+       gnutls_channel = g_new(GIOGnuTLSChannel, 1);
+
+       channel = (GIOChannel *) gnutls_channel;
+
+       g_io_channel_init(channel);
+       channel->funcs = &gnutls_channel_funcs;
+
+       gnutls_channel->fd = fd;
+
+       channel->is_seekable = FALSE;
+       channel->is_readable = TRUE;
+       channel->is_writeable = TRUE;
+
+       channel->do_encode = FALSE;
+
+       g_io_gnutls_global_init();
+
+        err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
+       if (err < 0) {
+               g_free(gnutls_channel);
+               return NULL;
+       }
+
+       gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
+        gnutls_transport_set_push_function(gnutls_channel->session,
+                                               g_io_gnutls_push_func);
+        gnutls_transport_set_pull_function(gnutls_channel->session,
+                                               g_io_gnutls_pull_func);
+#if GNUTLS_VERSION_NUMBER < 0x020c00
+       gnutls_transport_set_lowat(gnutls_channel->session, 0);
+
+       gnutls_priority_set_direct(gnutls_channel->session,
+                                               "NORMAL:%COMPAT", NULL);
+#else
+       gnutls_priority_set_direct(gnutls_channel->session,
+               "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
+#endif
+
+       gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
+       gnutls_credentials_set(gnutls_channel->session,
+                               GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
+
+       DBG("channel %p", channel);
+
+       return channel;
+}
diff --git a/gweb/giognutls.h b/gweb/giognutls.h
new file mode 100644 (file)
index 0000000..f16c75d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  Web service library with GLib integration
+ *
+ *  Copyright (C) 2009-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
+ *
+ */
+
+#include <glib.h>
+
+gboolean g_io_channel_supports_tls(void);
+
+GIOChannel *g_io_channel_gnutls_new(int fd);
diff --git a/gweb/gionotls.c b/gweb/gionotls.c
new file mode 100644 (file)
index 0000000..88db3e4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *
+ *  Web service library with GLib integration
+ *
+ *  Copyright (C) 2009-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 "giognutls.h"
+
+gboolean g_io_channel_supports_tls(void)
+{
+       return FALSE;
+}
+
+GIOChannel *g_io_channel_gnutls_new(int fd)
+{
+       return NULL;
+}
diff --git a/gweb/gresolv.c b/gweb/gresolv.c
new file mode 100644 (file)
index 0000000..4dd09b6
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ *
+ *  Resolver library with GLib integration
+ *
+ *  Copyright (C) 2009-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 <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <resolv.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <net/if.h>
+
+#include "gresolv.h"
+
+struct sort_result {
+       int precedence;
+       int src_scope;
+       int dst_scope;
+       int src_label;
+       int dst_label;
+       gboolean reachable;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in sin;
+               struct sockaddr_in6 sin6;
+       } src;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in sin;
+               struct sockaddr_in6 sin6;
+       } dst;
+};
+
+struct resolv_query;
+
+struct resolv_lookup {
+       GResolv *resolv;
+       guint id;
+
+       int nr_results;
+       struct sort_result *results;
+
+       struct resolv_query *ipv4_query;
+       struct resolv_query *ipv6_query;
+
+       guint ipv4_status;
+       guint ipv6_status;
+
+       GResolvResultFunc result_func;
+       gpointer result_data;
+};
+
+struct resolv_query {
+       GResolv *resolv;
+
+       guint timeout;
+
+       uint16_t msgid;
+
+       struct resolv_lookup *lookup;
+};
+
+struct resolv_nameserver {
+       GResolv *resolv;
+
+       char *address;
+       uint16_t port;
+       unsigned long flags;
+
+       GIOChannel *udp_channel;
+       guint udp_watch;
+};
+
+struct _GResolv {
+       int ref_count;
+
+       int result_family;
+
+       guint next_lookup_id;
+       GQueue *lookup_queue;
+       GQueue *query_queue;
+
+       int index;
+       GList *nameserver_list;
+
+       struct __res_state res;
+
+       GResolvDebugFunc debug_func;
+       gpointer debug_data;
+};
+
+static inline void debug(GResolv *resolv, const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (resolv->debug_func == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               resolv->debug_func(str, resolv->debug_data);
+
+       va_end(ap);
+}
+
+static void destroy_query(struct resolv_query *query)
+{
+       if (query->timeout > 0)
+               g_source_remove(query->timeout);
+
+       g_free(query);
+}
+
+static void destroy_lookup(struct resolv_lookup *lookup)
+{
+       if (lookup->ipv4_query != NULL) {
+               g_queue_remove(lookup->resolv->query_queue,
+                                               lookup->ipv4_query);
+               destroy_query(lookup->ipv4_query);
+       }
+
+       if (lookup->ipv6_query != NULL) {
+               g_queue_remove(lookup->resolv->query_queue,
+                                               lookup->ipv6_query);
+               destroy_query(lookup->ipv6_query);
+       }
+
+       g_free(lookup->results);
+       g_free(lookup);
+}
+
+static void find_srcaddr(struct sort_result *res)
+{
+       socklen_t sl = sizeof(res->src);
+       int fd;
+
+       fd = socket(res->dst.sa.sa_family, SOCK_DGRAM | SOCK_CLOEXEC,
+                       IPPROTO_IP);
+       if (fd < 0)
+               return;
+
+       if (connect(fd, &res->dst.sa, sizeof(res->dst)) < 0) {
+               close(fd);
+               return;
+       }
+
+       if (getsockname(fd, &res->src.sa, &sl) < 0) {
+               close(fd);
+               return;
+       }
+
+       res->reachable = TRUE;
+       close(fd);
+}
+
+struct gai_table
+{
+       unsigned char addr[NS_IN6ADDRSZ];
+       int mask;
+       int value;
+};
+
+static const struct gai_table gai_labels[] = {
+       {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+               .mask = 128,
+               .value = 0,
+       }, {
+               .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 16,
+               .value = 2,
+       }, {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 96,
+               .value = 3,
+       }, {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 96,
+               .value = 4,
+       }, {
+               /* Variations from RFC 3484, matching glibc behaviour */
+               .addr = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 10,
+               .value = 5,
+       }, {
+               .addr = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 7,
+               .value = 6,
+       }, {
+               .addr = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 32,
+               .value = 7,
+       }, {
+               /* catch-all */
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 0,
+               .value = 1,
+       }
+};
+
+static const struct gai_table gai_precedences[] = {
+       {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+               .mask = 128,
+               .value = 50,
+       }, {
+               .addr = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 16,
+               .value = 30,
+       }, {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 96,
+               .value = 20,
+       }, {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 96,
+               .value = 10,
+       }, {
+               .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+               .mask = 0,
+               .value = 40,
+       }
+};
+
+static unsigned char v4mapped[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
+
+static gboolean mask_compare(const unsigned char *one,
+                                       const unsigned char *two, int mask)
+{
+       if (mask > 8) {
+               if (memcmp(one, two, mask / 8))
+                       return FALSE;
+               one += mask / 8;
+               two += mask / 8;
+               mask %= 8;
+       }
+
+       if (mask && ((*one ^ *two) >> (8 - mask)))
+               return FALSE;
+
+       return TRUE;
+}
+
+static int match_gai_table(struct sockaddr *sa, const struct gai_table *tbl)
+{
+       struct sockaddr_in *sin = (void *)sa;
+       struct sockaddr_in6 *sin6 = (void *)sa;
+       void *addr;
+
+       if (sa->sa_family == AF_INET) {
+               addr = v4mapped;
+               memcpy(v4mapped+12, &sin->sin_addr, NS_INADDRSZ);
+       } else
+               addr = &sin6->sin6_addr;
+
+       while (1) {
+               if (mask_compare(addr, tbl->addr, tbl->mask))
+                       return tbl->value;
+               tbl++;
+       }
+}
+
+#define DQUAD(_a,_b,_c,_d) ( ((_a)<<24) | ((_b)<<16) | ((_c)<<8) | (_d) )
+#define V4MATCH(addr, a,b,c,d, m) ( ((addr) ^ DQUAD(a,b,c,d)) >> (32 - (m)) )
+
+#define RFC3484_SCOPE_LINK     2
+#define RFC3484_SCOPE_SITE     5
+#define RFC3484_SCOPE_GLOBAL   14
+
+static int addr_scope(struct sockaddr *sa)
+{
+       if (sa->sa_family == AF_INET) {
+               struct sockaddr_in *sin = (void *)sa;
+               guint32 addr = ntohl(sin->sin_addr.s_addr);
+
+               if (V4MATCH(addr, 169,254,0,0, 16) ||
+                                       V4MATCH(addr, 127,0,0,0, 8))
+                       return RFC3484_SCOPE_LINK;
+
+               /* Site-local */
+               if (V4MATCH(addr, 10,0,0,0, 8) ||
+                               V4MATCH(addr, 172,16,0,0, 12) ||
+                               V4MATCH(addr, 192,168,0,0, 16))
+                       return RFC3484_SCOPE_SITE;
+
+               /* Global */
+               return RFC3484_SCOPE_GLOBAL;
+       } else {
+               struct sockaddr_in6 *sin6 = (void *)sa;
+
+               /* Multicast addresses have a 4-bit scope field */
+               if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+                       return sin6->sin6_addr.s6_addr[1] & 0xf;
+
+               if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+                               IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+                       return RFC3484_SCOPE_LINK;
+
+               if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+                       return RFC3484_SCOPE_SITE;
+
+               return RFC3484_SCOPE_GLOBAL;
+       }
+}
+
+static int rfc3484_compare(const void *__one, const void *__two)
+{
+       const struct sort_result *one = __one;
+       const struct sort_result *two = __two;
+
+       /* Rule 1: Avoid unusable destinations */
+       if (one->reachable && !two->reachable)
+               return -1;
+       else if (two->reachable && !one->reachable)
+               return 1;
+
+       /* Rule 2: Prefer matching scope */
+       if (one->dst_scope == one->src_scope &&
+                               two->dst_scope != two->src_scope)
+               return -1;
+       else if (two->dst_scope == two->src_scope &&
+                               one->dst_scope != one->src_scope)
+               return 1;
+
+       /* Rule 3: Avoid deprecated addresses */
+
+       /* Rule 4: Prefer home addresses */
+
+       /* Rule 5: Prefer matching label */
+       if (one->dst_label == one->src_label &&
+                               two->dst_label != two->src_label)
+               return -1;
+       else if (two->dst_label == two->src_label &&
+                               one->dst_label != one->src_label)
+               return 1;
+
+       /* Rule 6: Prefer higher precedence */
+       if (one->precedence > two->precedence)
+               return -1;
+       else if (two->precedence > one->precedence)
+               return 1;
+
+       /* Rule 7: Prefer native transport */
+
+       /* Rule 8: Prefer smaller scope */
+       if (one->dst_scope != two->dst_scope)
+               return one->dst_scope - two->dst_scope;
+
+       /* Rule 9: Use longest matching prefix */
+       if (one->dst.sa.sa_family == AF_INET) {
+               /*
+                * Rule 9 is meaningless and counterproductive for Legacy IP
+                * unless perhaps we can tell that it's actually on the local
+                * subnet. But we don't (yet) have local interface config
+                * information, so do nothing here for Legacy IP for now.
+                */
+       } else {
+               int i;
+
+               for (i = 0; i < 4; i++) {
+                       guint32 cmp_one, cmp_two;
+
+                       cmp_one = one->src.sin6.sin6_addr.s6_addr32[i] ^
+                                       one->dst.sin6.sin6_addr.s6_addr32[i];
+                       cmp_two = two->src.sin6.sin6_addr.s6_addr32[i] ^
+                                       two->dst.sin6.sin6_addr.s6_addr32[i];
+
+                       if (!cmp_two && !cmp_one)
+                               continue;
+
+                       if (cmp_one && !cmp_two)
+                               return 1;
+                       if (cmp_two && !cmp_one)
+                               return -1;
+
+                       /* g_bit_storage() is effectively fls() */
+                       cmp_one = g_bit_storage(ntohl(cmp_one));
+                       cmp_two = g_bit_storage(ntohl(cmp_two));
+
+                       if (cmp_one == cmp_two)
+                               break;
+
+                       return cmp_one - cmp_two;
+               }
+       }
+
+
+       /* Rule 10: Otherwise, leave the order unchanged */
+       if (one < two)
+               return -1;
+       else
+               return 1;
+}
+
+static void rfc3484_sort_results(struct resolv_lookup *lookup)
+{
+       int i;
+
+       for (i = 0; i < lookup->nr_results; i++) {
+               struct sort_result *res = &lookup->results[i];
+               find_srcaddr(res);
+               res->precedence = match_gai_table(&res->dst.sa,
+                                                       gai_precedences);
+               res->dst_label = match_gai_table(&res->dst.sa, gai_labels);
+               res->src_label = match_gai_table(&res->src.sa, gai_labels);
+               res->dst_scope = addr_scope(&res->dst.sa);
+               res->src_scope = addr_scope(&res->src.sa);
+       }
+
+       qsort(lookup->results, lookup->nr_results,
+                       sizeof(struct sort_result), rfc3484_compare);
+}
+
+static void sort_and_return_results(struct resolv_lookup *lookup)
+{
+       char buf[INET6_ADDRSTRLEN + 1];
+       GResolvResultStatus status;
+       char **results = g_try_new0(char *, lookup->nr_results + 1);
+       int i, n = 0;
+
+       if (!results)
+               return;
+
+       memset(buf, 0, INET6_ADDRSTRLEN + 1);
+
+       rfc3484_sort_results(lookup);
+
+       for (i = 0; i < lookup->nr_results; i++) {
+               if (lookup->results[i].dst.sa.sa_family == AF_INET) {
+                       if (inet_ntop(AF_INET,
+                                       &lookup->results[i].dst.sin.sin_addr,
+                                       buf, sizeof(buf) - 1) == NULL)
+                               continue;
+               } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) {
+                       if (inet_ntop(AF_INET6,
+                                       &lookup->results[i].dst.sin6.sin6_addr,
+                                       buf, sizeof(buf) - 1) == NULL)
+                               continue;
+               } else
+                       continue;
+
+               results[n++] = strdup(buf);
+       }
+
+       results[n++] = NULL;
+
+       status = lookup->ipv4_status;
+
+       if (status == G_RESOLV_RESULT_STATUS_SUCCESS)
+               status = lookup->ipv6_status;
+
+       g_queue_remove(lookup->resolv->lookup_queue, lookup);
+
+       lookup->result_func(status, results, lookup->result_data);
+
+       g_strfreev(results);
+       destroy_lookup(lookup);
+}
+
+static gboolean query_timeout(gpointer user_data)
+{
+       struct resolv_query *query = user_data;
+       struct resolv_lookup *lookup = query->lookup;
+       GResolv *resolv = query->resolv;
+
+       query->timeout = 0;
+
+       if (query == lookup->ipv4_query) {
+               lookup->ipv4_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
+               lookup->ipv4_query = NULL;
+       } else if (query == lookup->ipv6_query) {
+               lookup->ipv6_status = G_RESOLV_RESULT_STATUS_NO_RESPONSE;
+               lookup->ipv6_query = NULL;
+       }
+
+       g_queue_remove(resolv->query_queue, query);
+
+       if (lookup->ipv4_query == NULL && lookup->ipv4_query == NULL)
+               sort_and_return_results(lookup);
+
+       destroy_query(query);
+
+       return FALSE;
+}
+
+static void free_nameserver(struct resolv_nameserver *nameserver)
+{
+       if (nameserver == NULL)
+               return;
+
+       if (nameserver->udp_watch > 0)
+               g_source_remove(nameserver->udp_watch);
+
+       if (nameserver->udp_channel != NULL)
+               g_io_channel_unref(nameserver->udp_channel);
+
+       g_free(nameserver->address);
+       g_free(nameserver);
+}
+
+static void flush_nameservers(GResolv *resolv)
+{
+       GList *list;
+
+       for (list = g_list_first(resolv->nameserver_list);
+                                       list; list = g_list_next(list))
+               free_nameserver(list->data);
+
+       g_list_free(resolv->nameserver_list);
+       resolv->nameserver_list = NULL;
+}
+
+static int send_query(GResolv *resolv, const unsigned char *buf, int len)
+{
+       GList *list;
+
+       if (resolv->nameserver_list == NULL)
+               return -ENOENT;
+
+       for (list = g_list_first(resolv->nameserver_list);
+                                       list; list = g_list_next(list)) {
+               struct resolv_nameserver *nameserver = list->data;
+               int sk, sent;
+
+               if (nameserver->udp_channel == NULL)
+                       continue;
+
+               sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
+
+               sent = send(sk, buf, len, 0);
+               if (sent < 0)
+                       continue;
+       }
+
+       return 0;
+}
+
+static gint compare_lookup_id(gconstpointer a, gconstpointer b)
+{
+       const struct resolv_lookup *lookup = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       if (lookup->id < id)
+               return -1;
+
+       if (lookup->id > id)
+               return 1;
+
+       return 0;
+}
+
+static gint compare_query_msgid(gconstpointer a, gconstpointer b)
+{
+       const struct resolv_query *query = a;
+       uint16_t msgid = GPOINTER_TO_UINT(b);
+
+       if (query->msgid < msgid)
+               return -1;
+
+       if (query->msgid > msgid)
+               return 1;
+
+       return 0;
+}
+
+static void add_result(struct resolv_lookup *lookup, int family,
+                                                       const void *data)
+{
+       int n = lookup->nr_results++;
+       lookup->results = g_try_realloc(lookup->results,
+                                       sizeof(struct sort_result) * (n + 1));
+       if (lookup->results == NULL)
+               return;
+
+       memset(&lookup->results[n], 0, sizeof(struct sort_result));
+
+       lookup->results[n].dst.sa.sa_family = family;
+       if (family == AF_INET)
+               memcpy(&lookup->results[n].dst.sin.sin_addr,
+                                               data, NS_INADDRSZ);
+       else
+               memcpy(&lookup->results[n].dst.sin6.sin6_addr,
+                                               data, NS_IN6ADDRSZ);
+}
+
+static void parse_response(struct resolv_nameserver *nameserver,
+                                       const unsigned char *buf, int len)
+{
+       GResolv *resolv = nameserver->resolv;
+       GResolvResultStatus status;
+       struct resolv_query *query;
+       struct resolv_lookup *lookup;
+       GList *list;
+       ns_msg msg;
+       ns_rr rr;
+       int i, rcode, count;
+
+       debug(resolv, "response from %s", nameserver->address);
+
+#if defined TIZEN_EXT
+       if (ns_initparse(buf, len, &msg) == -1)
+               return;
+#else
+               ns_initparse(buf, len, &msg);
+#endif
+
+       rcode = ns_msg_getflag(msg, ns_f_rcode);
+       count = ns_msg_count(msg, ns_s_an);
+
+       debug(resolv, "msg id: 0x%04x rcode: %d count: %d",
+                                       ns_msg_id(msg), rcode, count);
+
+       switch (rcode) {
+       case 0:
+               status = G_RESOLV_RESULT_STATUS_SUCCESS;
+               break;
+       case 1:
+               status = G_RESOLV_RESULT_STATUS_FORMAT_ERROR;
+               break;
+       case 2:
+               status = G_RESOLV_RESULT_STATUS_SERVER_FAILURE;
+               break;
+       case 3:
+               status = G_RESOLV_RESULT_STATUS_NAME_ERROR;
+               break;
+       case 4:
+               status = G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED;
+               break;
+       case 5:
+               status = G_RESOLV_RESULT_STATUS_REFUSED;
+               break;
+       default:
+               status = G_RESOLV_RESULT_STATUS_ERROR;
+               break;
+       }
+
+       list = g_queue_find_custom(resolv->query_queue,
+                       GUINT_TO_POINTER(ns_msg_id(msg)), compare_query_msgid);
+       if (!list)
+               return;
+
+       query = list->data;
+       lookup = query->lookup;
+
+       if (query == lookup->ipv6_query) {
+               lookup->ipv6_status = status;
+               lookup->ipv6_query = NULL;
+       } else if (query == lookup->ipv4_query) {
+               lookup->ipv4_status = status;
+               lookup->ipv4_query = NULL;
+       }
+
+       for (i = 0; i < count; i++) {
+#if defined TIZEN_EXT
+               if (ns_parserr(&msg, ns_s_an, i, &rr) == -1)
+                       continue;
+#else
+               ns_parserr(&msg, ns_s_an, i, &rr);
+#endif
+
+               if (ns_rr_class(rr) != ns_c_in)
+                       continue;
+
+               g_assert(offsetof(struct sockaddr_in, sin_addr) ==
+                               offsetof(struct sockaddr_in6, sin6_flowinfo));
+
+               if (ns_rr_type(rr) == ns_t_a &&
+                                       ns_rr_rdlen(rr) == NS_INADDRSZ) {
+                       add_result(lookup, AF_INET, ns_rr_rdata(rr));
+               } else if (ns_rr_type(rr) == ns_t_aaaa &&
+                                       ns_rr_rdlen(rr) == NS_IN6ADDRSZ) {
+                       add_result(lookup, AF_INET6, ns_rr_rdata(rr));
+               }
+       }
+
+       g_queue_remove(resolv->query_queue, query);
+
+       if (lookup->ipv4_query == NULL && lookup->ipv6_query == NULL)
+               sort_and_return_results(lookup);
+
+       destroy_query(query);
+}
+
+static gboolean received_udp_data(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct resolv_nameserver *nameserver = user_data;
+       unsigned char buf[4096];
+       int sk, len;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               nameserver->udp_watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(nameserver->udp_channel);
+
+       len = recv(sk, buf, sizeof(buf), 0);
+       if (len < 12)
+               return TRUE;
+
+       parse_response(nameserver, buf, len);
+
+       return TRUE;
+}
+
+static int connect_udp_channel(struct resolv_nameserver *nameserver)
+{
+       struct addrinfo hints, *rp;
+       char portnr[6];
+       int err, sk;
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
+
+       sprintf(portnr, "%d", nameserver->port);
+       err = getaddrinfo(nameserver->address, portnr, &hints, &rp);
+       if (err)
+               return -EINVAL;
+
+       /*
+        * Do not blindly copy this code elsewhere; it doesn't loop over the
+        * results using ->ai_next as it should. That's OK in *this* case
+        * because it was a numeric lookup; we *know* there's only one.
+        */
+       if (!rp)
+               return -EINVAL;
+
+       sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+       if (sk < 0) {
+               freeaddrinfo(rp);
+               return -EIO;
+       }
+
+       /*
+        * If nameserver points to localhost ip, their is no need to
+        * bind the socket on any interface.
+        */
+       if (nameserver->resolv->index > 0 &&
+                       strncmp(nameserver->address, "127.0.0.1", 9) != 0) {
+               char interface[IF_NAMESIZE];
+
+               memset(interface, 0, IF_NAMESIZE);
+               if (if_indextoname(nameserver->resolv->index,
+                                               interface) != NULL) {
+                       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                               interface, IF_NAMESIZE) < 0) {
+                               close(sk);
+                               freeaddrinfo(rp);
+                               return -EIO;
+                       }
+               }
+       }
+
+       if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
+               close(sk);
+               freeaddrinfo(rp);
+               return -EIO;
+       }
+
+       freeaddrinfo(rp);
+
+       nameserver->udp_channel = g_io_channel_unix_new(sk);
+       if (nameserver->udp_channel == NULL) {
+               close(sk);
+               return -ENOMEM;
+       }
+
+       g_io_channel_set_close_on_unref(nameserver->udp_channel, TRUE);
+
+       nameserver->udp_watch = g_io_add_watch(nameserver->udp_channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                               received_udp_data, nameserver);
+
+       return 0;
+}
+
+GResolv *g_resolv_new(int index)
+{
+       GResolv *resolv;
+
+       if (index < 0)
+               return NULL;
+
+       resolv = g_try_new0(GResolv, 1);
+       if (resolv == NULL)
+               return NULL;
+
+       resolv->ref_count = 1;
+
+       resolv->result_family = AF_UNSPEC;
+
+       resolv->next_lookup_id = 1;
+
+       resolv->query_queue = g_queue_new();
+       if (resolv->query_queue == NULL) {
+               g_free(resolv);
+               return NULL;
+       }
+
+       resolv->lookup_queue = g_queue_new();
+       if (resolv->lookup_queue == NULL) {
+               g_queue_free(resolv->query_queue);
+               g_free(resolv);
+               return NULL;
+       }
+
+       resolv->index = index;
+       resolv->nameserver_list = NULL;
+
+       res_ninit(&resolv->res);
+
+       return resolv;
+}
+
+GResolv *g_resolv_ref(GResolv *resolv)
+{
+       if (resolv == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&resolv->ref_count, 1);
+
+       return resolv;
+}
+
+void g_resolv_unref(GResolv *resolv)
+{
+       struct resolv_query *query;
+
+       if (resolv == NULL)
+               return;
+
+       if (__sync_fetch_and_sub(&resolv->ref_count, 1) != 1)
+               return;
+
+       while ((query = g_queue_pop_head(resolv->query_queue)))
+               destroy_query(query);
+
+       g_queue_free(resolv->query_queue);
+       g_queue_free(resolv->lookup_queue);
+
+       flush_nameservers(resolv);
+
+       res_nclose(&resolv->res);
+
+       g_free(resolv);
+}
+
+void g_resolv_set_debug(GResolv *resolv, GResolvDebugFunc func,
+                                               gpointer user_data)
+{
+       if (resolv == NULL)
+               return;
+
+       resolv->debug_func = func;
+       resolv->debug_data = user_data;
+}
+
+gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
+                                       uint16_t port, unsigned long flags)
+{
+       struct resolv_nameserver *nameserver;
+
+       if (resolv == NULL)
+               return FALSE;
+
+       nameserver = g_try_new0(struct resolv_nameserver, 1);
+       if (nameserver == NULL)
+               return FALSE;
+
+       nameserver->address = g_strdup(address);
+       nameserver->port = port;
+       nameserver->flags = flags;
+       nameserver->resolv = resolv;
+
+       if (connect_udp_channel(nameserver) < 0) {
+               free_nameserver(nameserver);
+               return FALSE;
+       }
+
+       resolv->nameserver_list = g_list_append(resolv->nameserver_list,
+                                                               nameserver);
+
+       debug(resolv, "setting nameserver %s", address);
+
+       return TRUE;
+}
+
+void g_resolv_flush_nameservers(GResolv *resolv)
+{
+       if (resolv == NULL)
+               return;
+
+       flush_nameservers(resolv);
+}
+
+static gint add_query(struct resolv_lookup *lookup, const char *hostname, int type)
+{
+       struct resolv_query *query = g_try_new0(struct resolv_query, 1);
+       unsigned char buf[4096];
+       int len;
+
+       if (query == NULL)
+               return -ENOMEM;
+
+       len = res_mkquery(ns_o_query, hostname, ns_c_in, type,
+                                       NULL, 0, NULL, buf, sizeof(buf));
+
+       query->msgid = buf[0] << 8 | buf[1];
+
+       if (send_query(lookup->resolv, buf, len) < 0) {
+               g_free(query);
+               return -EIO;
+       }
+
+       query->resolv = lookup->resolv;
+       query->lookup = lookup;
+
+       g_queue_push_tail(lookup->resolv->query_queue, query);
+
+       query->timeout = g_timeout_add_seconds(5, query_timeout, query);
+
+       if (type == ns_t_aaaa)
+               lookup->ipv6_query = query;
+       else
+               lookup->ipv4_query = query;
+
+       return 0;
+}
+
+guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
+                               GResolvResultFunc func, gpointer user_data)
+{
+       struct resolv_lookup *lookup;
+
+       debug(resolv, "lookup hostname %s", hostname);
+
+       if (resolv == NULL)
+               return 0;
+
+       if (resolv->nameserver_list == NULL) {
+               int i;
+
+               for (i = 0; i < resolv->res.nscount; i++) {
+                       char buf[100];
+                       int family = resolv->res.nsaddr_list[i].sin_family;
+                       void *sa_addr = &resolv->res.nsaddr_list[i].sin_addr;
+
+                       if (family != AF_INET &&
+                                       resolv->res._u._ext.nsaddrs[i]) {
+                               family = AF_INET6;
+                               sa_addr = &resolv->res._u._ext.nsaddrs[i]->sin6_addr;
+                       }
+
+                       if (family != AF_INET && family != AF_INET6)
+                               continue;
+
+                       if (inet_ntop(family, sa_addr, buf, sizeof(buf)))
+                               g_resolv_add_nameserver(resolv, buf, 53, 0);
+               }
+
+               if (resolv->nameserver_list == NULL)
+                       g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0);
+       }
+
+       lookup = g_try_new0(struct resolv_lookup, 1);
+       if (lookup == NULL)
+               return 0;
+
+       lookup->resolv = resolv;
+       lookup->result_func = func;
+       lookup->result_data = user_data;
+       lookup->id = resolv->next_lookup_id++;
+
+       if (resolv->result_family != AF_INET6) {
+               if (add_query(lookup, hostname, ns_t_a)) {
+                       g_free(lookup);
+                       return -EIO;
+               }
+       }
+
+       if (resolv->result_family != AF_INET) {
+               if (add_query(lookup, hostname, ns_t_aaaa)) {
+                       if (resolv->result_family != AF_INET6) {
+                               destroy_query(lookup->ipv4_query);
+                               g_queue_remove(resolv->query_queue,
+                                               lookup->ipv4_query);
+                       }
+
+                       g_free(lookup);
+                       return -EIO;
+               }
+       }
+
+       g_queue_push_tail(resolv->lookup_queue, lookup);
+       return lookup->id;
+}
+
+gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id)
+{
+       GList *list;
+
+       list = g_queue_find_custom(resolv->lookup_queue,
+                               GUINT_TO_POINTER(id), compare_lookup_id);
+
+       if (list == NULL)
+               return FALSE;
+
+       destroy_lookup(list->data);
+       g_queue_remove(resolv->query_queue, list->data);
+
+       return TRUE;
+}
+
+gboolean g_resolv_set_address_family(GResolv *resolv, int family)
+{
+       if (resolv == NULL)
+               return FALSE;
+
+       if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6)
+               return FALSE;
+
+       resolv->result_family = family;
+
+       return TRUE;
+}
diff --git a/gweb/gresolv.h b/gweb/gresolv.h
new file mode 100644 (file)
index 0000000..3402df9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ *  Resolver library with GLib integration
+ *
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_RESOLV_H
+#define __G_RESOLV_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GResolv;
+
+typedef struct _GResolv GResolv;
+
+typedef enum {
+       G_RESOLV_RESULT_STATUS_SUCCESS,
+       G_RESOLV_RESULT_STATUS_ERROR,
+       G_RESOLV_RESULT_STATUS_NO_RESPONSE,
+       G_RESOLV_RESULT_STATUS_FORMAT_ERROR,
+       G_RESOLV_RESULT_STATUS_SERVER_FAILURE,
+       G_RESOLV_RESULT_STATUS_NAME_ERROR,
+       G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED,
+       G_RESOLV_RESULT_STATUS_REFUSED,
+} GResolvResultStatus;
+
+typedef void (*GResolvResultFunc)(GResolvResultStatus status,
+                                       char **results, gpointer user_data);
+
+typedef void (*GResolvDebugFunc)(const char *str, gpointer user_data);
+
+GResolv *g_resolv_new(int index);
+
+GResolv *g_resolv_ref(GResolv *resolv);
+void g_resolv_unref(GResolv *resolv);
+
+void g_resolv_set_debug(GResolv *resolv,
+                               GResolvDebugFunc func, gpointer user_data);
+
+gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
+                                       uint16_t port, unsigned long flags);
+void g_resolv_flush_nameservers(GResolv *resolv);
+
+guint g_resolv_lookup_hostname(GResolv *resolv, const char *hostname,
+                               GResolvResultFunc func, gpointer user_data);
+
+gboolean g_resolv_cancel_lookup(GResolv *resolv, guint id);
+
+gboolean g_resolv_set_address_family(GResolv *resolv, int family);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_RESOLV_H */
diff --git a/gweb/gweb.c b/gweb/gweb.c
new file mode 100644 (file)
index 0000000..6324e4e
--- /dev/null
@@ -0,0 +1,1572 @@
+/*
+ *
+ *  Web service library with GLib integration
+ *
+ *  Copyright (C) 2009-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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <ifaddrs.h>
+
+#include "giognutls.h"
+#include "gresolv.h"
+#include "gweb.h"
+
+#define DEFAULT_BUFFER_SIZE  2048
+
+#define SESSION_FLAG_USE_TLS   (1 << 0)
+
+enum chunk_state {
+       CHUNK_SIZE,
+       CHUNK_R_BODY,
+       CHUNK_N_BODY,
+       CHUNK_DATA,
+};
+
+struct _GWebResult {
+       guint16 status;
+       const guint8 *buffer;
+       gsize length;
+       gboolean use_chunk;
+       gchar *last_key;
+       GHashTable *headers;
+};
+
+struct web_session {
+       GWeb *web;
+
+       char *address;
+       char *host;
+       uint16_t port;
+       unsigned long flags;
+       struct addrinfo *addr;
+
+       char *content_type;
+
+       GIOChannel *transport_channel;
+       guint transport_watch;
+       guint send_watch;
+
+       guint resolv_action;
+       char *request;
+
+       guint8 *receive_buffer;
+       gsize receive_space;
+       GString *send_buffer;
+       GString *current_header;
+       gboolean header_done;
+       gboolean body_done;
+       gboolean more_data;
+       gboolean request_started;
+
+       enum chunk_state chunck_state;
+       gsize chunk_size;
+       gsize chunk_left;
+       gsize total_len;
+
+       GWebResult result;
+
+       GWebResultFunc result_func;
+       GWebRouteFunc route_func;
+       GWebInputFunc input_func;
+       int fd;
+       gsize length;
+       gsize offset;
+       gpointer user_data;
+};
+
+struct _GWeb {
+       int ref_count;
+
+       guint next_query_id;
+
+       int family;
+
+       int index;
+       GList *session_list;
+
+       GResolv *resolv;
+       char *proxy;
+       char *accept_option;
+       char *user_agent;
+       char *user_agent_profile;
+       char *http_version;
+       gboolean close_connection;
+
+       GWebDebugFunc debug_func;
+       gpointer debug_data;
+};
+
+static inline void debug(GWeb *web, const char *format, ...)
+{
+       char str[256];
+       va_list ap;
+
+       if (web->debug_func == NULL)
+               return;
+
+       va_start(ap, format);
+
+       if (vsnprintf(str, sizeof(str), format, ap) > 0)
+               web->debug_func(str, web->debug_data);
+
+       va_end(ap);
+}
+
+static void free_session(struct web_session *session)
+{
+       GWeb *web;
+
+       if (session == NULL)
+               return;
+
+       g_free(session->request);
+
+       web = session->web;
+       if (session->resolv_action > 0)
+               g_resolv_cancel_lookup(web->resolv, session->resolv_action);
+
+       if (session->transport_watch > 0)
+               g_source_remove(session->transport_watch);
+
+       if (session->send_watch > 0)
+               g_source_remove(session->send_watch);
+
+       if (session->transport_channel != NULL)
+               g_io_channel_unref(session->transport_channel);
+
+       g_free(session->result.last_key);
+
+       if (session->result.headers != NULL)
+               g_hash_table_destroy(session->result.headers);
+
+       if (session->send_buffer != NULL)
+               g_string_free(session->send_buffer, TRUE);
+
+       if (session->current_header != NULL)
+               g_string_free(session->current_header, TRUE);
+
+       g_free(session->receive_buffer);
+
+       g_free(session->content_type);
+
+       g_free(session->host);
+       g_free(session->address);
+       if (session->addr != NULL)
+               freeaddrinfo(session->addr);
+
+       g_free(session);
+}
+
+static void flush_sessions(GWeb *web)
+{
+       GList *list;
+
+       for (list = g_list_first(web->session_list);
+                                       list; list = g_list_next(list))
+               free_session(list->data);
+
+       g_list_free(web->session_list);
+       web->session_list = NULL;
+}
+
+GWeb *g_web_new(int index)
+{
+       GWeb *web;
+
+       if (index < 0)
+               return NULL;
+
+       web = g_try_new0(GWeb, 1);
+       if (web == NULL)
+               return NULL;
+
+       web->ref_count = 1;
+
+       web->next_query_id = 1;
+
+       web->family = AF_UNSPEC;
+
+       web->index = index;
+       web->session_list = NULL;
+
+       web->resolv = g_resolv_new(index);
+       if (web->resolv == NULL) {
+               g_free(web);
+               return NULL;
+       }
+
+       web->accept_option = g_strdup("*/*");
+       web->user_agent = g_strdup_printf("GWeb/%s", VERSION);
+       web->close_connection = FALSE;
+
+       return web;
+}
+
+GWeb *g_web_ref(GWeb *web)
+{
+       if (web == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&web->ref_count, 1);
+
+       return web;
+}
+
+void g_web_unref(GWeb *web)
+{
+       if (web == NULL)
+               return;
+
+       if (__sync_fetch_and_sub(&web->ref_count, 1) != 1)
+               return;
+
+       flush_sessions(web);
+
+       g_resolv_unref(web->resolv);
+
+       g_free(web->proxy);
+
+       g_free(web->accept_option);
+       g_free(web->user_agent);
+       g_free(web->user_agent_profile);
+       g_free(web->http_version);
+
+       g_free(web);
+}
+
+gboolean g_web_supports_tls(void)
+{
+       return g_io_channel_supports_tls();
+}
+
+void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data)
+{
+       if (web == NULL)
+               return;
+
+       web->debug_func = func;
+       web->debug_data = user_data;
+
+       g_resolv_set_debug(web->resolv, func, user_data);
+}
+
+gboolean g_web_set_proxy(GWeb *web, const char *proxy)
+{
+       if (web == NULL)
+               return FALSE;
+
+       g_free(web->proxy);
+
+       if (proxy == NULL) {
+               web->proxy = NULL;
+               debug(web, "clearing proxy");
+       } else {
+               web->proxy = g_strdup(proxy);
+               debug(web, "setting proxy %s", web->proxy);
+       }
+
+       return TRUE;
+}
+
+gboolean g_web_set_address_family(GWeb *web, int family)
+{
+       if (web == NULL)
+               return FALSE;
+
+       if (family != AF_UNSPEC && family != AF_INET && family != AF_INET6)
+               return FALSE;
+
+       web->family = family;
+
+       g_resolv_set_address_family(web->resolv, family);
+
+       return TRUE;
+}
+
+gboolean g_web_add_nameserver(GWeb *web, const char *address)
+{
+       if (web == NULL)
+               return FALSE;
+
+       g_resolv_add_nameserver(web->resolv, address, 53, 0);
+
+       return TRUE;
+}
+
+static gboolean set_accept_option(GWeb *web, const char *format, va_list args)
+{
+       g_free(web->accept_option);
+
+       if (format == NULL) {
+               web->accept_option = NULL;
+               debug(web, "clearing accept option");
+       } else {
+               web->accept_option = g_strdup_vprintf(format, args);
+               debug(web, "setting accept %s", web->accept_option);
+       }
+
+       return TRUE;
+}
+
+gboolean g_web_set_accept(GWeb *web, const char *format, ...)
+{
+       va_list args;
+       gboolean result;
+
+       if (web == NULL)
+               return FALSE;
+
+       va_start(args, format);
+       result = set_accept_option(web, format, args);
+       va_end(args);
+
+       return result;
+}
+
+static gboolean set_user_agent(GWeb *web, const char *format, va_list args)
+{
+       g_free(web->user_agent);
+
+       if (format == NULL) {
+               web->user_agent = NULL;
+               debug(web, "clearing user agent");
+       } else {
+               web->user_agent = g_strdup_vprintf(format, args);
+               debug(web, "setting user agent %s", web->user_agent);
+       }
+
+       return TRUE;
+}
+
+gboolean g_web_set_user_agent(GWeb *web, const char *format, ...)
+{
+       va_list args;
+       gboolean result;
+
+       if (web == NULL)
+               return FALSE;
+
+       va_start(args, format);
+       result = set_user_agent(web, format, args);
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_web_set_ua_profile(GWeb *web, const char *profile)
+{
+       if (web == NULL)
+               return FALSE;
+
+       g_free(web->user_agent_profile);
+
+       web->user_agent_profile = g_strdup(profile);
+       debug(web, "setting user agent profile %s", web->user_agent);
+
+       return TRUE;
+}
+
+gboolean g_web_set_http_version(GWeb *web, const char *version)
+{
+       if (web == NULL)
+               return FALSE;
+
+       g_free(web->http_version);
+
+       if (version == NULL) {
+               web->http_version = NULL;
+               debug(web, "clearing HTTP version");
+       } else {
+               web->http_version = g_strdup(version);
+               debug(web, "setting HTTP version %s", web->http_version);
+       }
+
+       return TRUE;
+}
+
+void g_web_set_close_connection(GWeb *web, gboolean enabled)
+{
+       if (web == NULL)
+               return;
+
+       web->close_connection = enabled;
+}
+
+gboolean g_web_get_close_connection(GWeb *web)
+{
+       if (web == NULL)
+               return FALSE;
+
+       return web->close_connection;
+}
+
+static inline void call_result_func(struct web_session *session, guint16 status)
+{
+       if (session->result_func == NULL)
+               return;
+
+       if (status != 0)
+               session->result.status = status;
+
+       session->result_func(&session->result, session->user_data);
+}
+
+static inline void call_route_func(struct web_session *session)
+{
+       if (session->route_func != NULL)
+               session->route_func(session->address, session->addr->ai_family,
+                               session->web->index, session->user_data);
+}
+
+static gboolean process_send_buffer(struct web_session *session)
+{
+       GString *buf;
+       gsize count, bytes_written;
+       GIOStatus status;
+
+       if (session == NULL)
+               return FALSE;
+
+       buf = session->send_buffer;
+       count = buf->len;
+
+       if (count == 0) {
+               if (session->request_started == TRUE &&
+                                       session->more_data == FALSE &&
+                                       session->fd == -1)
+                       session->body_done = TRUE;
+
+               return FALSE;
+       }
+
+       status = g_io_channel_write_chars(session->transport_channel,
+                                       buf->str, count, &bytes_written, NULL);
+
+       debug(session->web, "status %u bytes to write %zu bytes written %zu",
+                                       status, count, bytes_written);
+
+       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+               return FALSE;
+
+       g_string_erase(buf, 0, bytes_written);
+
+       return TRUE;
+}
+
+static gboolean process_send_file(struct web_session *session)
+{
+       int sk;
+       off_t offset;
+       ssize_t bytes_sent;
+
+       if (session->fd == -1)
+               return FALSE;
+
+       if (session->request_started == FALSE || session->more_data == TRUE)
+               return FALSE;
+
+       sk = g_io_channel_unix_get_fd(session->transport_channel);
+       if (sk < 0)
+               return FALSE;
+
+       offset = session->offset;
+
+       bytes_sent = sendfile(sk, session->fd, &offset, session->length);
+
+       debug(session->web, "errno: %d, bytes to send %zu / bytes sent %zu",
+                       errno, session->length, bytes_sent);
+
+       if (bytes_sent < 0 && errno != EAGAIN)
+               return FALSE;
+
+       session->offset = offset;
+       session->length -= bytes_sent;
+
+       if (session->length == 0) {
+               session->body_done = TRUE;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void process_next_chunk(struct web_session *session)
+{
+       GString *buf = session->send_buffer;
+       const guint8 *body;
+       gsize length;
+
+       if (session->input_func == NULL) {
+               session->more_data = FALSE;
+               return;
+       }
+
+       session->more_data = session->input_func(&body, &length,
+                                               session->user_data);
+
+       if (length > 0) {
+               g_string_append_printf(buf, "%zx\r\n", length);
+               g_string_append_len(buf, (char *) body, length);
+               g_string_append(buf, "\r\n");
+       }
+
+       if (session->more_data == FALSE)
+               g_string_append(buf, "0\r\n\r\n");
+}
+
+static void start_request(struct web_session *session)
+{
+       GString *buf = session->send_buffer;
+       const char *version;
+       const guint8 *body;
+       gsize length;
+
+       debug(session->web, "request %s from %s",
+                                       session->request, session->host);
+
+       g_string_truncate(buf, 0);
+
+       if (session->web->http_version == NULL)
+               version = "1.1";
+       else
+               version = session->web->http_version;
+
+       if (session->content_type == NULL)
+               g_string_append_printf(buf, "GET %s HTTP/%s\r\n",
+                                               session->request, version);
+       else
+               g_string_append_printf(buf, "POST %s HTTP/%s\r\n",
+                                               session->request, version);
+
+       g_string_append_printf(buf, "Host: %s\r\n", session->host);
+
+       if (session->web->user_agent != NULL)
+               g_string_append_printf(buf, "User-Agent: %s\r\n",
+                                               session->web->user_agent);
+
+       if (session->web->user_agent_profile != NULL) {
+               g_string_append_printf(buf, "x-wap-profile: %s\r\n",
+                                      session->web->user_agent_profile);
+       }
+
+       if (session->web->accept_option != NULL)
+               g_string_append_printf(buf, "Accept: %s\r\n",
+                                               session->web->accept_option);
+
+       if (session->content_type != NULL) {
+               g_string_append_printf(buf, "Content-Type: %s\r\n",
+                                                       session->content_type);
+               if (session->input_func == NULL) {
+                       session->more_data = FALSE;
+                       length = session->length;
+               } else
+                       session->more_data = session->input_func(&body, &length,
+                                                       session->user_data);
+               if (session->more_data == FALSE)
+                       g_string_append_printf(buf, "Content-Length: %zu\r\n",
+                                                                       length);
+               else
+                       g_string_append(buf, "Transfer-Encoding: chunked\r\n");
+       }
+
+       if (session->web->close_connection == TRUE)
+               g_string_append(buf, "Connection: close\r\n");
+
+       g_string_append(buf, "\r\n");
+
+       if (session->content_type != NULL && length > 0) {
+               if (session->more_data == TRUE) {
+                       g_string_append_printf(buf, "%zx\r\n", length);
+                       g_string_append_len(buf, (char *) body, length);
+                       g_string_append(buf, "\r\n");
+               } else if (session->fd == -1)
+                       g_string_append_len(buf, (char *) body, length);
+       }
+}
+
+static gboolean send_data(GIOChannel *channel, GIOCondition cond,
+                                               gpointer user_data)
+{
+       struct web_session *session = user_data;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               session->send_watch = 0;
+               return FALSE;
+       }
+
+       if (process_send_buffer(session) == TRUE)
+               return TRUE;
+
+       if (process_send_file(session) == TRUE)
+               return TRUE;
+
+       if (session->request_started == FALSE) {
+               session->request_started = TRUE;
+               start_request(session);
+       } else if (session->more_data == TRUE)
+               process_next_chunk(session);
+
+       process_send_buffer(session);
+
+       if (session->body_done == TRUE) {
+               session->send_watch = 0;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int decode_chunked(struct web_session *session,
+                                       const guint8 *buf, gsize len)
+{
+       const guint8 *ptr = buf;
+       gsize counter;
+
+       while (len > 0) {
+               guint8 *pos;
+               gsize count;
+               char *str;
+
+               switch (session->chunck_state) {
+               case CHUNK_SIZE:
+                       pos = memchr(ptr, '\n', len);
+                       if (pos == NULL) {
+                               g_string_append_len(session->current_header,
+                                               (gchar *) ptr, len);
+                               return 0;
+                       }
+
+                       count = pos - ptr;
+                       if (count < 1 || ptr[count - 1] != '\r')
+                               return -EILSEQ;
+
+                       g_string_append_len(session->current_header,
+                                               (gchar *) ptr, count);
+
+                       len -= count + 1;
+                       ptr = pos + 1;
+
+                       str = session->current_header->str;
+
+                       counter = strtoul(str, NULL, 16);
+                       if ((counter == 0 && errno == EINVAL) ||
+                                               counter == ULONG_MAX)
+                               return -EILSEQ;
+
+                       session->chunk_size = counter;
+                       session->chunk_left = counter;
+
+                       session->chunck_state = CHUNK_DATA;
+                       break;
+               case CHUNK_R_BODY:
+                       if (*ptr != '\r')
+                               return -EILSEQ;
+                       ptr++;
+                       len--;
+                       session->chunck_state = CHUNK_N_BODY;
+                       break;
+               case CHUNK_N_BODY:
+                       if (*ptr != '\n')
+                               return -EILSEQ;
+                       ptr++;
+                       len--;
+                       session->chunck_state = CHUNK_SIZE;
+                       break;
+               case CHUNK_DATA:
+                       if (session->chunk_size == 0) {
+                               debug(session->web, "Download Done in chunk");
+                               g_string_truncate(session->current_header, 0);
+                               return 0;
+                       }
+
+                       if (session->chunk_left <= len) {
+                               session->result.buffer = ptr;
+                               session->result.length = session->chunk_left;
+                               call_result_func(session, 0);
+
+                               len -= session->chunk_left;
+                               ptr += session->chunk_left;
+
+                               session->total_len += session->chunk_left;
+                               session->chunk_left = 0;
+
+                               g_string_truncate(session->current_header, 0);
+                               session->chunck_state = CHUNK_R_BODY;
+                               break;
+                       }
+                       /* more data */
+                       session->result.buffer = ptr;
+                       session->result.length = len;
+                       call_result_func(session, 0);
+
+                       session->chunk_left -= len;
+                       session->total_len += len;
+
+                       len -= len;
+                       ptr += len;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int handle_body(struct web_session *session,
+                               const guint8 *buf, gsize len)
+{
+       int err;
+
+       debug(session->web, "[body] length %zu", len);
+
+       if (session->result.use_chunk == FALSE) {
+               if (len > 0) {
+                       session->result.buffer = buf;
+                       session->result.length = len;
+                       call_result_func(session, 0);
+               }
+               return 0;
+       }
+
+       err = decode_chunked(session, buf, len);
+       if (err < 0) {
+               debug(session->web, "Error in chunk decode %d", err);
+
+               session->result.buffer = NULL;
+               session->result.length = 0;
+               call_result_func(session, 400);
+       }
+
+       return err;
+}
+
+static void handle_multi_line(struct web_session *session)
+{
+       gsize count;
+       char *str;
+       gchar *value;
+
+       str = session->current_header->str;
+
+       if (str[0] != ' ' && str[0] != '\t')
+               return;
+
+       while (str[0] == ' ' || str[0] == '\t')
+               str++;
+
+       count = str - session->current_header->str;
+       if (count > 0) {
+               g_string_erase(session->current_header, 0, count);
+               g_string_insert_c(session->current_header, 0, ' ');
+       }
+
+       value = g_hash_table_lookup(session->result.headers,
+                                       session->result.last_key);
+       if (value != NULL) {
+               g_string_insert(session->current_header, 0, value);
+
+               str = session->current_header->str;
+
+               g_hash_table_replace(session->result.headers,
+                                       g_strdup(session->result.last_key),
+                                       g_strdup(str));
+       }
+}
+
+static void add_header_field(struct web_session *session)
+{
+       gsize count;
+       guint8 *pos;
+       char *str;
+       gchar *value;
+       gchar *key;
+
+       str = session->current_header->str;
+
+       pos = memchr(str, ':', session->current_header->len);
+       if (pos != NULL) {
+               *pos = '\0';
+               pos++;
+
+               key = g_strdup(str);
+
+               /* remove preceding white spaces */
+               while (*pos == ' ')
+                       pos++;
+
+               count = (char *) pos - str;
+
+               g_string_erase(session->current_header, 0, count);
+
+               value = g_hash_table_lookup(session->result.headers, key);
+               if (value != NULL) {
+                       g_string_insert_c(session->current_header, 0, ' ');
+                       g_string_insert_c(session->current_header, 0, ';');
+
+                       g_string_insert(session->current_header, 0, value);
+               }
+
+               str = session->current_header->str;
+               g_hash_table_replace(session->result.headers, key,
+                                                       g_strdup(str));
+
+               g_free(session->result.last_key);
+               session->result.last_key = g_strdup(key);
+       }
+}
+
+static gboolean received_data(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct web_session *session = user_data;
+       guint8 *ptr = session->receive_buffer;
+       gsize bytes_read;
+       GIOStatus status;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               session->transport_watch = 0;
+               session->result.buffer = NULL;
+               session->result.length = 0;
+               call_result_func(session, 400);
+               return FALSE;
+       }
+
+       status = g_io_channel_read_chars(channel,
+                               (gchar *) session->receive_buffer,
+                               session->receive_space - 1, &bytes_read, NULL);
+
+       debug(session->web, "bytes read %zu", bytes_read);
+
+       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) {
+               session->transport_watch = 0;
+               session->result.buffer = NULL;
+               session->result.length = 0;
+               call_result_func(session, 0);
+               return FALSE;
+       }
+
+       session->receive_buffer[bytes_read] = '\0';
+
+       if (session->header_done == TRUE) {
+               if (handle_body(session, session->receive_buffer,
+                                                       bytes_read) < 0) {
+                       session->transport_watch = 0;
+                       return FALSE;
+               }
+               return TRUE;
+       }
+
+       while (bytes_read > 0) {
+               guint8 *pos;
+               gsize count;
+               char *str;
+
+               pos = memchr(ptr, '\n', bytes_read);
+               if (pos == NULL) {
+                       g_string_append_len(session->current_header,
+                                               (gchar *) ptr, bytes_read);
+                       return TRUE;
+               }
+
+               *pos = '\0';
+               count = strlen((char *) ptr);
+               if (count > 0 && ptr[count - 1] == '\r') {
+                       ptr[--count] = '\0';
+                       bytes_read--;
+               }
+
+               g_string_append_len(session->current_header,
+                                               (gchar *) ptr, count);
+
+               bytes_read -= count + 1;
+               if (bytes_read > 0)
+                       ptr = pos + 1;
+               else
+                       ptr = NULL;
+
+               if (session->current_header->len == 0) {
+                       char *val;
+
+                       session->header_done = TRUE;
+
+                       val = g_hash_table_lookup(session->result.headers,
+                                                       "Transfer-Encoding");
+                       if (val != NULL) {
+                               val = g_strrstr(val, "chunked");
+                               if (val != NULL) {
+                                       session->result.use_chunk = TRUE;
+
+                                       session->chunck_state = CHUNK_SIZE;
+                                       session->chunk_left = 0;
+                                       session->total_len = 0;
+                               }
+                       }
+
+                       if (handle_body(session, ptr, bytes_read) < 0) {
+                               session->transport_watch = 0;
+                               return FALSE;
+                       }
+                       break;
+               }
+
+               str = session->current_header->str;
+
+               if (session->result.status == 0) {
+                       unsigned int code;
+
+                       if (sscanf(str, "HTTP/%*s %u %*s", &code) == 1)
+                               session->result.status = code;
+               }
+
+               debug(session->web, "[header] %s", str);
+
+               /* handle multi-line header */
+               if (str[0] == ' ' || str[0] == '\t')
+                       handle_multi_line(session);
+               else
+                       add_header_field(session);
+
+               g_string_truncate(session->current_header, 0);
+       }
+
+       return TRUE;
+}
+
+static int bind_to_address(int sk, const char *interface, int family)
+{
+       struct ifaddrs *ifaddr_list, *ifaddr;
+       int size, err = -1;
+
+       if (getifaddrs(&ifaddr_list) < 0)
+               return err;
+
+       for (ifaddr = ifaddr_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
+               if (g_strcmp0(ifaddr->ifa_name, interface) != 0)
+                       continue;
+
+               if (ifaddr->ifa_addr == NULL ||
+                               ifaddr->ifa_addr->sa_family != family)
+                       continue;
+
+               switch (family) {
+               case AF_INET:
+                       size = sizeof(struct sockaddr_in);
+                       break;
+               case AF_INET6:
+                       size = sizeof(struct sockaddr_in6);
+                       break;
+               default:
+                       continue;
+               }
+
+               err = bind(sk, (struct sockaddr *) ifaddr->ifa_addr, size);
+               break;
+       }
+
+       freeifaddrs(ifaddr_list);
+       return err;
+}
+
+static inline int bind_socket(int sk, int index, int family)
+{
+       char interface[IF_NAMESIZE];
+       int err;
+
+       if (if_indextoname(index, interface) == NULL)
+               return -1;
+
+       err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface, IF_NAMESIZE);
+       if (err < 0)
+               err = bind_to_address(sk, interface, family);
+
+       return err;
+}
+
+static int connect_session_transport(struct web_session *session)
+{
+       GIOFlags flags;
+       int sk;
+
+       sk = socket(session->addr->ai_family, SOCK_STREAM | SOCK_CLOEXEC,
+                       IPPROTO_TCP);
+       if (sk < 0)
+               return -EIO;
+
+       if (session->web->index > 0) {
+               if (bind_socket(sk, session->web->index,
+                                       session->addr->ai_family) < 0) {
+                       debug(session->web, "bind() %s", strerror(errno));
+                       close(sk);
+                       return -EIO;
+               }
+       }
+
+       if (session->flags & SESSION_FLAG_USE_TLS) {
+               debug(session->web, "using TLS encryption");
+               session->transport_channel = g_io_channel_gnutls_new(sk);
+       } else {
+               debug(session->web, "no encryption");
+               session->transport_channel = g_io_channel_unix_new(sk);
+       }
+
+       if (session->transport_channel == NULL) {
+               debug(session->web, "channel missing");
+               close(sk);
+               return -ENOMEM;
+       }
+
+       flags = g_io_channel_get_flags(session->transport_channel);
+       g_io_channel_set_flags(session->transport_channel,
+                                       flags | G_IO_FLAG_NONBLOCK, NULL);
+
+       g_io_channel_set_encoding(session->transport_channel, NULL, NULL);
+       g_io_channel_set_buffered(session->transport_channel, FALSE);
+
+       g_io_channel_set_close_on_unref(session->transport_channel, TRUE);
+
+       if (connect(sk, session->addr->ai_addr,
+                       session->addr->ai_addrlen) < 0) {
+               if (errno != EINPROGRESS) {
+                       debug(session->web, "connect() %s", strerror(errno));
+                       close(sk);
+                       return -EIO;
+               }
+       }
+
+       session->transport_watch = g_io_add_watch(session->transport_channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                                               received_data, session);
+
+       session->send_watch = g_io_add_watch(session->transport_channel,
+                               G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                                               send_data, session);
+
+       return 0;
+}
+
+static int create_transport(struct web_session *session)
+{
+       int err;
+
+       err = connect_session_transport(session);
+       if (err < 0)
+               return err;
+
+       debug(session->web, "creating session %s:%u",
+                                       session->address, session->port);
+
+       return 0;
+}
+
+static int parse_url(struct web_session *session,
+                               const char *url, const char *proxy)
+{
+       char *scheme, *host, *port, *path;
+
+       scheme = g_strdup(url);
+       if (scheme == NULL)
+               return -EINVAL;
+
+       host = strstr(scheme, "://");
+       if (host != NULL) {
+               *host = '\0';
+               host += 3;
+
+               if (strcasecmp(scheme, "https") == 0) {
+                       session->port = 443;
+                       session->flags |= SESSION_FLAG_USE_TLS;
+               } else if (strcasecmp(scheme, "http") == 0) {
+                       session->port = 80;
+               } else {
+                       g_free(scheme);
+                       return -EINVAL;
+               }
+       } else {
+               host = scheme;
+               session->port = 80;
+       }
+
+       path = strchr(host, '/');
+       if (path != NULL)
+               *(path++) = '\0';
+
+       if (proxy == NULL)
+               session->request = g_strdup_printf("/%s", path ? path : "");
+       else
+               session->request = g_strdup(url);
+
+       port = strrchr(host, ':');
+       if (port != NULL) {
+               char *end;
+               int tmp = strtol(port + 1, &end, 10);
+
+               if (*end == '\0') {
+                       *port = '\0';
+                       session->port = tmp;
+               }
+
+               if (proxy == NULL)
+                       session->host = g_strdup(host);
+               else
+                       session->host = g_strdup_printf("%s:%u", host, tmp);
+       } else
+               session->host = g_strdup(host);
+
+       g_free(scheme);
+
+       if (proxy == NULL)
+               return 0;
+
+       scheme = g_strdup(proxy);
+       if (scheme == NULL)
+               return -EINVAL;
+
+       host = strstr(proxy, "://");
+       if (host != NULL) {
+               *host = '\0';
+               host += 3;
+
+               if (strcasecmp(scheme, "http") != 0) {
+                       g_free(scheme);
+                       return -EINVAL;
+               }
+       } else
+               host = scheme;
+
+       path = strchr(host, '/');
+       if (path != NULL)
+               *(path++) = '\0';
+
+       port = strrchr(host, ':');
+       if (port != NULL) {
+               char *end;
+               int tmp = strtol(port + 1, &end, 10);
+
+               if (*end == '\0') {
+                       *port = '\0';
+                       session->port = tmp;
+               }
+       }
+
+       session->address = g_strdup(host);
+
+       g_free(scheme);
+
+       return 0;
+}
+
+static void resolv_result(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+       struct web_session *session = user_data;
+       struct addrinfo hints;
+       char *port;
+       int ret;
+
+       if (results == NULL || results[0] == NULL) {
+               call_result_func(session, 404);
+               return;
+       }
+
+       debug(session->web, "address %s", results[0]);
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_flags = AI_NUMERICHOST;
+       hints.ai_family = session->web->family;
+
+       if (session->addr != NULL) {
+               freeaddrinfo(session->addr);
+               session->addr = NULL;
+       }
+
+       port = g_strdup_printf("%u", session->port);
+       ret = getaddrinfo(results[0], port, &hints, &session->addr);
+       g_free(port);
+       if (ret != 0 || session->addr == NULL) {
+               call_result_func(session, 400);
+               return;
+       }
+
+       session->address = g_strdup(results[0]);
+       call_route_func(session);
+
+       if (create_transport(session) < 0) {
+               call_result_func(session, 409);
+               return;
+       }
+}
+
+static guint do_request(GWeb *web, const char *url,
+                               const char *type, GWebInputFunc input,
+                               int fd, gsize length, GWebResultFunc func,
+                               GWebRouteFunc route, gpointer user_data)
+{
+       struct web_session *session;
+
+       if (web == NULL || url == NULL)
+               return 0;
+
+       debug(web, "request %s", url);
+
+       session = g_try_new0(struct web_session, 1);
+       if (session == NULL)
+               return 0;
+
+       if (parse_url(session, url, web->proxy) < 0) {
+               free_session(session);
+               return 0;
+       }
+
+       debug(web, "address %s", session->address);
+       debug(web, "port %u", session->port);
+       debug(web, "host %s", session->host);
+       debug(web, "flags %lu", session->flags);
+       debug(web, "request %s", session->request);
+
+       if (type != NULL) {
+               session->content_type = g_strdup(type);
+
+               debug(web, "content-type %s", session->content_type);
+       }
+
+       session->web = web;
+
+       session->result_func = func;
+       session->route_func = route;
+       session->input_func = input;
+       session->fd = fd;
+       session->length = length;
+       session->offset = 0;
+       session->user_data = user_data;
+
+       session->receive_buffer = g_try_malloc(DEFAULT_BUFFER_SIZE);
+       if (session->receive_buffer == NULL) {
+               free_session(session);
+               return 0;
+       }
+
+       session->result.headers = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+       if (session->result.headers == NULL) {
+               free_session(session);
+               return 0;
+       }
+
+       session->receive_space = DEFAULT_BUFFER_SIZE;
+       session->send_buffer = g_string_sized_new(0);
+       session->current_header = g_string_sized_new(0);
+       session->header_done = FALSE;
+       session->body_done = FALSE;
+
+       if (session->address == NULL && inet_aton(session->host, NULL) == 0) {
+               session->resolv_action = g_resolv_lookup_hostname(web->resolv,
+                                       session->host, resolv_result, session);
+               if (session->resolv_action == 0) {
+                       free_session(session);
+                       return 0;
+               }
+       } else {
+               struct addrinfo hints;
+               char *port;
+               int ret;
+
+               if (session->address == NULL)
+                       session->address = g_strdup(session->host);
+
+               memset(&hints, 0, sizeof(struct addrinfo));
+               hints.ai_flags = AI_NUMERICHOST;
+               hints.ai_family = session->web->family;
+
+               if (session->addr != NULL) {
+                       freeaddrinfo(session->addr);
+                       session->addr = NULL;
+               }
+
+               port = g_strdup_printf("%u", session->port);
+               ret = getaddrinfo(session->address, port, &hints,
+                                                       &session->addr);
+               g_free(port);
+               if (ret != 0 || session->addr == NULL) {
+                       free_session(session);
+                       return 0;
+               }
+
+               if (create_transport(session) < 0) {
+                       free_session(session);
+                       return 0;
+               }
+       }
+
+       web->session_list = g_list_append(web->session_list, session);
+
+       return web->next_query_id++;
+}
+
+guint g_web_request_get(GWeb *web, const char *url, GWebResultFunc func,
+               GWebRouteFunc route, gpointer user_data)
+{
+       return do_request(web, url, NULL, NULL, -1, 0, func, route, user_data);
+}
+
+guint g_web_request_post(GWeb *web, const char *url,
+                               const char *type, GWebInputFunc input,
+                               GWebResultFunc func, gpointer user_data)
+{
+       return do_request(web, url, type, input, -1, 0, func, NULL, user_data);
+}
+
+guint g_web_request_post_file(GWeb *web, const char *url,
+                               const char *type, const char *file,
+                               GWebResultFunc func, gpointer user_data)
+{
+       struct stat st;
+       int fd;
+       guint ret;
+
+       if (stat(file, &st) < 0)
+               return 0;
+
+       fd = open(file, O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       ret = do_request(web, url, type, NULL, fd, st.st_size, func, NULL,
+                       user_data);
+       if (ret == 0)
+               close(fd);
+
+       return ret;
+}
+
+gboolean g_web_cancel_request(GWeb *web, guint id)
+{
+       if (web == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+guint16 g_web_result_get_status(GWebResult *result)
+{
+       if (result == NULL)
+               return 0;
+
+       return result->status;
+}
+
+gboolean g_web_result_get_chunk(GWebResult *result,
+                               const guint8 **chunk, gsize *length)
+{
+       if (result == NULL)
+               return FALSE;
+
+       if (chunk == NULL)
+               return FALSE;
+
+       *chunk = result->buffer;
+
+       if (length != NULL)
+               *length = result->length;
+
+       return TRUE;
+}
+
+gboolean g_web_result_get_header(GWebResult *result,
+                               const char *header, const char **value)
+{
+       if (result == NULL)
+               return FALSE;
+
+       if (value == NULL)
+               return FALSE;
+
+       *value = g_hash_table_lookup(result->headers, header);
+
+       if (*value == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+struct _GWebParser {
+       gint ref_count;
+       char *begin_token;
+       char *end_token;
+       const char *token_str;
+       size_t token_len;
+       size_t token_pos;
+       gboolean intoken;
+       GString *content;
+       GWebParserFunc func;
+       gpointer user_data;
+};
+
+GWebParser *g_web_parser_new(const char *begin, const char *end,
+                               GWebParserFunc func, gpointer user_data)
+{
+       GWebParser *parser;
+
+       parser = g_try_new0(GWebParser, 1);
+       if (parser == NULL)
+               return NULL;
+
+       parser->ref_count = 1;
+
+       parser->begin_token = g_strdup(begin);
+       parser->end_token = g_strdup(end);
+
+       if (parser->begin_token == NULL) {
+               g_free(parser);
+               return NULL;
+       }
+
+       parser->func = func;
+       parser->user_data = user_data;
+
+       parser->token_str = parser->begin_token;
+       parser->token_len = strlen(parser->token_str);
+       parser->token_pos = 0;
+
+       parser->intoken = FALSE;
+       parser->content = g_string_sized_new(0);
+
+       return parser;
+}
+
+GWebParser *g_web_parser_ref(GWebParser *parser)
+{
+       if (parser == NULL)
+               return NULL;
+
+       __sync_fetch_and_add(&parser->ref_count, 1);
+
+       return parser;
+}
+
+void g_web_parser_unref(GWebParser *parser)
+{
+       if (parser == NULL)
+               return;
+
+       if (__sync_fetch_and_sub(&parser->ref_count, 1) != 1)
+               return;
+
+       g_string_free(parser->content, TRUE);
+
+       g_free(parser->begin_token);
+       g_free(parser->end_token);
+       g_free(parser);
+}
+
+void g_web_parser_feed_data(GWebParser *parser,
+                               const guint8 *data, gsize length)
+{
+       const guint8 *ptr = data;
+
+       if (parser == NULL)
+               return;
+
+       while (length > 0) {
+               guint8 chr = parser->token_str[parser->token_pos];
+
+               if (parser->token_pos == 0) {
+                       guint8 *pos;
+
+                       pos = memchr(ptr, chr, length);
+                       if (pos == NULL) {
+                               if (parser->intoken == TRUE)
+                                       g_string_append_len(parser->content,
+                                                       (gchar *) ptr, length);
+                               break;
+                       }
+
+                       if (parser->intoken == TRUE)
+                               g_string_append_len(parser->content,
+                                               (gchar *) ptr, (pos - ptr) + 1);
+
+                       length -= (pos - ptr) + 1;
+                       ptr = pos + 1;
+
+                       parser->token_pos++;
+                       continue;
+               }
+
+               if (parser->intoken == TRUE)
+                       g_string_append_c(parser->content, ptr[0]);
+
+               if (ptr[0] != chr) {
+                       length--;
+                       ptr++;
+
+                       parser->token_pos = 0;
+                       continue;
+               }
+
+               length--;
+               ptr++;
+
+               parser->token_pos++;
+
+               if (parser->token_pos == parser->token_len) {
+                       if (parser->intoken == FALSE) {
+                               g_string_append(parser->content,
+                                                       parser->token_str);
+
+                               parser->intoken = TRUE;
+                               parser->token_str = parser->end_token;
+                               parser->token_len = strlen(parser->end_token);
+                               parser->token_pos = 0;
+                       } else {
+                               char *str;
+                               str = g_string_free(parser->content, FALSE);
+                               parser->content = g_string_sized_new(0);
+                               if (parser->func)
+                                       parser->func(str, parser->user_data);
+                               g_free(str);
+
+                               parser->intoken = FALSE;
+                               parser->token_str = parser->begin_token;
+                               parser->token_len = strlen(parser->begin_token);
+                               parser->token_pos = 0;
+                       }
+               }
+       }
+}
+
+void g_web_parser_end_data(GWebParser *parser)
+{
+       if (parser == NULL)
+               return;
+}
diff --git a/gweb/gweb.h b/gweb/gweb.h
new file mode 100644 (file)
index 0000000..104345e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *
+ *  Web service library with GLib integration
+ *
+ *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __G_WEB_H
+#define __G_WEB_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GWeb;
+struct _GWebResult;
+struct _GWebParser;
+
+typedef struct _GWeb GWeb;
+typedef struct _GWebResult GWebResult;
+typedef struct _GWebParser GWebParser;
+
+typedef gboolean (*GWebResultFunc)(GWebResult *result, gpointer user_data);
+
+typedef gboolean (*GWebRouteFunc)(const char *addr, int ai_family,
+               int if_index, gpointer user_data);
+
+typedef gboolean (*GWebInputFunc)(const guint8 **data, gsize *length,
+                                                       gpointer user_data);
+
+typedef void (*GWebDebugFunc)(const char *str, gpointer user_data);
+
+GWeb *g_web_new(int index);
+
+GWeb *g_web_ref(GWeb *web);
+void g_web_unref(GWeb *web);
+
+void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data);
+
+gboolean g_web_supports_tls(void);
+
+gboolean g_web_set_proxy(GWeb *web, const char *proxy);
+
+gboolean g_web_set_address_family(GWeb *web, int family);
+
+gboolean g_web_add_nameserver(GWeb *web, const char *address);
+
+gboolean g_web_set_accept(GWeb *web, const char *format, ...)
+                               __attribute__((format(printf, 2, 3)));
+gboolean g_web_set_user_agent(GWeb *web, const char *format, ...)
+                               __attribute__((format(printf, 2, 3)));
+gboolean g_web_set_ua_profile(GWeb *web, const char *profile);
+
+gboolean g_web_set_http_version(GWeb *web, const char *version);
+
+void g_web_set_close_connection(GWeb *web, gboolean enabled);
+gboolean g_web_get_close_connection(GWeb *web);
+
+guint g_web_request_get(GWeb *web, const char *url,
+                               GWebResultFunc func, GWebRouteFunc route,
+                               gpointer user_data);
+guint g_web_request_post(GWeb *web, const char *url,
+                               const char *type, GWebInputFunc input,
+                               GWebResultFunc func, gpointer user_data);
+guint g_web_request_post_file(GWeb *web, const char *url,
+                               const char *type, const char *file,
+                               GWebResultFunc func, gpointer user_data);
+
+gboolean g_web_cancel_request(GWeb *web, guint id);
+
+guint16 g_web_result_get_status(GWebResult *result);
+
+gboolean g_web_result_get_header(GWebResult *result,
+                               const char *header, const char **value);
+gboolean g_web_result_get_chunk(GWebResult *result,
+                               const guint8 **chunk, gsize *length);
+
+typedef void (*GWebParserFunc)(const char *str, gpointer user_data);
+
+GWebParser *g_web_parser_new(const char *begin, const char *end,
+                               GWebParserFunc func, gpointer user_data);
+
+GWebParser *g_web_parser_ref(GWebParser *parser);
+void g_web_parser_unref(GWebParser *parser);
+
+void g_web_parser_feed_data(GWebParser *parser,
+                               const guint8 *data, gsize length);
+void g_web_parser_end_data(GWebParser *parser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_WEB_H */
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644 (file)
index 0000000..3f8dbad
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_DBUS_H
+#define __CONNMAN_DBUS_H
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONNMAN_SERVICE                        "net.connman"
+#define CONNMAN_PATH                   "/net/connman"
+
+#define CONNMAN_DEBUG_INTERFACE                CONNMAN_SERVICE ".Debug"
+#define CONNMAN_ERROR_INTERFACE                CONNMAN_SERVICE ".Error"
+#define CONNMAN_AGENT_INTERFACE                CONNMAN_SERVICE ".Agent"
+#define CONNMAN_COUNTER_INTERFACE      CONNMAN_SERVICE ".Counter"
+
+#define CONNMAN_MANAGER_INTERFACE      CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH           "/"
+
+#define CONNMAN_CLOCK_INTERFACE                CONNMAN_SERVICE ".Clock"
+#define CONNMAN_TASK_INTERFACE         CONNMAN_SERVICE ".Task"
+#define CONNMAN_SERVICE_INTERFACE      CONNMAN_SERVICE ".Service"
+#define CONNMAN_PROVIDER_INTERFACE     CONNMAN_SERVICE ".Provider"
+#define CONNMAN_TECHNOLOGY_INTERFACE   CONNMAN_SERVICE ".Technology"
+#define CONNMAN_SESSION_INTERFACE      CONNMAN_SERVICE ".Session"
+#define CONNMAN_NOTIFICATION_INTERFACE CONNMAN_SERVICE ".Notification"
+
+#define CONNMAN_PRIVILEGE_MODIFY       1
+#define CONNMAN_PRIVILEGE_SECRET       2
+
+typedef void (* connman_dbus_append_cb_t) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+DBusConnection *connman_dbus_get_connection(void);
+
+void connman_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val);
+void connman_dbus_property_append_dict(DBusMessageIter *iter, const char *key,
+                       connman_dbus_append_cb_t function, void *user_data);
+void connman_dbus_property_append_array(DBusMessageIter *iter,
+                                               const char *key, int type,
+                       connman_dbus_append_cb_t function, void *user_data);
+void connman_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len);
+
+dbus_bool_t connman_dbus_property_changed_basic(const char *path,
+                               const char *interface, const char *key,
+                                                       int type, void *val);
+dbus_bool_t connman_dbus_property_changed_dict(const char *path,
+                               const char *interface, const char *key,
+                       connman_dbus_append_cb_t function, void *user_data);
+dbus_bool_t connman_dbus_property_changed_array(const char *path,
+                       const char *interface, const char *key, int type,
+                       connman_dbus_append_cb_t function, void *user_data);
+
+dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
+                               const char *path, const char *key,
+                               int type, void *val);
+dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
+                               const char *path, const char *key,
+                               connman_dbus_append_cb_t function,
+                               void *user_data);
+dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
+                               const char *path, const char *key, int type,
+                               connman_dbus_append_cb_t function,
+                               void *user_data);
+
+static inline void connman_dbus_dict_open(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void connman_dbus_dict_close(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_close_container(iter, dict);
+}
+
+static inline void connman_dbus_dict_append_basic(DBusMessageIter *dict,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       connman_dbus_property_append_basic(&entry, key, type, val);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void connman_dbus_dict_append_dict(DBusMessageIter *dict,
+                       const char *key, connman_dbus_append_cb_t function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       connman_dbus_property_append_dict(&entry, key, function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void connman_dbus_dict_append_array(DBusMessageIter *dict,
+               const char *key, int type, connman_dbus_append_cb_t function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       connman_dbus_property_append_array(&entry, key,
+                                               type, function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static inline void connman_dbus_dict_append_fixed_array(DBusMessageIter *dict,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       connman_dbus_property_append_fixed_array(&entry, key, type, val, len);
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+dbus_bool_t connman_dbus_validate_ident(const char *ident);
+char *connman_dbus_encode_string(const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DBUS_H */
diff --git a/include/device.h b/include/device.h
new file mode 100644 (file)
index 0000000..39a6499
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_DEVICE_H
+#define __CONNMAN_DEVICE_H
+
+#include <connman/types.h>
+#include <connman/network.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:device
+ * @title: Device premitives
+ * @short_description: Functions for handling devices
+ */
+
+enum connman_device_type {
+       CONNMAN_DEVICE_TYPE_UNKNOWN   = 0,
+       CONNMAN_DEVICE_TYPE_ETHERNET  = 1,
+       CONNMAN_DEVICE_TYPE_WIFI      = 2,
+       CONNMAN_DEVICE_TYPE_WIMAX     = 3,
+       CONNMAN_DEVICE_TYPE_BLUETOOTH = 4,
+       CONNMAN_DEVICE_TYPE_CELLULAR  = 5,
+       CONNMAN_DEVICE_TYPE_GPS       = 6,
+       CONNMAN_DEVICE_TYPE_GADGET    = 7,
+       CONNMAN_DEVICE_TYPE_VENDOR    = 10000,
+};
+
+#define CONNMAN_DEVICE_PRIORITY_LOW      -100
+#define CONNMAN_DEVICE_PRIORITY_DEFAULT     0
+#define CONNMAN_DEVICE_PRIORITY_HIGH      100
+
+struct connman_device;
+
+struct connman_device *connman_device_create(const char *node,
+                                               enum connman_device_type type);
+
+#define connman_device_ref(device) \
+       connman_device_ref_debug(device, __FILE__, __LINE__, __func__)
+
+#define connman_device_unref(device) \
+       connman_device_unref_debug(device, __FILE__, __LINE__, __func__)
+
+struct connman_device *
+connman_device_ref_debug(struct connman_device *device,
+                       const char *file, int line, const char *caller);
+void connman_device_unref_debug(struct connman_device *device,
+                       const char *file, int line, const char *caller);
+
+enum connman_device_type connman_device_get_type(struct connman_device *device);
+void connman_device_set_index(struct connman_device *device, int index);
+int connman_device_get_index(struct connman_device *device);
+void connman_device_set_interface(struct connman_device *device,
+                                               const char *interface);
+
+void connman_device_set_ident(struct connman_device *device,
+                                               const char *ident);
+const char *connman_device_get_ident(struct connman_device *device);
+
+int connman_device_set_powered(struct connman_device *device,
+                                               connman_bool_t powered);
+#if defined TIZEN_EXT
+/*
+ * Description: It checks significant and effective Wi-Fi profiles which
+ * can make an auto-connection. It saves power consumption not to scan if
+ * there is no valid profile to make an auto-connection.
+ */
+void connman_device_sig_wifi_profile_ref(struct connman_device *device);
+connman_bool_t connman_device_sig_wifi_profile_unref_and_test(
+                                       struct connman_device *device);
+
+connman_bool_t connman_device_load_sig_wifi_profile_refcount_from_storage(
+                                               struct connman_device *device);
+connman_bool_t connman_device_save_sig_wifi_profile_refcount2storage(
+                                       struct connman_device *device);
+#endif
+
+int connman_device_set_scanning(struct connman_device *device,
+                                               connman_bool_t scanning);
+connman_bool_t connman_device_get_scanning(struct connman_device *device);
+void connman_device_reset_scanning(struct connman_device *device);
+
+int connman_device_set_disconnected(struct connman_device *device,
+                                               connman_bool_t disconnected);
+connman_bool_t connman_device_get_disconnected(struct connman_device *device);
+
+int connman_device_set_string(struct connman_device *device,
+                                       const char *key, const char *value);
+const char *connman_device_get_string(struct connman_device *device,
+                                                       const char *key);
+
+int connman_device_add_network(struct connman_device *device,
+                                       struct connman_network *network);
+struct connman_network *connman_device_get_network(struct connman_device *device,
+                                                       const char *identifier);
+int connman_device_remove_network(struct connman_device *device,
+                                       struct connman_network *network);
+void connman_device_remove_all_networks(struct connman_device *device);
+
+int connman_device_register(struct connman_device *device);
+void connman_device_unregister(struct connman_device *device);
+
+void *connman_device_get_data(struct connman_device *device);
+void connman_device_set_data(struct connman_device *device, void *data);
+
+struct connman_device_driver {
+       const char *name;
+       enum connman_device_type type;
+       int priority;
+       int (*probe) (struct connman_device *device);
+       void (*remove) (struct connman_device *device);
+       int (*enable) (struct connman_device *device);
+       int (*disable) (struct connman_device *device);
+       int (*scan) (struct connman_device *device);
+       int (*scan_fast) (struct connman_device *device);
+       int (*scan_hidden)(struct connman_device *device,
+                       const char *ssid, unsigned int ssid_len,
+                       const char *identity, const char* passphrase,
+                       void *user_data);
+};
+
+int connman_device_driver_register(struct connman_device_driver *driver);
+void connman_device_driver_unregister(struct connman_device_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DEVICE_H */
diff --git a/include/inet.h b/include/inet.h
new file mode 100644 (file)
index 0000000..8b9b390
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_INET_H
+#define __CONNMAN_INET_H
+
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <connman/device.h>
+#include <connman/ipconfig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int connman_inet_ifindex(const char *name);
+char *connman_inet_ifname(int index);
+
+short int connman_inet_ifflags(int index);
+
+int connman_inet_ifup(int index);
+int connman_inet_ifdown(int index);
+
+struct connman_device *connman_inet_create_device(int index);
+connman_bool_t connman_inet_is_cfg80211(int index);
+
+int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress);
+int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress);
+int connman_inet_add_host_route(int index, const char *host, const char *gateway);
+int connman_inet_del_host_route(int index, const char *host);
+int connman_inet_add_network_route(int index, const char *host, const char *gateway,
+                                       const char *netmask);
+int connman_inet_del_network_route(int index, const char *host);
+int connman_inet_set_gateway_address(int index, const char *gateway);
+int connman_inet_clear_gateway_address(int index, const char *gateway);
+int connman_inet_set_gateway_interface(int index);
+int connman_inet_clear_gateway_interface(int index);
+connman_bool_t connman_inet_compare_subnet(int index, const char *host);
+int connman_inet_set_ipv6_address(int index,
+               struct connman_ipaddress *ipaddress);
+int connman_inet_clear_ipv6_address(int index,
+               const char *address, int prefix_len);
+int connman_inet_add_ipv6_network_route(int index, const char *host,
+                                       const char *gateway, unsigned char prefix_len);
+int connman_inet_add_ipv6_host_route(int index, const char *host,
+                                               const char *gateway);
+int connman_inet_del_ipv6_network_route(int index, const char *host,
+                                       unsigned char prefix_len);
+int connman_inet_del_ipv6_host_route(int index, const char *host);
+int connman_inet_set_ipv6_gateway_address(int index, const char *gateway);
+int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway);
+int connman_inet_set_ipv6_gateway_interface(int index);
+int connman_inet_clear_ipv6_gateway_interface(int index);
+
+int connman_inet_add_to_bridge(int index, const char *bridge);
+int connman_inet_remove_from_bridge(int index, const char *bridge);
+
+int connman_inet_set_mtu(int index, int mtu);
+int connman_inet_setup_tunnel(char *tunnel, int mtu);
+int connman_inet_create_tunnel(char **iface);
+int connman_inet_get_dest_addr(int index, char **dest);
+int connman_inet_ipv6_get_dest_addr(int index, char **dest);
+int connman_inet_check_ipaddress(const char *host);
+connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_INET_H */
diff --git a/include/ipconfig.h b/include/ipconfig.h
new file mode 100644 (file)
index 0000000..3612755
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_IPCONFIG_H
+#define __CONNMAN_IPCONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:ipconfig
+ * @title: IP configuration premitives
+ * @short_description: Functions for IP configuration handling
+ */
+
+struct connman_ipaddress;
+
+struct connman_ipaddress *connman_ipaddress_alloc(int family);
+void connman_ipaddress_free(struct connman_ipaddress *ipaddress);
+int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
+                               const char *address, const char *netmask,
+                               const char *gateway);
+int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress,
+                               const char *address,
+                               unsigned char prefix_length,
+                               const char *gateway);
+void connman_ipaddress_set_peer(struct connman_ipaddress *ipaddress,
+                               const char *peer);
+void connman_ipaddress_clear(struct connman_ipaddress *ipaddress);
+void connman_ipaddress_copy(struct connman_ipaddress *ipaddress,
+                                       struct connman_ipaddress *source);
+
+enum connman_ipconfig_type {
+       CONNMAN_IPCONFIG_TYPE_UNKNOWN = 0,
+       CONNMAN_IPCONFIG_TYPE_ALL     = 0,
+       CONNMAN_IPCONFIG_TYPE_IPV4    = 1,
+       CONNMAN_IPCONFIG_TYPE_IPV6    = 2,
+};
+
+enum connman_ipconfig_method {
+       CONNMAN_IPCONFIG_METHOD_UNKNOWN = 0,
+       CONNMAN_IPCONFIG_METHOD_OFF     = 1,
+       CONNMAN_IPCONFIG_METHOD_FIXED   = 2,
+       CONNMAN_IPCONFIG_METHOD_MANUAL  = 3,
+       CONNMAN_IPCONFIG_METHOD_DHCP    = 4,
+       CONNMAN_IPCONFIG_METHOD_AUTO    = 5,
+};
+
+struct connman_ipconfig;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_IPCONFIG_H */
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..2a7de10
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_LOG_H
+#define __CONNMAN_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:log
+ * @title: Logging premitives
+ * @short_description: Functions for logging error and debug information
+ */
+
+void connman_info(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void connman_warn(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void connman_error(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+void connman_debug(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+
+struct connman_debug_desc {
+       const char *name;
+       const char *file;
+#define CONNMAN_DEBUG_FLAG_DEFAULT (0)
+#define CONNMAN_DEBUG_FLAG_PRINT   (1 << 0)
+#define CONNMAN_DEBUG_FLAG_ALIAS   (1 << 1)
+       unsigned int flags;
+} __attribute__((aligned(8)));
+
+#define CONNMAN_DEBUG_DEFINE(name) \
+       static struct connman_debug_desc __debug_alias_ ## name \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               #name, __FILE__, CONNMAN_DEBUG_FLAG_ALIAS \
+       };
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around connman_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) do { \
+       static struct connman_debug_desc __connman_debug_desc \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               .file = __FILE__, .flags = CONNMAN_DEBUG_FLAG_DEFAULT, \
+       }; \
+       if (__connman_debug_desc.flags & CONNMAN_DEBUG_FLAG_PRINT) \
+               connman_debug("%s:%s() " fmt, \
+                                       __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_LOG_H */
diff --git a/include/network.h b/include/network.h
new file mode 100644 (file)
index 0000000..8541fd7
--- /dev/null
@@ -0,0 +1,189 @@
+
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_NETWORK_H
+#define __CONNMAN_NETWORK_H
+
+#include <connman/types.h>
+#include <connman/device.h>
+#include <connman/ipconfig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:network
+ * @title: Network premitives
+ * @short_description: Functions for handling networks
+ */
+
+enum connman_network_type {
+       CONNMAN_NETWORK_TYPE_UNKNOWN       = 0,
+       CONNMAN_NETWORK_TYPE_ETHERNET      = 1,
+       CONNMAN_NETWORK_TYPE_WIFI          = 2,
+       CONNMAN_NETWORK_TYPE_WIMAX         = 3,
+       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN = 8,
+       CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN = 9,
+       CONNMAN_NETWORK_TYPE_CELLULAR      = 10,
+       CONNMAN_NETWORK_TYPE_VENDOR        = 10000,
+};
+
+enum connman_network_error {
+       CONNMAN_NETWORK_ERROR_UNKNOWN         = 0,
+       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL  = 1,
+       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL  = 2,
+       CONNMAN_NETWORK_ERROR_INVALID_KEY     = 3,
+       CONNMAN_NETWORK_ERROR_CONNECT_FAIL    = 4,
+};
+
+#define CONNMAN_NETWORK_PRIORITY_LOW      -100
+#define CONNMAN_NETWORK_PRIORITY_DEFAULT     0
+#define CONNMAN_NETWORK_PRIORITY_HIGH      100
+
+struct connman_network;
+
+struct connman_network *connman_network_create(const char *identifier,
+                                       enum connman_network_type type);
+
+#define connman_network_ref(network) \
+       connman_network_ref_debug(network, __FILE__, __LINE__, __func__)
+
+#define connman_network_unref(network) \
+       connman_network_unref_debug(network, __FILE__, __LINE__, __func__)
+
+struct connman_network *
+connman_network_ref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller);
+void connman_network_unref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller);
+
+enum connman_network_type connman_network_get_type(struct connman_network *network);
+const char *connman_network_get_identifier(struct connman_network *network);
+
+void connman_network_set_index(struct connman_network *network, int index);
+int connman_network_get_index(struct connman_network *network);
+
+void connman_network_set_group(struct connman_network *network,
+                                               const char *group);
+const char *connman_network_get_group(struct connman_network *network);
+
+connman_bool_t connman_network_get_connecting(struct connman_network *network);
+int connman_network_set_available(struct connman_network *network,
+                                               connman_bool_t available);
+connman_bool_t connman_network_get_available(struct connman_network *network);
+int connman_network_set_associating(struct connman_network *network,
+                                               connman_bool_t associating);
+void connman_network_set_error(struct connman_network *network,
+                                       enum connman_network_error error);
+void connman_network_clear_error(struct connman_network *network);
+int connman_network_set_connected(struct connman_network *network,
+                                               connman_bool_t connected);
+connman_bool_t connman_network_get_connected(struct connman_network *network);
+
+connman_bool_t connman_network_get_associating(struct connman_network *network);
+
+void connman_network_clear_hidden(void *user_data);
+int connman_network_connect_hidden(struct connman_network *network,
+                       char *identity, char* passphrase, void *user_data);
+
+void connman_network_set_ipv4_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method);
+void connman_network_set_ipv6_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method);
+int connman_network_set_ipaddress(struct connman_network *network,
+                               struct connman_ipaddress *ipaddress);
+int connman_network_set_nameservers(struct connman_network *network,
+                               const char *nameservers);
+int connman_network_set_domain(struct connman_network *network,
+                                    const char *domain);
+#if defined TIZEN_EXT
+/*
+ * Description: Network client requires additional wifi specific info
+ */
+int connman_network_set_bssid(struct connman_network *network,
+                               const unsigned char *bssid);
+unsigned char *connman_network_get_bssid(struct connman_network *network);
+
+int connman_network_set_maxrate(struct connman_network *network,
+                               unsigned int maxrate);
+unsigned int connman_network_get_maxrate(struct connman_network *network);
+
+int connman_network_set_enc_mode(struct connman_network *network,
+                               const char *encryption_mode);
+const char *connman_network_get_enc_mode(struct connman_network *network);
+
+int connman_network_set_proxy(struct connman_network *network,
+                               const char *proxies);
+const char *connman_network_get_ifname(struct connman_network *network);
+#endif
+
+int connman_network_set_name(struct connman_network *network,
+                                                       const char *name);
+int connman_network_set_strength(struct connman_network *network,
+                                               connman_uint8_t strength);
+connman_uint8_t connman_network_get_strength(struct connman_network *network);
+int connman_network_set_frequency(struct connman_network *network,
+                                       connman_uint16_t frequency);
+connman_uint16_t connman_network_get_frequency(struct connman_network *network);
+int connman_network_set_wifi_channel(struct connman_network *network,
+                                       connman_uint16_t channel);
+connman_uint16_t connman_network_get_wifi_channel(struct connman_network *network);
+
+int connman_network_set_string(struct connman_network *network,
+                                       const char *key, const char *value);
+const char *connman_network_get_string(struct connman_network *network,
+                                                       const char *key);
+int connman_network_set_bool(struct connman_network *network,
+                                       const char *key, connman_bool_t value);
+connman_bool_t connman_network_get_bool(struct connman_network *network,
+                                                       const char *key);
+int connman_network_set_blob(struct connman_network *network,
+                       const char *key, const void *data, unsigned int size);
+const void *connman_network_get_blob(struct connman_network *network,
+                                       const char *key, unsigned int *size);
+
+struct connman_device *connman_network_get_device(struct connman_network *network);
+
+void *connman_network_get_data(struct connman_network *network);
+void connman_network_set_data(struct connman_network *network, void *data);
+
+void connman_network_update(struct connman_network *network);
+
+struct connman_network_driver {
+       const char *name;
+       enum connman_network_type type;
+       int priority;
+       int (*probe) (struct connman_network *network);
+       void (*remove) (struct connman_network *network);
+       int (*connect) (struct connman_network *network);
+       int (*disconnect) (struct connman_network *network);
+};
+
+int connman_network_driver_register(struct connman_network_driver *driver);
+void connman_network_driver_unregister(struct connman_network_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_NETWORK_H */
diff --git a/include/notifier.h b/include/notifier.h
new file mode 100644 (file)
index 0000000..4412717
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_NOTIFIER_H
+#define __CONNMAN_NOTIFIER_H
+
+#include <connman/service.h>
+#include <connman/ipconfig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:notifier
+ * @title: Notifier premitives
+ * @short_description: Functions for registering notifier modules
+ */
+
+#define CONNMAN_NOTIFIER_PRIORITY_LOW      -100
+#define CONNMAN_NOTIFIER_PRIORITY_DEFAULT     0
+#define CONNMAN_NOTIFIER_PRIORITY_HIGH      100
+
+struct connman_notifier {
+       const char *name;
+       int priority;
+       void (*default_changed) (struct connman_service *service);
+       void (*service_add) (struct connman_service *service,
+                               const char *name);
+       void (*service_remove) (struct connman_service *service);
+       void (*service_enabled) (enum connman_service_type type,
+                                               connman_bool_t enabled);
+       void (*offline_mode) (connman_bool_t enabled);
+       void (*proxy_changed) (struct connman_service *service);
+       void (*service_state_changed) (struct connman_service *service,
+                                       enum connman_service_state state);
+       void (*ipconfig_changed) (struct connman_service *service,
+                                       struct connman_ipconfig *ipconfig);
+       void (*idle_state) (connman_bool_t idle);
+};
+
+int connman_notifier_register(struct connman_notifier *notifier);
+void connman_notifier_unregister(struct connman_notifier *notifier);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_NOTIFIER_H */
diff --git a/include/option.h b/include/option.h
new file mode 100644 (file)
index 0000000..5e97ed4
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_OPTION_H
+#define __CONNMAN_OPTION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *connman_option_get_string(const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_OPTION_H */
diff --git a/include/plugin.h b/include/plugin.h
new file mode 100644 (file)
index 0000000..8d2bb08
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_PLUGIN_H
+#define __CONNMAN_PLUGIN_H
+
+#include <connman/version.h>
+#include <connman/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONNMAN_API_SUBJECT_TO_CHANGE
+#error "Please define CONNMAN_API_SUBJECT_TO_CHANGE to acknowledge your \
+understanding that ConnMan hasn't reached a stable API."
+#endif
+
+#define CONNMAN_PLUGIN_PRIORITY_LOW      -100
+#define CONNMAN_PLUGIN_PRIORITY_DEFAULT     0
+#define CONNMAN_PLUGIN_PRIORITY_HIGH      100
+
+/**
+ * SECTION:plugin
+ * @title: Plugin premitives
+ * @short_description: Functions for declaring plugins
+ */
+
+struct connman_plugin_desc {
+       const char *name;
+       const char *description;
+       const char *version;
+       int priority;
+       int (*init) (void);
+       void (*exit) (void);
+       void *debug_start;
+       void *debug_stop;
+};
+
+/**
+ * CONNMAN_PLUGIN_DEFINE:
+ * @name: plugin name
+ * @description: plugin description
+ * @version: plugin version string
+ * @init: init function called on plugin loading
+ * @exit: exit function called on plugin removal
+ *
+ * Macro for defining a plugin descriptor
+ *
+ * |[
+ * #include <connman/plugin.h>
+ *
+ * static int example_init(void)
+ * {
+ *     return 0;
+ * }
+ *
+ * static void example_exit(void)
+ * {
+ * }
+ *
+ * CONNMAN_PLUGIN_DEFINE(example, "Example plugin", CONNMAN_VERSION,
+ *                                     example_init, example_exit)
+ * ]|
+ */
+#ifdef CONNMAN_PLUGIN_BUILTIN
+#define CONNMAN_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               struct connman_plugin_desc __connman_builtin_ ## name = { \
+                       #name, description, version, priority, init, exit \
+               };
+#else
+#define CONNMAN_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               extern struct connman_debug_desc __start___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct connman_debug_desc __stop___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct connman_plugin_desc connman_plugin_desc \
+                               __attribute__ ((visibility("default"))); \
+               struct connman_plugin_desc connman_plugin_desc = { \
+                       #name, description, version, priority, init, exit, \
+                       __start___debug, __stop___debug \
+               };
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_PLUGIN_H */
diff --git a/include/provider.h b/include/provider.h
new file mode 100644 (file)
index 0000000..2a71b95
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_PROVIDER_H
+#define __CONNMAN_PROVIDER_H
+
+#include <glib.h>
+#include <connman/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:provider
+ * @title: Provider premitives
+ * @short_description: Functions for handling providers
+ */
+
+enum connman_provider_type {
+       CONNMAN_PROVIDER_TYPE_UNKNOWN = 0,
+       CONNMAN_PROVIDER_TYPE_VPN     = 1,
+};
+
+enum connman_provider_state {
+       CONNMAN_PROVIDER_STATE_UNKNOWN       = 0,
+       CONNMAN_PROVIDER_STATE_IDLE          = 1,
+       CONNMAN_PROVIDER_STATE_CONNECT       = 2,
+       CONNMAN_PROVIDER_STATE_READY         = 3,
+       CONNMAN_PROVIDER_STATE_DISCONNECT    = 4,
+       CONNMAN_PROVIDER_STATE_FAILURE       = 5,
+};
+
+enum connman_provider_error {
+       CONNMAN_PROVIDER_ERROR_UNKNOWN          = 0,
+       CONNMAN_PROVIDER_ERROR_CONNECT_FAILED   = 1,
+       CONNMAN_PROVIDER_ERROR_LOGIN_FAILED     = 2,
+       CONNMAN_PROVIDER_ERROR_AUTH_FAILED      = 3,
+};
+
+struct connman_provider;
+struct connman_ipaddress;
+
+#define connman_provider_ref(provider) \
+       connman_provider_ref_debug(provider, __FILE__, __LINE__, __func__)
+
+#define connman_provider_unref(provider) \
+       connman_provider_unref_debug(provider, __FILE__, __LINE__, __func__)
+
+struct connman_provider *
+connman_provider_ref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller);
+void connman_provider_unref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller);
+
+int connman_provider_set_string(struct connman_provider *provider,
+                                       const char *key, const char *value);
+const char *connman_provider_get_string(struct connman_provider *provider,
+                                                       const char *key);
+
+int connman_provider_set_state(struct connman_provider *provider,
+                                       enum connman_provider_state state);
+
+int connman_provider_indicate_error(struct connman_provider *provider,
+                                       enum connman_provider_error error);
+
+void connman_provider_set_index(struct connman_provider *provider, int index);
+int connman_provider_get_index(struct connman_provider *provider);
+
+void connman_provider_set_data(struct connman_provider *provider, void *data);
+void *connman_provider_get_data(struct connman_provider *provider);
+int connman_provider_set_ipaddress(struct connman_provider *provider,
+                                       struct connman_ipaddress *ipaddress);
+int connman_provider_set_pac(struct connman_provider *provider,
+                               const char *pac);
+int connman_provider_set_domain(struct connman_provider *provider,
+                               const char *domain);
+int connman_provider_set_nameservers(struct connman_provider *provider,
+                                       const char *nameservers);
+int connman_provider_append_route(struct connman_provider *provider,
+                                       const char *key, const char *value);
+
+const char *connman_provider_get_driver_name(struct connman_provider *provider);
+const char *connman_provider_get_save_group(struct connman_provider *provider);
+
+struct connman_provider_driver {
+       const char *name;
+       enum connman_provider_type type;
+       int (*probe) (struct connman_provider *provider);
+       int (*remove) (struct connman_provider *provider);
+       int (*connect) (struct connman_provider *provider);
+       int (*disconnect) (struct connman_provider *provider);
+       int (*save) (struct connman_provider *provider, GKeyFile *keyfile);
+};
+
+int connman_provider_driver_register(struct connman_provider_driver *driver);
+void connman_provider_driver_unregister(struct connman_provider_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_PROVIDER_H */
diff --git a/include/proxy.h b/include/proxy.h
new file mode 100644 (file)
index 0000000..7842f65
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_PROXY_H
+#define __CONNMAN_PROXY_H
+
+#include <connman/service.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:proxy
+ * @title: Proxy premitives
+ * @short_description: Functions for handling proxy configuration
+ */
+
+typedef void (*connman_proxy_lookup_cb) (const char *proxy, void *user_data);
+
+unsigned int connman_proxy_lookup(const char *interface, const char *url,
+                               struct connman_service *service,
+                               connman_proxy_lookup_cb cb, void *user_data);
+void connman_proxy_lookup_cancel(unsigned int token);
+
+void connman_proxy_driver_lookup_notify(struct connman_service *service,
+                                       const char *url, const char *result);
+
+#define CONNMAN_PROXY_PRIORITY_LOW      -100
+#define CONNMAN_PROXY_PRIORITY_DEFAULT     0
+#define CONNMAN_PROXY_PRIORITY_HIGH      100
+
+struct connman_proxy_driver {
+       const char *name;
+       int priority;
+       int (*request_lookup) (struct connman_service *service,
+                                                       const char *url);
+       void (*cancel_lookup) (struct connman_service *service,
+                                                       const char *url);
+};
+
+int connman_proxy_driver_register(struct connman_proxy_driver *driver);
+void connman_proxy_driver_unregister(struct connman_proxy_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_PROXY_H */
diff --git a/include/resolver.h b/include/resolver.h
new file mode 100644 (file)
index 0000000..5dcf698
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_RESOLVER_H
+#define __CONNMAN_RESOLVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:resolver
+ * @title: Resolver premitives
+ * @short_description: Functions for registering resolver modules
+ */
+
+int connman_resolver_append(const char *interface, const char *domain,
+                                                       const char *server);
+int connman_resolver_append_lifetime(const char *interface, const char *domain,
+                                    const char *server, unsigned int lifetime);
+int connman_resolver_remove(const char *interface, const char *domain,
+                                                       const char *server);
+int connman_resolver_remove_all(const char *interface);
+
+void connman_resolver_flush(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RESOLVER_H */
diff --git a/include/rtnl.h b/include/rtnl.h
new file mode 100644 (file)
index 0000000..aa70f4d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_RTNL_H
+#define __CONNMAN_RTNL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:rtnl
+ * @title: RTNL premitives
+ * @short_description: Functions for registering RTNL modules
+ */
+
+typedef void (* connman_rtnl_link_cb_t) (unsigned flags, unsigned change,
+                                                       void *user_data);
+
+unsigned int connman_rtnl_add_newlink_watch(int index,
+                       connman_rtnl_link_cb_t callback, void *user_data);
+
+void connman_rtnl_remove_watch(unsigned int id);
+
+#define CONNMAN_RTNL_PRIORITY_LOW      -100
+#define CONNMAN_RTNL_PRIORITY_DEFAULT     0
+#define CONNMAN_RTNL_PRIORITY_HIGH      100
+
+struct connman_rtnl {
+       const char *name;
+       int priority;
+       void (*newlink) (unsigned short type, int index,
+                                       unsigned flags, unsigned change);
+       void (*dellink) (unsigned short type, int index,
+                                       unsigned flags, unsigned change);
+       void (*newgateway) (int index, const char *gateway);
+       void (*delgateway) (int index, const char *gateway);
+};
+
+int connman_rtnl_register(struct connman_rtnl *rtnl);
+void connman_rtnl_unregister(struct connman_rtnl *rtnl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RTNL_H */
diff --git a/include/service.h b/include/service.h
new file mode 100644 (file)
index 0000000..5934f08
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_SERVICE_H
+#define __CONNMAN_SERVICE_H
+
+#include <connman/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:service
+ * @title: Service premitives
+ * @short_description: Functions for handling services
+ */
+
+enum connman_service_type {
+       CONNMAN_SERVICE_TYPE_UNKNOWN   = 0,
+       CONNMAN_SERVICE_TYPE_SYSTEM    = 1,
+       CONNMAN_SERVICE_TYPE_ETHERNET  = 2,
+       CONNMAN_SERVICE_TYPE_WIFI      = 3,
+       CONNMAN_SERVICE_TYPE_WIMAX     = 4,
+       CONNMAN_SERVICE_TYPE_BLUETOOTH = 5,
+       CONNMAN_SERVICE_TYPE_CELLULAR  = 6,
+       CONNMAN_SERVICE_TYPE_GPS       = 7,
+       CONNMAN_SERVICE_TYPE_VPN       = 8,
+       CONNMAN_SERVICE_TYPE_GADGET    = 9,
+};
+
+enum connman_service_security {
+       CONNMAN_SERVICE_SECURITY_UNKNOWN = 0,
+       CONNMAN_SERVICE_SECURITY_NONE    = 1,
+       CONNMAN_SERVICE_SECURITY_WEP     = 2,
+       CONNMAN_SERVICE_SECURITY_PSK     = 3,
+       CONNMAN_SERVICE_SECURITY_8021X   = 4,
+       CONNMAN_SERVICE_SECURITY_WPA     = 8,
+       CONNMAN_SERVICE_SECURITY_RSN     = 9,
+};
+
+enum connman_service_state {
+       CONNMAN_SERVICE_STATE_UNKNOWN       = 0,
+       CONNMAN_SERVICE_STATE_IDLE          = 1,
+       CONNMAN_SERVICE_STATE_ASSOCIATION   = 2,
+       CONNMAN_SERVICE_STATE_CONFIGURATION = 3,
+       CONNMAN_SERVICE_STATE_READY         = 4,
+       CONNMAN_SERVICE_STATE_ONLINE        = 5,
+       CONNMAN_SERVICE_STATE_DISCONNECT    = 6,
+       CONNMAN_SERVICE_STATE_FAILURE       = 7,
+};
+
+enum connman_service_error {
+       CONNMAN_SERVICE_ERROR_UNKNOWN        = 0,
+       CONNMAN_SERVICE_ERROR_OUT_OF_RANGE   = 1,
+       CONNMAN_SERVICE_ERROR_PIN_MISSING    = 2,
+       CONNMAN_SERVICE_ERROR_DHCP_FAILED    = 3,
+       CONNMAN_SERVICE_ERROR_CONNECT_FAILED = 4,
+       CONNMAN_SERVICE_ERROR_LOGIN_FAILED  = 5,
+       CONNMAN_SERVICE_ERROR_AUTH_FAILED    = 6,
+       CONNMAN_SERVICE_ERROR_INVALID_KEY    = 7,
+};
+
+enum connman_service_proxy_method {
+       CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN     = 0,
+       CONNMAN_SERVICE_PROXY_METHOD_DIRECT      = 1,
+       CONNMAN_SERVICE_PROXY_METHOD_MANUAL      = 2,
+       CONNMAN_SERVICE_PROXY_METHOD_AUTO        = 3,
+};
+
+struct connman_service;
+
+struct connman_service *connman_service_create(void);
+
+#define connman_service_ref(service) \
+       connman_service_ref_debug(service, __FILE__, __LINE__, __func__)
+
+#define connman_service_unref(service) \
+       connman_service_unref_debug(service, __FILE__, __LINE__, __func__)
+
+struct connman_service *
+connman_service_ref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller);
+void connman_service_unref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller);
+
+enum connman_service_type connman_service_get_type(struct connman_service *service);
+char *connman_service_get_interface(struct connman_service *service);
+
+const char *connman_service_get_domainname(struct connman_service *service);
+char **connman_service_get_nameservers(struct connman_service *service);
+char **connman_service_get_timeservers_config(struct connman_service *service);
+char **connman_service_get_timeservers(struct connman_service *service);
+void connman_service_set_proxy_method(struct connman_service *service, enum connman_service_proxy_method method);
+enum connman_service_proxy_method connman_service_get_proxy_method(struct connman_service *service);
+char **connman_service_get_proxy_servers(struct connman_service *service);
+char **connman_service_get_proxy_excludes(struct connman_service *service);
+const char *connman_service_get_proxy_url(struct connman_service *service);
+const char *connman_service_get_proxy_autoconfig(struct connman_service *service);
+
+#if defined TIZEN_EXT
+/*
+ * Increase reference count of user-initiated packet data network connection
+ */
+void connman_service_user_pdn_connection_ref(struct connman_service *service);
+
+/*
+ * Decrease reference count of user initiated packet data network connection
+ * and return TRUE if counter is zero.
+ */
+connman_bool_t connman_service_user_pdn_connection_unref_and_test(
+                                       struct connman_service *service);
+
+/*
+ * Test reference count of user initiated packet data network connection
+ * and return TRUE if counter is zero. No impact to reference count
+ */
+connman_bool_t connman_service_is_no_ref_user_pdn_connection(
+                                       struct connman_service *service);
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_SERVICE_H */
diff --git a/include/setting.h b/include/setting.h
new file mode 100644 (file)
index 0000000..e867a78
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_SETTING_H
+#define __CONNMAN_SETTING_H
+
+#include <connman/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+connman_bool_t connman_setting_get_bool(const char *key);
+char **connman_setting_get_string_list(const char *key);
+unsigned int *connman_setting_get_uint_list(const char *key);
+
+unsigned int connman_timeout_input_request(void);
+unsigned int connman_timeout_browser_launch(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_SETTING_H */
diff --git a/include/storage.h b/include/storage.h
new file mode 100644 (file)
index 0000000..4c23a14
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_STORAGE_H
+#define __CONNMAN_STORAGE_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gchar **connman_storage_get_services();
+GKeyFile *connman_storage_load_service(const char *service_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_STORAGE_H */
diff --git a/include/task.h b/include/task.h
new file mode 100644 (file)
index 0000000..9977d63
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_TASK_H
+#define __CONNMAN_TASK_H
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:task
+ * @title: Task premitives
+ * @short_description: Functions for handling tasks
+ */
+
+struct connman_task;
+
+typedef void (* connman_task_exit_t) (struct connman_task *task,
+                                               int exit_code, void *user_data);
+
+typedef DBusMessage * (* connman_task_notify_t) (struct connman_task *task,
+                               DBusMessage *message, void *user_data);
+
+struct connman_task *connman_task_create(const char *program);
+void connman_task_destroy(struct connman_task *task);
+
+const char *connman_task_get_path(struct connman_task *task);
+
+int connman_task_add_argument(struct connman_task *task,
+                               const char *name, const char *format, ...);
+int connman_task_add_variable(struct connman_task *task,
+                               const char *key, const char *format, ...);
+
+int connman_task_set_notify(struct connman_task *task, const char *member,
+                       connman_task_notify_t function, void *user_data);
+
+int connman_task_run(struct connman_task *task,
+                       connman_task_exit_t function, void *user_data,
+                       int *stdin_fd, int *stdout_fd, int *stderr_fd);
+int connman_task_stop(struct connman_task *task);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TASK_H */
diff --git a/include/technology.h b/include/technology.h
new file mode 100644 (file)
index 0000000..f3fab20
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_TECHNOLOGY_H
+#define __CONNMAN_TECHNOLOGY_H
+
+#include <connman/service.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:technology
+ * @title: technology premitives
+ * @short_description: Functions for handling technology details
+ */
+
+struct connman_technology;
+
+void connman_technology_tethering_notify(struct connman_technology *technology,
+                                                       connman_bool_t enabled);
+int connman_technology_set_regdom(const char *alpha2);
+void connman_technology_regdom_notify(struct connman_technology *technology,
+                                                       const char *alpha2);
+
+struct connman_technology_driver {
+       const char *name;
+       enum connman_service_type type;
+       int priority;
+       int (*probe) (struct connman_technology *technology);
+       void (*remove) (struct connman_technology *technology);
+       void (*add_interface) (struct connman_technology *technology,
+                                               int index, const char *name,
+                                                       const char *ident);
+       void (*remove_interface) (struct connman_technology *technology,
+                                                               int index);
+       int (*set_tethering) (struct connman_technology *technology,
+                               const char *identifier, const char *passphrase,
+                               const char *bridge, connman_bool_t enabled);
+       int (*set_regdom) (struct connman_technology *technology,
+                                               const char *alpha2);
+};
+
+int connman_technology_driver_register(struct connman_technology_driver *driver);
+void connman_technology_driver_unregister(struct connman_technology_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TECHNOLOGY_H */
diff --git a/include/timeserver.h b/include/timeserver.h
new file mode 100644 (file)
index 0000000..48ea194
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_TIMESERVER_H
+#define __CONNMAN_TIMESERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __connman_timeserver_system_set(char **server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TIMESERVER_H */
diff --git a/include/types.h b/include/types.h
new file mode 100644 (file)
index 0000000..0f671ec
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_TYPES_H
+#define __CONNMAN_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef int            connman_bool_t;
+typedef unsigned char  connman_uint8_t;
+typedef unsigned short connman_uint16_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TYPES_H */
diff --git a/include/utsname.h b/include/utsname.h
new file mode 100644 (file)
index 0000000..b8165f6
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_UTSNAME_H
+#define __CONNMAN_UTSNAME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:utsname
+ * @title: utsname premitives
+ * @short_description: Functions for handling utsname
+ */
+
+const char *connman_utsname_get_hostname(void);
+
+struct connman_utsname_driver {
+       const char *name;
+       int priority;
+       const char * (*get_hostname) (void);
+       int (*set_hostname) (const char *hostname);
+       int (*set_domainname) (const char *domainname);
+};
+
+int connman_utsname_driver_register(struct connman_utsname_driver *driver);
+void connman_utsname_driver_unregister(struct connman_utsname_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_UTSNAME_H */
diff --git a/include/version.h.in b/include/version.h.in
new file mode 100644 (file)
index 0000000..d3d6dd3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_VERSION_H
+#define __CONNMAN_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONNMAN_VERSION        "@VERSION@"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_VERSION_H */
diff --git a/packaging/connman.spec b/packaging/connman.spec
new file mode 100644 (file)
index 0000000..9d1ebfe
--- /dev/null
@@ -0,0 +1,126 @@
+Name:           connman
+Version:        1.3_8
+Release:        1
+License:        GPLv2
+Summary:        Connection Manager
+Url:            http://connman.net
+Group:          System/Networking
+Source0:        %{name}-%{version}.tar.gz
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(libiptc)
+BuildRequires:  pkgconfig(xtables)
+BuildRequires:  pkgconfig(gnutls)
+BuildRequires:  pkgconfig(libsmack)
+Requires:         systemd
+Requires(post):   systemd
+Requires(preun):  systemd
+Requires(postun): systemd
+
+%description
+Connection Manager provides a daemon for managing Internet connections
+within embedded devices running the Linux operating system.
+
+%package test
+Summary:        Test Scripts for Connection Manager
+Group:          Development/Tools
+Requires:       %{name} = %{version}
+Requires:       dbus-python
+Requires:       pygobject
+Requires:       python-xml
+
+%description test
+Scripts for testing Connman and its functionality
+
+%package devel
+Summary:        Development Files for connman
+Group:          Development/Tools
+Requires:       %{name} = %{version}
+
+%description devel
+Header files and development files for connman.
+
+%prep
+%setup -q
+
+
+%build
+CFLAGS+=" -DTIZEN_EXT -lsmack"
+
+./bootstrap
+
+%configure \
+            --sysconfdir=/etc \
+            --enable-threads \
+            --enable-wifi=builtin \
+%if 0%{?enable_connman_features}
+            %connman_features \
+%endif
+            --enable-test \
+           --enable-loopback \
+           --enable-ethernet \
+           --disable-linklocaladdr \
+           --sysconfdir=/opt/etc \
+            --with-systemdunitdir=%{_libdir}/systemd/system
+
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+mkdir -p %{buildroot}%{_libdir}/systemd/system/network.target.wants
+ln -s ../connman.service %{buildroot}%{_libdir}/systemd/system/network.target.wants/connman.service
+
+
+mkdir -p %{buildroot}%{_localstatedir}/lib/connman
+cp resources/var/lib/connman/settings %{buildroot}%{_localstatedir}/lib/connman/settings
+mkdir -p %{buildroot}%{_datadir}/dbus-1/services
+cp resources/usr/share/dbus-1/services/net.connman.service %{buildroot}%{_datadir}/dbus-1/services/net.connman.service
+mkdir -p %{buildroot}/opt/etc/connman
+cp src/main.conf %{buildroot}/opt/etc/connman/main.conf
+
+# FIXME: All of below has to go when systemd lands
+mkdir -p %{buildroot}%{_sysconfdir}/rc.d/init.d
+cp resources/etc/rc.d/init.d/connman %{buildroot}%{_sysconfdir}/rc.d/init.d/connman
+mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc3.d
+ln -s ../init.d/connman %{buildroot}%{_sysconfdir}/rc.d/rc3.d/S61connman
+mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc5.d
+ln -s ../init.d/connman %{buildroot}%{_sysconfdir}/rc.d/rc5.d/S61connman
+
+rm %{buildroot}%{_sysconfdir}/dbus-1/system.d/*.conf
+mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d/
+cp src/connman.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/
+
+%post
+systemctl daemon-reload
+systemctl restart connman.service
+
+%preun
+systemctl stop connman.service
+
+%postun
+systemctl daemon-reload
+
+
+%files
+%manifest connman.manifest
+%{_sbindir}/*
+%attr(600,root,root) %{_localstatedir}/lib/connman/settings
+%{_libdir}/connman/plugins/*.so
+%{_datadir}/dbus-1/services/*
+%{_sysconfdir}/dbus-1/system.d/*
+/opt/etc/connman/main.conf
+%{_sysconfdir}/dbus-1/system.d/*.conf
+%{_sysconfdir}/rc.d/init.d/connman
+%{_sysconfdir}/rc.d/rc3.d/S61connman
+%{_sysconfdir}/rc.d/rc5.d/S61connman
+%{_libdir}/systemd/system/connman.service
+%{_libdir}/systemd/system/network.target.wants/connman.service
+
+%files test
+%{_libdir}/%{name}/test/*
+
+
+%files devel
+%{_includedir}/*
+%{_libdir}/pkgconfig/*.pc
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c
new file mode 100644 (file)
index 0000000..6f476e2
--- /dev/null
@@ -0,0 +1,1290 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/technology.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define BLUEZ_SERVICE                  "org.bluez"
+#define BLUEZ_MANAGER_INTERFACE                BLUEZ_SERVICE ".Manager"
+#define BLUEZ_ADAPTER_INTERFACE                BLUEZ_SERVICE ".Adapter"
+#define BLUEZ_DEVICE_INTERFACE         BLUEZ_SERVICE ".Device"
+#define BLUEZ_NETWORK_INTERFACE                BLUEZ_SERVICE ".Network"
+#define BLUEZ_NETWORK_SERVER           BLUEZ_SERVICE ".NetworkServer"
+
+#define LIST_ADAPTERS                  "ListAdapters"
+#define ADAPTER_ADDED                  "AdapterAdded"
+#define ADAPTER_REMOVED                        "AdapterRemoved"
+#define DEVICE_REMOVED                 "DeviceRemoved"
+
+#define PROPERTY_CHANGED               "PropertyChanged"
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+
+#define CONNECT                                "Connect"
+#define DISCONNECT                     "Disconnect"
+
+#define REGISTER                       "Register"
+#define UNREGISTER                     "Unregister"
+
+#define UUID_NAP       "00001116-0000-1000-8000-00805f9b34fb"
+
+#define TIMEOUT 5000
+
+static DBusConnection *connection;
+
+static GHashTable *bluetooth_devices = NULL;
+static GHashTable *bluetooth_networks = NULL;
+
+static int pan_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void pan_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static void connect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       const char *interface = NULL;
+       int index;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+
+               goto err;
+       }
+
+       if (dbus_message_get_args(reply, &error,
+                                       DBUS_TYPE_STRING, &interface,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for connect");
+               goto err;
+       }
+
+       if (interface == NULL)
+               goto err;
+
+       DBG("interface %s", interface);
+
+       index = connman_inet_ifindex(interface);
+
+       connman_network_set_index(network, index);
+
+       connman_network_set_connected(network, TRUE);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+
+       return;
+err:
+
+       connman_network_set_connected(network, FALSE);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int pan_connect(struct connman_network *network)
+{
+       const char *path = connman_network_get_string(network, "Path");
+       const char *uuid = "nap";
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, CONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                       &call, TIMEOUT * 10) == FALSE) {
+               connman_error("Failed to connect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, connect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void disconnect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for disconnect");
+               goto done;
+       }
+
+       connman_network_set_connected(network, FALSE);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+
+       connman_network_unref(network);
+}
+
+static int pan_disconnect(struct connman_network *network)
+{
+       const char *path = connman_network_get_string(network, "Path");
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, DISCONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to disconnect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       connman_network_ref(network);
+
+       connman_network_set_associating(network, FALSE);
+
+       dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static struct connman_network_driver pan_driver = {
+       .name           = "bluetooth-pan",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
+       .probe          = pan_probe,
+       .remove         = pan_remove,
+       .connect        = pan_connect,
+       .disconnect     = pan_disconnect,
+};
+
+static gboolean network_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct connman_network *network;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       network = g_hash_table_lookup(bluetooth_networks, path);
+       if (network == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Connected") == TRUE) {
+               dbus_bool_t connected;
+
+               dbus_message_iter_get_basic(&value, &connected);
+
+               if (connected == TRUE)
+                       return TRUE;
+
+               connman_network_set_associating(network, FALSE);
+               connman_network_set_connected(network, FALSE);
+       }
+
+       return TRUE;
+}
+
+static void extract_properties(DBusMessage *reply, const char **parent,
+                                               const char **address,
+                                               const char **name,
+                                               const char **alias,
+                                               dbus_bool_t *powered,
+                                               dbus_bool_t *scanning,
+                                               DBusMessageIter *uuids,
+                                               DBusMessageIter *networks)
+{
+       DBusMessageIter array, dict;
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               return;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Adapter") == TRUE) {
+                       if (parent != NULL)
+                               dbus_message_iter_get_basic(&value, parent);
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       if (address != NULL)
+                               dbus_message_iter_get_basic(&value, address);
+               } else if (g_str_equal(key, "Name") == TRUE) {
+                       if (name != NULL)
+                               dbus_message_iter_get_basic(&value, name);
+               } else if (g_str_equal(key, "Alias") == TRUE) {
+                       if (alias != NULL)
+                               dbus_message_iter_get_basic(&value, alias);
+               } else if (g_str_equal(key, "Powered") == TRUE) {
+                       if (powered != NULL)
+                               dbus_message_iter_get_basic(&value, powered);
+               } else if (g_str_equal(key, "Discovering") == TRUE) {
+                       if (scanning != NULL)
+                               dbus_message_iter_get_basic(&value, scanning);
+               } else if (g_str_equal(key, "Devices") == TRUE) {
+                       if (networks != NULL)
+                               memcpy(networks, &value, sizeof(value));
+               } else if (g_str_equal(key, "UUIDs") == TRUE) {
+                       if (uuids != NULL)
+                               memcpy(uuids, &value, sizeof(value));
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static dbus_bool_t has_pan(DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+               const char *uuid;
+
+               dbus_message_iter_get_basic(&value, &uuid);
+
+               if (g_strcmp0(uuid, UUID_NAP) == 0)
+                       return TRUE;
+
+               dbus_message_iter_next(&value);
+       }
+
+       return FALSE;
+}
+
+static void network_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       char *path = user_data;
+       struct connman_device *device;
+       struct connman_network *network;
+       DBusMessage *reply;
+       DBusMessageIter uuids;
+       const char *parent = NULL, *address = NULL, *name = NULL;
+       struct ether_addr addr;
+       char ident[13];
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       extract_properties(reply, &parent, &address, NULL, &name,
+                                               NULL, NULL, &uuids, NULL);
+
+       if (parent == NULL)
+               goto done;
+
+       device = g_hash_table_lookup(bluetooth_devices, parent);
+       if (device == NULL)
+               goto done;
+
+       if (address == NULL)
+               goto done;
+
+       ether_aton_r(address, &addr);
+
+       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+                                               addr.ether_addr_octet[0],
+                                               addr.ether_addr_octet[1],
+                                               addr.ether_addr_octet[2],
+                                               addr.ether_addr_octet[3],
+                                               addr.ether_addr_octet[4],
+                                               addr.ether_addr_octet[5]);
+
+       if (has_pan(&uuids) == FALSE)
+               goto done;
+
+       network = connman_device_get_network(device, ident);
+       if (network != NULL)
+               goto done;
+
+       network = connman_network_create(ident,
+                                       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
+       if (network == NULL)
+               goto done;
+
+       connman_network_set_string(network, "Path", path);
+
+       connman_network_set_name(network, name);
+
+       connman_device_add_network(device, network);
+
+       connman_network_set_group(network, ident);
+
+       g_hash_table_replace(bluetooth_networks, g_strdup(path), network);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void add_network(struct connman_device *device, const char *path)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("path %s", path);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                               BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get network properties for %s", path);
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, network_properties_reply,
+                                               g_strdup(path), g_free);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void check_networks(struct connman_device *device,
+                                               DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               add_network(device, path);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static gboolean adapter_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct connman_device *device;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       device = g_hash_table_lookup(bluetooth_devices, path);
+       if (device == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_powered(device, val);
+       } else if (g_str_equal(key, "Discovering") == TRUE) {
+               dbus_bool_t val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_scanning(device, val);
+       } else if (g_str_equal(key, "Devices") == TRUE) {
+               check_networks(device, &value);
+       }
+
+       return TRUE;
+}
+
+static gboolean device_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *network_path;
+       struct connman_network *network;
+       struct connman_device *device;
+       DBusMessageIter iter;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &network_path);
+
+       network = g_hash_table_lookup(bluetooth_networks, network_path);
+       if (network == NULL)
+               return TRUE;
+
+       device = connman_network_get_device(network);
+       if (device == NULL)
+               return TRUE;
+
+       g_hash_table_remove(bluetooth_networks, network_path);
+
+       return TRUE;
+}
+
+static gboolean device_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       DBG("key %s", key);
+
+       if (g_str_equal(key, "UUIDs") == TRUE)
+               add_network(NULL, path);
+
+       return TRUE;
+}
+
+static void remove_device_networks(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       GSList *key_list = NULL;
+       GSList *list;
+
+       if (bluetooth_networks == NULL)
+               return;
+
+       g_hash_table_iter_init(&iter, bluetooth_networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_network *network = value;
+
+               if (connman_network_get_device(network) != device)
+                       continue;
+
+               key_list = g_slist_append(key_list, key);
+       }
+
+       for (list = key_list; list != NULL; list = list->next) {
+               const char *network_path = list->data;
+
+               g_hash_table_remove(bluetooth_networks, network_path);
+       }
+
+       g_slist_free(key_list);
+}
+
+static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       char *path = user_data;
+       struct connman_device *device;
+       DBusMessage *reply;
+       DBusMessageIter networks;
+       const char *address = NULL, *name = NULL;
+       dbus_bool_t powered = FALSE, scanning = FALSE;
+       struct ether_addr addr;
+       char ident[13];
+
+       DBG("path %s", path);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (path == NULL)
+               goto done;
+
+       extract_properties(reply, NULL, &address, &name, NULL,
+                                       &powered, &scanning, NULL, &networks);
+
+       if (address == NULL)
+               goto done;
+
+       if (g_strcmp0(address, "00:00:00:00:00:00") == 0)
+               goto done;
+
+       device = g_hash_table_lookup(bluetooth_devices, path);
+       if (device != NULL)
+               goto update;
+
+       ether_aton_r(address, &addr);
+
+       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+                                               addr.ether_addr_octet[0],
+                                               addr.ether_addr_octet[1],
+                                               addr.ether_addr_octet[2],
+                                               addr.ether_addr_octet[3],
+                                               addr.ether_addr_octet[4],
+                                               addr.ether_addr_octet[5]);
+
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (device == NULL)
+               goto done;
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", path);
+
+       if (connman_device_register(device) < 0) {
+               connman_device_unref(device);
+               goto done;
+       }
+
+       g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
+
+update:
+       connman_device_set_string(device, "Address", address);
+       connman_device_set_string(device, "Name", name);
+       connman_device_set_string(device, "Path", path);
+
+       connman_device_set_powered(device, powered);
+       connman_device_set_scanning(device, scanning);
+
+       if (powered == TRUE)
+               check_networks(device, &networks);
+       else
+               remove_device_networks(device);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void add_adapter(DBusConnection *connection, const char *path)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("path %s", path);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                               BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get adapter properties for %s", path);
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, adapter_properties_reply,
+                                               g_strdup(path), g_free);
+
+done:
+       dbus_message_unref(message);
+}
+
+static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       add_adapter(connection, path);
+       return TRUE;
+}
+
+static void remove_adapter(DBusConnection *connection, const char *path)
+{
+       DBG("path %s", path);
+
+       g_hash_table_remove(bluetooth_devices, path);
+}
+
+static gboolean adapter_removed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       remove_adapter(connection, path);
+       return TRUE;
+}
+
+static void list_adapters_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       char **adapters;
+       int i, num_adapters;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, &error,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                                               &adapters, &num_adapters,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for adapter list");
+               goto done;
+       }
+
+       for (i = 0; i < num_adapters; i++)
+               add_adapter(connection, adapters[i]);
+
+       g_strfreev(adapters);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static void unregister_device(gpointer data)
+{
+       struct connman_device *device = data;
+
+       DBG("");
+
+       remove_device_networks(device);
+
+       connman_device_unregister(device);
+       connman_device_unref(device);
+}
+
+static void remove_network(gpointer data)
+{
+       struct connman_network *network = data;
+       struct connman_device *device;
+
+       DBG("network %p", network);
+
+       device = connman_network_get_device(network);
+       if (device != NULL)
+               connman_device_remove_network(device, network);
+
+       connman_network_unref(network);
+}
+
+static void bluetooth_connect(DBusConnection *connection, void *user_data)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("connection %p", connection);
+
+       bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, unregister_device);
+
+       bluetooth_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_network);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
+                               BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get Bluetooth adapters");
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       if (bluetooth_devices == NULL)
+               return;
+
+       g_hash_table_destroy(bluetooth_networks);
+       bluetooth_networks = NULL;
+       g_hash_table_destroy(bluetooth_devices);
+       bluetooth_devices = NULL;
+}
+
+static int bluetooth_probe(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       if (bluetooth_devices == NULL)
+               return -ENOTSUP;
+
+       g_hash_table_iter_init(&iter, bluetooth_devices);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_device *device_pan = value;
+
+               if (device == device_pan)
+                       return 0;
+       }
+
+       return -ENOTSUP;
+}
+
+static void bluetooth_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static void powered_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       add_adapter(connection, user_data);
+}
+
+static int change_powered(DBusConnection *connection, const char *path,
+                                                       dbus_bool_t powered)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, "Powered",
+                                               DBUS_TYPE_BOOLEAN, &powered);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change Powered property");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, powered_reply,
+                                       g_strdup(path), g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int bluetooth_enable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+
+       DBG("device %p", device);
+
+       return change_powered(connection, path, TRUE);
+}
+
+static int bluetooth_disable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+
+       DBG("device %p", device);
+
+       return change_powered(connection, path, FALSE);
+}
+
+static struct connman_device_driver bluetooth_driver = {
+       .name           = "bluetooth",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = bluetooth_probe,
+       .remove         = bluetooth_remove,
+       .enable         = bluetooth_enable,
+       .disable        = bluetooth_disable,
+};
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+}
+
+static void server_register_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       connman_technology_tethering_notify(technology, TRUE);
+}
+
+static void server_unregister_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusError error;
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               dbus_message_unref(reply);
+               dbus_pending_call_unref(call);
+               return;
+       }
+
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+
+       connman_technology_tethering_notify(technology, FALSE);
+}
+
+
+static void server_register(const char *path, const char *uuid,
+                               struct connman_technology *technology,
+                               const char *bridge, connman_bool_t enabled)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+       char *command;
+
+       DBG("path %s enabled %d", path, enabled);
+
+       command = enabled ? REGISTER : UNREGISTER;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_SERVER, command);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID);
+
+       if (enabled == TRUE)
+               dbus_message_append_args(message, DBUS_TYPE_STRING, &bridge,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to enable PAN server");
+               dbus_message_unref(message);
+               return;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return;
+       }
+
+       if (enabled == TRUE)
+               dbus_pending_call_set_notify(call, server_register_reply,
+                                               technology, NULL);
+       else
+               dbus_pending_call_set_notify(call, server_unregister_reply,
+                                               technology, NULL);
+
+       dbus_message_unref(message);
+}
+
+struct tethering_info {
+       struct connman_technology *technology;
+       const char *bridge;
+};
+
+static void enable_nap(gpointer key, gpointer value, gpointer user_data)
+{
+       struct tethering_info *info = user_data;
+       struct connman_device *device = value;
+       const char *path;
+
+       DBG("");
+
+       path = connman_device_get_string(device, "Path");
+
+       server_register(path, "nap", info->technology, info->bridge, TRUE);
+}
+
+static void disable_nap(gpointer key, gpointer value, gpointer user_data)
+{
+       struct tethering_info *info = user_data;
+       struct connman_device *device = value;
+       const char *path;
+
+       DBG("");
+
+       path = connman_device_get_string(device, "Path");
+
+       server_register(path, "nap", info->technology, info->bridge, FALSE);
+}
+
+static int tech_set_tethering(struct connman_technology *technology,
+                               const char *identifier, const char *passphrase,
+                               const char *bridge, connman_bool_t enabled)
+{
+       struct tethering_info info = {
+               .technology     = technology,
+               .bridge         = bridge,
+       };
+
+       DBG("bridge %s", bridge);
+
+       if (enabled)
+               g_hash_table_foreach(bluetooth_devices, enable_nap, &info);
+       else
+               g_hash_table_foreach(bluetooth_devices, disable_nap, &info);
+
+       return 0;
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "bluetooth",
+       .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+       .set_tethering  = tech_set_tethering,
+};
+
+static guint watch;
+static guint added_watch;
+static guint removed_watch;
+static guint adapter_watch;
+static guint device_watch;
+static guint device_removed_watch;
+static guint network_watch;
+
+static int bluetooth_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
+                       bluetooth_connect, bluetooth_disconnect, NULL, NULL);
+
+       added_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_ADDED, adapter_added,
+                                               NULL, NULL);
+
+       removed_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_REMOVED, adapter_removed,
+                                               NULL, NULL);
+
+       adapter_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
+                                               NULL, BLUEZ_ADAPTER_INTERFACE,
+                                               PROPERTY_CHANGED, adapter_changed,
+                                               NULL, NULL);
+
+       device_removed_watch = g_dbus_add_signal_watch(connection,
+                                               BLUEZ_SERVICE, NULL,
+                                               BLUEZ_ADAPTER_INTERFACE,
+                                               DEVICE_REMOVED, device_removed,
+                                               NULL, NULL);
+
+       device_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
+                                               BLUEZ_DEVICE_INTERFACE,
+                                               PROPERTY_CHANGED, device_changed,
+                                               NULL, NULL);
+
+       network_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
+                                               NULL, BLUEZ_NETWORK_INTERFACE,
+                                               PROPERTY_CHANGED, network_changed,
+                                               NULL, NULL);
+
+       if (watch == 0 || added_watch == 0 || removed_watch == 0
+                       || adapter_watch == 0 || network_watch == 0
+                               || device_watch == 0
+                                       || device_removed_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&pan_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&bluetooth_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&pan_driver);
+               goto remove;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&bluetooth_driver);
+               connman_network_driver_unregister(&pan_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, adapter_watch);
+       g_dbus_remove_watch(connection, device_removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+       g_dbus_remove_watch(connection, network_watch);
+
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void bluetooth_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, adapter_watch);
+       g_dbus_remove_watch(connection, device_removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+       g_dbus_remove_watch(connection, network_watch);
+
+       bluetooth_disconnect(connection, NULL);
+
+       connman_technology_driver_unregister(&tech_driver);
+
+       connman_device_driver_unregister(&bluetooth_driver);
+       connman_network_driver_unregister(&pan_driver);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)
diff --git a/plugins/connman-nmcompat.conf b/plugins/connman-nmcompat.conf
new file mode 100644 (file)
index 0000000..5887a34
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.freedesktop.NetworkManager"/>
+        <allow send_destination="org.freedesktop.NetworkManager"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="org.freedesktop.NetworkManager"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="org.freedesktop.NetworkManager"/>
+    </policy>
+</busconfig>
diff --git a/plugins/dundee.c b/plugins/dundee.c
new file mode 100644 (file)
index 0000000..d0ecdef
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BMW Car IT GmbH. 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 <string.h>
+#include <errno.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/network.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+
+#define DUNDEE_SERVICE                 "org.ofono.dundee"
+#define DUNDEE_MANAGER_INTERFACE       DUNDEE_SERVICE ".Manager"
+#define DUNDEE_DEVICE_INTERFACE                DUNDEE_SERVICE ".Device"
+
+#define DEVICE_ADDED                   "DeviceAdded"
+#define DEVICE_REMOVED                 "DeviceRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
+
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+#define GET_DEVICES                    "GetDevices"
+
+#define TIMEOUT 40000
+
+static DBusConnection *connection;
+
+static GHashTable *dundee_devices = NULL;
+
+struct dundee_data {
+       char *path;
+       char *name;
+
+       struct connman_device *device;
+       struct connman_network *network;
+
+       connman_bool_t active;
+
+       int index;
+
+       /* IPv4 Settings */
+       enum connman_ipconfig_method method;
+       struct connman_ipaddress *address;
+       char *nameservers;
+
+       DBusPendingCall *call;
+};
+
+static char *get_ident(const char *path)
+{
+       char *pos;
+
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
+}
+
+static void create_device(struct dundee_data *info)
+{
+       struct connman_device *device;
+       char *ident;
+
+       DBG("%s", info->path);
+
+       ident = g_strdup(get_ident(info->path));
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (device == NULL)
+               goto out;
+
+       DBG("device %p", device);
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", info->path);
+
+       connman_device_set_data(device, info);
+
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register DUN device");
+               connman_device_unref(device);
+               goto out;
+       }
+
+       info->device = device;
+
+out:
+       g_free(ident);
+}
+
+static void destroy_device(struct dundee_data *info)
+{
+       connman_device_set_powered(info->device, FALSE);
+
+       if (info->call != NULL)
+               dbus_pending_call_cancel(info->call);
+
+       if (info->network != NULL) {
+               connman_device_remove_network(info->device, info->network);
+               connman_network_unref(info->network);
+               info->network = NULL;
+       }
+
+       connman_device_unregister(info->device);
+       connman_device_unref(info->device);
+
+       info->device = NULL;
+}
+
+static void device_destroy(gpointer data)
+{
+       struct dundee_data *info = data;
+
+       if (info->device != NULL)
+               destroy_device(info);
+
+       g_free(info->path);
+       g_free(info->name);
+
+       g_free(info);
+}
+
+static void create_network(struct dundee_data *info)
+{
+       struct connman_network *network;
+       const char *group;
+
+       DBG("%s", info->path);
+
+       network = connman_network_create(info->path,
+                               CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
+       if (network == NULL)
+               return;
+
+       DBG("network %p", network);
+
+       connman_network_set_data(network, info);
+
+       connman_network_set_string(network, "Path",
+                               info->path);
+
+       connman_network_set_name(network, info->name);
+
+       group = get_ident(info->path);
+       connman_network_set_group(network, group);
+
+       connman_network_set_available(network, TRUE);
+
+       if (connman_device_add_network(info->device, network) < 0) {
+               connman_network_unref(network);
+               return;
+       }
+
+       info->network = network;
+}
+
+static void set_connected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_inet_ifup(info->index);
+
+       connman_network_set_index(info->network, info->index);
+       connman_network_set_ipv4_method(info->network,
+                                       CONNMAN_IPCONFIG_METHOD_FIXED);
+       connman_network_set_ipaddress(info->network, info->address);
+       connman_network_set_nameservers(info->network, info->nameservers);
+
+       connman_network_set_connected(info->network, TRUE);
+}
+
+static void set_disconnected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_network_set_connected(info->network, FALSE);
+       connman_inet_ifdown(info->index);
+}
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct dundee_data *info = user_data;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s", info->path);
+
+       info->call = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to change property: %s %s %s",
+                               info->path, error.name, error.message);
+               dbus_error_free(&error);
+
+               connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+       }
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int set_property(struct dundee_data *info,
+                       const char *property, int type, void *value)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+
+       DBG("%s %s", info->path, property);
+
+       message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
+                                       DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, property, type, value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &info->call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s",
+                               info->path, property);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (info->call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(info->call, set_property_reply,
+                                       info, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int device_set_active(struct dundee_data *info)
+{
+       dbus_bool_t active = TRUE;
+
+       DBG("%s", info->path);
+
+       return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+}
+
+static int device_set_inactive(struct dundee_data *info)
+{
+       dbus_bool_t active = FALSE;
+       int err;
+
+       DBG("%s", info->path);
+
+       err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+       if (err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static int network_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       struct dundee_data *info = connman_network_get_data(network);
+
+       DBG("network %p", network);
+
+       return device_set_active(info);
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       struct dundee_data *info = connman_network_get_data(network);
+
+       DBG("network %p", network);
+
+       return device_set_inactive(info);
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "network",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int dundee_probe(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       if (dundee_devices == NULL)
+               return -ENOTSUP;
+
+       g_hash_table_iter_init(&iter, dundee_devices);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct dundee_data *info = value;
+
+               if (device == info->device)
+                       return 0;
+       }
+
+       return -ENOTSUP;
+}
+
+static void dundee_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static int dundee_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static int dundee_disable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static struct connman_device_driver dundee_driver = {
+       .name           = "dundee",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = dundee_probe,
+       .remove         = dundee_remove,
+       .enable         = dundee_enable,
+       .disable        = dundee_disable,
+};
+
+static char *extract_nameservers(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       char *nameservers = NULL;
+       char *tmp;
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *nameserver;
+
+               dbus_message_iter_get_basic(&entry, &nameserver);
+
+               if (nameservers == NULL) {
+                       nameservers = g_strdup(nameserver);
+               } else {
+                       tmp = nameservers;
+                       nameservers = g_strdup_printf("%s %s", tmp, nameserver);
+                       g_free(tmp);
+               }
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return nameservers;
+}
+
+static void extract_settings(DBusMessageIter *array,
+                               struct dundee_data *info)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *gateway = NULL;
+       char *nameservers = NULL;
+       const char *interface = NULL;
+       int index = -1;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("Interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+
+                       DBG("index %d", index);
+
+                       if (index < 0)
+                               break;
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("Address %s", address);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
+
+                       DBG("Nameservers %s", nameservers);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("Gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (index < 0)
+               goto out;
+
+       info->address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (info->address == NULL)
+               goto out;
+
+       info->index = index;
+       connman_ipaddress_set_ipv4(info->address, address, NULL, gateway);
+
+       info->nameservers = nameservers;
+
+out:
+       if (info->nameservers != nameservers)
+               g_free(nameservers);
+
+       g_free(address);
+       g_free(gateway);
+}
+
+static gboolean device_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct dundee_data *info = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
+       const char *signature = DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       info = g_hash_table_lookup(dundee_devices, path);
+       if (info == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       /*
+        * Dundee guarantees the ordering of Settings and
+        * Active. Settings will always be send before Active = True.
+        * That means we don't have to order here.
+        */
+       if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value, &info->active);
+
+               DBG("%s Active %d", info->path, info->active);
+
+               if (info->active == TRUE)
+                       set_connected(info);
+               else
+                       set_disconnected(info);
+       } else if (g_str_equal(key, "Settings") == TRUE) {
+               DBG("%s Settings", info->path);
+
+               extract_settings(&value, info);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               char *name;
+
+               dbus_message_iter_get_basic(&value, &name);
+
+               g_free(info->name);
+               info->name = g_strdup(name);
+
+               DBG("%s Name %s", info->path, info->name);
+
+               connman_network_set_name(info->network, info->name);
+               connman_network_update(info->network);
+       }
+
+       return TRUE;
+}
+
+static void add_device(const char *path, DBusMessageIter *properties)
+{
+       struct dundee_data *info;
+
+       info = g_hash_table_lookup(dundee_devices, path);
+       if (info != NULL)
+               return;
+
+       info = g_try_new0(struct dundee_data, 1);
+       if (info == NULL)
+               return;
+
+       info->path = g_strdup(path);
+
+       while (dbus_message_iter_get_arg_type(properties) ==
+                       DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(properties, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Active") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &info->active);
+
+                       DBG("%s Active %d", info->path, info->active);
+               } else if (g_str_equal(key, "Settings") == TRUE) {
+                       DBG("%s Settings", info->path);
+
+                       extract_settings(&value, info);
+               } else if (g_str_equal(key, "Name") == TRUE) {
+                       char *name;
+
+                       dbus_message_iter_get_basic(&value, &name);
+
+                       info->name = g_strdup(name);
+
+                       DBG("%s Name %s", info->path, info->name);
+               }
+
+               dbus_message_iter_next(properties);
+       }
+
+       g_hash_table_insert(dundee_devices, g_strdup(path), info);
+
+       create_device(info);
+       create_network(info);
+
+       if (info->active == TRUE)
+               set_connected(info);
+}
+
+static gboolean device_added(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+       const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
+               DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       add_device(path, &properties);
+
+       return TRUE;
+}
+
+static void remove_device(DBusConnection *connection, const char *path)
+{
+       DBG("path %s", path);
+
+       g_hash_table_remove(dundee_devices, path);
+}
+
+static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path;
+       const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       remove_device(connection, path);
+       return TRUE;
+}
+
+static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_OBJECT_PATH_AS_STRING
+               DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+               DBUS_STRUCT_END_CHAR_AS_STRING;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_has_signature(reply, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               goto done;
+       }
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter value, properties;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict, &value);
+               dbus_message_iter_get_basic(&value, &path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value, &properties);
+
+               add_device(path, &properties);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int manager_get_devices(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
+                                       DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetDevices()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, manager_get_devices_reply,
+                                       NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void dundee_connect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       g_free, device_destroy);
+
+       manager_get_devices();
+}
+
+static void dundee_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       g_hash_table_destroy(dundee_devices);
+       dundee_devices = NULL;
+}
+
+static guint watch;
+static guint added_watch;
+static guint removed_watch;
+static guint device_watch;
+
+static int dundee_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
+                       dundee_connect, dundee_disconnect, NULL, NULL);
+
+       added_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE, NULL,
+                                               DUNDEE_MANAGER_INTERFACE,
+                                               DEVICE_ADDED, device_added,
+                                               NULL, NULL);
+
+       removed_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
+                                               NULL, DUNDEE_MANAGER_INTERFACE,
+                                               DEVICE_REMOVED, device_removed,
+                                               NULL, NULL);
+
+       device_watch = g_dbus_add_signal_watch(connection, DUNDEE_SERVICE,
+                                               NULL, DUNDEE_DEVICE_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               device_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
+                       device_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&dundee_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void dundee_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, added_watch);
+       g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
+
+       connman_device_driver_unregister(&dundee_driver);
+       connman_network_driver_unregister(&network_driver);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)
diff --git a/plugins/ethernet.c b/plugins/ethernet.c
new file mode 100644 (file)
index 0000000..fbfff63
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ *
+ *  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 <errno.h>
+#include <net/if.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000
+#endif
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/technology.h>
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/rtnl.h>
+#include <connman/log.h>
+
+struct ethernet_data {
+       int index;
+       unsigned flags;
+       unsigned int watch;
+       struct connman_network *network;
+};
+
+static int cable_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void cable_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static int cable_connect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       connman_network_set_connected(network, TRUE);
+
+       return 0;
+}
+
+static int cable_disconnect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       connman_network_set_connected(network, FALSE);
+
+       return 0;
+}
+
+static struct connman_network_driver cable_driver = {
+       .name           = "cable",
+       .type           = CONNMAN_NETWORK_TYPE_ETHERNET,
+       .probe          = cable_probe,
+       .remove         = cable_remove,
+       .connect        = cable_connect,
+       .disconnect     = cable_disconnect,
+};
+
+static void add_network(struct connman_device *device,
+                       struct ethernet_data *ethernet)
+{
+       struct connman_network *network;
+       int index;
+
+       network = connman_network_create("carrier",
+                                       CONNMAN_NETWORK_TYPE_ETHERNET);
+       if (network == NULL)
+               return;
+
+       index = connman_device_get_index(device);
+       connman_network_set_index(network, index);
+
+       connman_network_set_name(network, "Wired");
+
+       if (connman_device_add_network(device, network) < 0) {
+               connman_network_unref(network);
+               return;
+       }
+
+       connman_network_set_available(network, TRUE);
+
+       connman_network_set_group(network, "cable");
+
+       ethernet->network = network;
+}
+
+static void remove_network(struct connman_device *device,
+                               struct ethernet_data *ethernet)
+{
+       if (ethernet->network == NULL)
+               return;
+
+       connman_device_remove_network(device, ethernet->network);
+       connman_network_unref(ethernet->network);
+
+       ethernet->network = NULL;
+}
+
+static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("index %d flags %d change %d", ethernet->index, flags, change);
+
+       if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
+               if (flags & IFF_UP) {
+                       DBG("power on");
+                       connman_device_set_powered(device, TRUE);
+               } else {
+                       DBG("power off");
+                       connman_device_set_powered(device, FALSE);
+               }
+       }
+
+       if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
+               if (flags & IFF_LOWER_UP) {
+                       DBG("carrier on");
+                       add_network(device, ethernet);
+               } else {
+                       DBG("carrier off");
+                       remove_network(device, ethernet);
+               }
+       }
+
+       ethernet->flags = flags;
+}
+
+static int ethernet_probe(struct connman_device *device)
+{
+       struct ethernet_data *ethernet;
+
+       DBG("device %p", device);
+
+       ethernet = g_try_new0(struct ethernet_data, 1);
+       if (ethernet == NULL)
+               return -ENOMEM;
+
+       connman_device_set_data(device, ethernet);
+
+       ethernet->index = connman_device_get_index(device);
+       ethernet->flags = 0;
+
+       ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
+                                               ethernet_newlink, device);
+
+       return 0;
+}
+
+static void ethernet_remove(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       connman_device_set_data(device, NULL);
+
+       connman_rtnl_remove_watch(ethernet->watch);
+
+       remove_network(device, ethernet);
+
+       g_free(ethernet);
+}
+
+static int ethernet_enable(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       return connman_inet_ifup(ethernet->index);
+}
+
+static int ethernet_disable(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       return connman_inet_ifdown(ethernet->index);
+}
+
+static struct connman_device_driver ethernet_driver = {
+       .name           = "ethernet",
+       .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
+       .probe          = ethernet_probe,
+       .remove         = ethernet_remove,
+       .enable         = ethernet_enable,
+       .disable        = ethernet_disable,
+};
+
+static GList *cdc_interface_list = NULL;
+
+static void tech_add_interface(struct connman_technology *technology,
+                       int index, const char *name, const char *ident)
+{
+       DBG("index %d name %s ident %s", index, name, ident);
+
+       if (g_list_find(cdc_interface_list,
+                       GINT_TO_POINTER((int) index)) != NULL)
+               return;
+
+       cdc_interface_list = g_list_prepend(cdc_interface_list,
+                                       (GINT_TO_POINTER((int) index)));
+}
+
+static void tech_remove_interface(struct connman_technology *technology,
+                                                               int index)
+{
+       DBG("index %d", index);
+
+       cdc_interface_list = g_list_remove(cdc_interface_list,
+                                       GINT_TO_POINTER((int) index));
+}
+
+static void enable_tethering(struct connman_technology *technology,
+                                               const char *bridge)
+{
+       GList *list;
+
+       for (list = cdc_interface_list; list; list = list->next) {
+               int index = GPOINTER_TO_INT(list->data);
+
+               connman_technology_tethering_notify(technology, TRUE);
+
+               connman_inet_ifup(index);
+
+               connman_inet_add_to_bridge(index, bridge);
+       }
+}
+
+static void disable_tethering(struct connman_technology *technology,
+                                               const char *bridge)
+{
+       GList *list;
+
+       for (list = cdc_interface_list; list; list = list->next) {
+               int index = GPOINTER_TO_INT(list->data);
+
+               connman_inet_remove_from_bridge(index, bridge);
+
+               connman_inet_ifdown(index);
+
+               connman_technology_tethering_notify(technology, FALSE);
+       }
+}
+
+static int tech_set_tethering(struct connman_technology *technology,
+                               const char *identifier, const char *passphrase,
+                               const char *bridge, connman_bool_t enabled)
+{
+       DBG("bridge %s enabled %d", bridge, enabled);
+
+       if (enabled)
+               enable_tethering(technology, bridge);
+       else
+               disable_tethering(technology, bridge);
+
+       return 0;
+}
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+       g_list_free(cdc_interface_list);
+
+       cdc_interface_list = NULL;
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name                   = "cdc_ethernet",
+       .type                   = CONNMAN_SERVICE_TYPE_GADGET,
+       .probe                  = tech_probe,
+       .remove                 = tech_remove,
+       .add_interface          = tech_add_interface,
+       .remove_interface       = tech_remove_interface,
+       .set_tethering          = tech_set_tethering,
+};
+
+static int eth_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void eth_remove(struct connman_technology *technology)
+{
+       DBG("");
+}
+
+static struct connman_technology_driver eth_driver = {
+       .name                   = "ethernet",
+       .type                   = CONNMAN_SERVICE_TYPE_ETHERNET,
+       .probe                  = eth_probe,
+       .remove                 = eth_remove,
+};
+
+static int ethernet_init(void)
+{
+       int err;
+
+       err = connman_technology_driver_register(&eth_driver);
+       if (err < 0)
+               return err;
+
+       err = connman_network_driver_register(&cable_driver);
+       if (err < 0)
+               return err;
+
+       err = connman_device_driver_register(&ethernet_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&cable_driver);
+               return err;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&ethernet_driver);
+               connman_network_driver_unregister(&cable_driver);
+               return err;
+       }
+
+       return 0;
+}
+
+static void ethernet_exit(void)
+{
+       connman_technology_driver_unregister(&eth_driver);
+
+       connman_technology_driver_unregister(&tech_driver);
+
+       connman_network_driver_unregister(&cable_driver);
+
+       connman_device_driver_unregister(&ethernet_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)
diff --git a/plugins/hh2serial-gps.c b/plugins/hh2serial-gps.c
new file mode 100644 (file)
index 0000000..20521b0
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *
+ *  hh2serial GPS
+ *
+ *  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 <errno.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/log.h>
+
+static struct connman_device *hh2serial_device;
+
+static int set_reg(char *key, char *value)
+{
+       FILE *reg_file;
+
+       reg_file = fopen(key, "w");
+       if (!reg_file)
+               return -errno;
+
+       fprintf(reg_file, "%s", value);
+       fclose(reg_file);
+       return 0;
+}
+
+static int hh2serial_probe(struct connman_device *device)
+{
+       return 0;
+}
+
+static void hh2serial_remove(struct connman_device *device)
+{
+}
+
+static int hh2serial_enable(struct connman_device *device)
+{
+       DBG("");
+
+       set_reg("/sys/class/gpio/expo_rt", "85");
+       set_reg("/sys/class/gpio/gpio85/direction", "out");
+       set_reg("/sys/class/gpio/gpio85/value", "1");
+
+       return 0;
+}
+
+static int hh2serial_disable(struct connman_device *device)
+{
+       DBG("");
+
+       set_reg("/sys/class/gpio/expo_rt", "85");
+       set_reg("/sys/class/gpio/gpio85/direction", "out");
+       set_reg("/sys/class/gpio/gpio85/value", "0");
+
+       return 0;
+}
+
+static struct connman_device_driver hh2seial_device_driver = {
+       .name           = "hh2serial GPS",
+       .type           = CONNMAN_DEVICE_TYPE_GPS,
+       .enable         = hh2serial_enable,
+       .disable        = hh2serial_disable,
+       .probe          = hh2serial_probe,
+       .remove         = hh2serial_remove,
+};
+
+static int hh2serial_init(void)
+{
+       connman_device_driver_register(&hh2seial_device_driver);
+
+       hh2serial_device = connman_device_create("hh2serial_gps",
+                                               CONNMAN_DEVICE_TYPE_GPS);
+       if (hh2serial_device == NULL)
+               return -ENODEV;
+
+       connman_device_register(hh2serial_device);
+
+       return 0;
+}
+
+static void hh2serial_exit(void)
+{
+       if (hh2serial_device != NULL) {
+               connman_device_unregister(hh2serial_device);
+               connman_device_unref(hh2serial_device);
+       }
+}
+
+CONNMAN_PLUGIN_DEFINE(hh2serial_gps, "hh2serial GPS", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_LOW, hh2serial_init, hh2serial_exit)
diff --git a/plugins/iospm.c b/plugins/iospm.c
new file mode 100644 (file)
index 0000000..1ea56a9
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *
+ *  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
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/notifier.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define IOSPM_SERVICE          "com.intel.mid.ospm"
+#define IOSPM_INTERFACE                IOSPM_SERVICE ".Comms"
+
+#define IOSPM_BLUETOOTH                "/com/intel/mid/ospm/bluetooth"
+#define IOSPM_FLIGHT_MODE      "/com/intel/mid/ospm/flight_mode"
+
+static DBusConnection *connection;
+
+static void send_indication(const char *path, connman_bool_t enabled)
+{
+       DBusMessage *message;
+       const char *method;
+
+       DBG("path %s enabled %d", path, enabled);
+
+       if (enabled == TRUE)
+               method = "IndicateStart";
+       else
+               method = "IndicateStop";
+
+       message = dbus_message_new_method_call(IOSPM_SERVICE, path,
+                                               IOSPM_INTERFACE, method);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+}
+
+static void iospm_service_enabled(enum connman_service_type type,
+                                               connman_bool_t enabled)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               send_indication(IOSPM_BLUETOOTH, enabled);
+               break;
+       }
+}
+
+static void iospm_offline_mode(connman_bool_t enabled)
+{
+       send_indication(IOSPM_FLIGHT_MODE, enabled);
+}
+
+static struct connman_notifier iospm_notifier = {
+       .name           = "iospm",
+       .priority       = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+       .service_enabled= iospm_service_enabled,
+       .offline_mode   = iospm_offline_mode,
+};
+
+static int iospm_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return connman_notifier_register(&iospm_notifier);
+}
+
+static void iospm_exit(void)
+{
+       connman_notifier_unregister(&iospm_notifier);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(ospm, "Intel OSPM notification plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, iospm_init, iospm_exit)
diff --git a/plugins/iwmx.c b/plugins/iwmx.c
new file mode 100644 (file)
index 0000000..7c1e792
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/log.h>
+
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/*
+ * Connman plugin interface
+ *
+ * This part deals with the connman internals
+ */
+
+/* WiMAX network driver probe/remove, nops */
+static int iwmx_cm_network_probe(struct connman_network *nw)
+{
+       return 0;
+}
+
+static void iwmx_cm_network_remove(struct connman_network *nw)
+{
+}
+
+/*
+ * Called by connman when it wants us to tell the device to connect to
+ * the network @network_el; the device is @network_el->parent.
+ *
+ * We do a synchronous call to start the connection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_connect(struct connman_network *nw)
+{
+       int result;
+       struct wmxsdk *wmxsdk;
+       const char *station_name = connman_network_get_identifier(nw);
+
+       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+       result = iwmx_sdk_connect(wmxsdk, nw);
+       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+       return result;
+}
+
+/*
+ * Called by connman to have the device @nw->parent
+ * disconnected from @nw.
+ *
+ * We do a synchronous call to start the disconnection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_disconnect(struct connman_network *nw)
+{
+       int result;
+       struct wmxsdk *wmxsdk;
+       const char *station_name = connman_network_get_identifier(nw);
+
+       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+       result = iwmx_sdk_disconnect(wmxsdk);
+       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+       return 0;
+}
+
+/*
+ * "Driver" for the networks detected by a device.
+ */
+static struct connman_network_driver iwmx_cm_network_driver = {
+       .name           = "iwmx",
+       .type           = CONNMAN_NETWORK_TYPE_WIMAX,
+       .probe          = iwmx_cm_network_probe,
+       .remove         = iwmx_cm_network_remove,
+       .connect        = iwmx_cm_network_connect,
+       .disconnect     = iwmx_cm_network_disconnect,
+};
+
+/*
+ * A (maybe) new network is available, create/update its data
+ *
+ * If the network is new, we create and register a new element; if it
+ * is not, we reuse the one in the list.
+ *
+ * NOTE:
+ *   wmxsdk->network_mutex has to be locked
+ */
+struct connman_network *__iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength)
+{
+       struct connman_network *nw = NULL;
+       struct connman_device *dev = wmxsdk->dev;
+       char group[3 * strlen(station_name) + 1];
+       unsigned cnt;
+
+       nw = connman_device_get_network(dev, station_name);
+       if (nw == NULL) {
+               DBG("new network %s", station_name);
+               nw = connman_network_create(station_name,
+                                           CONNMAN_NETWORK_TYPE_WIMAX);
+               connman_network_register(nw);
+               connman_network_set_index(nw, connman_device_get_index(dev));
+               connman_network_set_name(nw, station_name);
+               connman_network_set_blob(nw, "WiMAX.NSP.name",
+                                        sdk_nspname, sdk_nspname_size);
+               /* FIXME: add roaming info? */
+               /* Set the group name -- this has to be a unique
+                * [a-zA-Z0-9_] string common to all the networks that
+                * are actually the same provider. In WiMAX each
+                * network from the CAPI is a single provider, so we
+                * just set this as the network name, encoded in
+                * hex. */
+               for (cnt = 0; station_name[cnt] != 0; cnt++)
+                       sprintf(group + 3 * cnt, "%02x", station_name[cnt]);
+               group[3 * cnt + 1] = 0;
+               connman_network_set_group(nw, station_name);
+               if (connman_device_add_network(dev, nw) < 0) {
+                       connman_network_unregister(nw);
+                       connman_network_unref(nw);
+                       goto error_add;
+               }
+       } else
+               DBG("updating network %s nw %p\n", station_name, nw);
+       connman_network_set_available(nw, TRUE);
+       connman_network_set_strength(nw, strength);
+error_add:
+       return nw;
+}
+
+/*
+ * A new network is available [locking version]
+ *
+ * See __iwmx_cm_network_available() for docs
+ */
+struct connman_network *iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength)
+{
+       struct connman_network *nw;
+
+       g_static_mutex_lock(&wmxsdk->network_mutex);
+       nw = __iwmx_cm_network_available(wmxsdk, station_name,
+                                       sdk_nspname, sdk_nspname_size,
+                                       strength);
+       g_static_mutex_unlock(&wmxsdk->network_mutex);
+       return nw;
+}
+
+/*
+ * The device has been enabled, make sure connman knows
+ */
+static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk)
+{
+       struct connman_device *dev = wmxsdk->dev;
+       connman_inet_ifup(connman_device_get_index(dev));
+       connman_device_set_powered(dev, TRUE);
+}
+
+/*
+ * The device has been disabled, make sure connman is aware of it.
+ */
+static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk)
+{
+       struct connman_device *dev = wmxsdk->dev;
+       connman_inet_ifdown(connman_device_get_index(dev));
+       connman_device_set_powered(dev, FALSE);
+}
+
+/*
+ * The device has been (externally to connman) connnected to a
+ * network, make sure connman knows.
+ *
+ * When the device is connected to a network, this function is called
+ * to change connman's internal state to reflect the fact.
+ *
+ * If the change came from an external entity, that means that our
+ * connect code wasn't called. Our connect code sets
+ * @wmxsdk->connecting_nw to the network we were connecting
+ * to. If it is unset, it means an external entity forced the device
+ * to connect. In that case, we need to find out which network it was
+ * connected to, and create/lookup a @nw for it.
+ *
+ * Once the nw is set, then we are done.
+ */
+static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       nw = wmxsdk->connecting_nw;
+       if (nw == NULL) {
+               nw = __iwmx_sdk_get_connected_network(wmxsdk);
+               if (nw == NULL) {
+                       connman_error("wmxsdk: can't find connected network\n");
+                       goto error_nw_find;
+               }
+       }
+       wmxsdk->nw = connman_network_ref(nw);
+       wmxsdk->connecting_nw = NULL;
+       connman_network_set_ipv4_method(nw, CONNMAN_IPCONFIG_METHOD_DHCP);
+       connman_network_set_connected(nw, TRUE);
+       DBG("connected to network %s\n",
+           connman_network_get_identifier(nw));
+error_nw_find:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * The device has been (externally to connman) disconnnected, make
+ * sure connman knows
+ *
+ * We need to reverse the steps done in iwmx_cm_dev_connected().
+ * If the event was caused by an external entity and we had no record
+ * of being connected to a network...well, bad luck. We'll just
+ * pretend it happened ok.
+ */
+static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw = wmxsdk->nw;
+
+       if (nw != NULL) {
+               DBG("disconnected from network %s\n",
+                                       connman_network_get_identifier(nw));
+               connman_network_set_connected(nw, FALSE);
+               connman_network_unregister(nw);
+               connman_network_unref(nw);
+               wmxsdk->nw = NULL;
+       } else
+               DBG("disconnected from unknown network\n");
+}
+
+/*
+ * The device has been disconnnected, make sure connman knows
+ *
+ * See __iwmx_cm_dev_disconnect() for more information.
+ */
+static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+       g_mutex_lock(wmxsdk->connect_mutex);
+       __iwmx_cm_dev_disconnected(wmxsdk);
+       g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * Handle a change in state
+ *
+ * This is were most of the action happens. When the device changes
+ * state, this will catch it (through the state change callback or an
+ * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what
+ * happened.
+ *
+ * Finally, cache the new device status.
+ */
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status)
+{
+       WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status;
+       WIMAX_API_DEVICE_STATUS old_status;
+       WIMAX_API_DEVICE_STATUS new_status;
+
+       /*
+        * Simplify state transition computations.
+        *
+        * For practical effects, some states are the same
+        */
+
+#if HAVE_IWMXSDK_STATUS_IDLE
+       /* Conection_Idle is the same as Data_Connected */
+       if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+               old_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+       else
+               old_status = __old_status;
+       if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+               new_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+       else
+               new_status = __new_status;
+#endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
+       /* Radio off: all are just RF_OFF_SW (the highest) */
+       switch (__old_status) {
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+               break;
+       default:
+               old_status = __old_status;
+               break;
+       }
+
+       switch (__new_status) {
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+               break;
+       default:
+               new_status = __new_status;
+               break;
+       }
+
+       /* If no real state change, do nothing */
+       if (old_status == new_status) {
+               DBG("no state changed\n");
+               return;
+       } else
+               DBG("state change from %d (%d: %s) to %d (%d: %s)\n",
+                   old_status, __old_status,
+                   iwmx_sdk_dev_status_to_str(__old_status),
+                   new_status, __new_status,
+                   iwmx_sdk_dev_status_to_str(__new_status));
+
+       /* Cleanup old state */
+       switch (old_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               /* This means the plugin is starting but the device is
+                * in some state already, so we need to update our
+                * internal knowledge of it. */
+               if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+                       iwmx_cm_dev_enabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               /* This means the radio is being turned on, so enable
+                * the device ( unless going to uninitialized). */
+               if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+                       iwmx_cm_dev_enabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               iwmx_cm_dev_disconnected(wmxsdk);
+               break;
+       default:
+               connman_error("wmxsdk: unknown old status %d\n", old_status);
+               return;
+       };
+
+       /* Implement new state */
+       switch (new_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               /* This means the radio is being turned off, so
+                * disable the device unless coming from uninitialized. */
+               if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized)
+                       iwmx_cm_dev_disabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               iwmx_cm_dev_connected(wmxsdk);
+               break;
+       default:
+               connman_error("wmxsdk: unknown new status %d\n", old_status);
+               return;
+       };
+       wmxsdk->status = __new_status;
+}
+
+/*
+ * Implement a device state transition [locking version]
+ *
+ * See __iwmx_cm_state_change()
+ */
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                WIMAX_API_DEVICE_STATUS __new_status)
+{
+       g_mutex_lock(wmxsdk->status_mutex);
+       __iwmx_cm_state_change(wmxsdk, __new_status);
+       g_mutex_unlock(wmxsdk->status_mutex);
+}
+
+/*
+ * Read the cached device status
+ */
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk)
+{
+       WIMAX_API_DEVICE_STATUS status;
+
+       g_mutex_lock(wmxsdk->status_mutex);
+       status = wmxsdk->status;
+       g_mutex_unlock(wmxsdk->status_mutex);
+       return status;
+}
+
+/*
+ * Called by connman when a device is enabled by the user
+ *
+ * We need to turn the radio on; the state change function will poke
+ * the internals.
+ */
+static int iwmx_cm_enable(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+       connman_inet_ifup(connman_device_get_index(dev));
+       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON);
+       return result;
+}
+
+/*
+ * Called by connman when a device is disabled by the user
+ *
+ * Simple: just make sure the radio is off; the state change function
+ * will poke the internals.
+ */
+static int iwmx_cm_disable(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF);
+       connman_inet_ifdown(connman_device_get_index(dev));
+       return 0;
+}
+
+/*
+ * Probe deferred call from when the mainloop is idle
+ *
+ * probe() schedules this to be called from the mainloop when idle to
+ * do a device status evaluation. Needed because of an internal race
+ * condition in connman. FIXME: deploy into _probe() when fixed.
+ */
+static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk)
+{
+       int result;
+       struct wmxsdk *wmxsdk = _wmxsdk;
+       result = iwmx_sdk_get_device_status(wmxsdk);
+       if (result < 0)
+               connman_error("wmxsdk: can't get status: %d\n", result);
+       else
+               iwmx_cm_state_change(wmxsdk, result);
+       return FALSE;
+}
+
+/*
+ * Called by connman when a new device pops in
+ *
+ * We allocate our private structure, register with the WiMAX API,
+ * open their device, subscribe to all the callbacks.
+ *
+ * At the end, we launch a deferred call (to work around current
+ * connman issues that need to be fixed in the future) and update the
+ * device's status. This allows us to pick up the current status and
+ * adapt connman's idea of the device to it.
+ */
+static int iwmx_cm_probe(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = NULL;
+
+       wmxsdk = connman_device_get_data(dev);
+       if (wmxsdk == NULL)
+               /* not called from a discovery done by the WiMAX
+                * Network Service, ignore */
+               return -ENODEV;
+
+       result = iwmx_sdk_setup(wmxsdk);
+       if (result < 0)
+               goto error_setup;
+
+       /* There is a race condition in the connman core that doesn't
+        * allow us to call this directly and things to work properly
+        * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */
+       g_idle_add(__iwmx_cm_probe_dpc, wmxsdk);
+       return 0;
+
+       iwmx_sdk_remove(wmxsdk);
+error_setup:
+       return result;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in _probe. Remove callbacks, unregister
+ * from the WiMAX API.
+ */
+static void iwmx_cm_remove(struct connman_device *dev)
+{
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+       iwmx_sdk_remove(wmxsdk);
+}
+
+/*
+ * Called by connman to ask the device to scan for networks
+ *
+ * We have set in the WiMAX API the scan result callbacks, so we just
+ * start a simple scan (not a wide one).
+ *
+ * First we obtain the current list of networks and pass it to the
+ * callback processor. Then we start an scan cycle.
+ */
+static int iwmx_cm_scan(struct connman_device *dev)
+{
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+       return iwmx_sdk_scan(wmxsdk);
+}
+
+/*
+ * Driver for a WiMAX API based device.
+ */
+static struct connman_device_driver iwmx_cm_device_driver = {
+       .name           = "iwmx",
+       .type           = CONNMAN_DEVICE_TYPE_WIMAX,
+       .probe          = iwmx_cm_probe,
+       .remove         = iwmx_cm_remove,
+       .enable         = iwmx_cm_enable,
+       .disable        = iwmx_cm_disable,
+       .scan           = iwmx_cm_scan,
+};
+
+static int iwmx_cm_init(void)
+{
+       int result;
+
+       result = connman_device_driver_register(&iwmx_cm_device_driver);
+       if (result < 0)
+               goto error_driver_register;
+       result = connman_network_driver_register(&iwmx_cm_network_driver);
+       if (result < 0)
+               goto error_network_driver_register;
+       result = iwmx_sdk_api_init();
+       if (result < 0)
+               goto error_iwmx_sdk_init;
+       return 0;
+
+error_iwmx_sdk_init:
+       connman_network_driver_unregister(&iwmx_cm_network_driver);
+error_network_driver_register:
+       connman_device_driver_unregister(&iwmx_cm_device_driver);
+error_driver_register:
+       return result;
+}
+
+static void iwmx_cm_exit(void)
+{
+       iwmx_sdk_api_exit();
+       connman_network_driver_unregister(&iwmx_cm_network_driver);
+       connman_device_driver_unregister(&iwmx_cm_device_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin",
+                       CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW,
+                                               iwmx_cm_init, iwmx_cm_exit);
diff --git a/plugins/iwmx.h b/plugins/iwmx.h
new file mode 100644 (file)
index 0000000..2ff522c
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *
+ *  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
+ *
+ */
+
+/* Fix source compat brakage from 1.4 to 1.5...*/
+#ifndef HAVE_WIMAX_API_DEVICE_ID
+typedef struct WIMAX_API_DEVICE_ID WIMAX_API_DEVICE_ID;
+#endif
+
+#ifndef HAVE_WIMAX_API_CONNECTED_NSP_INFO
+typedef struct WIMAX_API_CONNECTED_NSP_INFO WIMAX_API_CONNECTED_NSP_INFO;
+#endif
+
+#ifndef HAVE_WIMAX_API_NSP_INFO_EX
+typedef struct WIMAX_API_NSP_INFO_EX WIMAX_API_NSP_INFO_EX;
+#endif
+
+#ifndef HAVE_WIMAX_API_HW_DEVICE_ID
+typedef struct WIMAX_API_HW_DEVICE_ID WIMAX_API_HW_DEVICE_ID;
+#endif
+
+
+/*
+ *
+ * The plugin is broken in two main parts: the glue to connman
+ * (iwmx_cm_*() functions) and the glue to the libiWmxSdk (iwmx_sdk_*()
+ * functions). They connect using a well defined interface.
+ *
+ * The plugin is state based and operates reactively to state
+ * transtitions on the WiMAX device or to user requests, even from
+ * external control tools that are not aware of connman.
+ *
+ * When the user requests connman to do something, it goes into a call
+ * implemented by the 'struct connman_driver iwmx_cm_driver' (or
+ * iwmx_cm_network_driver) that will instruct libiWmxSDK to change the
+ * device's state.
+ *
+ * When the device changes state, a state change callback is sent back
+ * by libiWmxSDK, which gets fed to iwmx_cm_state_change(), which
+ * evaluates the state change and updates connman's internal state in
+ * response.
+ *
+ * This allows the device to be also controlled by external tools
+ * without driving connman out of state.
+ *
+ * Device's state changes can be caused through:
+ *
+ *  - connman (by user request)
+ *
+ *  - any other external utility (eg: WmxSDK's wimaxcu)
+ *
+ *  - external stimuli: network connection broken when going out of
+ *    range
+ *
+ * Functions named __*() normally indicate that require locking. See
+ * their doc header.
+ *
+ * ENUMERATION
+ *
+ * When we receive a normal probe request [iwmx_cm_probe()] from
+ * connman, we ignore it (we can tell based on the connman device
+ * having NULL data).
+ *
+ * The plugin has registered with the WiMAX Network Service and it
+ * will listen to its device add/rm messages [iwmx_sdk_addremove_cb()]
+ * and use that to create a  device [iwmx_sdk_dev_add()] which will be
+ * registered with connman. [iwmx_cm_dev_add()]. Then connman will
+ * enumerate the device, call again iwmx_cm_probe() and at this time,
+ * we'll recognize it, pass through iwmx_sdk_setup() and complete the
+ * probe process.
+ *
+ * If the daemon dies, in theory the plugin will realize and remove
+ * the WiMAX device.
+ */
+
+struct wmxsdk {
+       WIMAX_API_DEVICE_ID device_id;
+       struct connman_device *dev;
+
+       GStaticMutex network_mutex;
+
+       WIMAX_API_DEVICE_STATUS status;
+       GMutex *status_mutex;
+
+       /*
+        * nw points to the network we are connected to. connecting_nw
+        * points to the network we have requested to connect.
+        */
+       GMutex *connect_mutex;
+       struct connman_network *connecting_nw, *nw;
+
+       char name[100];
+       char ifname[16];
+};
+
+/* Initialize a [zeroed] struct wmxsdk */
+static inline void wmxsdk_init(struct wmxsdk *wmxsdk)
+{
+       g_static_mutex_init(&wmxsdk->network_mutex);
+
+       wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
+       wmxsdk->status_mutex = g_mutex_new();
+       g_assert(wmxsdk->status_mutex);
+
+       wmxsdk->connect_mutex = g_mutex_new();
+       g_assert(wmxsdk->connect_mutex);
+}
+
+/* Misc utilities */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define container_of(pointer, type, member)                            \
+({                                                                     \
+       type *object = NULL;                                            \
+       size_t offset = (void *) &object->member - (void *) object;     \
+       (type *) ((void *) pointer - offset);                           \
+})
+
+/* Misc values */
+enum {
+       /**
+        * Time we wait for callbacks: 5s
+        *
+        * I know, it is huge, but L4 and the device sometimes take
+        * some time, especially when there is crypto involved.
+        */
+       IWMX_SDK_L4_TIMEOUT_US = 5 * 1000 * 1000,
+
+       /*
+        * WARNING!!!!!
+        *
+        * ONLY ONE DEVICE SUPPORTED
+        *
+        * - on removal, there is no way to know which device was
+        *   removed (the removed device is removed from the list and
+        *   the callback doesn't have any more information than the
+        *   index in the list that getlistdevice would return -- racy
+        *   as hell).
+        *
+        * - on insertion, there is not enough information provided.
+        */
+       IWMX_SDK_DEV_MAX = 1,
+};
+
+struct connman_network *__iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength);
+
+struct connman_network *iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength);
+
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk);
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status);
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status);
+
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw);
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk);
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk);
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status);
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state);
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk);
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk);
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk);
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk);
+int iwmx_sdk_api_init(void);
+void iwmx_sdk_api_exit(void);
diff --git a/plugins/iwmxsdk.c b/plugins/iwmxsdk.c
new file mode 100644 (file)
index 0000000..06de4c3
--- /dev/null
@@ -0,0 +1,1044 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/log.h>
+
+#include <WiMaxType.h>
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX*/
+static struct wmxsdk *g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
+
+static struct wmxsdk *deviceid_to_wmxsdk(WIMAX_API_DEVICE_ID *device_id)
+{
+       unsigned cnt;
+       for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
+               struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
+               if (wmxsdk &&
+                   wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
+                       return wmxsdk;
+       }
+       return NULL;
+}
+
+static WIMAX_API_DEVICE_ID g_api;
+
+
+/*
+ * FIXME: pulled it it out of some hole
+ *
+ * the cinr to percentage computation comes from the L3/L4 doc
+ *
+ * But some other places (L4 code) have a more complex, seemingly
+ * logarithmical computation.
+ *
+ * Oh well...
+ *
+ */
+static int cinr_to_percentage(int cinr)
+{
+       int strength;
+       if (cinr <= -5)
+               strength = 0;
+       else if (cinr >= 25)
+               strength = 100;
+       else    /* Calc percentage on the value from -5 to 25 */
+               strength = ((100UL * (cinr - -5)) / (25 - -5));
+       return strength;
+}
+
+/*
+ * Convert a WiMAX API status to an string.
+ */
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
+{
+       switch (status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               return "Uninitialized";
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+               return "Device RF Off(both H/W and S/W)";
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+               return "Device RF Off(via H/W switch)";
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               return "Device RF Off(via S/W switch)";
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               return "Device is ready";
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               return "Device is scanning";
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               return "Connection in progress";
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               return "Layer 2 connected";
+#if HAVE_IWMXSDK_STATUS_IDLE
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+               return "Idle connection";
+#endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
+       default:
+               return "unknown state";
+       }
+}
+
+/*
+ * Get the device's status from the device
+ *
+ * Does NOT cache the result
+ * Does NOT trigger a state change in connman
+ *
+ * Returns < 0 errno code on error, status code if ok.
+ */
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
+{
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       WIMAX_API_DEVICE_STATUS dev_status;
+       WIMAX_API_CONNECTION_PROGRESS_INFO pi;
+
+       r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
+                       r, errstr);
+               dev_status = -EIO;
+       }
+       return dev_status;
+}
+
+/*
+ * Get the device's status from the device but return a string describing it
+ *
+ * Same conditions as iwmx_sdk_get_device_status().
+ */
+static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
+{
+       const char *result;
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0)
+               result = "cannot read device state";
+       else
+               result = iwmx_sdk_dev_status_to_str(dev_status);
+       return result;
+}
+
+/*
+ * Translate a WiMAX network type to a readable name.
+ */
+static const char *iwmx_sdk_network_type_name(enum _WIMAX_API_NETWORK_TYPE network_type)
+{
+       static char *network_type_name[] = {
+               [WIMAX_API_HOME] = "",
+               [WIMAX_API_PARTNER] = " (partner network)",
+               [WIMAX_API_ROAMING_PARTNER] = " (roaming partner network)",
+               [WIMAX_API_UNKNOWN] = " (unknown network)",
+       };
+       if (network_type > WIMAX_API_UNKNOWN)
+               return "(BUG! UNKNOWN NETWORK_TYPE MODE)";
+       else
+               return network_type_name[network_type];
+}
+
+/*
+ * If the device is connected but we don't know about the network,
+ * create the knowledge of it.
+ *
+ * Asks the WiMAX API to report which NSP we are connected to and we
+ * create/update a network_el in the device's network list. Then
+ * return it.
+ *
+ * Returns NULL on error.
+ *
+ * NOTE: wmxsdk->network_mutex has to be taken
+ */
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw;
+
+       WIMAX_API_CONNECTED_NSP_INFO nsp_info;
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       /* The device is getting connected due to an external (to
+        * connman) event; find which is the nw we are getting
+        * connected to. if we don't have it, add it */
+       r = GetConnectedNSP(&wmxsdk->device_id, &nsp_info);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error(
+                       "wmxsdk: Cannot get connected NSP info: %d (%s)\n",
+                       r, errstr);
+               strcpy((char *) nsp_info.NSPName, "unknown");
+               nw = iwmx_cm_network_available(
+                       wmxsdk, "unknown",
+                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+                       cinr_to_percentage(nsp_info.CINR - 10));
+       } else {
+               nw = iwmx_cm_network_available(
+                       wmxsdk, (char *) nsp_info.NSPName,
+                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+                       cinr_to_percentage(nsp_info.CINR - 10));
+       }
+       return nw;
+}
+
+/*
+ * Callback for a RF State command
+ *
+ * Called by the WiMAX API when a command sent to change the RF state
+ * is completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * We don't do anything, as when the device changes state, the state
+ * change callback is called and that will fiddle with the connman
+ * internals.
+ */
+static void __iwmx_sdk_rf_state_cb(WIMAX_API_DEVICE_ID *device_id,
+                                  WIMAX_API_RF_STATE rf_state)
+{
+       DBG("rf_state changed to %d\n", rf_state);
+}
+
+/*
+ * Turn the radio on or off
+ *
+ * First it checks that we are in the right state before doing
+ * anything; there might be no need to do anything.
+ *
+ * Issue a command to the WiMAX API, wait for a callback confirming it
+ * is done. Sometimes the callback is missed -- in that case, do force
+ * a state change evaluation.
+ *
+ * Frustration note:
+ *
+ *      Geezoos efing Xist, they make difficult even the most simple
+ *      of the operations
+ *
+ *      This thing is definitely a pain. If the radio is ON already
+ *      and you switch it on again...well, there is no way to tell
+ *      because you don't get a callback saying it basically
+ *      suceeded. But on the other hand, if the thing was in a
+ *      different state and action needs to be taken, you have to wait
+ *      for a callback to confirm it's done. However, there is also an
+ *      state change callback, which is almost the same, so now you
+ *      have to handle things in two "unrelated" threads of execution.
+ *
+ *      How the shpx are you expected to tell the difference? Check
+ *      status first? On timeout? Nice gap (eighteen wheeler size) for
+ *      race conditions.
+ */
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
+
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+               connman_error(
+                       "wmxsdk: cannot turn on radio: hw switch is off\n");
+               result = -EPERM;
+               goto error_cant_do;
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               if (rf_state == WIMAX_API_RF_OFF) {
+                       result = 0;
+                       DBG("radio is already off\n");
+                       goto out_done;
+               }
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+#if HAVE_IWMXSDK_STATUS_IDLE
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+#endif
+               if (rf_state == WIMAX_API_RF_ON) {
+                       result = 0;
+                       DBG("radio is already on\n");
+                       goto out_done;
+               }
+               break;
+       default:
+               g_assert(1);
+       }
+       /* Ok, flip the radio */
+       r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot flip radio to %d: %d (%s) "
+                             "[device is in state %s]\n",
+                             rf_state, r, errstr,
+                             iwmx_sdk_get_device_status_str(wmxsdk));
+               result = -EIO;
+       } else
+               result = -EINPROGRESS;
+out_done:
+error_cant_do:
+error_get_status:
+       return result;
+}
+
+/*
+ * Callback for a Connect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * WE DON'T DO MUCH HERE -- the real meat happens when a state change
+ * callback is sent, where we detect we move to connected state (or
+ * from disconnecting to something else); the state change callback is
+ * called and that will fiddle with the connman internals.
+ */
+static void __iwmx_sdk_connect_cb(WIMAX_API_DEVICE_ID *device_id,
+                                 WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+       WIMAX_API_DEVICE_STATUS status;
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+
+       status = iwmx_cm_status_get(wmxsdk);
+       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+               if (status != WIMAX_API_DEVICE_STATUS_Data_Connected
+#if HAVE_IWMXSDK_STATUS_IDLE
+                   && status != WIMAX_API_DEVICE_STATUS_Connection_Idle
+#endif
+                       )
+                       connman_error("wmxsdk: error: connect worked, but state"
+                                     " didn't change (now it is %d [%s])\n",
+                                     status,
+                                     iwmx_sdk_dev_status_to_str(status));
+       } else
+               connman_error("wmxsdk: failed to connect (status %d: %s)\n",
+                             status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Connect to a network
+ *
+ * This function starts the connection process to a given network;
+ * when the device changes status, the status change callback will
+ * tell connman if the network is finally connected or not.
+ *
+ * One of the reasons it is done like that is to allow external tools
+ * to control the device and the plugin just passing the status so
+ * connman displays the right info.
+ */
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+       const char *station_name = connman_network_get_identifier(nw);
+       const void *sdk_nspname;
+       unsigned int sdk_nspname_size;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_cm_status_get(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               connman_error("wmxsdk: Cannot connect: radio is off\n");
+               result = -EPERM;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               DBG("Connect already pending, waiting for it\n");
+               result = -EINPROGRESS;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+#if HAVE_IWMXSDK_STATUS_IDLE
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+#endif
+               connman_error("wmxsdk: BUG? need to disconnect?\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       default:
+               g_assert(1);
+       }
+
+       /* Ok, do the connection, wait for a callback */
+       wmxsdk->connecting_nw = connman_network_ref(nw);
+       sdk_nspname = connman_network_get_blob(nw, "WiMAX.NSP.name",
+                                                       &sdk_nspname_size);
+       g_assert(sdk_nspname != NULL);
+       r = CmdConnectToNetwork(&wmxsdk->device_id, (void *) sdk_nspname, 0, 0);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot connect to network %s: %d (%s)"
+                             " - device is in state '%s'\n",
+                             station_name, r, errstr,
+                             iwmx_sdk_get_device_status_str(wmxsdk));
+               result = -EIO;
+               connman_network_unref(nw);
+               wmxsdk->connecting_nw = NULL;
+       } else
+               result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+       return result;
+}
+
+/*
+ * Callback for a Disconnect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * When the device changes state, the state change callback is called
+ * and that will fiddle with the connman internals.
+ *
+ * We just update the result of the command and wake up anybody who is
+ * waiting for this conditional variable.
+ */
+static void __iwmx_sdk_disconnect_cb(WIMAX_API_DEVICE_ID *device_id,
+                                    WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       WIMAX_API_DEVICE_STATUS status;
+
+       status = iwmx_cm_status_get(wmxsdk);
+       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+               if (status == WIMAX_API_DEVICE_STATUS_Data_Connected
+#if HAVE_IWMXSDK_STATUS_IDLE
+                   || status == WIMAX_API_DEVICE_STATUS_Connection_Idle
+#endif
+                       )
+                       connman_error("wmxsdk: error: disconnect worked, "
+                                     "but state didn't change (now it is "
+                                     "%d [%s])\n", status,
+                                     iwmx_sdk_dev_status_to_str(status));
+       } else
+               connman_error("wmxsdk: failed to disconnect (status %d: %s)\n",
+                             status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Disconnect from a network
+ *
+ * This function tells the device to disconnect; the state change
+ * callback will take care of inform connman's internals.
+ */
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               DBG("Cannot disconnect, radio is off; ignoring\n");
+               result = 0;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               DBG("Cannot disconnect, already disconnected; ignoring\n");
+               result = 0;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+#if HAVE_IWMXSDK_STATUS_IDLE
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+#endif
+               break;
+       default:
+               g_assert(1);
+       }
+       /* Ok, flip the radio */
+       r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot disconnect from network: "
+                             "%d (%s)\n", r, errstr);
+               result = -EIO;
+       } else
+               result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+       return result;
+}
+
+/*
+ * Callback for state change messages
+ *
+ * Just pass them to the state transition handler
+ */
+static void __iwmx_sdk_state_change_cb(WIMAX_API_DEVICE_ID *device_id,
+                                       WIMAX_API_DEVICE_STATUS status,
+                                       WIMAX_API_STATUS_REASON reason,
+                                       WIMAX_API_CONNECTION_PROGRESS_INFO pi)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       iwmx_cm_state_change(wmxsdk, status);
+}
+
+/*
+ * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
+ * are available.
+ *
+ * From here we update the connman core idea of which networks are
+ * available.
+ */
+static void __iwmx_sdk_scan_common_cb(WIMAX_API_DEVICE_ID *device_id,
+                                     WIMAX_API_NSP_INFO_EX *nsp_list,
+                                     UINT32 nsp_list_size)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       unsigned itr;
+       char station_name[256];
+
+       g_static_mutex_lock(&wmxsdk->network_mutex);
+       for (itr = 0; itr < nsp_list_size; itr++) {
+               int strength;
+               WIMAX_API_NSP_INFO_EX *nsp_info = &nsp_list[itr];
+               snprintf(station_name, sizeof(station_name),
+                        "%s", (char *)nsp_info->NSPName);
+               /* CAPI is reporing link quality as zero -- if it is
+                * zero, check if it is a bug by computing it based on
+                * CINR. If it is different, use the computed one. */
+               strength = nsp_info->linkQuality;
+               if (strength == 0) {    /* huh */
+                       int linkq_expected =
+                               cinr_to_percentage(nsp_info->CINR - 10);
+                       if (linkq_expected != strength)
+                               strength = linkq_expected;
+               }
+
+               __iwmx_cm_network_available(
+                       wmxsdk, station_name,
+                       nsp_info->NSPName,
+                       strlen((char *) nsp_info->NSPName) + 1,
+                       strength);
+       }
+       g_static_mutex_unlock(&wmxsdk->network_mutex);
+}
+
+/*
+ * Called by the WiMAX API when we get a wide scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_wide_scan_cb(WIMAX_API_DEVICE_ID *device_id,
+                                   WIMAX_API_NSP_INFO_EX *nsp_list,
+                                   UINT32 nsp_list_size)
+{
+       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called by the WiMAX API when we get a normal (non wide) scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_scan_cb(WIMAX_API_DEVICE_ID *device_id,
+                               WIMAX_API_NSP_INFO_EX *nsp_list,
+                               UINT32 nsp_list_size, UINT32 searchProgress)
+{
+       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called to ask the device to scan for networks
+ *
+ * We don't really scan as the WiMAX SDK daemon scans in the
+ * background for us. We just get the results. See iwmx_sdk_setup().
+ */
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       UINT32 nsp_list_length = 10;
+       WIMAX_API_NSP_INFO_EX nsp_list[10];     /* FIXME: up to 32? */
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot get network list: %d (%s)\n",
+                             r, errstr);
+               result = -EIO;
+               goto error_scan;
+       }
+
+       if (nsp_list_length == 0)
+               DBG("no networks\n");
+       else
+               __iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
+                                       nsp_list_length);
+       result = 0;
+error_scan:
+       return result;
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks
+ *
+ * Called through
+ *
+ * iwmx_sdk_dev_add
+ *   connman_inet_create_device
+ *      connman_register
+ *         iwmx_cm_probe()
+ */
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       WIMAX_API_RET r;
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       result = -ENFILE;
+
+       /* device_id initialized by iwmx_sdk_dev_add */
+
+       r = WiMaxDeviceOpen(&wmxsdk->device_id);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot open device: %d (%s)\n",
+                             r, errstr);
+               goto error_wimaxdeviceopen;
+       }
+
+       /*
+        * We scan in auto mode (in the background)
+        *
+        * Otherwise is messy -- if we have connman triggering a scan
+        * when we call iwmx_cm_scan() -> iwmx_sdk_scan(), most of the
+        * times that causes a race condition when the UI asks for a
+        * scan right before displaying the network menu. As there is
+        * no way to cancel an ongoing scan before connecting, we are
+        * stuck. So we do auto bg and have iwmx_sdk_scan() just return
+        * the current network list.
+        */
+       r = SetConnectionMode(&wmxsdk->device_id,
+                             WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot set connectin mode to manual: "
+                             "%d (%s)\n", r, errstr);
+               goto error_connection_mode;
+       }
+
+       r = SubscribeControlPowerManagement(&wmxsdk->device_id,
+                                           __iwmx_sdk_rf_state_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to radio change "
+                             "events: %u (%s)\n", r, errstr);
+               result = -EIO;
+               goto error_subscribe_rf_state;
+       }
+
+       r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
+                                       __iwmx_sdk_state_change_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to state chaneg events:"
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_state_change;
+       }
+
+       r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
+                                            __iwmx_sdk_wide_scan_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to wide scan events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_wide_scan;
+       }
+       r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to scan events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_scan;
+       }
+
+       r = SubscribeConnectToNetwork(&wmxsdk->device_id,
+                                     __iwmx_sdk_connect_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to connect events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_connect;
+       }
+
+       r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
+                                        __iwmx_sdk_disconnect_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to disconnect events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_disconnect;
+       }
+       result = 0;
+out:
+       return result;
+
+       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+error_subscribe_disconnect:
+       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+error_subscribe_connect:
+       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+error_subscribe_scan:
+       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+error_subscribe_wide_scan:
+       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+error_subscribe_state_change:
+       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+error_subscribe_rf_state:
+error_connection_mode:
+       WiMaxDeviceClose(&wmxsdk->device_id);
+error_wimaxdeviceopen:
+       goto out;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
+ * unregister from the WiMAX API.
+ */
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
+{
+       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+       WiMaxDeviceClose(&wmxsdk->device_id);
+}
+
+static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
+{
+       int result, ifindex;
+       struct wmxsdk *wmxsdk;
+       const char *s;
+
+       if (idx >= IWMX_SDK_DEV_MAX) {
+               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+                             idx, IWMX_SDK_DEV_MAX);
+               goto error_bug;
+       }
+       if (g_iwmx_sdk_devs[idx] != NULL) {
+               connman_error("BUG! device index %u already enumerated?\n",
+                             idx);
+               goto error_bug;
+       }
+
+       wmxsdk = malloc(sizeof(*wmxsdk));
+       if (wmxsdk == NULL) {
+               connman_error("Can't allocate %zu bytes\n",
+                             sizeof(*wmxsdk));
+               goto error_bug;
+       }
+
+       memset(wmxsdk, 0, sizeof(*wmxsdk));
+       wmxsdk_init(wmxsdk);
+       /*
+        * This depends on a hack in the WiMAX Network Service; it has
+        * to return, as part of the device name, a string "if:IFNAME"
+        * where the OS's device name is stored.
+        */
+       s = strstr(name, "if:");
+       if (s == NULL
+           || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
+               connman_error("Cannot extract network interface name off '%s'",
+                             name);
+               goto error_noifname;
+       }
+       DBG("network interface name: '%s'", wmxsdk->ifname);
+
+       ifindex = if_nametoindex(wmxsdk->ifname);
+       if (ifindex <= 0) {
+               result = -ENFILE;
+               connman_error("wxmsdk: %s: cannot find interface index\n",
+                             wmxsdk->ifname);
+               goto error_noifname;
+       }
+
+       wmxsdk->dev = connman_inet_create_device(ifindex);
+       if (wmxsdk->dev == NULL) {
+               connman_error("wmxsdk: %s: failed to create connman_device\n",
+                             name);
+               goto error_create;
+       }
+       strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
+       connman_device_set_data(wmxsdk->dev, wmxsdk);
+
+       wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+       wmxsdk->device_id.deviceIndex = api_idx;
+
+       result = connman_device_register(wmxsdk->dev);
+       if (result < 0) {
+               connman_error("wmxsdk: %s: failed to register: %d\n",
+                             wmxsdk->ifname, result);
+               goto error_dev_add;
+       }
+       g_iwmx_sdk_devs[idx] = wmxsdk;
+       return;
+
+error_dev_add:
+       wmxsdk->name[0] = 0;
+       connman_device_unref(wmxsdk->dev);
+       wmxsdk->dev = NULL;
+error_noifname:
+error_create:
+error_bug:
+       return;
+}
+
+static void iwmx_sdk_dev_rm(unsigned idx)
+{
+       struct wmxsdk *wmxsdk;
+
+       if (idx >= IWMX_SDK_DEV_MAX) {
+               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+                             idx, IWMX_SDK_DEV_MAX);
+               goto error_bug;
+       }
+       wmxsdk = g_iwmx_sdk_devs[idx];
+       if (wmxsdk->dev == NULL) {
+               DBG("device index %u not enumerated? ignoring\n", idx);
+               goto error_bug;
+       }
+
+       connman_device_unregister(wmxsdk->dev);
+       wmxsdk->name[0] = 0;
+       connman_device_unref(wmxsdk->dev);
+       memset(wmxsdk, 0, sizeof(*wmxsdk));
+       g_iwmx_sdk_devs[idx] = NULL;
+       free(wmxsdk);
+error_bug:
+       return;
+}
+
+static void iwmx_sdk_addremove_cb(WIMAX_API_DEVICE_ID *devid,
+                                 BOOL presence)
+{
+       unsigned int cnt;
+       WIMAX_API_RET r;
+       WIMAX_API_HW_DEVICE_ID device_id_list[5];
+       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       DBG("cb: handle %u index #%u is %d\n", devid->sdkHandle,
+           devid->deviceIndex, presence);
+
+       r = GetListDevice(devid, device_id_list, &device_id_list_size);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(devid, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot obtain list "
+                             "of devices: %d (%s)\n", r, errstr);
+               return;
+       }
+
+       if (device_id_list_size == 0)
+               DBG("No WiMAX devices reported\n");
+       else
+               for (cnt = 0; cnt < device_id_list_size; cnt++) {
+                       WIMAX_API_HW_DEVICE_ID *dev =
+                               device_id_list + cnt;
+                       DBG("#%u index #%u device %s\n",
+                           cnt, dev->deviceIndex, dev->deviceName);
+               }
+       if (device_id_list_size < devid->deviceIndex) {
+               connman_error("wmxsdk: changed device (%u) not in the list? "
+                             "(%u items)\n",
+                             devid->deviceIndex, device_id_list_size);
+               return;
+       }
+
+       if (presence) {
+               WIMAX_API_HW_DEVICE_ID *dev =
+                       device_id_list + devid->deviceIndex;
+               iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex,
+                              dev->deviceName);
+       } else {
+               iwmx_sdk_dev_rm(devid->deviceIndex);
+       }
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks for
+ * device coming up / dissapearing
+ */
+int iwmx_sdk_api_init(void)
+{
+       int result;
+       unsigned int cnt;
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       WIMAX_API_HW_DEVICE_ID device_id_list[5];
+       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+       memset(&g_api, 0, sizeof(g_api));
+       g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+
+       result = -EIO;
+       r = WiMaxAPIOpen(&g_api);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: WiMaxAPIOpen failed with %d (%s)\n",
+                             r, errstr);
+               goto error_wimaxapiopen;
+       }
+
+       r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: insert/remove subscribe failed with "
+                             "%d (%s)\n", r, errstr);
+               goto error_close;
+       }
+
+       r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot obtain list "
+                             "of devices: %d (%s)\n", r, errstr);
+               goto error_close;
+       }
+       if (device_id_list_size < g_api.deviceIndex) {
+               connman_error("wmxsdk: changed device (%u) not in the list? "
+                             "(%u items)\n",
+                             g_api.deviceIndex, device_id_list_size);
+       }
+
+       if (device_id_list_size == 0)
+               DBG("No WiMAX devices reported\n");
+       else
+               for (cnt = 0; cnt < device_id_list_size; cnt++) {
+                       WIMAX_API_HW_DEVICE_ID *dev =
+                               device_id_list + cnt;
+                       DBG("#%u index #%u device %s\n",
+                           cnt, dev->deviceIndex, dev->deviceName);
+                       iwmx_sdk_dev_add(cnt, dev->deviceIndex,
+                                        dev->deviceName);
+               }
+       return 0;
+
+error_close:
+       WiMaxAPIClose(&g_api);
+error_wimaxapiopen:
+       return result;
+}
+
+void iwmx_sdk_api_exit(void)
+{
+       WIMAX_API_RET r;
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       r = WiMaxAPIClose(&g_api);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: WiMaxAPIClose failed with %d (%s)\n",
+                             r, errstr);
+       }
+       return;
+}
diff --git a/plugins/l2tp.c b/plugins/l2tp.c
new file mode 100644 (file)
index 0000000..5a655e6
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
+enum {
+       OPT_ALL = 1,
+       OPT_L2G = 2,
+       OPT_L2  = 3,
+       OPT_PPPD = 4,
+};
+
+struct {
+       const char *cm_opt;
+       const char *pppd_opt;
+       int sub;
+       const char *vpn_default;
+       int type;
+} pppd_options[] = {
+       { "L2TP.User", "name", OPT_ALL, NULL, OPT_STRING },
+       { "L2TP.BPS", "bps", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.LengthBit", "length bit", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.Challenge", "challenge", OPT_L2, NULL, OPT_STRING },
+       { "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.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.MaxRedials", "max redials", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.RequirePAP", "require pap", OPT_L2, "no", OPT_STRING },
+       { "L2TP.RequireCHAP", "require chap", OPT_L2, "yes", OPT_STRING },
+       { "L2TP.ReqAuth", "require authentication", OPT_L2, "no", OPT_STRING },
+       { "L2TP.AccessControl", "access control", OPT_L2G, "yes", OPT_STRING },
+       { "L2TP.AuthFile", "auth file", OPT_L2G, NULL, 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.Port", "port", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.EchoFailure", "lcp-echo-failure", OPT_PPPD, "0", OPT_STRING },
+       { "L2TP.EchoInterval", "lcp-echo-interval", OPT_PPPD, "0", OPT_STRING },
+       { "L2TP.Debug", "debug", OPT_PPPD, NULL, OPT_STRING },
+       { "L2TP.RefuseEAP", "refuse-eap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefusePAP", "refuse-pap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseCHAP", "refuse-chap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseMSCHAP", "refuse-mschap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseMSCHAP2", "refuse-mschapv2", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoBSDComp", "nobsdcomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoPcomp", "nopcomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.UseAccomp", "accomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoDeflate", "nodeflatey", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE", "require-mppe", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE40", "require-mppe-40", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE128", "require-mppe-128", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPEStateful", "mppe-stateful", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoVJ", "no-vj-comp", OPT_PPPD, NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *l2tp_get_sec(struct connman_task *task,
+                       DBusMessage *msg, void *user_data)
+{
+       const char *user, *passwd;
+       struct connman_provider *provider = user_data;
+
+       if (dbus_message_get_no_reply(msg) == FALSE) {
+               DBusMessage *reply;
+
+               user = connman_provider_get_string(provider, "L2TP.User");
+               passwd = connman_provider_get_string(provider, "L2TP.Password");
+
+               if (user == NULL || strlen(user) == 0 ||
+                               passwd == NULL || strlen(passwd) == 0)
+                       return NULL;
+
+               reply = dbus_message_new_method_return(msg);
+               if (reply == NULL)
+                       return NULL;
+
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+                                               DBUS_TYPE_STRING, &passwd,
+                                               DBUS_TYPE_INVALID);
+
+               return reply;
+       }
+
+       return NULL;
+}
+
+static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+       char *ifname = NULL, *nameservers = NULL;
+       struct connman_ipaddress *ipaddress = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (!provider) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "auth failed") == 0)
+               return VPN_STATE_AUTH_FAILURE;
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+                       connman_provider_set_string(provider, "Address", value);
+                       addressv4 = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+                       connman_provider_set_string(provider, "Netmask", value);
+                       netmask = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+                       connman_provider_set_string(provider, "DNS", value);
+                       nameservers = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IFNAME"))
+                       ifname = g_strdup(value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (vpn_set_ifname(provider, ifname) < 0) {
+               g_free(ifname);
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       if (addressv4 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET);
+
+       g_free(ifname);
+
+       if (ipaddress == NULL) {
+               connman_error("No IP address for provider");
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       value = connman_provider_get_string(provider, "Host");
+       if (value != NULL) {
+               connman_provider_set_string(provider, "Gateway", value);
+               gateway = g_strdup(value);
+       }
+
+       if (addressv4 != NULL)
+               connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+                                       gateway);
+
+       connman_provider_set_ipaddress(provider, ipaddress);
+       connman_provider_set_nameservers(provider, nameservers);
+
+       g_free(addressv4);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(nameservers);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int l2tp_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (strncmp(pppd_options[i].cm_opt, "L2TP.", 5) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       pppd_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       pppd_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+       ssize_t byte_write;
+
+       while (len) {
+               byte_write = write(fd, buf, len);
+               if (byte_write < 0) {
+                       connman_error("failed to write config to l2tp: %s\n",
+                                       strerror(errno));
+                       return byte_write;
+               }
+               len -= byte_write;
+               buf += byte_write;
+       }
+
+       return 0;
+}
+
+static ssize_t l2tp_write_bool_option(int fd,
+                                       const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               if (strcmp(value, "yes") == 0) {
+                       buf = g_strdup_printf("%s\n", key);
+                       ret = full_write(fd, buf, strlen(buf));
+
+                       g_free(buf);
+               }
+       }
+
+       return ret;
+}
+
+static int l2tp_write_option(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL) {
+               if (value != NULL)
+                       buf = g_strdup_printf("%s %s\n", key, value);
+               else
+                       buf = g_strdup_printf("%s\n", key);
+
+               ret = full_write(fd, buf, strlen(buf));
+
+               g_free(buf);
+       }
+
+       return ret;
+}
+
+static int l2tp_write_section(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               buf = g_strdup_printf("%s = %s\n", key, value);
+               ret = full_write(fd, buf, strlen(buf));
+
+               g_free(buf);
+       }
+
+       return ret;
+}
+
+static int write_pppd_option(struct connman_provider *provider, int fd)
+{
+       int i;
+       const char *opt_s;
+
+       l2tp_write_option(fd, "nodetach", NULL);
+       l2tp_write_option(fd, "lock", NULL);
+       l2tp_write_option(fd, "usepeerdns", NULL);
+       l2tp_write_option(fd, "noipdefault", NULL);
+       l2tp_write_option(fd, "noauth", NULL);
+       l2tp_write_option(fd, "nodefaultroute", NULL);
+       l2tp_write_option(fd, "ipparam", "l2tp_plugin");
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (pppd_options[i].sub != OPT_ALL &&
+                       pppd_options[i].sub != OPT_PPPD)
+                       continue;
+
+               opt_s = connman_provider_get_string(provider,
+                                       pppd_options[i].cm_opt);
+               if (!opt_s)
+                       opt_s = pppd_options[i].vpn_default;
+
+               if (!opt_s)
+                       continue;
+
+               if (pppd_options[i].type == OPT_STRING)
+                       l2tp_write_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+               else if (pppd_options[i].type == OPT_BOOL)
+                       l2tp_write_bool_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+       }
+
+       l2tp_write_option(fd, "plugin",
+                               SCRIPTDIR "/libppp-plugin.so");
+
+       return 0;
+}
+
+
+static int l2tp_write_fields(struct connman_provider *provider,
+                                               int fd, int sub)
+{
+       int i;
+       const char *opt_s;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (pppd_options[i].sub != sub)
+                       continue;
+
+               opt_s = connman_provider_get_string(provider,
+                                       pppd_options[i].cm_opt);
+               if (!opt_s)
+                       opt_s = pppd_options[i].vpn_default;
+
+               if (!opt_s)
+                       continue;
+
+               if (pppd_options[i].type == OPT_STRING)
+                       l2tp_write_section(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+               else if (pppd_options[i].type == OPT_BOOL)
+                       l2tp_write_bool_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+       }
+
+       return 0;
+}
+
+static int l2tp_write_config(struct connman_provider *provider,
+                                       const char *pppd_name, int fd)
+{
+       const char *option;
+
+       l2tp_write_option(fd, "[global]", NULL);
+       l2tp_write_fields(provider, fd, OPT_L2G);
+
+       l2tp_write_option(fd, "[lac l2tp]", NULL);
+
+       option = connman_provider_get_string(provider, "Host");
+       l2tp_write_option(fd, "lns =", option);
+
+       l2tp_write_fields(provider, fd, OPT_ALL);
+       l2tp_write_fields(provider, fd, OPT_L2);
+
+       l2tp_write_option(fd, "pppoptfile =", pppd_name);
+
+       return 0;
+}
+
+static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
+{
+       char *conf_file;
+
+       vpn_died(task, exit_code, user_data);
+
+       conf_file = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+       unlink(conf_file);
+       g_free(conf_file);
+
+       conf_file = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+       unlink(conf_file);
+       g_free(conf_file);
+}
+
+static int l2tp_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *host;
+       char *l2tp_name, *pppd_name;
+       int l2tp_fd, pppd_fd;
+       int err;
+
+       if (connman_task_set_notify(task, "getsec",
+                                       l2tp_get_sec, provider))
+               return -ENOMEM;
+
+       host = connman_provider_get_string(provider, "Host");
+       if (host == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       l2tp_name = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+
+       l2tp_fd = open(l2tp_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+       if (l2tp_fd < 0) {
+               g_free(l2tp_name);
+               connman_error("Error writing l2tp config");
+               return -EIO;
+       }
+
+       pppd_name = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+
+       pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+       if (pppd_fd < 0) {
+               connman_error("Error writing pppd config");
+               g_free(l2tp_name);
+               g_free(pppd_name);
+               close(l2tp_fd);
+               return -EIO;
+       }
+
+       l2tp_write_config(provider, pppd_name, l2tp_fd);
+
+       write_pppd_option(provider, pppd_fd);
+
+       connman_task_add_argument(task, "-D", NULL);
+       connman_task_add_argument(task, "-c", l2tp_name);
+
+       g_free(l2tp_name);
+       g_free(pppd_name);
+
+       err = connman_task_run(task, l2tp_died, provider,
+                               NULL, NULL, NULL);
+       if (err < 0) {
+               connman_error("l2tp failed to start");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int l2tp_error_code(int exit_code)
+{
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .flags          = VPN_FLAG_NO_TUN,
+       .notify         = l2tp_notify,
+       .connect        = l2tp_connect,
+       .error_code     = l2tp_error_code,
+       .save           = l2tp_save,
+};
+
+static int l2tp_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return vpn_register("l2tp", &vpn_driver, L2TP);
+}
+
+static void l2tp_exit(void)
+{
+       vpn_unregister("l2tp");
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(l2tp, "l2tp plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, l2tp_init, l2tp_exit)
diff --git a/plugins/loopback.c b/plugins/loopback.c
new file mode 100644 (file)
index 0000000..a111eee
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/utsname.h>
+#include <connman/log.h>
+#include <connman/inet.h>
+
+static in_addr_t loopback_address;
+static in_addr_t loopback_netmask;
+
+static char system_hostname[HOST_NAME_MAX + 1];
+
+static void create_hostname(void)
+{
+       const char *name = "localhost";
+
+       if (sethostname(name, strlen(name)) < 0)
+               connman_error("Failed to set hostname to %s", name);
+
+       strncpy(system_hostname, name, HOST_NAME_MAX);
+}
+
+static int setup_hostname(void)
+{
+       char name[HOST_NAME_MAX + 1];
+
+       memset(system_hostname, 0, sizeof(system_hostname));
+
+       if (gethostname(system_hostname, HOST_NAME_MAX) < 0) {
+               connman_error("Failed to get current hostname");
+               return -EIO;
+       }
+
+       if (strlen(system_hostname) > 0 &&
+                               strcmp(system_hostname, "(none)") != 0)
+               connman_info("System hostname is %s", system_hostname);
+       else
+               create_hostname();
+
+       memset(name, 0, sizeof(name));
+
+       if (getdomainname(name, HOST_NAME_MAX) < 0) {
+               connman_error("Failed to get current domainname");
+               return -EIO;
+       }
+
+       if (strlen(name) > 0 && strcmp(name, "(none)") != 0)
+               connman_info("System domainname is %s", name);
+
+       return 0;
+}
+
+static gboolean valid_loopback(int sk, struct ifreq *ifr)
+{
+       struct sockaddr_in *addr;
+       int err;
+       char buf[INET_ADDRSTRLEN];
+
+       /* It is possible to end up in situations in which the
+        * loopback interface is up but has no valid address. In that
+        * case, we expect EADDRNOTAVAIL and should return FALSE.
+        */
+
+       err = ioctl(sk, SIOCGIFADDR, ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Getting address failed (%s)", strerror(-err));
+               return err != -EADDRNOTAVAIL ? TRUE : FALSE;
+       }
+
+       addr = (struct sockaddr_in *) &ifr->ifr_addr;
+       if (addr->sin_addr.s_addr != loopback_address) {
+               connman_warn("Invalid loopback address %s",
+                       inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)));
+               return FALSE;
+       }
+
+       err = ioctl(sk, SIOCGIFNETMASK, ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Getting netmask failed (%s)", strerror(-err));
+               return TRUE;
+       }
+
+       addr = (struct sockaddr_in *) &ifr->ifr_netmask;
+       if (addr->sin_addr.s_addr != loopback_netmask) {
+               connman_warn("Invalid loopback netmask %s",
+                       inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int setup_loopback(void)
+{
+       struct ifreq ifr;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, "lo");
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP) {
+               connman_info("Checking loopback interface settings");
+               if (valid_loopback(sk, &ifr) == TRUE) {
+                       err = -EALREADY;
+                       goto done;
+               }
+
+               connman_warn("Correcting wrong lookback settings");
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = loopback_address;
+       memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr));
+
+       err = ioctl(sk, SIOCSIFADDR, &ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Setting address failed (%s)", strerror(-err));
+               goto done;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = loopback_netmask;
+       memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask));
+
+       err = ioctl(sk, SIOCSIFNETMASK, &ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Setting netmask failed (%s)", strerror(-err));
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       ifr.ifr_flags |= IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               connman_error("Activating loopback interface failed (%s)",
+                                                       strerror(-err));
+               goto done;
+       }
+
+done:
+       close(sk);
+
+       return err;
+}
+
+static const char *loopback_get_hostname(void)
+{
+       return system_hostname;
+}
+
+static int loopback_set_hostname(const char *hostname)
+{
+       const char *ptr;
+       int err, len;
+
+       if (g_strcmp0(hostname, "<hostname>") == 0)
+               return 0;
+
+       len = strlen(hostname);
+
+       if (connman_inet_check_hostname(hostname, len) == FALSE)
+               return -EINVAL;
+
+       if ((ptr = strstr(hostname, ".")) != NULL)
+               len = ptr - hostname;
+
+       if (sethostname(hostname, len) < 0) {
+               err = -errno;
+               connman_error("Failed to set hostname to %s", hostname);
+               return err;
+       }
+
+       connman_info("Setting hostname to %s", hostname);
+
+       return 0;
+}
+
+static int loopback_set_domainname(const char *domainname)
+{
+       int err, len;
+
+       len = strlen(domainname);
+
+       if (connman_inet_check_hostname(domainname, len) == FALSE)
+               return -EINVAL;
+
+       if (setdomainname(domainname, len) < 0) {
+               err = -errno;
+               connman_error("Failed to set domainname to %s", domainname);
+               return err;
+       }
+
+       connman_info("Setting domainname to %s", domainname);
+
+       return 0;
+}
+
+static struct connman_utsname_driver loopback_driver = {
+       .name           = "loopback",
+       .get_hostname   = loopback_get_hostname,
+       .set_hostname   = loopback_set_hostname,
+       .set_domainname = loopback_set_domainname,
+};
+
+static int loopback_init(void)
+{
+       loopback_address = inet_addr("127.0.0.1");
+       loopback_netmask = inet_addr("255.0.0.0");
+
+       setup_loopback();
+
+       setup_hostname();
+
+       connman_utsname_driver_register(&loopback_driver);
+
+       return 0;
+}
+
+static void loopback_exit(void)
+{
+       connman_utsname_driver_unregister(&loopback_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(loopback, "Loopback device plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_HIGH, loopback_init, loopback_exit)
diff --git a/plugins/mcc.h b/plugins/mcc.h
new file mode 100644 (file)
index 0000000..0e0407c
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Mobile country code list from
+ * "LIST OF MOBILE COUNTRY OR GEOGRAPHICAL AREA CODES"
+ * (http://www.itu.int/publ/T-SP-E.212A-2007)
+ * combined with ISO 3166-1 alpha-2 list for two letter country codes
+ */
+
+char *mcc_country_codes[] = {
+       NULL, /* 200 */
+       NULL,
+       NULL,
+       NULL,
+       "NL",
+       NULL,
+       "BE",
+       NULL,
+       "FR",
+       NULL,
+       NULL,
+       NULL,
+       "MC",
+       "AD",
+       "ES",
+       NULL,
+       "HU",
+       NULL,
+       "BA",
+       "HR",
+       "RS",
+       NULL,
+       "IT",
+       NULL,
+       NULL,
+       "VA",
+       "RO",
+       NULL,
+       "CH",
+       NULL,
+       "CZ",
+       "SK",
+       "AT",
+       NULL,
+       "GB",
+       "GB",
+       NULL,
+       NULL,
+       "DK",
+       NULL,
+       "SE",
+       NULL,
+       "NO",
+       NULL,
+       "FI",
+       NULL,
+       "LT",
+       "LV",
+       "EE",
+       NULL,
+       "RU",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "UA",
+       NULL,
+       "BY",
+       NULL,
+       "MD",
+       "PL",
+       NULL,
+       "DE",
+       NULL,
+       NULL,
+       NULL,
+       "GI",
+       NULL,
+       "PT",
+       NULL,
+       "LU",
+       NULL,
+       "IE",
+       NULL,
+       "IS",
+       NULL,
+       "AL",
+       NULL,
+       "MT",
+       NULL,
+       "CY",
+       NULL,
+       "GE",
+       "AM",
+       "BG",
+       NULL,
+       "TR",
+       NULL,
+       "FO",
+       NULL,
+       "GL",
+       NULL,
+       "SM",
+       "SI",
+       "MK",
+       "LI",
+       NULL,
+       "ME",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "CA",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "PM",
+       NULL,
+       "US",
+       "US",
+       "US",
+       "US",
+       "US",
+       "US",
+       "US",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "PR",
+       NULL,
+       "VI",
+       NULL,
+       "MX",
+       NULL,
+       NULL,
+       NULL,
+       "JM",
+       NULL,
+       "MQ",
+       NULL,
+       "BB",
+       NULL,
+       "AG",
+       NULL,
+       "KY",
+       NULL,
+       "VG",
+       NULL,
+       "BM",
+       NULL,
+       "GD",
+       NULL,
+       "MS",
+       NULL,
+       "KN",
+       NULL,
+       "LC",
+       NULL,
+       "VC",
+       NULL,
+       "AN",
+       "AW",
+       "BS",
+       "AI",
+       "DM",
+       NULL,
+       "CU",
+       NULL,
+       "DO",
+       NULL,
+       "HT",
+       NULL,
+       "TT",
+       NULL,
+       "TC",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "AZ",
+       "KZ",
+       "BT",
+       NULL,
+       "IN",
+       "IN",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "PK",
+       NULL,
+       "AF",
+       "LK",
+       "MM",
+       "LB",
+       "JO",
+       "SY",
+       "IQ",
+       "KW",
+       "SA",
+       "YE",
+       "OM",
+       NULL,
+       "AE",
+       "IL",
+       "BH",
+       "QA",
+       "MN",
+       "NP",
+       "AE",
+       "AE",
+       "IR",
+       NULL,
+       "UZ",
+       NULL,
+       "TJ",
+       "KG",
+       "TM",
+       NULL,
+       "JP",
+       "JP",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "KR",
+       NULL,
+       "VN",
+       NULL,
+       "HK",
+       "MO",
+       "KH",
+       "LA",
+       NULL,
+       NULL,
+       "CN",
+       "CN",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "TW",
+       "KP",
+       NULL,
+       NULL,
+       "BD",
+       NULL,
+       "MV",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 500 */
+       NULL,
+       "MY",
+       NULL,
+       NULL,
+       "AU",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "ID",
+       NULL,
+       NULL,
+       NULL,
+       "TL",
+       "PH",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "TH",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "SG",
+       NULL,
+       NULL,
+       "BN",
+       NULL,
+       "NZ",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "NR",
+       "PG",
+       NULL,
+       "TO",
+       "SB",
+       "VU",
+       "FJ",
+       "WF",
+       "AS",
+       "KI",
+       "NC",
+       "PF",
+       "CK",
+       "WS",
+       "FM",
+       "MH",
+       "PW",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 600 */
+       NULL,
+       "EG",
+       "DZ",
+       "MA",
+       "TN",
+       "LY",
+       "GM",
+       "SN",
+       "MR",
+       "ML",
+       "GN",
+       "CI",
+       "BF",
+       "NE",
+       "TG",
+       "BJ",
+       "MU",
+       "LR",
+       "SL",
+       "GH",
+       "NG",
+       "TD",
+       "CF",
+       "CM",
+       "CV",
+       "ST",
+       "GQ",
+       "GA",
+       "CG",
+       "CD",
+       "AO",
+       "GW",
+       "SC",
+       "SD",
+       "RW",
+       "ET",
+       "SO",
+       "DJ",
+       "KE",
+       "TZ",
+       "UG",
+       "BI",
+       "MZ",
+       NULL,
+       "ZM",
+       "MG",
+       "TF",
+       "ZW",
+       "NA",
+       "MW",
+       "LS",
+       "BW",
+       "SZ",
+       "KM",
+       "ZA",
+       NULL,
+       "ER",
+       NULL,
+       NULL,
+       NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 700 */
+       NULL,
+       "BZ",
+       NULL,
+       "GT",
+       NULL,
+       "SV",
+       NULL,
+       "HN",
+       NULL,
+       "NI",
+       NULL,
+       "CR",
+       NULL,
+       "PA",
+       NULL,
+       "PE",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "AR",
+       NULL,
+       "BR",
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "CL",
+       NULL,
+       "CO",
+       NULL,
+       "VE",
+       NULL,
+       "BO",
+       NULL,
+       "GY",
+       NULL,
+       "EC",
+       NULL,
+       "GF",
+       NULL,
+       "PY",
+       NULL,
+       "SR",
+       NULL,
+       "UY",
+       NULL,
+       "FK",
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 799 */
+};
diff --git a/plugins/nmcompat.c b/plugins/nmcompat.c
new file mode 100644 (file)
index 0000000..400389b
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ *
+ *  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 <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/notifier.h>
+#include <connman/dbus.h>
+
+enum {
+       NM_STATE_UNKNOWN          = 0,
+       NM_STATE_ASLEEP           = 10,
+       NM_STATE_DISCONNECTED     = 20,
+       NM_STATE_DISCONNECTING    = 30,
+       NM_STATE_CONNECTING       = 40,
+       NM_STATE_CONNECTED_LOCAL  = 50,
+       NM_STATE_CONNECTED_SITE   = 60,
+       NM_STATE_CONNECTED_GLOBAL = 70
+};
+
+#define NM_SERVICE    "org.freedesktop.NetworkManager"
+#define NM_PATH       "/org/freedesktop/NetworkManager"
+#define NM_INTERFACE  NM_SERVICE
+
+#define DBUS_PROPERTIES_INTERFACE      "org.freedesktop.DBus.Properties"
+
+static DBusConnection *connection = NULL;
+static struct connman_service *current_service = NULL;
+static dbus_uint32_t nm_state = NM_STATE_UNKNOWN;
+
+static void state_changed(dbus_uint32_t state)
+{
+       DBusMessage *signal;
+
+       DBG("state %d", state);
+
+       signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE,
+                                               "StateChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state,
+                                               DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static void properties_changed(dbus_uint32_t state)
+{
+       const char *key = "State";
+       DBusMessageIter iter, dict, dict_entry, dict_val;
+       DBusMessage *signal;
+
+       DBG("state %d", state);
+
+       signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE,
+                                               "PropertiesChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                                       &dict);
+
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &dict_entry);
+
+       dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_UINT32_AS_STRING, &dict_val);
+
+       dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_UINT32, &state);
+
+       dbus_message_iter_close_container(&dict_entry, &dict_val);
+       dbus_message_iter_close_container(&dict, &dict_entry);
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static void default_changed(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       if (service == NULL)
+               nm_state = NM_STATE_DISCONNECTED;
+       else
+               nm_state = NM_STATE_CONNECTED_LOCAL;
+
+       state_changed(nm_state);
+       properties_changed(nm_state);
+
+       current_service = service;
+}
+
+static void service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       DBG("service %p state %d", service, state);
+
+       if (current_service == NULL || current_service != service)
+               return;
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+               nm_state = NM_STATE_UNKNOWN;
+               break;
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_IDLE:
+               nm_state = NM_STATE_DISCONNECTED;
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               nm_state = NM_STATE_CONNECTING;
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+               nm_state = NM_STATE_CONNECTED_LOCAL;
+               break;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               nm_state = NM_STATE_CONNECTED_GLOBAL;
+               break;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               nm_state = NM_STATE_DISCONNECTING;
+               break;
+       }
+
+       state_changed(nm_state);
+       properties_changed(nm_state);
+}
+
+static void offline_mode(connman_bool_t enabled)
+{
+       DBG("enabled %d", enabled);
+
+       if (enabled == TRUE)
+               nm_state = NM_STATE_ASLEEP;
+       else
+               nm_state = NM_STATE_DISCONNECTED;
+
+       state_changed(nm_state);
+       properties_changed(nm_state);
+
+       current_service = NULL;
+}
+
+static struct connman_notifier notifier = {
+       .name                   = "nmcompat",
+       .priority               = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+       .default_changed        = default_changed,
+       .service_state_changed  = service_state_changed,
+       .offline_mode           = offline_mode,
+};
+
+static DBusMessage *property_get(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *interface, *key;
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface,
+                               DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);
+
+       DBG("interface %s property %s", interface, key);
+
+       if (g_str_equal(interface, NM_INTERFACE) == FALSE)
+               return dbus_message_new_error(msg, DBUS_ERROR_FAILED,
+                                               "Unsupported interface");
+
+       if (g_str_equal(key, "State") == TRUE) {
+               DBusMessage *reply;
+               DBusMessageIter iter, value;
+
+               DBG("state %d", nm_state);
+
+               reply = dbus_message_new_method_return(msg);
+               if (reply == NULL)
+                       return NULL;
+
+               dbus_message_iter_init_append(reply, &iter);
+
+               dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_UINT32_AS_STRING, &value);
+               dbus_message_iter_append_basic(&value,
+                                               DBUS_TYPE_UINT32, &nm_state);
+               dbus_message_iter_close_container(&iter, &value);
+
+               return reply;
+       }
+
+       return dbus_message_new_error(msg, DBUS_ERROR_FAILED,
+                                               "Unsupported property");
+}
+
+static const GDBusMethodTable methods[] = {
+       { GDBUS_METHOD("Get",
+                       GDBUS_ARGS({ "interface", "s" }, { "key", "s" }),
+                       GDBUS_ARGS({ "property", "v" }), property_get) },
+       { },
+};
+
+static const GDBusSignalTable signals[] = {
+       { GDBUS_SIGNAL("PropertiesChanged",
+                       GDBUS_ARGS({ "properties", "a{sv}" })) },
+       { GDBUS_SIGNAL("StateChanged",
+                       GDBUS_ARGS({ "state", "u" })) },
+       { },
+};
+
+static int nmcompat_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       if (g_dbus_request_name(connection, NM_SERVICE, NULL) == FALSE) {
+               connman_error("nmcompat: failed to register service");
+               return -1;
+       }
+
+       if (connman_notifier_register(&notifier) < 0) {
+               connman_error("nmcompat: failed to register notifier");
+               return -1;
+       }
+
+       if (g_dbus_register_interface(connection, NM_PATH,
+                               DBUS_PROPERTIES_INTERFACE,
+                               methods, signals, NULL, NULL, NULL) == FALSE) {
+               connman_error("nmcompat: failed to register "
+                                               DBUS_PROPERTIES_INTERFACE);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void nmcompat_exit(void)
+{
+       DBG("");
+
+       connman_notifier_unregister(&notifier);
+
+       if (connection == NULL)
+               return;
+
+       g_dbus_unregister_interface(connection, NM_PATH,
+                                       DBUS_PROPERTIES_INTERFACE);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(nmcompat, "NetworkManager compatibility interfaces",
+                       VERSION, CONNMAN_PLUGIN_PRIORITY_DEFAULT,
+                       nmcompat_init, nmcompat_exit)
diff --git a/plugins/ofono.c b/plugins/ofono.c
new file mode 100644 (file)
index 0000000..d9696d7
--- /dev/null
@@ -0,0 +1,2717 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  BWM Car IT GmbH. 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 <errno.h>
+#include <stdlib.h>
+
+#include <gdbus.h>
+#include <string.h>
+#include <stdint.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/network.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+#include <connman/technology.h>
+
+#include "mcc.h"
+
+#define OFONO_SERVICE                  "org.ofono"
+
+#define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
+#define OFONO_MODEM_INTERFACE          OFONO_SERVICE ".Modem"
+#define OFONO_SIM_INTERFACE            OFONO_SERVICE ".SimManager"
+#define OFONO_NETREG_INTERFACE         OFONO_SERVICE ".NetworkRegistration"
+#define OFONO_CM_INTERFACE             OFONO_SERVICE ".ConnectionManager"
+#define OFONO_CONTEXT_INTERFACE                OFONO_SERVICE ".ConnectionContext"
+#define OFONO_CDMA_CM_INTERFACE                OFONO_SERVICE ".cdma.ConnectionManager"
+#define OFONO_CDMA_NETREG_INTERFACE    OFONO_SERVICE ".cdma.NetworkRegistration"
+
+#define MODEM_ADDED                    "ModemAdded"
+#define MODEM_REMOVED                  "ModemRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
+#define CONTEXT_ADDED                  "ContextAdded"
+#define CONTEXT_REMOVED                        "ContextRemoved"
+
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+#define GET_MODEMS                     "GetModems"
+#define GET_CONTEXTS                   "GetContexts"
+
+#define TIMEOUT 40000
+
+enum ofono_api {
+       OFONO_API_SIM =         0x1,
+       OFONO_API_NETREG =      0x2,
+       OFONO_API_CM =          0x4,
+       OFONO_API_CDMA_NETREG = 0x8,
+       OFONO_API_CDMA_CM =     0x10,
+};
+
+/*
+ * The way this plugin works is following:
+ *
+ *   powered -> SubscriberIdentity or Online = True -> gprs, context ->
+ *     attached -> netreg -> ready
+ *
+ * Depending on the modem type, this plugin will behave differently.
+ *
+ * GSM working flow:
+ *
+ * When a new modem appears, the plugin always powers it up. This
+ * allows the plugin to create a connman_device. The core will call
+ * modem_enable() if the technology is enabled. modem_enable() will
+ * then set the modem online. If the technology is disabled then
+ * modem_disable() will just set the modem offline. The modem is
+ * always kept powered all the time.
+ *
+ * After setting the modem online the plugin waits for the
+ * ConnectionManager and ConnectionContext to appear. When the context
+ * signals that it is attached and the NetworkRegistration interface
+ * appears, a new Service will be created and registered at the core.
+ *
+ * When asked to connect to the network (network_connect()) the plugin
+ * will set the Active property on the context. If this operation is
+ * successful the modem is connected to the network. oFono will inform
+ * the plugin about IP configuration through the updating the context's
+ * properties.
+ *
+ * CDMA working flow:
+ *
+ * When a new modem appears, the plugin always powers it up. This
+ * allows the plugin to create connman_device either using IMSI either
+ * using modem Serial if the modem got a SIM interface or not.
+ *
+ * As for GSM, the core will call modem_enable() if the technology
+ * is enabled. modem_enable() will then set the modem online.
+ * If the technology is disabled then modem_disable() will just set the
+ * modem offline. The modem is always kept powered all the time.
+ *
+ * After setting the modem online the plugin waits for CdmaConnectionManager
+ * interface to appear. Then, once CdmaNetworkRegistration appears, a new
+ * Service will be created and registered at the core.
+ *
+ * When asked to connect to the network (network_connect()) the plugin
+ * will power up the CdmaConnectionManager interface.
+ * If the operation is successful the modem is connected to the network.
+ * oFono will inform the plugin about IP configuration through the
+ * updating CdmaConnectionManager settings properties.
+ */
+
+static DBusConnection *connection;
+
+static GHashTable *modem_hash;
+static GHashTable *context_hash;
+
+struct network_context {
+       char *path;
+       int index;
+
+       enum connman_ipconfig_method ipv4_method;
+       struct connman_ipaddress *ipv4_address;
+       char *ipv4_nameservers;
+
+       enum connman_ipconfig_method ipv6_method;
+       struct connman_ipaddress *ipv6_address;
+       char *ipv6_nameservers;
+};
+
+struct modem_data {
+       char *path;
+
+       struct connman_device *device;
+       struct connman_network *network;
+
+       struct network_context *context;
+
+       /* Modem Interface */
+       char *serial;
+       connman_bool_t powered;
+       connman_bool_t online;
+       uint8_t interfaces;
+       connman_bool_t ignore;
+
+       connman_bool_t set_powered;
+
+       /* CDMA ConnectionManager Interface */
+       connman_bool_t cdma_cm_powered;
+
+       /* ConnectionManager Interface */
+       connman_bool_t attached;
+       connman_bool_t cm_powered;
+
+       /* ConnectionContext Interface */
+       connman_bool_t active;
+       connman_bool_t set_active;
+       connman_bool_t valid_apn; /* APN is 'valid' if length > 0 */
+
+       /* SimManager Interface */
+       char *imsi;
+
+       /* Netreg Interface */
+       char *name;
+       uint8_t strength;
+       uint8_t data_strength; /* 1xEVDO signal strength */
+       connman_bool_t roaming;
+
+       /* pending calls */
+       DBusPendingCall *call_set_property;
+       DBusPendingCall *call_get_properties;
+       DBusPendingCall *call_get_contexts;
+};
+
+static const char *api2string(enum ofono_api api)
+{
+       switch (api) {
+       case OFONO_API_SIM:
+               return "sim";
+       case OFONO_API_NETREG:
+               return "netreg";
+       case OFONO_API_CM:
+               return "cm";
+       case OFONO_API_CDMA_NETREG:
+               return "cdma-netreg";
+       case OFONO_API_CDMA_CM:
+               return "cmda-cm";
+       }
+
+       return "unknown";
+}
+
+static char *get_ident(const char *path)
+{
+       char *pos;
+
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
+}
+
+static struct network_context *network_context_alloc(const char *path)
+{
+       struct network_context *context;
+
+       context = g_try_new0(struct network_context, 1);
+       if (context == NULL)
+               return NULL;
+
+       context->path = g_strdup(path);
+       context->index = -1;
+
+       context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       context->ipv4_address = NULL;
+       context->ipv4_nameservers = NULL;
+
+       context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       context->ipv6_address = NULL;
+       context->ipv6_nameservers = NULL;
+
+       return context;
+}
+
+static void network_context_free(struct network_context *context)
+{
+       g_free(context->path);
+
+       connman_ipaddress_free(context->ipv4_address);
+       g_free(context->ipv4_nameservers);
+
+       connman_ipaddress_free(context->ipv6_address);
+       g_free(context->ipv6_nameservers);
+
+       free(context);
+}
+
+static void set_connected(struct modem_data *modem)
+{
+       connman_bool_t setip = FALSE;
+
+       DBG("%s", modem->path);
+
+       if (modem->context->index < 0 ||
+                       modem->context->ipv4_address == NULL) {
+               connman_error("Invalid index and/or address");
+               return;
+       }
+
+       connman_network_set_index(modem->network, modem->context->index);
+
+       switch (modem->context->ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->context->ipv4_method);
+               connman_network_set_ipaddress(modem->network,
+                                               modem->context->ipv4_address);
+               connman_network_set_nameservers(modem->network,
+                                       modem->context->ipv4_nameservers);
+               setip = TRUE;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->context->ipv4_method);
+               setip = TRUE;
+               break;
+       }
+
+       switch (modem->context->ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv6_method(modem->network,
+                                                       modem->context->ipv6_method);
+               connman_network_set_ipaddress(modem->network,
+                                                       modem->context->ipv6_address);
+               setip = TRUE;
+               break;
+       }
+
+       if (setip == TRUE)
+               connman_network_set_connected(modem->network, TRUE);
+}
+
+static void set_disconnected(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       if (modem->network == NULL)
+               return;
+
+       connman_network_set_connected(modem->network, FALSE);
+}
+
+typedef void (*set_property_cb)(struct modem_data *data,
+                               connman_bool_t success);
+typedef void (*get_properties_cb)(struct modem_data *data,
+                               DBusMessageIter *dict);
+
+struct property_info {
+       struct modem_data *modem;
+       const char *path;
+       const char *interface;
+       const char *property;
+       set_property_cb set_property_cb;
+       get_properties_cb get_properties_cb;
+};
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_info *info = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       connman_bool_t success = TRUE;
+
+       DBG("%s path %s %s.%s", info->modem->path,
+               info->path, info->interface, info->property);
+
+       info->modem->call_set_property = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to change property: %s %s.%s: %s %s",
+                               info->path, info->interface, info->property,
+                               error.name, error.message);
+               dbus_error_free(&error);
+               success = FALSE;
+       }
+
+       if (info->set_property_cb != NULL)
+               (*info->set_property_cb)(info->modem, success);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int set_property(struct modem_data *modem,
+                       const char *path, const char *interface,
+                       const char *property, int type, void *value,
+                       set_property_cb notify)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       struct property_info *info;
+
+       DBG("%s path %s %s.%s", modem->path, path, interface, property);
+
+       if (modem->call_set_property != NULL) {
+               DBG("Cancel pending SetProperty");
+
+               dbus_pending_call_cancel(modem->call_set_property);
+               modem->call_set_property = NULL;
+       }
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, path,
+                                       interface, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, property, type, value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_set_property, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s.%s",
+                               path, interface, property);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_set_property == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->property = property;
+       info->set_property_cb = notify;
+
+       dbus_pending_call_set_notify(modem->call_set_property,
+                                       set_property_reply, info, g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_info *info = user_data;
+       DBusMessageIter array, dict;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s path %s %s", info->modem->path, info->path, info->interface);
+
+       info->modem->call_get_properties = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to get properties: %s %s: %s %s",
+                               info->path, info->interface,
+                               error.name, error.message);
+               dbus_error_free(&error);
+
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       if (info->get_properties_cb != NULL)
+               (*info->get_properties_cb)(info->modem, &dict);
+
+done:
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int get_properties(const char *path, const char *interface,
+                               get_properties_cb notify,
+                               struct modem_data *modem)
+{
+       DBusMessage *message;
+       struct property_info *info;
+
+       DBG("%s path %s %s", modem->path, path, interface);
+
+       if (modem->call_get_properties != NULL) {
+               connman_error("Pending GetProperties");
+               return -EBUSY;
+       }
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, path,
+                                       interface, GET_PROPERTIES);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_properties, TIMEOUT) == FALSE) {
+               connman_error("Failed to call %s.GetProperties()", interface);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_get_properties == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->get_properties_cb = notify;
+
+       dbus_pending_call_set_notify(modem->call_get_properties,
+                                       get_properties_reply, info, g_free);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void context_set_active_reply(struct modem_data *modem,
+                                       connman_bool_t success)
+{
+       DBG("%s", modem->path);
+
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
+               return;
+       }
+
+       /*
+        * Active = True might fail due a timeout. That means oFono
+        * still tries to go online. If we retry to set Active = True,
+        * we just get a InProgress error message. Should we power
+        * cycle the modem in such cases?
+        */
+
+       if (modem->network == NULL) {
+               /*
+                * In the case where we power down the device
+                * we don't wait for the reply, therefore the network
+                * might already be gone.
+                */
+               return;
+       }
+
+       connman_network_set_error(modem->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+}
+
+static int context_set_active(struct modem_data *modem,
+                               connman_bool_t active)
+{
+       int err;
+
+       DBG("%s active %d", modem->path, active);
+
+       err = set_property(modem, modem->context->path,
+                               OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN,
+                               &active,
+                               context_set_active_reply);
+
+       if (active == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static void cdma_cm_set_powered_reply(struct modem_data *modem,
+                                       connman_bool_t success)
+{
+       DBG("%s", modem->path);
+
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
+               return;
+       }
+
+       /*
+        * Powered = True might fail due a timeout. That means oFono
+        * still tries to go online. If we retry to set Powered = True,
+        * we just get a InProgress error message. Should we power
+        * cycle the modem in such cases?
+        */
+
+       if (modem->network == NULL) {
+               /*
+                * In the case where we power down the device
+                * we don't wait for the reply, therefore the network
+                * might already be gone.
+                */
+               return;
+       }
+
+       connman_network_set_error(modem->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+}
+
+static int cdma_cm_set_powered(struct modem_data *modem, connman_bool_t powered)
+{
+       int err;
+
+       DBG("%s powered %d", modem->path, powered);
+
+       err = set_property(modem, modem->path, OFONO_CDMA_CM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               cdma_cm_set_powered_reply);
+
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static int modem_set_online(struct modem_data *modem, connman_bool_t online)
+{
+       DBG("%s online %d", modem->path, online);
+
+       return set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Online", DBUS_TYPE_BOOLEAN,
+                               &online,
+                               NULL);
+}
+
+static int cm_set_powered(struct modem_data *modem, connman_bool_t powered)
+{
+       int err;
+
+       DBG("%s powered %d", modem->path, powered);
+
+       err = set_property(modem, modem->path,
+                               OFONO_CM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               NULL);
+
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static int modem_set_powered(struct modem_data *modem, connman_bool_t powered)
+{
+       int err;
+
+       DBG("%s powered %d", modem->path, powered);
+
+       modem->set_powered = powered;
+
+       err = set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               NULL);
+
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
+static connman_bool_t has_interface(uint8_t interfaces,
+                                       enum ofono_api api)
+{
+       if ((interfaces & api) == api)
+               return TRUE;
+
+       return FALSE;
+}
+
+static uint8_t extract_interfaces(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       uint8_t interfaces = 0;
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *name;
+
+               dbus_message_iter_get_basic(&entry, &name);
+
+               if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_SIM;
+               else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_NETREG;
+               else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CM;
+               else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CDMA_CM;
+               else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CDMA_NETREG;
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return interfaces;
+}
+
+static char *extract_nameservers(DBusMessageIter *array)
+{
+       DBusMessageIter entry;
+       char *nameservers = NULL;
+       char *tmp;
+
+       dbus_message_iter_recurse(array, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *nameserver;
+
+               dbus_message_iter_get_basic(&entry, &nameserver);
+
+               if (nameservers == NULL) {
+                       nameservers = g_strdup(nameserver);
+               } else {
+                       tmp = nameservers;
+                       nameservers = g_strdup_printf("%s %s", tmp, nameserver);
+                       g_free(tmp);
+               }
+
+               dbus_message_iter_next(&entry);
+       }
+
+       return nameservers;
+}
+
+static void extract_ipv4_settings(DBusMessageIter *array,
+                               struct network_context *context)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *netmask = NULL, *gateway = NULL;
+       char *nameservers = NULL;
+       const char *interface = NULL;
+       int index = -1;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("Interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+
+                       DBG("index %d", index);
+               } else if (g_str_equal(key, "Method") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       DBG("Method %s", val);
+
+                       if (g_strcmp0(val, "static") == 0) {
+                               context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+                       } else if (g_strcmp0(val, "dhcp") == 0) {
+                               context->ipv4_method = CONNMAN_IPCONFIG_METHOD_DHCP;
+                               break;
+                       }
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("Address %s", address);
+               } else if (g_str_equal(key, "Netmask") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       netmask = g_strdup(val);
+
+                       DBG("Netmask %s", netmask);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
+
+                       DBG("Nameservers %s", nameservers);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("Gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (index < 0)
+               goto out;
+
+       if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED)
+               goto out;
+
+       context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (context->ipv4_address == NULL)
+               goto out;
+
+       context->index = index;
+       connman_ipaddress_set_ipv4(context->ipv4_address, address,
+                               netmask, gateway);
+
+       context->ipv4_nameservers = nameservers;
+
+out:
+       if (context->ipv4_nameservers != nameservers)
+               g_free(nameservers);
+
+       g_free(address);
+       g_free(netmask);
+       g_free(gateway);
+}
+
+static void extract_ipv6_settings(DBusMessageIter *array,
+                               struct network_context *context)
+{
+       DBusMessageIter dict;
+       char *address = NULL, *gateway = NULL;
+       unsigned char prefix_length = 0;
+       char *nameservers = NULL;
+       const char *interface = NULL;
+       int index = -1;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Interface") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &interface);
+
+                       DBG("Interface %s", interface);
+
+                       index = connman_inet_ifindex(interface);
+
+                       DBG("index %d", index);
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       address = g_strdup(val);
+
+                       DBG("Address %s", address);
+               } else if (g_str_equal(key, "PrefixLength") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &prefix_length);
+
+                       DBG("prefix length %d", prefix_length);
+               } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
+
+                       DBG("Nameservers %s", nameservers);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+
+                       gateway = g_strdup(val);
+
+                       DBG("Gateway %s", gateway);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (index < 0)
+               goto out;
+
+       context->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+
+       context->ipv6_address =
+               connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (context->ipv6_address == NULL)
+               goto out;
+
+       context->index = index;
+       connman_ipaddress_set_ipv6(context->ipv6_address, address,
+                               prefix_length, gateway);
+
+       context->ipv6_nameservers = nameservers;
+
+out:
+       if (context->ipv6_nameservers != nameservers)
+               g_free(nameservers);
+
+       g_free(address);
+       g_free(gateway);
+}
+
+static connman_bool_t ready_to_create_device(struct modem_data *modem)
+{
+       /*
+        * There are three different modem types which behave slightly
+        * different:
+        * - GSM modems will expose the SIM interface then the
+        *   CM interface.
+        * - CDMA modems will expose CM first and sometime later
+        *   a unique serial number.
+        *
+        * This functions tests if we have the necessary information gathered
+        * before we are able to create a device.
+        */
+
+       if (modem->device != NULL)
+               return FALSE;
+
+       if (modem->imsi != NULL || modem->serial != NULL)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void create_device(struct modem_data *modem)
+{
+       struct connman_device *device;
+       char *ident = NULL;
+
+       DBG("%s", modem->path);
+
+       if (modem->imsi != NULL)
+               ident = modem->imsi;
+       else if (modem->serial != NULL)
+               ident = modem->serial;
+
+       if (connman_dbus_validate_ident(ident) == FALSE)
+               ident = connman_dbus_encode_string(ident);
+       else
+               ident = g_strdup(ident);
+
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR);
+       if (device == NULL)
+               goto out;
+
+       DBG("device %p", device);
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", modem->path);
+
+       connman_device_set_data(device, modem);
+
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register cellular device");
+               connman_device_unref(device);
+               goto out;
+       }
+
+       modem->device = device;
+
+       connman_device_set_powered(modem->device, modem->online);
+out:
+       g_free(ident);
+}
+
+static void destroy_device(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       connman_device_set_powered(modem->device, FALSE);
+
+       if (modem->network != NULL) {
+               connman_device_remove_network(modem->device, modem->network);
+               connman_network_unref(modem->network);
+               modem->network = NULL;
+       }
+
+       connman_device_unregister(modem->device);
+       connman_device_unref(modem->device);
+
+       modem->device = NULL;
+}
+
+static void add_network(struct modem_data *modem)
+{
+       const char *group;
+
+       DBG("%s", modem->path);
+
+       if (modem->network != NULL)
+               return;
+
+       modem->network = connman_network_create(modem->context->path,
+                                               CONNMAN_NETWORK_TYPE_CELLULAR);
+       if (modem->network == NULL)
+               return;
+
+       DBG("network %p", modem->network);
+
+       connman_network_set_data(modem->network, modem);
+
+       connman_network_set_string(modem->network, "Path",
+                                       modem->context->path);
+
+       connman_network_set_index(modem->network, modem->context->index);
+
+       if (modem->name != NULL)
+               connman_network_set_name(modem->network, modem->name);
+       else
+               connman_network_set_name(modem->network, "");
+
+       connman_network_set_strength(modem->network, modem->strength);
+
+       group = get_ident(modem->context->path);
+       connman_network_set_group(modem->network, group);
+
+       connman_network_set_available(modem->network, TRUE);
+
+       connman_network_set_bool(modem->network, "Roaming",
+                                       modem->roaming);
+
+       if (connman_device_add_network(modem->device, modem->network) < 0) {
+               connman_network_unref(modem->network);
+               modem->network = NULL;
+               return;
+       }
+
+       /*
+        * Create the ipconfig layer before trying to connect. Withouth
+        * the ipconfig layer the core is not ready to process errors.
+        */
+       connman_network_set_index(modem->network, -1);
+}
+
+static void remove_network(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
+
+       if (modem->network == NULL)
+               return;
+
+       DBG("network %p", modem->network);
+
+       connman_device_remove_network(modem->device, modem->network);
+       connman_network_unref(modem->network);
+       modem->network = NULL;
+}
+
+static int add_cm_context(struct modem_data *modem, const char *context_path,
+                               DBusMessageIter *dict)
+{
+       const char *context_type = NULL;
+       struct network_context *context = NULL;
+       connman_bool_t active = FALSE;
+
+       DBG("%s context path %s", modem->path, context_path);
+
+       if (modem->context != NULL) {
+               /*
+                * We have already assigned a context to this modem
+                * and we do only support one Internet context.
+                */
+               return -EALREADY;
+       }
+
+       context = network_context_alloc(context_path);
+       if (context == NULL)
+               return -ENOMEM;
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Type") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &context_type);
+
+                       DBG("%s context %s type %s", modem->path,
+                               context_path, context_type);
+               } else if (g_str_equal(key, "Settings") == TRUE) {
+                       DBG("%s Settings", modem->path);
+
+                       extract_ipv4_settings(&value, context);
+               } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
+                       DBG("%s IPv6.Settings", modem->path);
+
+                       extract_ipv6_settings(&value, context);
+               } else if (g_str_equal(key, "Active") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &active);
+
+                       DBG("%s Active %d", modem->path, active);
+               } else if (g_str_equal(key, "AccessPointName") == TRUE) {
+                       const char *apn;
+
+                       dbus_message_iter_get_basic(&value, &apn);
+                       if (apn != NULL && strlen(apn) > 0)
+                               modem->valid_apn = TRUE;
+                       else
+                               modem->valid_apn = FALSE;
+
+                       DBG("%s AccessPointName '%s'", modem->path, apn);
+               }
+               dbus_message_iter_next(dict);
+       }
+
+       if (g_strcmp0(context_type, "internet") != 0) {
+               network_context_free(context);
+               return -EINVAL;
+       }
+
+       modem->context = context;
+       modem->active = active;
+
+       g_hash_table_replace(context_hash, g_strdup(context_path), modem);
+
+       if (modem->valid_apn == TRUE && modem->attached == TRUE &&
+                       has_interface(modem->interfaces,
+                               OFONO_API_NETREG) == TRUE) {
+               add_network(modem);
+       }
+
+       return 0;
+}
+
+static void remove_cm_context(struct modem_data *modem,
+                               const char *context_path)
+{
+       if (modem->context == NULL)
+               return;
+
+       if (modem->network != NULL)
+               remove_network(modem);
+
+       g_hash_table_remove(context_hash, context_path);
+
+       network_context_free(modem->context);
+       modem->context = NULL;
+
+       modem->valid_apn = FALSE;
+
+       if (modem->network != NULL)
+               remove_network(modem);
+}
+
+static gboolean context_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       const char *context_path = dbus_message_get_path(message);
+       struct modem_data *modem = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("context_path %s", context_path);
+
+       modem = g_hash_table_lookup(context_hash, context_path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       /*
+        * oFono guarantees the ordering of Settings and
+        * Active. Settings will always be send before Active = True.
+        * That means we don't have to order here.
+        */
+       if (g_str_equal(key, "Settings") == TRUE) {
+               DBG("%s Settings", modem->path);
+
+               extract_ipv4_settings(&value, modem->context);
+       } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
+               DBG("%s IPv6.Settings", modem->path);
+
+               extract_ipv6_settings(&value, modem->context);
+       } else if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->active);
+
+               DBG("%s Active %d", modem->path, modem->active);
+
+               if (modem->active == TRUE)
+                       set_connected(modem);
+               else
+                       set_disconnected(modem);
+       } else if (g_str_equal(key, "AccessPointName") == TRUE) {
+               const char *apn;
+
+               dbus_message_iter_get_basic(&value, &apn);
+
+               DBG("%s AccessPointName %s", modem->path, apn);
+
+               if (apn != NULL && strlen(apn) > 0) {
+                       modem->valid_apn = TRUE;
+
+                       if (modem->network != NULL)
+                               return TRUE;
+
+                       if (modem->attached == FALSE)
+                               return TRUE;
+
+                       if (has_interface(modem->interfaces,
+                                       OFONO_API_NETREG) == FALSE) {
+                               return TRUE;
+                       }
+
+                       add_network(modem);
+
+                       if (modem->active == TRUE)
+                               set_connected(modem);
+               } else {
+                       modem->valid_apn = FALSE;
+
+                       if (modem->network == NULL)
+                               return TRUE;
+
+                       remove_network(modem);
+               }
+       }
+
+       return TRUE;
+}
+
+static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data)
+{
+       struct modem_data *modem = user_data;
+       DBusMessageIter array, dict, entry, value;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s", modem->path);
+
+       modem->call_get_contexts = NULL;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               const char *context_path;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &context_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (add_cm_context(modem, context_path, &value) == 0)
+                       break;
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int cm_get_contexts(struct modem_data *modem)
+{
+       DBusMessage *message;
+
+       DBG("%s", modem->path);
+
+       if (modem->call_get_contexts != NULL)
+               return -EBUSY;
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
+                                       OFONO_CM_INTERFACE, GET_CONTEXTS);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_contexts, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetContexts()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (modem->call_get_contexts == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(modem->call_get_contexts,
+                                       cm_get_contexts_reply,
+                                       modem, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static gboolean cm_context_added(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       char *context_path;
+       struct modem_data *modem;
+       DBusMessageIter iter, properties;
+
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &context_path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       if (add_cm_context(modem, context_path, &properties) != 0)
+               return TRUE;
+
+       return TRUE;
+}
+
+static gboolean cm_context_removed(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       const char *context_path;
+       struct modem_data *modem;
+       DBusMessageIter iter;
+
+       DBG("context path %s", path);
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &context_path);
+
+       modem = g_hash_table_lookup(context_hash, context_path);
+       if (modem == NULL)
+               return TRUE;
+
+       remove_cm_context(modem, context_path);
+
+       return TRUE;
+}
+
+static void netreg_update_name(struct modem_data *modem,
+                               DBusMessageIter* value)
+{
+       char *name;
+
+       dbus_message_iter_get_basic(value, &name);
+
+       DBG("%s Name %s", modem->path, name);
+
+       g_free(modem->name);
+       modem->name = g_strdup(name);
+
+       if (modem->network == NULL)
+               return;
+
+       connman_network_set_name(modem->network, modem->name);
+       connman_network_update(modem->network);
+}
+
+static void netreg_update_strength(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->strength);
+
+       DBG("%s Strength %d", modem->path, modem->strength);
+
+       if (modem->network == NULL)
+               return;
+
+       /*
+        * GSM:
+        * We don't have 2 signal notifications we always report the strength
+        * signal. data_strength is always equal to 0.
+        *
+        * CDMA:
+        * In the case we have a data_strength signal (from 1xEVDO network)
+        * we don't need to update the value with strength signal (from 1xCDMA)
+        * because the modem is registered to 1xEVDO network for data call.
+        * In case we have no data_strength signal (not registered to 1xEVDO
+        * network), we must report the strength signal (registered to 1xCDMA
+        * network e.g slow mode).
+        */
+       if (modem->data_strength != 0)
+               return;
+
+       connman_network_set_strength(modem->network, modem->strength);
+       connman_network_update(modem->network);
+}
+
+/* Retrieve 1xEVDO Data Strength signal */
+static void netreg_update_datastrength(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->data_strength);
+
+       DBG("%s Data Strength %d", modem->path, modem->data_strength);
+
+       if (modem->network == NULL)
+               return;
+
+       /*
+        * CDMA modem is not registered to 1xEVDO network, let
+        * update_signal_strength() reporting the value on the Strength signal
+        * notification.
+        */
+       if (modem->data_strength == 0)
+               return;
+
+       connman_network_set_strength(modem->network, modem->data_strength);
+       connman_network_update(modem->network);
+}
+
+static void netreg_update_roaming(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       char *status;
+       connman_bool_t roaming;
+
+       dbus_message_iter_get_basic(value, &status);
+
+       if (g_str_equal(status, "roaming") == TRUE)
+               roaming = TRUE;
+       else
+               roaming = FALSE;
+
+       if (roaming == modem->roaming)
+               return;
+
+       modem->roaming = roaming;
+
+       if (modem->network == NULL)
+               return;
+
+       connman_network_set_bool(modem->network,
+                               "Roaming", modem->roaming);
+       connman_network_update(modem->network);
+}
+
+static void netreg_update_regdom(struct modem_data *modem,
+                               DBusMessageIter *value)
+{
+       char *mobile_country_code;
+       char *alpha2;
+       int mcc;
+
+       dbus_message_iter_get_basic(value, &mobile_country_code);
+
+       DBG("%s MobileContryCode %s", modem->path, mobile_country_code);
+
+
+       mcc = atoi(mobile_country_code);
+       if (mcc > 799 || mcc < 200)
+               return;
+
+       alpha2 = mcc_country_codes[mcc - 200];
+       if (alpha2 != NULL)
+               connman_technology_set_regdom(alpha2);
+}
+
+static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->ignore == TRUE)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Name") == TRUE)
+               netreg_update_name(modem, &value);
+       else if (g_str_equal(key, "Strength") == TRUE)
+               netreg_update_strength(modem, &value);
+       else if (g_str_equal(key, "Status") == TRUE)
+               netreg_update_roaming(modem, &value);
+       else if (g_str_equal(key, "MobileCountryCode") == TRUE)
+               netreg_update_regdom(modem, &value);
+
+       return TRUE;
+}
+
+static void netreg_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Name") == TRUE)
+                       netreg_update_name(modem, &value);
+               else if (g_str_equal(key, "Strength") == TRUE)
+                       netreg_update_strength(modem, &value);
+               else if (g_str_equal(key, "Status") == TRUE)
+                       netreg_update_roaming(modem, &value);
+               else if (g_str_equal(key, "MobileCountryCode") == TRUE)
+                       netreg_update_regdom(modem, &value);
+
+               dbus_message_iter_next(dict);
+       }
+
+       if (modem->context == NULL) {
+               /*
+                * netgreg_get_properties() was issued after we got
+                * cm_get_contexts_reply() where we create the
+                * context. Though before we got the
+                * netreg_properties_reply the context was removed
+                * again. Therefore we have to skip the network
+                * creation.
+                */
+               return;
+       }
+
+       if (modem->valid_apn == TRUE)
+               add_network(modem);
+
+       if (modem->active == TRUE)
+               set_connected(modem);
+}
+
+static int netreg_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_NETREG_INTERFACE,
+                       netreg_properties_reply, modem);
+}
+
+static void add_cdma_network(struct modem_data *modem)
+{
+       /* Be sure that device is created before adding CDMA network */
+       if (modem->device == NULL)
+               return;
+
+       /*
+        * CDMA modems don't need contexts for data call, however the current
+        * add_network() logic needs one, so we create one to proceed.
+        */
+       if (modem->context == NULL)
+               modem->context = network_context_alloc(modem->path);
+
+       if (modem->name == NULL)
+               modem->name = g_strdup("CDMA Network");
+
+       add_network(modem);
+
+       if (modem->cdma_cm_powered == TRUE)
+               set_connected(modem);
+}
+
+static gboolean cdma_netreg_changed(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("");
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->ignore == TRUE)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Name") == TRUE)
+               netreg_update_name(modem, &value);
+       else if (g_str_equal(key, "Strength") == TRUE)
+               netreg_update_strength(modem, &value);
+       else if (g_str_equal(key, "DataStrength") == TRUE)
+               netreg_update_datastrength(modem, &value);
+       else if (g_str_equal(key, "Status") == TRUE)
+               netreg_update_roaming(modem, &value);
+
+       add_cdma_network(modem);
+
+       return TRUE;
+}
+
+static void cdma_netreg_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Name") == TRUE)
+                       netreg_update_name(modem, &value);
+               else if (g_str_equal(key, "Strength") == TRUE)
+                       netreg_update_strength(modem, &value);
+               else if (g_str_equal(key, "DataStrength") == TRUE)
+                       netreg_update_datastrength(modem, &value);
+               else if (g_str_equal(key, "Status") == TRUE)
+                       netreg_update_roaming(modem, &value);
+
+               dbus_message_iter_next(dict);
+       }
+
+       add_cdma_network(modem);
+}
+
+static int cdma_netreg_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CDMA_NETREG_INTERFACE,
+                       cdma_netreg_properties_reply, modem);
+}
+
+static void cm_update_attached(struct modem_data *modem,
+                               DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->attached);
+
+       DBG("%s Attached %d", modem->path, modem->attached);
+
+       if (modem->attached == FALSE)
+               return;
+
+       if (has_interface(modem->interfaces,
+                               OFONO_API_NETREG) == FALSE) {
+               return;
+       }
+
+       netreg_get_properties(modem);
+}
+
+static void cm_update_powered(struct modem_data *modem,
+                               DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->cm_powered);
+
+       DBG("%s ConnnectionManager Powered %d", modem->path,
+               modem->cm_powered);
+
+       if (modem->cm_powered == TRUE)
+               return;
+
+       cm_set_powered(modem, TRUE);
+}
+
+static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->ignore == TRUE)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Attached") == TRUE)
+               cm_update_attached(modem, &value);
+       else if (g_str_equal(key, "Powered") == TRUE)
+               cm_update_powered(modem, &value);
+
+       return TRUE;
+}
+
+static void cdma_cm_update_powered(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->cdma_cm_powered);
+
+       DBG("%s CDMA cm Powered %d", modem->path, modem->cdma_cm_powered);
+
+       if (modem->network == NULL)
+               return;
+
+       if (modem->cdma_cm_powered == TRUE)
+               set_connected(modem);
+       else
+               set_disconnected(modem);
+}
+
+static void cdma_cm_update_settings(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       DBG("%s Settings", modem->path);
+
+       extract_ipv4_settings(value, modem->context);
+}
+
+static gboolean cdma_cm_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->online == TRUE && modem->network == NULL)
+               cdma_netreg_get_properties(modem);
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Powered") == TRUE)
+               cdma_cm_update_powered(modem, &value);
+       if (g_str_equal(key, "Settings") == TRUE)
+               cdma_cm_update_settings(modem, &value);
+
+       return TRUE;
+}
+
+static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Attached") == TRUE)
+                       cm_update_attached(modem, &value);
+               else if (g_str_equal(key, "Powered") == TRUE)
+                       cm_update_powered(modem, &value);
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static int cm_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CM_INTERFACE,
+                               cm_properties_reply, modem);
+}
+
+static void cdma_cm_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       if (modem->online == TRUE)
+               cdma_netreg_get_properties(modem);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Powered") == TRUE)
+                       cdma_cm_update_powered(modem, &value);
+               if (g_str_equal(key, "Settings") == TRUE)
+                       cdma_cm_update_settings(modem, &value);
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static int cdma_cm_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CDMA_CM_INTERFACE,
+                               cdma_cm_properties_reply, modem);
+}
+
+static void sim_update_imsi(struct modem_data *modem,
+                               DBusMessageIter* value)
+{
+       char *imsi;
+
+       dbus_message_iter_get_basic(value, &imsi);
+
+       DBG("%s imsi %s", modem->path, imsi);
+
+       g_free(modem->imsi);
+       modem->imsi = g_strdup(imsi);
+}
+
+static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->ignore == TRUE)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
+               sim_update_imsi(modem, &value);
+
+               if (ready_to_create_device(modem) == FALSE)
+                       return TRUE;
+
+               /*
+                * This is a GSM modem. Create the device and
+                * register it at the core. Enabling (setting
+                * it online is done through the
+                * modem_enable() callback.
+                */
+               create_device(modem);
+       }
+
+       return TRUE;
+}
+
+static void sim_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
+                       sim_update_imsi(modem, &value);
+
+                       if (ready_to_create_device(modem) == FALSE)
+                               return;
+
+                       /*
+                        * This is a GSM modem. Create the device and
+                        * register it at the core. Enabling (setting
+                        * it online is done through the
+                        * modem_enable() callback.
+                        */
+                       create_device(modem);
+
+                       if (modem->online == FALSE)
+                               return;
+
+                       /*
+                        * The modem is already online and we have the CM interface.
+                        * There will be no interface update and therefore our
+                        * state machine will not go to next step. We have to
+                        * trigger it from here.
+                        */
+                       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
+                               cm_get_properties(modem);
+                               cm_get_contexts(modem);
+                       }
+                       return;
+               }
+
+               dbus_message_iter_next(dict);
+       }
+}
+
+static int sim_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_SIM_INTERFACE,
+                               sim_properties_reply, modem);
+}
+
+static connman_bool_t api_added(uint8_t old_iface, uint8_t new_iface,
+                               enum ofono_api api)
+{
+       if (has_interface(old_iface, api) == FALSE &&
+                       has_interface(new_iface, api) == TRUE) {
+               DBG("%s added", api2string(api));
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t api_removed(uint8_t old_iface, uint8_t new_iface,
+                               enum ofono_api api)
+{
+       if (has_interface(old_iface, api) == TRUE &&
+                       has_interface(new_iface, api) == FALSE) {
+               DBG("%s removed", api2string(api));
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void modem_update_interfaces(struct modem_data *modem,
+                               uint8_t old_ifaces,
+                               uint8_t new_ifaces)
+{
+       DBG("%s", modem->path);
+
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_SIM) == TRUE) {
+               if (modem->imsi == NULL &&
+                               modem->set_powered == FALSE) {
+                       /*
+                        * Only use do GetProperties() when
+                        * device has not been powered up.
+                        */
+                       sim_get_properties(modem);
+               }
+       }
+
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CM) == TRUE) {
+               if (modem->device != NULL) {
+                       cm_get_properties(modem);
+                       cm_get_contexts(modem);
+               }
+       }
+
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) {
+               if (ready_to_create_device(modem) == TRUE)
+                       create_device(modem);
+
+               if (modem->device != NULL)
+                       cdma_cm_get_properties(modem);
+       }
+
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) {
+               if (modem->attached == TRUE)
+                       netreg_get_properties(modem);
+       }
+
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG) == TRUE) {
+               cdma_netreg_get_properties(modem);
+       }
+
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CM) == TRUE) {
+               remove_cm_context(modem, modem->context->path);
+       }
+
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) {
+               remove_cm_context(modem, modem->context->path);
+       }
+
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) {
+               remove_network(modem);
+       }
+
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG == TRUE)) {
+               remove_network(modem);
+       }
+}
+
+static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
+
+       if (modem->ignore == TRUE)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->powered);
+
+               DBG("%s Powered %d", modem->path, modem->powered);
+
+               if (modem->powered == FALSE)
+                       modem_set_powered(modem, TRUE);
+       } else if (g_str_equal(key, "Online") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->online);
+
+               DBG("%s Online %d", modem->path, modem->online);
+
+               if (modem->device == NULL)
+                       return TRUE;
+
+               connman_device_set_powered(modem->device, modem->online);
+       } else if (g_str_equal(key, "Interfaces") == TRUE) {
+               uint8_t interfaces;
+
+               interfaces = extract_interfaces(&value);
+
+               if (interfaces == modem->interfaces)
+                       return TRUE;
+
+               DBG("%s Interfaces 0x%02x", modem->path, interfaces);
+
+               modem_update_interfaces(modem, modem->interfaces, interfaces);
+
+               modem->interfaces = interfaces;
+       } else if (g_str_equal(key, "Serial") == TRUE) {
+               char *serial;
+
+               dbus_message_iter_get_basic(&value, &serial);
+
+               g_free(modem->serial);
+               modem->serial = g_strdup(serial);
+
+               DBG("%s Serial %s", modem->path, modem->serial);
+
+               if (has_interface(modem->interfaces,
+                                        OFONO_API_CDMA_CM) == TRUE) {
+                       if (ready_to_create_device(modem) == TRUE)
+                               create_device(modem);
+               }
+       }
+
+       return TRUE;
+}
+
+static void add_modem(const char *path, DBusMessageIter *prop)
+{
+       struct modem_data *modem;
+
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem != NULL) {
+               /*
+                * When oFono powers up we ask for the modems and oFono is
+                * reporting with modem_added signal the modems. Only
+                * handle them once.
+                */
+               return;
+       }
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return;
+
+       modem->path = g_strdup(path);
+
+       g_hash_table_insert(modem_hash, g_strdup(path), modem);
+
+       while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(prop, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Powered") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &modem->powered);
+
+                       DBG("%s Powered %d", modem->path, modem->powered);
+               } else if (g_str_equal(key, "Online") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &modem->online);
+
+                       DBG("%s Online %d", modem->path, modem->online);
+               } else if (g_str_equal(key, "Interfaces") == TRUE) {
+                       modem->interfaces = extract_interfaces(&value);
+
+                       DBG("%s Interfaces 0x%02x", modem->path,
+                               modem->interfaces);
+               } else if (g_str_equal(key, "Serial") == TRUE) {
+                       char *serial;
+
+                       dbus_message_iter_get_basic(&value, &serial);
+                       modem->serial = g_strdup(serial);
+
+                       DBG("%s Serial %s", modem->path, modem->serial);
+               } else if (g_str_equal(key, "Type") == TRUE) {
+                       char *type;
+
+                       dbus_message_iter_get_basic(&value, &type);
+
+                       DBG("%s Type %s", modem->path, type);
+                       if (g_strcmp0(type, "hardware") != 0) {
+                               DBG("%s Ignore this modem", modem->path);
+                               modem->ignore = TRUE;
+                       }
+               }
+
+               dbus_message_iter_next(prop);
+       }
+
+       if (modem->ignore == TRUE)
+               return;
+
+       if (modem->powered == FALSE) {
+               modem_set_powered(modem, TRUE);
+               return;
+       }
+
+       modem_update_interfaces(modem, 0, modem->interfaces);
+}
+
+static void modem_power_down(gpointer key, gpointer value, gpointer user_data)
+{
+       struct modem_data *modem = value;
+
+       DBG("%s", modem->path);
+
+       if (modem->ignore ==  TRUE)
+               return;
+
+       modem_set_powered(modem, FALSE);
+}
+
+static void remove_modem(gpointer data)
+{
+       struct modem_data *modem = data;
+
+       DBG("%s", modem->path);
+
+       if (modem->call_set_property != NULL)
+               dbus_pending_call_cancel(modem->call_set_property);
+
+       if (modem->call_get_properties != NULL)
+               dbus_pending_call_cancel(modem->call_get_properties);
+
+       if (modem->call_get_contexts != NULL)
+               dbus_pending_call_cancel(modem->call_get_contexts);
+
+       if (modem->device != NULL)
+               destroy_device(modem);
+
+       if (modem->context != NULL)
+               remove_cm_context(modem, modem->context->path);
+
+       g_free(modem->serial);
+       g_free(modem->name);
+       g_free(modem->imsi);
+       g_free(modem->path);
+
+       g_free(modem);
+}
+
+static gboolean modem_added(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       add_modem(path, &properties);
+
+       return TRUE;
+}
+
+static gboolean modem_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       g_hash_table_remove(modem_hash, path);
+
+       return TRUE;
+}
+
+static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter value, properties;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict, &value);
+               dbus_message_iter_get_basic(&value, &path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value, &properties);
+
+               add_modem(path, &properties);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int manager_get_modems(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, "/",
+                                       OFONO_MANAGER_INTERFACE, GET_MODEMS);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                              &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetModems()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, manager_get_modems_reply,
+                                       NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void ofono_connect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_modem);
+       if (modem_hash == NULL)
+               return;
+
+       context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+       if (context_hash == NULL) {
+               g_hash_table_destroy(modem_hash);
+               return;
+       }
+
+       manager_get_modems();
+}
+
+static void ofono_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       if (modem_hash == NULL || context_hash == NULL)
+               return;
+
+       g_hash_table_destroy(modem_hash);
+       modem_hash = NULL;
+
+       g_hash_table_destroy(context_hash);
+       context_hash = NULL;
+}
+
+static int network_probe(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
+               return context_set_active(modem, TRUE);
+       else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
+               return cdma_cm_set_powered(modem, TRUE);
+
+       connman_error("Connection manager interface not available");
+
+       return -ENOSYS;
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
+               return context_set_active(modem, FALSE);
+       else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
+               return cdma_cm_set_powered(modem, FALSE);
+
+       connman_error("Connection manager interface not available");
+
+       return -ENOSYS;
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "cellular",
+       .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int modem_probe(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       return 0;
+}
+
+static void modem_remove(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+}
+
+static int modem_enable(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       if (modem->online == TRUE)
+               return 0;
+
+       return modem_set_online(modem, TRUE);
+}
+
+static int modem_disable(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       if (modem->online == FALSE)
+               return 0;
+
+       return modem_set_online(modem, FALSE);
+}
+
+static struct connman_device_driver modem_driver = {
+       .name           = "modem",
+       .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
+       .probe          = modem_probe,
+       .remove         = modem_remove,
+       .enable         = modem_enable,
+       .disable        = modem_disable,
+};
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "cellular",
+       .type           = CONNMAN_SERVICE_TYPE_CELLULAR,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+};
+
+static guint watch;
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint modem_watch;
+static guint cm_watch;
+static guint sim_watch;
+static guint context_added_watch;
+static guint context_removed_watch;
+static guint netreg_watch;
+static guint context_watch;
+static guint cdma_cm_watch;
+static guint cdma_netreg_watch;
+
+static int ofono_init(void)
+{
+       int err;
+
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection,
+                                       OFONO_SERVICE, ofono_connect,
+                                       ofono_disconnect, NULL, NULL);
+
+       modem_added_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_MANAGER_INTERFACE,
+                                               MODEM_ADDED,
+                                               modem_added,
+                                               NULL, NULL);
+
+       modem_removed_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
+                                               OFONO_MANAGER_INTERFACE,
+                                               MODEM_REMOVED,
+                                               modem_removed,
+                                               NULL, NULL);
+
+       modem_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
+                                               OFONO_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               modem_changed,
+                                               NULL, NULL);
+
+       cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cm_changed,
+                                               NULL, NULL);
+
+       sim_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
+                                               OFONO_SIM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               sim_changed,
+                                               NULL, NULL);
+
+       context_added_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_ADDED,
+                                               cm_context_added,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection,
+                                               OFONO_SERVICE, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               cm_context_removed,
+                                               NULL, NULL);
+
+       context_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CONTEXT_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               context_changed,
+                                               NULL, NULL);
+
+       netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE, NULL,
+                                               OFONO_NETREG_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               netreg_changed,
+                                               NULL, NULL);
+
+       cdma_cm_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CDMA_CM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cdma_cm_changed,
+                                               NULL, NULL);
+
+       cdma_netreg_watch = g_dbus_add_signal_watch(connection, OFONO_SERVICE,
+                                               NULL, OFONO_CDMA_NETREG_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cdma_netreg_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
+                       modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
+                       context_added_watch == 0 ||
+                       context_removed_watch == 0 ||
+                       context_watch == 0 || netreg_watch == 0 ||
+                       cdma_cm_watch == 0 || cdma_netreg_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&modem_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&modem_driver);
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, cdma_netreg_watch);
+       g_dbus_remove_watch(connection, cdma_cm_watch);
+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, watch);
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void ofono_exit(void)
+{
+       DBG("");
+
+       if (modem_hash != NULL) {
+               /*
+                * We should propably wait for the SetProperty() reply
+                * message, because ...
+                */
+               g_hash_table_foreach(modem_hash, modem_power_down, NULL);
+
+               /*
+                * ... here we will cancel the call.
+                */
+               g_hash_table_destroy(modem_hash);
+               modem_hash = NULL;
+       }
+
+       if (context_hash != NULL) {
+               g_hash_table_destroy(context_hash);
+               context_hash = NULL;
+       }
+
+       connman_technology_driver_unregister(&tech_driver);
+       connman_device_driver_unregister(&modem_driver);
+       connman_network_driver_unregister(&network_driver);
+
+       g_dbus_remove_watch(connection, cdma_netreg_watch);
+       g_dbus_remove_watch(connection, cdma_cm_watch);
+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, watch);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)
diff --git a/plugins/openconnect.c b/plugins/openconnect.c
new file mode 100644 (file)
index 0000000..70be7ae
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *
+ *  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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/ipconfig.h>
+
+#include "vpn.h"
+
+static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       const char *domain = NULL;
+       char *addressv4 = NULL, *addressv6 = NULL;
+       char *netmask = NULL, *gateway = NULL;
+       unsigned char prefix_len = 0;
+       struct connman_ipaddress *ipaddress;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (!provider) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       domain = connman_provider_get_string(provider, "VPN.Domain");
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               if (strcmp(key, "CISCO_CSTP_OPTIONS"))
+                       DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "VPNGATEWAY"))
+                       gateway = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
+                       addressv4 = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
+                       addressv6 = g_strdup(value);
+                       prefix_len = 128;
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
+                       netmask = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
+                       char *sep;
+
+                       /* The netmask contains the address and the prefix */
+                       sep = strchr(value, '/');
+                       if (sep != NULL) {
+                               unsigned char ip_len = sep - value;
+
+                               addressv6 = g_strndup(value, ip_len);
+                               prefix_len = (unsigned char)
+                                               strtol(sep + 1, NULL, 10);
+                       }
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS") ||
+                               !strcmp(key, "INTERNAL_IP6_DNS"))
+                       connman_provider_set_nameservers(provider, value);
+
+               if (!strcmp(key, "CISCO_PROXY_PAC"))
+                       connman_provider_set_pac(provider, value);
+
+               if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
+                       domain = value;
+
+               if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
+                       g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
+                       connman_provider_append_route(provider, key, value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       DBG("%p %p", addressv4, addressv6);
+
+       if (addressv4 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET);
+       else if (addressv6 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET6);
+       else
+               ipaddress = NULL;
+
+       if (ipaddress == NULL) {
+               g_free(addressv4);
+               g_free(addressv6);
+               g_free(netmask);
+               g_free(gateway);
+
+               return VPN_STATE_FAILURE;
+       }
+
+       if (addressv4 != NULL)
+               connman_ipaddress_set_ipv4(ipaddress, addressv4,
+                                               netmask, gateway);
+       else
+               connman_ipaddress_set_ipv6(ipaddress, addressv6,
+                                               prefix_len, gateway);
+       connman_provider_set_ipaddress(provider, ipaddress);
+       connman_provider_set_domain(provider, domain);
+
+       g_free(addressv4);
+       g_free(addressv6);
+       g_free(netmask);
+       g_free(gateway);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int oc_connect(struct connman_provider *provider,
+                       struct connman_task *task, const char *if_name)
+{
+       const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
+       int fd, err;
+
+       vpnhost = connman_provider_get_string(provider, "Host");
+       if (!vpnhost) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       vpncookie = connman_provider_get_string(provider, "OpenConnect.Cookie");
+       if (!vpncookie) {
+               connman_error("OpenConnect.Cookie not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       certsha1 = connman_provider_get_string(provider,
+                                               "OpenConnect.ServerCert");
+       if (certsha1)
+               connman_task_add_argument(task, "--servercert",
+                                                       (char *)certsha1);
+
+       cafile = connman_provider_get_string(provider, "OpenConnect.CACert");
+       mtu = connman_provider_get_string(provider, "VPN.MTU");
+
+       if (cafile)
+               connman_task_add_argument(task, "--cafile",
+                                                       (char *)cafile);
+       if (mtu)
+               connman_task_add_argument(task, "--mtu", (char *)mtu);
+
+       connman_task_add_argument(task, "--syslog", NULL);
+       connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+
+       connman_task_add_argument(task, "--script",
+                                 SCRIPTDIR "/openconnect-script");
+
+       connman_task_add_argument(task, "--interface", if_name);
+
+       connman_task_add_argument(task, (char *)vpnhost, NULL);
+
+       err = connman_task_run(task, vpn_died, provider,
+                              &fd, NULL, NULL);
+       if (err < 0) {
+               connman_error("openconnect failed to start");
+               return -EIO;
+       }
+
+       if (write(fd, vpncookie, strlen(vpncookie)) !=
+                       (ssize_t)strlen(vpncookie) ||
+                       write(fd, "\n", 1) != 1) {
+               connman_error("openconnect failed to take cookie on stdin");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int oc_save (struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *setting;
+
+       setting = connman_provider_get_string(provider,
+                                       "OpenConnect.ServerCert");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "OpenConnect.ServerCert", setting);
+
+       setting = connman_provider_get_string(provider,
+                                       "OpenConnect.CACert");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "OpenConnect.CACert", setting);
+
+       setting = connman_provider_get_string(provider,
+                                       "VPN.MTU");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "VPN.MTU", setting);
+
+       return 0;
+}
+
+static int oc_error_code(int exit_code)
+{
+
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       case 2:
+               return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .notify         = oc_notify,
+       .connect        = oc_connect,
+       .error_code     = oc_error_code,
+       .save           = oc_save,
+};
+
+static int openconnect_init(void)
+{
+       return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
+}
+
+static void openconnect_exit(void)
+{
+       vpn_unregister("openconnect");
+}
+
+CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)
diff --git a/plugins/openvpn.c b/plugins/openvpn.c
new file mode 100644 (file)
index 0000000..8df8a3f
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/ipconfig.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static DBusConnection *connection;
+
+struct {
+       const char *cm_opt;
+       const char *ov_opt;
+       char       has_value;
+} ov_options[] = {
+       { "Host", "--remote", 1 },
+       { "OpenVPN.CACert", "--ca", 1 },
+       { "OpenVPN.Cert", "--cert", 1 },
+       { "OpenVPN.Key", "--key", 1 },
+       { "OpenVPN.MTU", "--mtu", 1 },
+       { "OpenVPN.Proto", "--proto", 1 },
+       { "OpenVPN.Port", "--port", 1 },
+       { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
+       { "OpenVPN.TLSRemote", "--tls-remote", 1 },
+       { "OpenVPN.Cipher", "--cipher", 1 },
+       { "OpenVPN.Auth", "--auth", 1 },
+       { "OpenVPN.CompLZO", "--comp-lzo", 0 },
+       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
+};
+
+static void ov_append_dns_entries(const char *key, const char *value,
+                                       char **dns_entries)
+{
+       gchar **options;
+
+       if (g_str_has_prefix(key, "foreign_option_") == FALSE)
+               return;
+
+       options = g_strsplit(value, " ", 3);
+       if (options[0] != NULL &&
+               !strcmp(options[0], "dhcp-option") &&
+                       options[1] != NULL &&
+                       !strcmp(options[1], "DNS") &&
+                               options[2] != NULL) {
+
+               if (*dns_entries != NULL) {
+                       char *tmp;
+
+                       tmp = g_strjoin(" ", *dns_entries,
+                                               options[2], NULL);
+                       g_free(*dns_entries);
+                       *dns_entries = tmp;
+               } else {
+                       *dns_entries = g_strdup(options[2]);
+               }
+       }
+
+       g_strfreev(options);
+}
+
+static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       char *nameservers = NULL;
+       char *address = NULL, *gateway = NULL, *peer = NULL;
+       struct connman_ipaddress *ipaddress;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (!provider) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "up"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "trusted_ip")) {
+                       connman_provider_set_string(provider, "Gateway", value);
+                       gateway = g_strdup(value);
+               }
+
+               if (!strcmp(key, "ifconfig_local")) {
+                       connman_provider_set_string(provider, "Address", value);
+                       address = g_strdup(value);
+               }
+
+               if (!strcmp(key, "ifconfig_remote")) {
+                       connman_provider_set_string(provider, "Peer", value);
+                       peer = g_strdup(value);
+               }
+
+               if (g_str_has_prefix(key, "route_") == TRUE)
+                       connman_provider_append_route(provider, key, value);
+
+               ov_append_dns_entries(key, value, &nameservers);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       ipaddress = connman_ipaddress_alloc(AF_INET);
+       if (ipaddress == NULL) {
+               g_free(nameservers);
+               g_free(address);
+               g_free(gateway);
+               g_free(peer);
+
+               return VPN_STATE_FAILURE;
+       }
+
+       connman_ipaddress_set_ipv4(ipaddress, address, NULL, gateway);
+       connman_ipaddress_set_peer(ipaddress, peer);
+       connman_provider_set_ipaddress(provider, ipaddress);
+
+       connman_provider_set_nameservers(provider, nameservers);
+
+       g_free(nameservers);
+       g_free(address);
+       g_free(gateway);
+       g_free(peer);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int ov_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+               if (strncmp(ov_options[i].cm_opt, "OpenVPN.", 8) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       ov_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       ov_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static int task_append_config_data(struct connman_provider *provider,
+                                       struct connman_task *task)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+               option = connman_provider_get_string(provider,
+                                       ov_options[i].cm_opt);
+               if (option == NULL)
+                       continue;
+
+               if (connman_task_add_argument(task,
+                                       ov_options[i].ov_opt,
+                                       ov_options[i].has_value ? option : NULL) < 0) {
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+static int ov_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *option;
+       int err, fd;
+
+       option = connman_provider_get_string(provider, "Host");
+       if (option == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       task_append_config_data(provider, task);
+
+       connman_task_add_argument(task, "--syslog", NULL);
+
+       connman_task_add_argument(task, "--script-security", "2");
+
+       connman_task_add_argument(task, "--up",
+                                       SCRIPTDIR "/openvpn-script");
+       connman_task_add_argument(task, "--up-restart", NULL);
+
+       connman_task_add_argument(task, "--setenv", NULL);
+       connman_task_add_argument(task, "CONNMAN_BUSNAME",
+                                       dbus_bus_get_unique_name(connection));
+
+       connman_task_add_argument(task, "--setenv", NULL);
+       connman_task_add_argument(task, "CONNMAN_INTERFACE",
+                                       CONNMAN_TASK_INTERFACE);
+
+       connman_task_add_argument(task, "--setenv", NULL);
+       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-type", "tun");
+
+       connman_task_add_argument(task, "--tls-client", NULL);
+       connman_task_add_argument(task, "--nobind", NULL);
+       connman_task_add_argument(task, "--persist-key", NULL);
+       connman_task_add_argument(task, "--persist-tun", NULL);
+
+       connman_task_add_argument(task, "--route-noexec", NULL);
+       connman_task_add_argument(task, "--ifconfig-noexec", NULL);
+
+       /*
+        * Disable client restarts because we can't handle this at the
+        * moment. The problem is that when OpenVPN decides to switch
+        * 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
+        * trying to resolve the OpenVPN servers address.
+        */
+       connman_task_add_argument(task, "--ping-restart", "0");
+
+       connman_task_add_argument(task, "--client", NULL);
+
+       fd = fileno(stderr);
+       err = connman_task_run(task, vpn_died, provider,
+                       NULL, &fd, &fd);
+       if (err < 0) {
+               connman_error("openvpn failed to start");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static struct vpn_driver vpn_driver = {
+       .notify = ov_notify,
+       .connect        = ov_connect,
+       .save           = ov_save,
+};
+
+static int openvpn_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return vpn_register("openvpn", &vpn_driver, OPENVPN);
+}
+
+static void openvpn_exit(void)
+{
+       vpn_unregister("openvpn");
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(openvpn, "OpenVPN plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, openvpn_init, openvpn_exit)
diff --git a/plugins/pacrunner.c b/plugins/pacrunner.c
new file mode 100644 (file)
index 0000000..418dce2
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/notifier.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+#include <connman/proxy.h>
+
+#define PACRUNNER_SERVICE      "org.pacrunner"
+#define PACRUNNER_INTERFACE    "org.pacrunner.Manager"
+#define PACRUNNER_PATH         "/org/pacrunner/manager"
+
+#define PACRUNNER_CLIENT_INTERFACE     "org.pacrunner.Client"
+#define PACRUNNER_CLIENT_PATH          "/org/pacrunner/client"
+
+#define DBUS_TIMEOUT   5000
+
+struct proxy_data {
+       struct connman_service *service;
+       char *url;
+};
+
+static DBusConnection *connection;
+static dbus_bool_t daemon_running = FALSE;
+
+static struct connman_service *default_service = NULL;
+static char *current_config = NULL;
+
+static void create_config_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               connman_error("Failed to create proxy configuration");
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto done;
+
+       g_free(current_config);
+       current_config = g_strdup(path);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void append_string(DBusMessageIter *iter, void *user_data)
+{
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, user_data);
+}
+
+static void append_string_list(DBusMessageIter *iter, void *user_data)
+{
+       char **list = user_data;
+       int i;
+
+       for (i = 0; list[i] != NULL; i++)
+               dbus_message_iter_append_basic(iter,
+                                       DBUS_TYPE_STRING, &list[i]);
+}
+
+static void create_proxy_configuration(void)
+{
+       DBusMessage *msg;
+       DBusMessageIter iter, dict;
+       DBusPendingCall *call;
+       dbus_bool_t result;
+       char *interface;
+       const char *method;
+       const char *str;
+       char **str_list;
+
+       if (default_service == NULL)
+               return;
+
+       DBG("");
+
+       msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
+                       PACRUNNER_INTERFACE, "CreateProxyConfiguration");
+       if (msg == NULL)
+               return;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       dbus_message_iter_init_append(msg, &iter);
+       connman_dbus_dict_open(&iter, &dict);
+
+       switch(connman_service_get_proxy_method(default_service)) {
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               connman_dbus_dict_close(&iter, &dict);
+               goto done;
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               method= "direct";
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               method = "manual";
+
+               str_list = connman_service_get_proxy_servers(default_service);
+               if (str_list == NULL) {
+                       connman_dbus_dict_close(&iter, &dict);
+                       goto done;
+               }
+
+               connman_dbus_dict_append_array(&dict, "Servers",
+                                       DBUS_TYPE_STRING, append_string_list,
+                                       str_list);
+               g_strfreev(str_list);
+
+               str_list = connman_service_get_proxy_excludes(default_service);
+               if (str_list == NULL)
+                       break;
+
+               connman_dbus_dict_append_array(&dict, "Excludes",
+                                       DBUS_TYPE_STRING, append_string_list,
+                                       str_list);
+               g_strfreev(str_list);
+
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               method = "auto";
+
+               str = connman_service_get_proxy_url(default_service);
+               if (str == NULL) {
+                       str = connman_service_get_proxy_autoconfig(
+                                                       default_service);
+                       if (str == NULL) {
+                               connman_dbus_dict_close(&iter, &dict);
+                               goto done;
+                       }
+               }
+
+               connman_dbus_dict_append_basic(&dict, "URL",
+                                       DBUS_TYPE_STRING, &str);
+               break;
+       }
+
+       connman_dbus_dict_append_basic(&dict, "Method",
+                               DBUS_TYPE_STRING, &method);
+
+       interface = connman_service_get_interface(default_service);
+       if (interface != NULL) {
+               connman_dbus_dict_append_basic(&dict, "Interface",
+                                               DBUS_TYPE_STRING, &interface);
+               g_free(interface);
+       }
+
+       str = connman_service_get_domainname(default_service);
+       if (str != NULL)
+               connman_dbus_dict_append_array(&dict, "Domains",
+                                       DBUS_TYPE_STRING, append_string, &str);
+
+       str_list = connman_service_get_nameservers(default_service);
+       if (str_list != NULL)
+               connman_dbus_dict_append_array(&dict, "Nameservers",
+                                       DBUS_TYPE_STRING, append_string_list,
+                                       str_list);
+       g_strfreev(str_list);
+
+       connman_dbus_dict_close(&iter, &dict);
+
+       result = dbus_connection_send_with_reply(connection, msg,
+                                                       &call, DBUS_TIMEOUT);
+
+       if (result == FALSE || call == NULL)
+               goto done;
+
+       dbus_pending_call_set_notify(call, create_config_reply, NULL, NULL);
+
+       dbus_pending_call_unref(call);
+
+done:
+       dbus_message_unref(msg);
+}
+
+static void destroy_config_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       DBG("");
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               connman_error("Failed to destoy proxy configuration");
+
+       dbus_message_unref(reply);
+}
+
+static void destroy_proxy_configuration(void)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       dbus_bool_t result;
+
+       if (current_config == NULL)
+               return;
+
+       DBG("");
+
+       msg = dbus_message_new_method_call(PACRUNNER_SERVICE, PACRUNNER_PATH,
+                       PACRUNNER_INTERFACE, "DestroyProxyConfiguration");
+       if (msg == NULL)
+               return;
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &current_config,
+                                                       DBUS_TYPE_INVALID);
+
+       result = dbus_connection_send_with_reply(connection, msg,
+                                                       &call, DBUS_TIMEOUT);
+
+       dbus_message_unref(msg);
+
+       if (result == FALSE || call == NULL)
+               return;
+
+       dbus_pending_call_set_notify(call, destroy_config_reply, NULL, NULL);
+
+       dbus_pending_call_unref(call);
+
+       g_free(current_config);
+       current_config = NULL;
+}
+
+static void default_service_changed(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       if (service == default_service)
+               return;
+
+       default_service = service;
+
+       if (daemon_running == FALSE)
+               return;
+
+       destroy_proxy_configuration();
+
+       create_proxy_configuration();
+}
+
+static void proxy_changed(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       if (service != default_service)
+               return;
+
+       if (daemon_running == FALSE)
+               return;
+
+       destroy_proxy_configuration();
+
+       create_proxy_configuration();
+}
+
+static struct connman_notifier pacrunner_notifier = {
+       .name                   = "pacrunner",
+       .default_changed        = default_service_changed,
+       .proxy_changed          = proxy_changed,
+};
+
+static void pacrunner_connect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       daemon_running = TRUE;
+
+       create_proxy_configuration();
+}
+
+static void pacrunner_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       daemon_running = FALSE;
+
+       g_free(current_config);
+       current_config = NULL;
+}
+
+static char * parse_url(const char *url)
+{
+       char *scheme, *host, *path, *host_ret;
+
+       scheme = g_strdup(url);
+       if (scheme == NULL)
+               return NULL;
+
+       host = strstr(scheme, "://");
+       if (host != NULL) {
+               *host = '\0';
+               host += 3;
+       } else
+               host = scheme;
+
+       path = strchr(host, '/');
+       if (path != NULL)
+               *(path++) = '\0';
+
+       host_ret = g_strdup(host);
+
+       g_free(scheme);
+
+       return host_ret;
+}
+
+static void request_lookup_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       struct proxy_data *data = user_data;
+       const char *proxy;
+
+       DBG("");
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               connman_error("Failed to find URL:%s", data->url);
+               proxy = NULL;
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &proxy,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               proxy = NULL;
+
+done:
+       connman_proxy_driver_lookup_notify(data->service, data->url, proxy);
+
+       connman_service_unref(data->service);
+
+       g_free(data->url);
+       g_free(data);
+
+       dbus_message_unref(reply);
+}
+
+static int request_lookup(struct connman_service *service, const char *url)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       dbus_bool_t result;
+       char *host;
+       struct proxy_data *data;
+
+       DBG("");
+
+       if (daemon_running == FALSE)
+               return -EINVAL;
+
+       msg = dbus_message_new_method_call(PACRUNNER_SERVICE,
+                                               PACRUNNER_CLIENT_PATH,
+                                               PACRUNNER_CLIENT_INTERFACE,
+                                               "FindProxyForURL");
+       if (msg == NULL)
+               return -1;
+
+       host = parse_url(url);
+       if (host == NULL) {
+               dbus_message_unref(msg);
+               return -EINVAL;
+       }
+
+       data = g_try_new0(struct proxy_data, 1);
+       if (data == NULL) {
+               dbus_message_unref(msg);
+               g_free(host);
+               return -ENOMEM;
+       }
+
+       data->url = g_strdup(url);
+       data->service = connman_service_ref(service);
+
+       dbus_message_set_auto_start(msg, FALSE);
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &url,
+                                       DBUS_TYPE_STRING, &host,
+                                       DBUS_TYPE_INVALID);
+
+       result = dbus_connection_send_with_reply(connection, msg,
+                                                       &call, DBUS_TIMEOUT);
+
+       dbus_message_unref(msg);
+
+       if (result == FALSE || call == NULL) {
+               g_free(host);
+               g_free(data->url);
+               g_free(data);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, request_lookup_reply,
+                                                       data, NULL);
+
+       dbus_pending_call_unref(call);
+       g_free(host);
+
+       return 0;
+}
+
+static void cancel_lookup(struct connman_service *service, const char *url)
+{
+       DBG("");
+}
+
+static struct connman_proxy_driver pacrunner_proxy = {
+       .name           = "pacrunnerproxy",
+       .priority       = CONNMAN_PROXY_PRIORITY_HIGH,
+       .request_lookup = request_lookup,
+       .cancel_lookup  = cancel_lookup,
+};
+
+static guint pacrunner_watch;
+
+static int pacrunner_init(void)
+{
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       pacrunner_watch = g_dbus_add_service_watch(connection,
+                                       PACRUNNER_SERVICE, pacrunner_connect,
+                                       pacrunner_disconnect, NULL, NULL);
+       if (pacrunner_watch == 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       connman_notifier_register(&pacrunner_notifier);
+
+       connman_proxy_driver_register(&pacrunner_proxy);
+
+       return 0;
+}
+
+static void pacrunner_exit(void)
+{
+       connman_proxy_driver_unregister(&pacrunner_proxy);
+
+       connman_notifier_unregister(&pacrunner_notifier);
+
+       g_dbus_remove_watch(connection, pacrunner_watch);
+
+       destroy_proxy_configuration();
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(pacrunner, "PAC runner proxy plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, pacrunner_init, pacrunner_exit)
diff --git a/plugins/polkit.c b/plugins/polkit.c
new file mode 100644 (file)
index 0000000..ae38364
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  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 <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/dbus.h>
+
+static const GDBusSecurityTable polkit_security[] = {
+       { CONNMAN_PRIVILEGE_MODIFY, "net.connman.modify",
+                               G_DBUS_SECURITY_FLAG_BUILTIN |
+                               G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION },
+       { CONNMAN_PRIVILEGE_SECRET, "net.connman.secret",
+                               G_DBUS_SECURITY_FLAG_BUILTIN |
+                               G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION },
+       { }
+};
+
+static int polkit_init(void)
+{
+       g_dbus_register_security(polkit_security);
+
+       return 0;
+}
+
+static void polkit_exit(void)
+{
+       g_dbus_unregister_security(polkit_security);
+}
+
+CONNMAN_PLUGIN_DEFINE(polkit, "PolicyKit authorization plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, polkit_init, polkit_exit)
diff --git a/plugins/polkit.policy b/plugins/polkit.policy
new file mode 100644 (file)
index 0000000..0c2629a
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+  <vendor>Connection Manager</vendor>
+  <icon_name>network-wireless</icon_name>
+
+  <action id="net.connman.modify">
+    <description>Settings configuration</description>
+    <message>Policy prevents modification of settings</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_self_keep_session</allow_active>
+    </defaults>
+  </action>
+
+  <action id="net.connman.secret">
+    <description>Secrets configuration</description>
+    <message>Policy prevents modification of secrets</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin_keep_session</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/plugins/pptp.c b/plugins/pptp.c
new file mode 100644 (file)
index 0000000..9db1b89
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
+struct {
+       const char *cm_opt;
+       const char *pptp_opt;
+       const char *vpnc_default;
+       int type;
+} pptp_options[] = {
+       { "PPTP.User", "user", NULL, OPT_STRING },
+       { "PPTP.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
+       { "PPTP.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
+       { "PPTP.Debug", "debug", NULL, OPT_STRING },
+       { "PPTP.RefuseEAP", "refuse-eap", NULL, OPT_BOOL },
+       { "PPTP.RefusePAP", "refuse-pap", NULL, OPT_BOOL },
+       { "PPTP.RefuseCHAP", "refuse-chap", NULL, OPT_BOOL },
+       { "PPTP.RefuseMSCHAP", "refuse-mschap", NULL, OPT_BOOL },
+       { "PPTP.RefuseMSCHAP2", "refuse-mschapv2", NULL, OPT_BOOL },
+       { "PPTP.NoBSDComp", "nobsdcomp", NULL, OPT_BOOL },
+       { "PPTP.NoDeflate", "nodeflatey", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE", "require-mppe", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE40", "require-mppe-40", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE128", "require-mppe-128", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPEStateful", "mppe-stateful", NULL, OPT_BOOL },
+       { "PPTP.NoVJ", "no-vj-comp", NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *pptp_get_sec(struct connman_task *task,
+                               DBusMessage *msg, void *user_data)
+{
+       const char *user, *passwd;
+       struct connman_provider *provider = user_data;
+       DBusMessage *reply;
+
+       if (dbus_message_get_no_reply(msg) == TRUE)
+               return NULL;
+
+       user = connman_provider_get_string(provider, "PPTP.User");
+       passwd = connman_provider_get_string(provider, "PPTP.Password");
+       if (user == NULL || strlen(user) == 0 ||
+                               passwd == NULL || strlen(passwd) == 0)
+               return NULL;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+                               DBUS_TYPE_STRING, &passwd,
+                               DBUS_TYPE_INVALID);
+       return reply;
+}
+
+static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+       char *ifname = NULL, *nameservers = NULL;
+       struct connman_ipaddress *ipaddress = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (provider == NULL) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "auth failed") == 0)
+               return VPN_STATE_AUTH_FAILURE;
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+                       connman_provider_set_string(provider, "Address", value);
+                       addressv4 = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+                       connman_provider_set_string(provider, "Netmask", value);
+                       netmask = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+                       connman_provider_set_string(provider, "DNS", value);
+                       nameservers = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IFNAME"))
+                       ifname = g_strdup(value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (vpn_set_ifname(provider, ifname) < 0) {
+               g_free(ifname);
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       if (addressv4 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET);
+
+       g_free(ifname);
+
+       if (ipaddress == NULL) {
+               connman_error("No IP address for provider");
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       value = connman_provider_get_string(provider, "Host");
+       if (value != NULL) {
+               connman_provider_set_string(provider, "Gateway", value);
+               gateway = g_strdup(value);
+       }
+
+       if (addressv4 != NULL)
+               connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+                                       gateway);
+
+       connman_provider_set_ipaddress(provider, ipaddress);
+       connman_provider_set_nameservers(provider, nameservers);
+
+       g_free(addressv4);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(nameservers);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int pptp_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+               if (strncmp(pptp_options[i].cm_opt, "PPTP.", 5) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       pptp_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       pptp_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static void pptp_write_bool_option(struct connman_task *task,
+                               const char *key, const char *value)
+{
+       if (key != NULL && value != NULL) {
+               if (strcmp(value, "yes") == 0)
+                       connman_task_add_argument(task, key, NULL);
+       }
+}
+
+static int pptp_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *opt_s, *host;
+       char *str;
+       int err, i;
+
+       if (connman_task_set_notify(task, "getsec",
+                                       pptp_get_sec, provider))
+               return -ENOMEM;
+
+       host = connman_provider_get_string(provider, "Host");
+       if (host == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
+                               PPTP, host);
+       if (str == NULL) {
+               connman_error("can not allocate memory");
+               return -ENOMEM;
+       }
+
+       connman_task_add_argument(task, "pty", str);
+       g_free(str);
+
+       connman_task_add_argument(task, "nodetach", NULL);
+       connman_task_add_argument(task, "lock", NULL);
+       connman_task_add_argument(task, "usepeerdns", NULL);
+       connman_task_add_argument(task, "noipdefault", NULL);
+       connman_task_add_argument(task, "noauth", NULL);
+       connman_task_add_argument(task, "nodefaultroute", NULL);
+       connman_task_add_argument(task, "ipparam", "pptp_plugin");
+
+       for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+               opt_s = connman_provider_get_string(provider,
+                                       pptp_options[i].cm_opt);
+               if (opt_s == NULL)
+                       opt_s = pptp_options[i].vpnc_default;
+
+               if (opt_s == NULL)
+                       continue;
+
+               if (pptp_options[i].type == OPT_STRING)
+                       connman_task_add_argument(task,
+                                       pptp_options[i].pptp_opt, opt_s);
+               else if (pptp_options[i].type == OPT_BOOL)
+                       pptp_write_bool_option(task,
+                                       pptp_options[i].pptp_opt, opt_s);
+       }
+
+       connman_task_add_argument(task, "plugin",
+                               SCRIPTDIR "/libppp-plugin.so");
+
+       err = connman_task_run(task, vpn_died, provider,
+                               NULL, NULL, NULL);
+       if (err < 0) {
+               connman_error("pptp failed to start");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int pptp_error_code(int exit_code)
+{
+
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       case 2:
+               return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+       case 16:
+               return CONNMAN_PROVIDER_ERROR_AUTH_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .flags          = VPN_FLAG_NO_TUN,
+       .notify         = pptp_notify,
+       .connect        = pptp_connect,
+       .error_code     = pptp_error_code,
+       .save           = pptp_save,
+};
+
+static int pptp_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return vpn_register("pptp", &vpn_driver, PPPD);
+}
+
+static void pptp_exit(void)
+{
+       vpn_unregister("pptp");
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(pptp, "pptp plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, pptp_init, pptp_exit)
diff --git a/plugins/telephony.c b/plugins/telephony.c
new file mode 100644 (file)
index 0000000..57888be
--- /dev/null
@@ -0,0 +1,1776 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011-2013  Samsung Electronics Co., 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
+ *  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 <errno.h>
+#include <stdlib.h>
+
+#include <gdbus.h>
+#include <string.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/network.h>
+#include <connman/ipconfig.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+#include <connman/technology.h>
+#include <connman/log.h>
+
+#define PS_DBUS_SERVICE                                "com.tcore.ps"
+
+#define PS_MASTER_INTERFACE            PS_DBUS_SERVICE ".master"
+#define PS_MODEM_INTERFACE             PS_DBUS_SERVICE ".modem"
+#define PS_SERVICE_INTERFACE           PS_DBUS_SERVICE ".service"
+#define PS_CONTEXT_INTERFACE           PS_DBUS_SERVICE ".context"
+
+/* methods */
+#define GET_MODEMS                     "GetModems"
+#define GET_SERVICES                   "GetServices"
+#define GET_CONTEXTS                   "GetContexts"
+#define ACTIVATE_CONTEXT               "Activate"
+#define DEACTIVATE_CONTEXT             "Deactivate"
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperties"
+
+/* signals */
+#define MODEM_ADDED                    "ModemAdded"
+#define MODEM_REMOVED                  "ModemRemoved"
+#define SERVICE_ADDED                  "ServiceAdded"
+#define SERVICE_REMOVED                        "ServiceRemoved"
+#define CONTEXT_ADDED                  "ContextAdded"
+#define CONTEXT_REMOVED                        "ContextRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
+
+#define TIMEOUT 40000
+
+#define STRING2BOOL(a) ((g_str_equal(a, "TRUE")) ?  (TRUE):(FALSE))
+
+static DBusConnection *connection;
+static GHashTable      *modem_hash;
+static GHashTable      *service_hash;
+static GHashTable      *network_hash;
+
+struct telephony_service {
+       char *path;
+
+       gpointer p_modem;
+       char *act;
+       gboolean roaming; /* global roaming state */
+       gboolean ps_attached; /* packet service is available */
+};
+
+struct telephony_modem {
+       char *path;
+
+       char *operator;
+       gboolean powered;
+       gboolean sim_init;
+       gboolean flight_mode;
+       gboolean data_allowed;
+       gboolean roaming_allowed;
+
+       struct connman_device *device;
+       struct telephony_service *s_service;
+};
+
+struct telephony_network {
+       char *path;
+       struct connman_network *network;
+
+       enum connman_ipconfig_method ipv4_method;
+       struct connman_ipaddress *ipv4_address;
+
+       enum connman_ipconfig_method ipv6_method;
+       struct connman_ipaddress *ipv6_address;
+};
+
+/* function prototype */
+static void telephony_connect(DBusConnection *connection, void *user_data);
+static void telephony_disconnect(DBusConnection *connection, void *user_data);
+static void __remove_modem(gpointer data);
+static void __remove_service(gpointer data);
+static void __remove_network(gpointer data);
+
+static int __modem_probe(struct connman_device *device);
+static void __modem_remove(struct connman_device *device);
+static int __modem_enable(struct connman_device *device);
+static int __modem_disable(struct connman_device *device);
+
+static int __network_probe(struct connman_network *network);
+static void __network_remove(struct connman_network *network);
+static int __network_connect(struct connman_network *network);
+static int __network_disconnect(struct connman_network *network);
+
+
+/* dbus request and reply */
+static int __dbus_request(const char *path, const char *interface,
+                       const char *method,
+                       DBusPendingCallNotifyFunction notify, void *user_data,
+                       DBusFreeFunction free_function, int type, ...);
+
+static int __request_get_modems(void);
+static void __response_get_modems(DBusPendingCall *call, void *user_data);
+static int __request_get_services(const char *path);
+static void __response_get_services(DBusPendingCall *call, void *user_data);
+static int __request_get_contexts(struct telephony_modem *modem);
+static void __response_get_contexts(DBusPendingCall *call, void *user_data);
+static int __request_network_activate(struct connman_network *network);
+static void __response_network_activate(DBusPendingCall *call, void *user_data);
+static int __request_network_deactivate(struct connman_network *network);
+
+/* telephony internal function */
+static void __add_modem(const char *path, DBusMessageIter *prop);
+static void __add_service(struct telephony_modem *modem,
+                       const char *service_path, DBusMessageIter *prop);
+static void __add_connman_device(const char *modem_path, const char *operator);
+static void __remove_connman_device(struct telephony_modem *modem);
+static void __remove_connman_networks(struct connman_device *device);
+static void __set_device_powered(struct telephony_modem *modem,
+                                       gboolean powered);
+static int __check_device_powered(const char *path, gboolean online);
+static gboolean __check_network_available(struct connman_network *network);
+static void __create_service(struct connman_network *network);
+static int __add_context(struct connman_device *device, const char *path,
+                                                       DBusMessageIter *prop);
+static gboolean __set_network_ipconfig(struct telephony_network *network,
+                                                       DBusMessageIter *dict);
+static void __set_network_connected(struct telephony_network *network,
+                                                       gboolean connected);
+static char *__get_ident(const char *path);
+
+/* signal handler */
+static gboolean __changed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __changed_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __changed_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __added_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+static gboolean __removed_context(DBusConnection *connection,
+                               DBusMessage *message, void *user_data);
+
+/* device driver */
+static struct connman_device_driver modem_driver = {
+       .name           = "device",
+       .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
+       .probe          = __modem_probe,
+       .remove         = __modem_remove,
+       .enable         = __modem_enable,
+       .disable        = __modem_disable,
+};
+
+/* network driver */
+static struct connman_network_driver network_driver = {
+       .name           = "network",
+       .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
+       .probe          = __network_probe,
+       .remove         = __network_remove,
+       .connect        = __network_connect,
+       .disconnect     = __network_disconnect,
+};
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "cellular",
+       .type           = CONNMAN_SERVICE_TYPE_CELLULAR,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+};
+
+/* local function */
+static void telephony_connect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_modem);
+       service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_service);
+       network_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, __remove_network);
+       __request_get_modems();
+       return;
+}
+
+static void telephony_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       if (modem_hash != NULL) {
+               g_hash_table_destroy(modem_hash);
+               modem_hash = NULL;
+       }
+
+       if (network_hash != NULL) {
+               g_hash_table_destroy(network_hash);
+               network_hash = NULL;
+       }
+
+       return;
+}
+
+static void __remove_modem(gpointer data)
+{
+       struct telephony_modem *modem = data;
+
+       __remove_connman_device(modem);
+
+       g_free(modem->path);
+       g_free(modem->operator);
+       g_free(modem);
+}
+
+static void __remove_service(gpointer data)
+{
+       struct telephony_service *service = data;
+
+       g_free(service->path);
+       g_free(service->act);
+       g_free(service);
+}
+
+static void __remove_network(gpointer data)
+{
+       struct telephony_network *info = data;
+       struct connman_device *device;
+
+       device = connman_network_get_device(info->network);
+       if (device != NULL)
+               connman_device_remove_network(device, info->network);
+
+       connman_network_unref(info->network);
+
+       g_free(info->path);
+       connman_ipaddress_free(info->ipv4_address);
+       connman_ipaddress_free(info->ipv6_address);
+       g_free(info);
+}
+
+static int __modem_probe(struct connman_device *device)
+{
+       DBG("device %p", device);
+       return 0;
+}
+
+static void __modem_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static int __modem_enable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+       DBG("device %p, path, %s", device, path);
+
+       return __check_device_powered(path, TRUE);
+}
+
+static int __modem_disable(struct connman_device *device)
+{
+       const char *path = connman_device_get_string(device, "Path");
+       DBG("device %p, path, %s", device, path);
+
+       return __check_device_powered(path, FALSE);
+}
+
+static int __network_probe(struct connman_network *network)
+{
+       DBG("network_prove network(%p)", network);
+       return 0;
+}
+
+static int __network_connect(struct connman_network *network)
+{
+       struct connman_device *device;
+       struct telephony_modem *modem;
+
+       DBG("network %p", network);
+
+       device = connman_network_get_device(network);
+       if (device == NULL)
+               return -ENODEV;
+
+       modem = connman_device_get_data(device);
+       if (modem == NULL)
+               return -ENODEV;
+
+       if (modem->powered == FALSE)
+               return -ENOLINK;
+
+/*
+       if (modem->online == FALSE)
+               return -ENOLINK;
+
+       if (modem->roaming_allowed == FALSE && modem->roaming == TRUE)
+               return -ENOLINK;
+*/
+
+       return __request_network_activate(network);
+}
+
+static int __network_disconnect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       if (connman_network_get_index(network) < 0)
+               return -ENOTCONN;
+
+       connman_network_set_associating(network, FALSE);
+
+       return __request_network_deactivate(network);
+}
+
+static void __network_remove(struct connman_network *network)
+{
+       char const *path = connman_network_get_string(network, "Path");
+       DBG("network %p path %s", network, path);
+
+       g_hash_table_remove(network_hash, path);
+       return;
+}
+
+static int __dbus_request(const char *path, const char *interface,
+                       const char *method,
+                       DBusPendingCallNotifyFunction notify, void *user_data,
+                       DBusFreeFunction free_function, int type, ...)
+{
+       DBG("telephony request");
+
+       DBusMessage *message;
+       DBusPendingCall *call;
+       dbus_bool_t ok;
+       va_list va;
+
+       DBG("path %s %s.%s", path, interface, method);
+
+       if (path == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(PS_DBUS_SERVICE, path,
+                                               interface, method);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       va_start(va, type);
+       ok = dbus_message_append_args_valist(message, type, va);
+       va_end(va);
+
+       if (!ok)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call %s.%s", interface, method);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, notify, user_data, free_function);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int __request_get_modems(void)
+{
+       DBG("request get modem");
+       /* call connect master */
+       return __dbus_request("/", PS_MASTER_INTERFACE, GET_MODEMS,
+                       __response_get_modems, NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static void __response_get_modems(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter args, dict;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("GetModems() %s %s", error.name, error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       DBG("message signature (%s)", dbus_message_get_signature(reply));
+
+       if (dbus_message_iter_init(reply, &args) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       /* DBG("message type (%d) dic(%d)",
+        *      dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+        */
+
+       while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry, property;
+               const char *modem_path;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &modem_path);
+               DBG("modem path (%s)", modem_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &property);
+
+               __add_modem(modem_path, &property);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       return;
+}
+
+static int __request_get_services(const char *path)
+{
+       DBG("request get service");
+       return __dbus_request(path, PS_MODEM_INTERFACE, GET_SERVICES,
+                               __response_get_services, g_strdup(path),
+                               g_free, DBUS_TYPE_INVALID);
+}
+
+static void __response_get_services(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter args, dict;
+
+       const char *path = user_data;
+       struct telephony_modem *modem;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return;
+       if (modem->device == NULL)
+               return;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("GetServices() %s %s", error.name, error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       DBG("message signature (%s)", dbus_message_get_signature(reply));
+
+       if (dbus_message_iter_init(reply, &args) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       /* DBG("message type (%d) dic(%d)",
+        *       dbus_message_iter_get_arg_type(&dict), DBUS_TYPE_DICT_ENTRY);
+        */
+
+       while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry, property;
+               const char *service_path;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &service_path);
+               DBG("service path (%s)", service_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &property);
+
+               __add_service(modem, service_path, &property);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       return;
+}
+
+static int __request_get_contexts(struct telephony_modem *modem)
+{
+       DBG("request get contexts");
+       return __dbus_request(modem->s_service->path,
+                               PS_SERVICE_INTERFACE, GET_CONTEXTS,
+                               __response_get_contexts, g_strdup(modem->path),
+                               g_free, DBUS_TYPE_INVALID);
+}
+
+static void __response_get_contexts(DBusPendingCall *call, void *user_data)
+{
+       DBusError error;
+       DBusMessage *reply;
+       DBusMessageIter args, dict;
+
+       const char *path = user_data;
+       struct telephony_modem *modem;
+
+       DBG("");
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return;
+       if (modem->s_service == NULL)
+                       return;
+       if (modem->device == NULL)
+               return;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("GetContexts() %s %s", error.name, error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       DBG("message signature (%s)", dbus_message_get_signature(reply));
+
+       if (dbus_message_iter_init(reply, &args) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry, property;
+               const char *context_path;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &context_path);
+               DBG("context path (%s)", context_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &property);
+
+               __add_context(modem->device, context_path, &property);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       return;
+}
+
+static int __request_network_activate(struct connman_network *network)
+{
+       DBG("request network activate");
+
+       const char *path = connman_network_get_string(network, "Path");
+       DBG("network %p, path %s", network, path);
+
+       return __dbus_request(path, PS_CONTEXT_INTERFACE, ACTIVATE_CONTEXT,
+                       __response_network_activate,
+                       g_strdup(path), NULL, DBUS_TYPE_INVALID);
+}
+
+static void __response_network_activate(DBusPendingCall *call, void *user_data)
+{
+       DBG("network activation response");
+
+       DBusError error;
+       DBusMessage *reply;
+
+       struct telephony_network *info;
+       const char *path = user_data;
+
+       info = g_hash_table_lookup(network_hash, path);
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (info == NULL)
+               goto done;
+
+       if (!__check_network_available(info->network)) {
+               g_hash_table_remove(network_hash, path);
+               goto done;
+       }
+
+       dbus_error_init(&error);
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("connection activate() %s %s",
+                                       error.name, error.message);
+
+               if (connman_network_get_index(info->network) < 0)
+                       connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+
+               dbus_error_free(&error);
+               goto done;
+       }
+
+done:
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       return;
+}
+
+static int __request_network_deactivate(struct connman_network *network)
+{
+       DBG("request network deactivate");
+
+       const char *path = connman_network_get_string(network, "Path");
+       DBG("network %p, path %s", network, path);
+
+       return __dbus_request(path, PS_CONTEXT_INTERFACE, DEACTIVATE_CONTEXT,
+               NULL, NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static void __add_modem(const char *path, DBusMessageIter *prop)
+{
+       struct telephony_modem *modem;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem != NULL)
+               return;
+
+       modem = (struct telephony_modem *)malloc(
+                                       sizeof(struct telephony_modem));
+       memset(modem, 0, sizeof(struct telephony_modem));
+
+       modem->path = g_strdup(path);
+       modem->device = NULL;
+       modem->s_service = NULL;
+
+       g_hash_table_insert(modem_hash, g_strdup(path), modem);
+
+       while (dbus_message_iter_get_arg_type(prop) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *tmp;
+
+               dbus_message_iter_recurse(prop, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &tmp);
+
+               DBG("key (%s) value(%s)", key, tmp);
+
+               if (g_str_equal(key, "powered") == TRUE) {
+                       modem->powered = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "operator") == TRUE) {
+                       modem->operator = g_strdup(tmp);
+               } else if (g_str_equal(key, "sim_init") == TRUE) {
+                       modem->sim_init = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "flight_mode") == TRUE) {
+                       modem->flight_mode = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "roaming_allowed") == TRUE) {
+                       modem->roaming_allowed = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "data_allowed") == TRUE) {
+                       modem->data_allowed = STRING2BOOL(tmp);
+               }
+               dbus_message_iter_next(prop);
+       }
+
+       __add_connman_device(path, modem->operator);
+       __set_device_powered(modem, modem->powered);
+
+       if (modem->powered != TRUE) {
+               DBG("modem is not powered");
+               return;
+       }
+
+       __request_get_services(modem->path);
+
+       return;
+}
+
+static void __add_service(struct telephony_modem *modem,
+                               const char *service_path, DBusMessageIter *prop)
+{
+       struct telephony_service *service;
+
+       if (modem->s_service != NULL)
+               return;
+
+       service = (struct telephony_service *)malloc(
+                                       sizeof(struct telephony_service));
+       memset(service, 0, sizeof(struct telephony_service));
+
+       service->path = g_strdup(service_path);
+       service->p_modem = modem;
+       g_hash_table_insert(service_hash, g_strdup(service_path), service);
+
+       while (dbus_message_iter_get_arg_type(prop) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *tmp;
+
+               dbus_message_iter_recurse(prop, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &tmp);
+
+               DBG("key (%s) value(%s)", key, tmp);
+
+               if (g_str_equal(key, "roaming") == TRUE) {
+                       service->roaming = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "act") == TRUE) {
+                       service->act = g_strdup(tmp);
+               } else if (g_str_equal(key, "ps_attached") == TRUE) {
+                       service->ps_attached = STRING2BOOL(tmp);
+               }
+
+               dbus_message_iter_next(prop);
+       }
+
+       modem->s_service = service;
+       __request_get_contexts(modem);
+
+       return;
+}
+
+static void __add_connman_device(const char *modem_path, const char *operator)
+{
+       struct telephony_modem *modem;
+       struct connman_device *device;
+
+       DBG("path %s operator %s", modem_path, operator);
+
+       if (modem_path == NULL)
+               return;
+
+       if (operator == NULL)
+               return;
+
+       modem = g_hash_table_lookup(modem_hash, modem_path);
+       if (modem == NULL)
+               return;
+
+       if (modem->device) {
+               if (!g_strcmp0(operator,
+                               connman_device_get_ident(modem->device)))
+                       return;
+
+               __remove_connman_device(modem);
+       }
+
+       if (strlen(operator) == 0)
+               return;
+
+       device = connman_device_create(operator, CONNMAN_DEVICE_TYPE_CELLULAR);
+       if (device == NULL)
+               return;
+
+       connman_device_set_ident(device, operator);
+       connman_device_set_string(device, "Path", modem_path);
+       connman_device_set_data(device, modem);
+
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register cellular device");
+               connman_device_unref(device);
+               return;
+       }
+
+       modem->device = device;
+
+       return;
+}
+
+static void __remove_connman_device(struct telephony_modem *modem)
+{
+       DBG("modem %p path %s device %p", modem, modem->path, modem->device);
+
+       if (modem->device == NULL)
+               return;
+
+       __remove_connman_networks(modem->device);
+
+       connman_device_unregister(modem->device);
+       connman_device_unref(modem->device);
+
+       modem->device = NULL;
+
+       return;
+}
+
+static void __remove_connman_networks(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       GSList *info_list = NULL;
+       GSList *list;
+
+       if (network_hash == NULL)
+               return;
+
+       g_hash_table_iter_init(&iter, network_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct telephony_network *info = value;
+
+               if (connman_network_get_device(info->network) != device)
+                       continue;
+
+               info_list = g_slist_append(info_list, info);
+       }
+
+       for (list = info_list; list != NULL; list = list->next) {
+               struct telephony_network *info = list->data;
+               connman_device_remove_network(device, info->network);
+       }
+
+       g_slist_free(info_list);
+}
+
+static void __set_device_powered(struct telephony_modem *modem,
+                                       gboolean powered)
+{
+       DBG("set modem(%s) powered(%d)", modem->path, powered);
+
+       if (modem->device)
+               connman_device_set_powered(modem->device, powered);
+
+       return;
+}
+
+static int __check_device_powered(const char *path, gboolean powered)
+{
+       struct telephony_modem *modem = g_hash_table_lookup(modem_hash, path);
+
+       if (modem == NULL)
+               return -ENODEV;
+
+       DBG("check modem (%s) powered (%d)", modem->path, modem->powered);
+
+       if (modem->powered == powered)
+               return -EALREADY;
+
+       return 0;
+}
+
+static gboolean __check_network_available(struct connman_network *network)
+{
+       if (network == NULL || connman_network_get_device(network) == NULL) {
+               DBG("Modem or network was removed");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int __add_context(struct connman_device *device, const char *path,
+                               DBusMessageIter *prop)
+{
+       char *ident;
+       gboolean active = FALSE;
+
+       struct telephony_modem *modem = connman_device_get_data(device);
+       struct connman_network *network;
+       struct telephony_network *info;
+
+       DBG("modem %p device %p path %s", modem, device, path);
+
+       ident = __get_ident(path);
+
+       network = connman_device_get_network(device, ident);
+       if (network != NULL)
+               return -EALREADY;
+
+       info = g_hash_table_lookup(network_hash, path);
+       if (info != NULL) {
+               DBG("path %p already exists with device %p", path,
+                       connman_network_get_device(info->network));
+
+               if (connman_network_get_device(info->network))
+                       return -EALREADY;
+
+               g_hash_table_remove(network_hash, path);
+       }
+
+       network = connman_network_create(ident, CONNMAN_NETWORK_TYPE_CELLULAR);
+       if (network == NULL)
+               return -ENOMEM;
+
+       info = (struct telephony_network *)malloc(
+                                       sizeof(struct telephony_network));
+       if (info == NULL) {
+               connman_network_unref(network);
+               return -ENOMEM;
+       }
+
+       memset(info, 0, sizeof(struct telephony_network));
+
+       info->path = g_strdup(path);
+
+       connman_ipaddress_clear(info->ipv4_address);
+       connman_ipaddress_clear(info->ipv6_address);
+       info->network = network;
+
+       connman_network_set_string(network, "Path", path);
+       connman_network_set_name(network, path);
+
+       __create_service(network);
+
+       g_hash_table_insert(network_hash, g_strdup(path), info);
+
+       connman_network_set_available(network, TRUE);
+       connman_network_set_index(network, -1);
+       connman_network_set_bool(network, "Roaming", modem->s_service->roaming);
+
+       if (connman_device_add_network(device, network) != 0) {
+               g_hash_table_remove(network_hash, path);
+               return -EIO;
+       }
+
+       active = __set_network_ipconfig(info, prop);
+
+       if (active && (connman_network_get_connecting(network) ||
+                               connman_network_get_associating(network)))
+               __set_network_connected(info, active);
+
+       return 0;
+}
+
+static void __create_service(struct connman_network *network)
+{
+       const char *path;
+       char *group;
+
+       DBG("");
+
+       path = connman_network_get_string(network, "Path");
+
+       group = __get_ident(path);
+
+       connman_network_set_group(network, group);
+}
+
+static gboolean __set_network_ipconfig(struct telephony_network *network,
+                                       DBusMessageIter *dict)
+{
+       DBG("set network info");
+
+       gboolean active = FALSE;
+       char *dev_name = NULL, *proxy_addr = NULL;
+       char *ipv4_addr = NULL, *ipv4_gw = NULL, *ipv4_netmask = NULL,
+                                       *ipv4_dns1 = NULL, *ipv4_dns2 = NULL;
+       char *ipv6_addr = NULL, *ipv6_gw = NULL, *ipv6_netmask = NULL,
+                                       *ipv6_dns1 = NULL, *ipv6_dns2 = NULL;
+       int index;
+       int dns_flag = 0;
+
+       while (dbus_message_iter_get_arg_type(dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *tmp;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               DBG("key (%s)", key);
+
+               if (g_str_equal(key, "dev_name") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &dev_name);
+                       DBG("dev_name (%s)", dev_name);
+               } else if (g_str_equal(key, "proxy") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &proxy_addr);
+               } else if (g_str_equal(key, "ipv4_address") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv4_addr);
+                       DBG("ipv4 address (%s)", ipv4_addr);
+               } else if (g_str_equal(key, "ipv4_gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv4_gw);
+               } else if (g_str_equal(key, "ipv4_netmask") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv4_netmask);
+               } else if (g_str_equal(key, "ipv4_dns1") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv4_dns1);
+               } else if (g_str_equal(key, "ipv4_dns2") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv4_dns2);
+               } else if (g_str_equal(key, "ipv6_address") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv6_addr);
+                       DBG("ipv6 address (%s)", ipv6_addr);
+               } else if (g_str_equal(key, "ipv6_gateway") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv6_gw);
+               } else if (g_str_equal(key, "ipv6_netmask") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv6_netmask);
+               } else if (g_str_equal(key, "ipv6_dns1") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv6_dns1);
+               } else if (g_str_equal(key, "ipv6_dns2") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &ipv6_dns2);
+               } else if (g_str_equal(key, "active") == TRUE) {
+                       dbus_message_iter_get_basic(&entry, &tmp);
+                       DBG("active (%s)", tmp);
+                       active = STRING2BOOL(tmp);
+               }
+
+               dbus_message_iter_next(dict);
+       }
+
+       /* interface index set */
+       if (dev_name == NULL)
+               dev_name = "";
+
+       if (g_str_equal(dev_name, "")) {
+               const char *ifname = NULL;
+               ifname = connman_network_get_ifname(network->network);
+               index = connman_inet_ifindex(ifname);
+       } else
+               index = connman_inet_ifindex(dev_name);
+
+       DBG("interface %s, index %d", dev_name, index);
+       connman_network_set_index(network->network, index);
+
+       /* proxy set */
+       if (proxy_addr == NULL)
+               proxy_addr = "";
+
+       DBG("proxy (%s) is set", proxy_addr);
+       connman_network_set_proxy(network->network, proxy_addr);
+
+       /* ipv4 set */
+       if (ipv4_addr == NULL)
+               ipv4_addr = "0.0.0.0";
+
+       if (g_str_equal(ipv4_addr, "0.0.0.0")) {
+               network->ipv4_method = CONNMAN_IPCONFIG_METHOD_OFF;
+       } else {
+               network->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+               network->ipv4_address =
+                       connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+               if (network->ipv4_address == NULL)
+                       return FALSE;
+
+               connman_ipaddress_set_ipv4(network->ipv4_address, ipv4_addr,
+                                               ipv4_netmask, ipv4_gw);
+
+               if (ipv4_dns1 == NULL)
+                       ipv4_dns1 = "0.0.0.0";
+               if (ipv4_dns2 == NULL)
+                       ipv4_dns2 = "0.0.0.0";
+
+               if (g_str_equal(ipv4_dns1, "0.0.0.0"))
+                       dns_flag += 1;
+
+               if (g_str_equal(ipv4_dns2, "0.0.0.0"))
+                       dns_flag += 2;
+
+               gchar *nameservers = NULL;
+
+               switch (dns_flag) {
+               case 0:
+                       nameservers = g_strdup_printf("%s %s", ipv4_dns1,
+                                                               ipv4_dns2);
+                       break;
+               case 1:
+                       nameservers = g_strdup_printf("%s", ipv4_dns2);
+                       break;
+               case 2:
+                       nameservers = g_strdup_printf("%s", ipv4_dns1);
+               }
+
+               connman_network_set_nameservers(network->network, nameservers);
+               g_free(nameservers);
+       }
+
+       /* ipv6 set */
+       if (ipv6_addr == NULL)
+               ipv6_addr = "::";
+
+       if (g_str_equal(ipv6_addr, "::")) {
+               network->ipv6_method = CONNMAN_IPCONFIG_METHOD_OFF;
+       } else {
+               network->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+               unsigned char prefix_length = 64;
+               network->ipv6_address =
+                       connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+               if (network->ipv6_address == NULL)
+                       return FALSE;
+
+               connman_ipaddress_set_ipv6(network->ipv6_address, ipv6_addr,
+                                               prefix_length, ipv6_gw);
+
+               dns_flag = 0;
+
+               if (ipv6_dns1 == NULL)
+                       ipv6_dns1 = "::";
+               if (ipv6_dns2 == NULL)
+                       ipv6_dns2 = "::";
+
+               if (g_str_equal(ipv6_dns1, "::"))
+                       dns_flag += 1;
+
+               if (g_str_equal(ipv6_dns2, "::"))
+                       dns_flag += 2;
+
+               gchar *nameservers = NULL;
+
+               switch (dns_flag) {
+               case 0:
+                       nameservers = g_strdup_printf("%s %s", ipv6_dns1,
+                                                               ipv6_dns2);
+                       break;
+               case 1:
+                       nameservers = g_strdup_printf("%s", ipv6_dns2);
+                       break;
+               case 2:
+                       nameservers = g_strdup_printf("%s", ipv6_dns1);
+               }
+
+               connman_network_set_nameservers(network->network, nameservers);
+               g_free(nameservers);
+       }
+
+       if (active)
+               connman_network_set_associating(network->network, TRUE);
+
+       return active;
+}
+
+static void __set_network_connected(struct telephony_network *network,
+                                       gboolean connected)
+{
+       gboolean setip = FALSE;
+
+       DBG("network %p connected %d", network, connected);
+
+       switch (network->ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               setip = TRUE;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(network->network,
+                                                       network->ipv4_method);
+               connman_network_set_ipaddress(network->network,
+                                                       network->ipv4_address);
+               setip = TRUE;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(network->network,
+                                                       network->ipv4_method);
+               setip = TRUE;
+               break;
+       }
+
+       switch (network->ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               DBG("ipv6 not supported");
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv6_method(network->network,
+                                                       network->ipv6_method);
+               connman_network_set_ipaddress(network->network,
+                                                       network->ipv6_address);
+               setip = TRUE;
+               break;
+       }
+
+       if (setip == TRUE)
+               connman_network_set_connected(network->network, connected);
+
+       return;
+}
+
+static char *__get_ident(const char *path)
+{
+       char *pos;
+
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
+}
+
+static gboolean __changed_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBG("modem changed signal");
+
+       DBusMessageIter args, dict;
+       const char *path = dbus_message_get_path(message);
+       struct telephony_modem *modem;
+
+       DBG("modem path %s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL) {
+               DBG("modem object does not exists");
+               return TRUE;
+       }
+
+       DBG("message signature (%s)", dbus_message_get_signature(message));
+
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *tmp;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &tmp);
+
+               DBG("key(%s), value(%s)", key, tmp);
+
+               if (g_str_equal(key, "powered") == TRUE) {
+                       modem->powered = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "operator") == TRUE) {
+                       modem->operator = g_strdup(tmp);
+               } else if (g_str_equal(key, "sim_init") == TRUE) {
+                       modem->sim_init = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "flight_mode") == TRUE) {
+                       modem->flight_mode = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "roaming_allowed") == TRUE) {
+                       modem->roaming_allowed = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "data_allowed") == TRUE) {
+                       modem->data_allowed = STRING2BOOL(tmp);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (modem->device == NULL)
+               __add_connman_device(path, modem->operator);
+
+       __set_device_powered(modem, modem->powered);
+
+       if (modem->powered != TRUE) {
+               DBG("modem is not powered");
+               return TRUE;
+       }
+
+       if (!modem->s_service) {
+               __request_get_services(modem->path);
+               return TRUE;
+       }
+
+       if (modem->flight_mode || !modem->data_allowed) {
+               DBG("modem(%s) flight mode(%d) data allowed(%d)",
+                       modem->path, modem->flight_mode, modem->data_allowed);
+               return TRUE;
+       }
+
+       return TRUE;
+}
+
+static gboolean __added_modem(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBG("modem added signal");
+
+       const char *modem_path = NULL;
+       DBusMessageIter args, dict, tmp;
+
+       DBG("message signature (%s)", dbus_message_get_signature(message));
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+       memcpy(&tmp, &dict, sizeof(struct DBusMessageIter));
+
+       while (dbus_message_iter_get_arg_type(&tmp) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *value;
+
+               dbus_message_iter_recurse(&tmp, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("key (%s) value(%s)", key, value);
+
+               if (g_str_equal(key, "path") == TRUE)
+                       modem_path = g_strdup(value);
+
+               dbus_message_iter_next(&tmp);
+       }
+
+       if (modem_path != NULL)
+               __add_modem(modem_path, &dict);
+
+       return TRUE;
+}
+
+static gboolean __removed_modem(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("modem removed signal");
+
+       DBusMessageIter iter;
+       const char *modem_path;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &modem_path);
+       g_hash_table_remove(modem_hash, modem_path);
+
+       return TRUE;
+}
+
+static gboolean __changed_service(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("service changed signal");
+
+       DBusMessageIter args, dict;
+       const char *service_path = dbus_message_get_path(message);
+       struct telephony_modem *modem;
+       struct telephony_service *s_service;
+       gboolean roaming_option = TRUE;
+
+       DBG("service path %s", service_path);
+
+       s_service = g_hash_table_lookup(service_hash, service_path);
+       if (s_service == NULL) {
+               DBG("service object does not exists");
+               return TRUE;
+       }
+
+       modem = s_service->p_modem;
+       if (modem == NULL) {
+               DBG("modem object does not exists");
+               return TRUE;
+       }
+
+       DBG("message signature (%s)", dbus_message_get_signature(message));
+
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *tmp;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &tmp);
+
+               DBG("key(%s), value(%s)", key, tmp);
+
+               if (g_str_equal(key, "roaming") == TRUE) {
+                       s_service->roaming = STRING2BOOL(tmp);
+               } else if (g_str_equal(key, "act") == TRUE) {
+                       s_service->act = g_strdup(tmp);
+               } else if (g_str_equal(key, "ps_attached") == TRUE) {
+                       s_service->ps_attached = STRING2BOOL(tmp);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       roaming_option &= (!s_service->roaming && !modem->roaming_allowed)
+                               || modem->roaming_allowed;
+
+       return TRUE;
+}
+
+
+static gboolean __added_service(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBG("service added signal");
+
+       const char *path = dbus_message_get_path(message);
+       const char *service_path = NULL;
+       DBusMessageIter args, dict, tmp;
+       struct telephony_modem *modem;
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL || modem->device == NULL)
+               return TRUE;
+
+       DBG("message signature (%s)", dbus_message_get_signature(message));
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+       memcpy(&tmp, &dict, sizeof(struct DBusMessageIter));
+
+       while (dbus_message_iter_get_arg_type(&tmp) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *value;
+
+               dbus_message_iter_recurse(&tmp, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("key (%s) value(%s)", key, value);
+
+               if (g_str_equal(key, "path") == TRUE) {
+                       service_path = g_strdup(value);
+               }
+
+               dbus_message_iter_next(&tmp);
+       }
+
+       if (service_path != NULL)
+               __add_service(modem, service_path, &dict);
+
+       return TRUE;
+}
+
+static gboolean __removed_service(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("service removed signal");
+
+       DBusMessageIter iter;
+       const char *service_path;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &service_path);
+       g_hash_table_remove(service_hash, service_path);
+
+       return TRUE;
+}
+
+static gboolean __changed_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("network changed signal");
+
+       gboolean active = FALSE;
+       const char *path = dbus_message_get_path(message);
+       struct telephony_network *info;
+       DBusMessageIter args, dict;
+
+       DBG("path %s", path);
+       info = g_hash_table_lookup(network_hash, path);
+       if (info == NULL)
+               return TRUE;
+
+       if (!__check_network_available(info->network)) {
+               g_hash_table_remove(network_hash, path);
+               return TRUE;
+       }
+
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+
+       active = __set_network_ipconfig(info, &dict);
+
+       if (active == FALSE)
+               __set_network_connected(info, active);
+       else if ((connman_network_get_connecting(info->network) ||
+                       connman_network_get_associating(info->network)))
+               __set_network_connected(info, active);
+
+       return TRUE;
+}
+
+static gboolean __added_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("network added signal");
+
+       DBusMessageIter args, dict, tmp;
+       const char *path = dbus_message_get_path(message);
+       const char *network_path = NULL;
+       struct telephony_service *service = NULL;
+       struct telephony_modem *modem = NULL;
+
+       service = g_hash_table_lookup(service_hash, path);
+       if (service == NULL || service->p_modem == NULL)
+               return TRUE;
+
+       modem = service->p_modem;
+       if (modem == NULL || modem->device == NULL)
+               return TRUE;
+
+       DBG("message signature (%s)", dbus_message_get_signature(message));
+       if (dbus_message_iter_init(message, &args) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&args, &dict);
+       memcpy(&tmp, &dict, sizeof(struct DBusMessageIter));
+
+       while (dbus_message_iter_get_arg_type(&tmp) != DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key, *value;
+
+               dbus_message_iter_recurse(&tmp, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("key (%s) value(%s)", key, value);
+
+               if (g_str_equal(key, "path") == TRUE)
+                       network_path = g_strdup(value);
+
+               dbus_message_iter_next(&tmp);
+       }
+
+       if (network_path != NULL)
+               __add_context(modem->device, network_path, &dict);
+
+       return TRUE;
+}
+
+static gboolean __removed_context(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBG("network removed signal");
+
+       DBusMessageIter iter;
+       const char *path = dbus_message_get_path(message);
+       const char *network_path = NULL;
+       struct telephony_service *service = NULL;
+
+       service = g_hash_table_lookup(service_hash, path);
+       if (service == NULL || service->p_modem == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE) {
+               DBG("error to read message");
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &network_path);
+       g_hash_table_remove(network_hash, network_path);
+
+       return TRUE;
+}
+
+/* telephony initialization */
+static guint watch;
+static guint modem_watch;
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint service_watch;
+static guint service_added_watch;
+static guint service_removed_watch;
+static guint context_watch;
+static guint context_added_watch;
+static guint context_removed_watch;
+
+static int telephony_init(void)
+{
+       DBG("telephony plugin");
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       /* telephony watch */
+       watch = g_dbus_add_service_watch(connection, PS_DBUS_SERVICE,
+                                       telephony_connect, telephony_disconnect,
+                                       NULL, NULL);
+
+       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_modem, NULL, NULL);
+
+       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MASTER_INTERFACE,
+                                               MODEM_ADDED, __added_modem,
+                                               NULL, NULL);
+
+       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MASTER_INTERFACE,
+                                               MODEM_REMOVED, __removed_modem,
+                                               NULL, NULL);
+
+       service_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_service,
+                                               NULL, NULL);
+
+       service_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               SERVICE_ADDED, __added_service,
+                                               NULL, NULL);
+
+       service_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_MODEM_INTERFACE,
+                                               SERVICE_REMOVED,
+                                               __removed_service,
+                                               NULL, NULL);
+
+       context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_CONTEXT_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               __changed_context,
+                                               NULL, NULL);
+
+       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               CONTEXT_ADDED, __added_context,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               PS_SERVICE_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               __removed_context,
+                                               NULL, NULL);
+
+       if (watch == 0 || modem_watch == 0 || modem_added_watch == 0
+                       || modem_removed_watch == 0 || service_watch == 0
+                       || service_added_watch == 0 || context_watch == 0
+                       || service_removed_watch == 0
+                       || context_added_watch == 0
+                       || context_removed_watch == 0) {
+               err = -EIO;
+               goto remove;
+       }
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&modem_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&modem_driver);
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
+       return 0;
+
+remove:
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, service_watch);
+       g_dbus_remove_watch(connection, service_added_watch);
+       g_dbus_remove_watch(connection, service_removed_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+
+       dbus_connection_unref(connection);
+       return err;
+}
+
+static void telephony_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, service_watch);
+       g_dbus_remove_watch(connection, service_added_watch);
+       g_dbus_remove_watch(connection, service_removed_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+
+       telephony_disconnect(connection, NULL);
+
+       connman_device_driver_unregister(&modem_driver);
+       connman_network_driver_unregister(&network_driver);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(telephony, "Samsung Telephony Framework plug-in", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, telephony_init, telephony_exit)
diff --git a/plugins/tist.c b/plugins/tist.c
new file mode 100644 (file)
index 0000000..4d3561e
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+
+#define TIST_SYSFS_INSTALL "/sys/devices/platform/kim/install"
+#define TIST_SYSFS_UART "/sys/devices/platform/kim/dev_name"
+#define TIST_SYSFS_BAUD "/sys/devices/platform/kim/baud_rate"
+
+/* Shared transport line discipline */
+#define N_TI_WL 22
+
+static GIOChannel *install_channel = NULL;
+static GIOChannel *uart_channel = NULL;
+static char uart_dev_name[32];
+static unsigned long baud_rate = 0;
+
+static guint install_watch = 0;
+static guint uart_watch = 0;
+
+static int install_count = 0;
+
+#define NCCS2 19
+struct termios2 {
+       tcflag_t c_iflag;               /* input mode flags */
+       tcflag_t c_oflag;               /* output mode flags */
+       tcflag_t c_cflag;               /* control mode flags */
+       tcflag_t c_lflag;               /* local mode flags */
+       cc_t c_line;                    /* line discipline */
+       cc_t c_cc[NCCS2];               /* control characters */
+       speed_t c_ispeed;               /* input speed */
+       speed_t c_ospeed;               /* output speed */
+};
+
+#define  BOTHER         0x00001000
+
+/* HCI definitions */
+#define HCI_HDR_OPCODE          0xff36
+#define HCI_COMMAND_PKT         0x01
+#define HCI_EVENT_PKT           0x04
+#define EVT_CMD_COMPLETE        0x0E
+
+/* HCI Command structure to set the target baud rate */
+struct speed_change_cmd {
+       uint8_t uart_prefix;
+       uint16_t opcode;
+       uint8_t plen;
+       uint32_t speed;
+} __attribute__ ((packed));
+
+/* HCI Event structure to set the cusrom baud rate*/
+struct cmd_complete {
+       uint8_t uart_prefix;
+       uint8_t evt;
+       uint8_t plen;
+       uint8_t ncmd;
+       uint16_t opcode;
+       uint8_t status;
+       uint8_t data[16];
+} __attribute__ ((packed));
+
+static int read_baud_rate(unsigned long *baud)
+{
+       int err;
+       FILE *f;
+
+       DBG("");
+
+       f = fopen(TIST_SYSFS_BAUD, "r");
+       if (f == NULL)
+               return -EIO;
+
+       err = fscanf(f, "%lu", baud);
+       fclose(f);
+
+       DBG("baud rate %lu", *baud);
+
+       return err;
+}
+
+static int read_uart_name(char uart_name[], size_t uart_name_len)
+{
+       int err;
+       FILE *f;
+
+       DBG("");
+
+       memset(uart_name, 0, uart_name_len);
+
+       f = fopen(TIST_SYSFS_UART, "r");
+       if (f == NULL)
+               return -EIO;
+
+        err = fscanf(f, "%s", uart_name);
+       fclose(f);
+
+       DBG("UART name %s", uart_name);
+
+       return err;
+}
+
+static int read_hci_event(int fd, unsigned char *buf, int size)
+{
+       int prefix_len, param_len;
+
+       if (size <= 0)
+               return -EINVAL;
+
+       /* First 3 bytes are prefix, event and param length */
+       prefix_len = read(fd, buf, 3);
+       if (prefix_len < 0)
+               return prefix_len;
+
+       if (prefix_len < 3) {
+               connman_error("Truncated HCI prefix %d bytes 0x%x",
+                                               prefix_len, buf[0]);
+               return -EIO;
+       }
+
+       DBG("type 0x%x event 0x%x param len %d", buf[0], buf[1], buf[2]);
+
+       param_len = buf[2];
+       if (param_len > size - 3) {
+               connman_error("Buffer is too small %d", size);
+               return -EINVAL;
+       }
+
+       return read(fd, buf + 3, param_len);
+}
+
+static int read_command_complete(int fd, unsigned short opcode)
+{
+       struct cmd_complete resp;
+       int err;
+
+       DBG("");
+
+       err = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp));
+       if (err < 0)
+               return err;
+
+       DBG("HCI event %d bytes", err);
+
+       if (resp.uart_prefix != HCI_EVENT_PKT) {
+               connman_error("Not an event packet");
+               return -EIO;
+       }
+
+       if (resp.evt != EVT_CMD_COMPLETE) {
+               connman_error("Not a cmd complete event");
+               return -EIO;
+       }
+
+       if (resp.plen < 4) {
+               connman_error("HCI header length %d", resp.plen);
+               return -EIO;
+       }
+
+       if (resp.opcode != (unsigned short) opcode) {
+               connman_error("opcode 0x%04x 0x%04x", resp.opcode, opcode);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/* The default baud rate is 115200 */
+static int set_default_baud_rate(int fd)
+{
+       struct termios ti;
+       int err;
+
+       DBG("");
+
+       err = tcflush(fd, TCIOFLUSH);
+       if (err < 0)
+               goto err;
+
+       err = tcgetattr(fd, &ti);
+       if (err < 0)
+               goto err;
+
+       cfmakeraw(&ti);
+
+       ti.c_cflag |= 1;
+       ti.c_cflag |= CRTSCTS;
+
+       err = tcsetattr(fd, TCSANOW, &ti);
+       if (err < 0)
+               goto err;
+
+       cfsetospeed(&ti, B115200);
+       cfsetispeed(&ti, B115200);
+
+       err = tcsetattr(fd, TCSANOW, &ti);
+       if (err < 0)
+               goto err;
+
+       err = tcflush(fd, TCIOFLUSH);
+       if (err < 0)
+               goto err;
+
+       return 0;
+
+err:
+       connman_error("%s", strerror(errno));
+
+       return err;
+}
+
+static int set_custom_baud_rate(int fd, unsigned long cus_baud_rate, int flow_ctrl)
+{
+       struct termios ti;
+       struct termios2 ti2;
+       int err;
+
+       DBG("baud rate %lu flow_ctrl %d", cus_baud_rate, flow_ctrl);
+
+       err = tcflush(fd, TCIOFLUSH);
+       if (err < 0)
+               goto err;
+
+       err = tcgetattr(fd, &ti);
+       if (err < 0)
+               goto err;
+
+       if (flow_ctrl)
+               ti.c_cflag |= CRTSCTS;
+       else
+               ti.c_cflag &= ~CRTSCTS;
+
+       /*
+        * Set the parameters associated with the UART
+        * The change will occur immediately by using TCSANOW.
+        */
+       err = tcsetattr(fd, TCSANOW, &ti);
+       if (err < 0)
+               goto err;
+
+       err = tcflush(fd, TCIOFLUSH);
+       if (err < 0)
+               goto err;
+
+       /* Set the actual baud rate */
+       err = ioctl(fd, TCGETS2, &ti2);
+       if (err < 0)
+               goto err;
+
+       ti2.c_cflag &= ~CBAUD;
+       ti2.c_cflag |= BOTHER;
+       ti2.c_ospeed = cus_baud_rate;
+
+       err = ioctl(fd, TCSETS2, &ti2);
+       if (err < 0)
+               goto err;
+
+       return 0;
+
+err:
+       DBG("%s", strerror(errno));
+
+       return err;
+}
+
+static gboolean uart_event(GIOChannel *channel,
+                               GIOCondition cond, gpointer data)
+{
+       int uart_fd, ldisc;
+
+       DBG("");
+
+       uart_fd = g_io_channel_unix_get_fd(channel);
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               connman_error("UART event 0x%x", cond);
+               if (uart_watch > 0)
+                       g_source_remove(uart_watch);
+
+               goto err;
+       }
+
+       if (read_command_complete(uart_fd, HCI_HDR_OPCODE) < 0)
+               goto err;
+
+       if (set_custom_baud_rate(uart_fd, baud_rate, 1) < 0)
+               goto err;
+
+       ldisc = N_TI_WL;
+       if (ioctl(uart_fd, TIOCSETD, &ldisc) < 0)
+               goto err;
+
+       install_count = 1;
+       __sync_synchronize();
+
+       return FALSE;
+
+err:
+       install_count = 0;
+       __sync_synchronize();
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       return FALSE;
+}
+
+static int install_ldisc(GIOChannel *channel, gboolean install)
+{
+       int uart_fd, err;
+       struct speed_change_cmd cmd;
+       GIOFlags flags;
+
+       DBG("%d %p", install, uart_channel);
+
+       if (install == FALSE) {
+               install_count = 0;
+               __sync_synchronize();
+
+               if (uart_channel == NULL) {
+                       DBG("UART channel is NULL");
+                       return 0;
+               }
+
+               g_io_channel_shutdown(uart_channel, TRUE, NULL);
+               g_io_channel_unref(uart_channel);
+
+               uart_channel = NULL;
+
+               return 0;
+       }
+
+       if (uart_channel != NULL) {
+               g_io_channel_shutdown(uart_channel, TRUE, NULL);
+               g_io_channel_unref(uart_channel);
+               uart_channel = NULL;
+       }
+
+       DBG("opening %s custom baud %lu", uart_dev_name, baud_rate);
+       
+       uart_fd = open(uart_dev_name, O_RDWR | O_CLOEXEC);
+       if (uart_fd < 0)
+               return -EIO;
+
+       uart_channel = g_io_channel_unix_new(uart_fd);  
+       g_io_channel_set_close_on_unref(uart_channel, TRUE);
+
+       g_io_channel_set_encoding(uart_channel, NULL, NULL);
+       g_io_channel_set_buffered(uart_channel, FALSE);
+
+       flags = g_io_channel_get_flags(uart_channel);
+       flags |= G_IO_FLAG_NONBLOCK;
+       g_io_channel_set_flags(uart_channel, flags, NULL);
+
+        err = set_default_baud_rate(uart_fd);
+       if (err < 0) {
+               g_io_channel_shutdown(uart_channel, TRUE, NULL);
+               g_io_channel_unref(uart_channel);
+               uart_channel = NULL;
+
+               return err;
+       }
+
+       if (baud_rate == 115200) {
+               int ldisc;
+
+               ldisc = N_TI_WL;
+               if (ioctl(uart_fd, TIOCSETD, &ldisc) < 0) {
+                       g_io_channel_shutdown(uart_channel, TRUE, NULL);
+                       g_io_channel_unref(uart_channel);
+                       uart_channel = NULL;
+               }
+
+               install_count = 0;
+               __sync_synchronize();
+
+               return 0;
+       }
+
+       cmd.uart_prefix = HCI_COMMAND_PKT;
+       cmd.opcode = HCI_HDR_OPCODE;
+       cmd.plen = sizeof(unsigned long);
+       cmd.speed = baud_rate;
+
+       uart_watch = g_io_add_watch(uart_channel,
+                       G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                               uart_event, NULL);
+
+       err = write(uart_fd, &cmd, sizeof(cmd));
+       if (err < 0) {
+               connman_error("Write failed %d", err);
+
+               g_io_channel_shutdown(uart_channel, TRUE, NULL);
+               g_io_channel_unref(uart_channel);
+               uart_channel = NULL;
+       }
+
+       return err;
+}
+
+
+static gboolean install_event(GIOChannel *channel,
+                               GIOCondition cond, gpointer data)
+{
+       GIOStatus status = G_IO_STATUS_NORMAL;
+       unsigned int install_state;
+       gboolean install;
+       char buf[8];
+       gsize len;
+
+       DBG("");
+
+       if (cond & (G_IO_HUP | G_IO_NVAL)) {
+               connman_error("install event 0x%x", cond);
+               return FALSE;
+       }
+
+       __sync_synchronize();
+       if (install_count != 0) {
+               status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       g_io_channel_shutdown(channel, TRUE, NULL);
+                       g_io_channel_unref(channel);
+                       return FALSE;
+               }
+
+               /* Read the install value */
+               status = g_io_channel_read_chars(channel, (gchar *) buf, 8, &len, NULL);
+               if (status != G_IO_STATUS_NORMAL) {
+                       g_io_channel_shutdown(channel, TRUE, NULL);
+                       g_io_channel_unref(channel);
+                       return FALSE;
+               }
+
+               install_state = atoi(buf);
+               DBG("install event while installing %d %c", install_state, buf[0]);
+
+               return TRUE;
+       } else {
+               install_count = 1;
+               __sync_synchronize();
+       }
+
+       status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_io_channel_shutdown(channel, TRUE, NULL);
+               g_io_channel_unref(channel);
+               return FALSE;
+       }
+
+       /* Read the install value */
+       status = g_io_channel_read_chars(channel, (gchar *) buf, 8, &len, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_io_channel_shutdown(channel, TRUE, NULL);
+               g_io_channel_unref(channel);
+               return FALSE;
+       }
+
+       install_state = atoi(buf);
+
+       DBG("install state %d", install_state);
+
+       install = !!install_state;
+
+       if (install_ldisc(channel, install) < 0) {
+               connman_error("ldisc installation failed");
+               install_count = 0;
+               __sync_synchronize();
+               return TRUE;
+       }
+
+       return TRUE;
+}
+
+
+static int tist_init(void)
+{
+       GIOStatus status = G_IO_STATUS_NORMAL;
+       GIOFlags flags;
+       unsigned int install_state;
+       char buf[8];
+       int fd, err;
+       gsize len;
+
+       err = read_uart_name(uart_dev_name, sizeof(uart_dev_name));
+       if (err < 0) {
+               connman_error("Could not read the UART name");
+               return err;
+       }
+
+       err = read_baud_rate(&baud_rate);
+       if (err < 0) {
+               connman_error("Could not read the baud rate");
+               return err;
+       }
+
+       fd = open(TIST_SYSFS_INSTALL, O_RDONLY | O_CLOEXEC);
+       if (fd < 0) {
+               connman_error("Failed to open TI ST sysfs install file");
+               return -EIO;
+       }
+
+       install_channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(install_channel, TRUE);
+
+       g_io_channel_set_encoding(install_channel, NULL, NULL);
+       g_io_channel_set_buffered(install_channel, FALSE);
+
+       flags = g_io_channel_get_flags(install_channel);
+       flags |= G_IO_FLAG_NONBLOCK;
+       g_io_channel_set_flags(install_channel, flags, NULL);
+
+       status = g_io_channel_read_chars(install_channel, (gchar *) buf, 8,
+                                                               &len, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_io_channel_shutdown(install_channel, TRUE, NULL);
+               g_io_channel_unref(install_channel);
+               return status;
+       }
+
+       status = g_io_channel_seek_position(install_channel, 0, G_SEEK_SET, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               connman_error("Initial seek failed");
+               g_io_channel_shutdown(install_channel, TRUE, NULL);
+               g_io_channel_unref(install_channel);
+               return -EIO;
+       }
+
+       install_state = atoi(buf);
+
+       DBG("Initial state %d", install_state);
+
+       install_watch = g_io_add_watch_full(install_channel, G_PRIORITY_HIGH,
+                               G_IO_PRI | G_IO_ERR,
+                                           install_event, NULL, NULL);
+
+       if (install_state) {
+               install_count = 1;
+               __sync_synchronize();
+
+               err = install_ldisc(install_channel, TRUE);
+               if (err < 0) {
+                       connman_error("ldisc installtion failed");
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+
+static void tist_exit(void)
+{
+
+       if (install_watch > 0)
+               g_source_remove(install_watch);
+
+       DBG("uart_channel %p", uart_channel);
+
+       g_io_channel_shutdown(install_channel, TRUE, NULL);
+       g_io_channel_unref(install_channel);
+
+       if (uart_channel != NULL) {
+               g_io_channel_shutdown(uart_channel, TRUE, NULL);
+               g_io_channel_unref(uart_channel);
+               uart_channel = NULL;
+       }
+
+}
+
+CONNMAN_PLUGIN_DEFINE(tist, "TI shared transport support", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, tist_init, tist_exit)
diff --git a/plugins/vpn.c b/plugins/vpn.c
new file mode 100644 (file)
index 0000000..165c325
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+
+#include <dbus/dbus.h>
+
+#include <glib/gprintf.h>
+
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/rtnl.h>
+#include <connman/task.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+struct vpn_data {
+       struct connman_provider *provider;
+       char *if_name;
+       unsigned flags;
+       unsigned int watch;
+       unsigned int state;
+       struct connman_task *task;
+};
+
+struct vpn_driver_data {
+       const char *name;
+       const char *program;
+       struct vpn_driver *vpn_driver;
+       struct connman_provider_driver provider_driver;
+};
+
+GHashTable *driver_hash = NULL;
+
+static int stop_vpn(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       struct ifreq ifr;
+       int fd, err;
+
+       if (data == NULL)
+               return -EINVAL;
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return -EINVAL;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+                       vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+               return 0;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+       sprintf(ifr.ifr_name, "%s", data->if_name);
+
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               err = -errno;
+               connman_error("Failed to open /dev/net/tun to device %s: %s",
+                             data->if_name, strerror(errno));
+               return err;
+       }
+
+       if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
+               err = -errno;
+               connman_error("Failed to TUNSETIFF for device %s to it: %s",
+                             data->if_name, strerror(errno));
+               close(fd);
+               return err;
+       }
+
+       if (ioctl(fd, TUNSETPERSIST, 0)) {
+               err = -errno;
+               connman_error("Failed to set tun device %s nonpersistent: %s",
+                             data->if_name, strerror(errno));
+               close(fd);
+               return err;
+       }
+       close(fd);
+       DBG("Killed tun device %s", data->if_name);
+       return 0;
+}
+
+void vpn_died(struct connman_task *task, int exit_code, void *user_data)
+{
+       struct connman_provider *provider = user_data;
+       struct vpn_data *data = connman_provider_get_data(provider);
+       int state = VPN_STATE_FAILURE;
+       enum connman_provider_error ret;
+
+       DBG("provider %p data %p", provider, data);
+
+       if (data == NULL)
+               goto vpn_exit;
+
+       state = data->state;
+
+       stop_vpn(provider);
+       connman_provider_set_data(provider, NULL);
+
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
+               connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
+
+vpn_exit:
+       if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
+               const char *name;
+               struct vpn_driver_data *vpn_data = NULL;
+
+               name = connman_provider_get_driver_name(provider);
+               if (name != NULL)
+                       vpn_data = g_hash_table_lookup(driver_hash, name);
+
+               if (vpn_data != NULL &&
+                               vpn_data->vpn_driver->error_code != NULL)
+                       ret = vpn_data->vpn_driver->error_code(exit_code);
+               else
+                       ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
+
+               connman_provider_indicate_error(provider, ret);
+       } else
+               connman_provider_set_state(provider,
+                                               CONNMAN_PROVIDER_STATE_IDLE);
+
+       connman_provider_set_index(provider, -1);
+
+       if (data != NULL) {
+               connman_provider_unref(data->provider);
+               g_free(data->if_name);
+               g_free(data);
+       }
+
+       connman_task_destroy(task);
+}
+
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       int index;
+
+       if (ifname == NULL || data == NULL)
+               return  -EIO;
+
+       index = connman_inet_ifindex(ifname);
+       if (index < 0)
+               return  -EIO;
+
+       if (data->if_name != NULL)
+               g_free(data->if_name);
+
+       data->if_name = (char *)g_strdup(ifname);
+       connman_provider_set_index(provider, index);
+
+       return 0;
+}
+
+static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       struct connman_provider *provider = user_data;
+       struct vpn_data *data = connman_provider_get_data(provider);
+
+       if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
+               if (flags & IFF_UP) {
+                       data->state = VPN_STATE_READY;
+                       connman_provider_set_state(provider,
+                                       CONNMAN_PROVIDER_STATE_READY);
+               }
+       }
+       data->flags = flags;
+}
+
+static DBusMessage *vpn_notify(struct connman_task *task,
+                       DBusMessage *msg, void *user_data)
+{
+       struct connman_provider *provider = user_data;
+       struct vpn_data *data;
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       int state, index;
+
+       data = connman_provider_get_data(provider);
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return NULL;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (vpn_driver_data == NULL)
+               return NULL;
+
+       state = vpn_driver_data->vpn_driver->notify(msg, provider);
+       switch (state) {
+       case VPN_STATE_CONNECT:
+       case VPN_STATE_READY:
+               index = connman_provider_get_index(provider);
+               connman_provider_ref(provider);
+               data->watch = connman_rtnl_add_newlink_watch(index,
+                                                    vpn_newlink, provider);
+               connman_inet_ifup(index);
+               break;
+
+       case VPN_STATE_UNKNOWN:
+       case VPN_STATE_IDLE:
+       case VPN_STATE_DISCONNECT:
+       case VPN_STATE_FAILURE:
+               connman_provider_set_state(provider,
+                                       CONNMAN_PROVIDER_STATE_DISCONNECT);
+               break;
+
+       case VPN_STATE_AUTH_FAILURE:
+               connman_provider_indicate_error(provider,
+                                       CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
+               break;
+       }
+
+       return NULL;
+}
+
+static int vpn_create_tun(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct ifreq ifr;
+       int i, fd, index;
+       int ret = 0;
+
+       if (data == NULL)
+               return -EISCONN;
+
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               i = -errno;
+               connman_error("Failed to open /dev/net/tun: %s",
+                             strerror(errno));
+               ret = i;
+               goto exist_err;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+
+       for (i = 0; i < 256; i++) {
+               sprintf(ifr.ifr_name, "vpn%d", i);
+
+               if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
+                       break;
+       }
+
+       if (i == 256) {
+               connman_error("Failed to find available tun device");
+               close(fd);
+               ret = -ENODEV;
+               goto exist_err;
+       }
+
+       data->if_name = (char *)g_strdup(ifr.ifr_name);
+       if (data->if_name == NULL) {
+               connman_error("Failed to allocate memory");
+               close(fd);
+               ret = -ENOMEM;
+               goto exist_err;
+       }
+
+       if (ioctl(fd, TUNSETPERSIST, 1)) {
+               i = -errno;
+               connman_error("Failed to set tun persistent: %s",
+                             strerror(errno));
+               close(fd);
+               ret = i;
+               goto exist_err;
+       }
+
+       close(fd);
+
+       index = connman_inet_ifindex(data->if_name);
+       if (index < 0) {
+               connman_error("Failed to get tun ifindex");
+               stop_vpn(provider);
+               ret = -EIO;
+               goto exist_err;
+       }
+       connman_provider_set_index(provider, index);
+
+       return 0;
+
+exist_err:
+       return ret;
+}
+
+static int vpn_connect(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       int ret = 0;
+
+       if (data != NULL)
+               return -EISCONN;
+
+       data = g_try_new0(struct vpn_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->provider = connman_provider_ref(provider);
+       data->watch = 0;
+       data->flags = 0;
+       data->task = NULL;
+       data->state = VPN_STATE_IDLE;
+
+       connman_provider_set_data(provider, data);
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return -EINVAL;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
+               ret = -EINVAL;
+               goto exist_err;
+       }
+
+       if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+               ret = vpn_create_tun(provider);
+               if (ret < 0)
+                       goto exist_err;
+       }
+
+       data->task = connman_task_create(vpn_driver_data->program);
+
+       if (data->task == NULL) {
+               ret = -ENOMEM;
+               stop_vpn(provider);
+               goto exist_err;
+       }
+
+       if (connman_task_set_notify(data->task, "notify",
+                                       vpn_notify, provider)) {
+               ret = -ENOMEM;
+               stop_vpn(provider);
+               connman_task_destroy(data->task);
+               data->task = NULL;
+               goto exist_err;
+       }
+
+       ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
+                                                       data->if_name);
+       if (ret < 0) {
+               stop_vpn(provider);
+               connman_task_destroy(data->task);
+               data->task = NULL;
+               goto exist_err;
+       }
+
+       DBG("%s started with dev %s",
+               vpn_driver_data->provider_driver.name, data->if_name);
+
+       data->state = VPN_STATE_CONNECT;
+
+       return -EINPROGRESS;
+
+exist_err:
+       connman_provider_set_index(provider, -1);
+       connman_provider_set_data(provider, NULL);
+       connman_provider_unref(data->provider);
+       g_free(data->if_name);
+       g_free(data);
+
+       return ret;
+}
+
+static int vpn_probe(struct connman_provider *provider)
+{
+       return 0;
+}
+
+static int vpn_disconnect(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+
+       DBG("disconnect provider %p:", provider);
+
+       if (data == NULL)
+               return 0;
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return 0;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (vpn_driver_data->vpn_driver->disconnect)
+               vpn_driver_data->vpn_driver->disconnect();
+
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
+               connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
+
+       data->state = VPN_STATE_DISCONNECT;
+       connman_task_stop(data->task);
+
+       return 0;
+}
+
+static int vpn_remove(struct connman_provider *provider)
+{
+       struct vpn_data *data;
+
+       data = connman_provider_get_data(provider);
+       if (data == NULL)
+               return 0;
+
+       if (data->watch != 0) {
+               connman_provider_unref(provider);
+               connman_rtnl_remove_watch(data->watch);
+               data->watch = 0;
+       }
+
+       connman_task_stop(data->task);
+
+       g_usleep(G_USEC_PER_SEC);
+       stop_vpn(provider);
+       return 0;
+}
+
+static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
+{
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+
+       name = connman_provider_get_driver_name(provider);
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (vpn_driver_data != NULL &&
+                       vpn_driver_data->vpn_driver->save != NULL)
+               return vpn_driver_data->vpn_driver->save(provider, keyfile);
+
+       return 0;
+}
+
+int vpn_register(const char *name, struct vpn_driver *vpn_driver,
+                       const char *program)
+{
+       struct vpn_driver_data *data;
+
+       data = g_try_new0(struct vpn_driver_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->name = name;
+       data->program = program;
+
+       data->vpn_driver = vpn_driver;
+
+       data->provider_driver.name = name;
+       data->provider_driver.disconnect = vpn_disconnect;
+       data->provider_driver.connect = vpn_connect;
+       data->provider_driver.probe = vpn_probe;
+       data->provider_driver.remove = vpn_remove;
+       data->provider_driver.save = vpn_save;
+
+       if (driver_hash == NULL)
+               driver_hash = g_hash_table_new_full(g_str_hash,
+                                                       g_str_equal,
+                                                       NULL, g_free);
+
+       if (driver_hash == NULL) {
+               connman_error("driver_hash not initialized for %s", name);
+               g_free(data);
+               return -ENOMEM;
+       }
+
+       g_hash_table_replace(driver_hash, (char *)name, data);
+
+       connman_provider_driver_register(&data->provider_driver);
+
+       return 0;
+}
+
+void vpn_unregister(const char *name)
+{
+       struct vpn_driver_data *data;
+
+       data = g_hash_table_lookup(driver_hash, name);
+       if (data == NULL)
+               return;
+
+       connman_provider_driver_unregister(&data->provider_driver);
+
+       g_hash_table_remove(driver_hash, name);
+
+       if (g_hash_table_size(driver_hash) == 0)
+               g_hash_table_destroy(driver_hash);
+}
diff --git a/plugins/vpn.h b/plugins/vpn.h
new file mode 100644 (file)
index 0000000..1ecfba2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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
+ *
+ */
+
+#define VPN_FLAG_NO_TUN        1
+
+enum vpn_state {
+       VPN_STATE_UNKNOWN       = 0,
+       VPN_STATE_IDLE          = 1,
+       VPN_STATE_CONNECT       = 2,
+       VPN_STATE_READY         = 3,
+       VPN_STATE_DISCONNECT    = 4,
+       VPN_STATE_FAILURE       = 5,
+       VPN_STATE_AUTH_FAILURE  = 6,
+};
+
+struct vpn_driver {
+       int flags;
+       int (*notify) (DBusMessage *msg, struct connman_provider *provider);
+       int (*connect) (struct connman_provider *provider,
+                       struct connman_task *task, const char *if_name);
+       void (*disconnect) (void);
+       int (*error_code) (int exit_code);
+       int (*save) (struct connman_provider *provider, GKeyFile *keyfile);
+};
+
+int vpn_register(const char *name, struct vpn_driver *driver,
+                       const char *program);
+void vpn_unregister(const char *provider_name);
+void vpn_died(struct connman_task *task, int exit_code, void *user_data);
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname);
diff --git a/plugins/vpnc.c b/plugins/vpnc.c
new file mode 100644 (file)
index 0000000..fea1c15
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/ipconfig.h>
+#include <connman/dbus.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static DBusConnection *connection;
+
+enum {
+       OPT_STRING = 1,
+       OPT_BOOLEAN = 2,
+};
+
+struct {
+       const char *cm_opt;
+       const char *vpnc_opt;
+       const char *vpnc_default;
+       int type;
+       connman_bool_t cm_save;
+} vpnc_options[] = {
+       { "Host", "IPSec gateway", NULL, OPT_STRING, TRUE },
+       { "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING, TRUE },
+       { "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING, FALSE },
+       { "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING, FALSE },
+       { "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING, FALSE },
+       { "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING, TRUE },
+       { "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING, TRUE },
+       { "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING, TRUE },
+       { "VPNC.Domain", "Domain", NULL, OPT_STRING, TRUE },
+       { "VPNC.Vendor", "Vendor", NULL, OPT_STRING, TRUE },
+       { "VPNC.LocalPort", "Local Port", "0", OPT_STRING, TRUE, },
+       { "VPNC.CiscoPort","Cisco UDP Encapsulation Port", "0", OPT_STRING,
+         TRUE },
+       { "VPNC.AppVersion", "Application Version", NULL, OPT_STRING, TRUE },
+       { "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING,
+         TRUE },
+       { "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
+         TRUE },
+       { "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, TRUE },
+       { "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
+         TRUE },
+};
+
+static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       char *address = NULL, *netmask = NULL, *gateway = NULL;
+       struct connman_ipaddress *ipaddress;
+       const char *reason, *key, *value;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (!provider) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "VPNGATEWAY"))
+                       gateway = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
+                       address = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
+                       netmask = g_strdup(value);
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS"))
+                       connman_provider_set_nameservers(provider, value);
+
+               if (!strcmp(key, "CISCO_DEF_DOMAIN"))
+                       connman_provider_set_domain(provider, value);
+
+               if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
+                       g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
+                       connman_provider_append_route(provider, key, value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+
+       ipaddress = connman_ipaddress_alloc(AF_INET);
+       if (ipaddress == NULL) {
+               g_free(address);
+               g_free(netmask);
+               g_free(gateway);
+
+               return VPN_STATE_FAILURE;
+       }
+
+       connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
+       connman_provider_set_ipaddress(provider, ipaddress);
+
+       g_free(address);
+       g_free(netmask);
+       g_free(gateway);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+       ssize_t byte_write;
+
+       while (len) {
+               byte_write = write(fd, buf, len);
+               if (byte_write < 0) {
+                       connman_error("failed to write config to vpnc: %s\n",
+                                       strerror(errno));
+                       return byte_write;
+               }
+               len -= byte_write;
+               buf += byte_write;
+       }
+
+       return 0;
+}
+
+static ssize_t write_option(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               buf = g_strdup_printf("%s %s\n", key, value);
+               ret = full_write(fd, buf, strlen(buf));
+
+               g_free(buf);
+       }
+
+       return ret;
+}
+
+static ssize_t write_bool_option(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               if (strcmp(value, "yes") == 0) {
+                       buf = g_strdup_printf("%s\n", key);
+                       ret = full_write(fd, buf, strlen(buf));
+
+                       g_free(buf);
+               }
+       }
+
+       return ret;
+}
+
+static int vc_write_config_data(struct connman_provider *provider, int fd)
+{
+       const char *opt_s;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
+               opt_s = connman_provider_get_string(provider,
+                                       vpnc_options[i].cm_opt);
+               if (!opt_s)
+                       opt_s= vpnc_options[i].vpnc_default;
+
+               if(!opt_s)
+                       continue;
+
+               if (vpnc_options[i].type == OPT_STRING) {
+                       if (write_option(fd,
+                                       vpnc_options[i].vpnc_opt, opt_s) < 0)
+                               return -EIO;
+               } else if (vpnc_options[i].type == OPT_BOOLEAN) {
+                       if (write_bool_option(fd,
+                                       vpnc_options[i].vpnc_opt, opt_s) < 0)
+                               return -EIO;
+               }
+
+       }
+
+       return 0;
+}
+
+static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
+               if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
+
+                       if (vpnc_options[i].cm_save == FALSE)
+                               continue;
+
+                       option = connman_provider_get_string(provider,
+                                                       vpnc_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       vpnc_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static int vc_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *option;
+       int err, fd;
+
+       option = connman_provider_get_string(provider, "Host");
+       if (option == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+       option = connman_provider_get_string(provider, "VPNC.IPSec.ID");
+       if (option == NULL) {
+               connman_error("Group not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       connman_task_add_argument(task, "--non-inter", NULL);
+       connman_task_add_argument(task, "--no-detach", NULL);
+
+       connman_task_add_argument(task, "--ifname", if_name);
+       connman_task_add_argument(task, "--ifmode", "tun");
+
+       connman_task_add_argument(task, "--script",
+                               SCRIPTDIR "/openconnect-script");
+
+       option = connman_provider_get_string(provider, "VPNC.Debug");
+       if (option != NULL)
+               connman_task_add_argument(task, "--debug", option);
+
+       connman_task_add_argument(task, "-", NULL);
+
+       err = connman_task_run(task, vpn_died, provider,
+                               &fd, NULL, NULL);
+       if (err < 0) {
+               connman_error("vpnc failed to start");
+               return -EIO;
+       }
+
+       err = vc_write_config_data(provider, fd);
+
+       close(fd);
+
+       return err;
+}
+
+static int vc_error_code(int exit_code)
+{
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       case 2:
+               return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .notify         = vc_notify,
+       .connect        = vc_connect,
+       .error_code     = vc_error_code,
+       .save           = vc_save,
+};
+
+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,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, vpnc_init, vpnc_exit)
diff --git a/plugins/wifi.c b/plugins/wifi.c
new file mode 100644 (file)
index 0000000..c50f877
--- /dev/null
@@ -0,0 +1,1997 @@
+/*
+ *
+ *  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 <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <net/ethernet.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/inet.h>
+#include <connman/device.h>
+#include <connman/rtnl.h>
+#include <connman/technology.h>
+#include <connman/log.h>
+#include <connman/option.h>
+#include <connman/storage.h>
+#include <include/setting.h>
+
+#include <gsupplicant/gsupplicant.h>
+
+#define CLEANUP_TIMEOUT   8    /* in seconds */
+#define INACTIVE_TIMEOUT  12   /* in seconds */
+#define MAXIMUM_RETRIES   4
+
+#define BGSCAN_DEFAULT "simple:30:-45:300"
+#define AUTOSCAN_DEFAULT "exponential:3:300"
+
+static struct connman_technology *wifi_technology = NULL;
+
+struct hidden_params {
+       char ssid[32];
+       unsigned int ssid_len;
+       char *identity;
+       char *passphrase;
+       gpointer user_data;
+};
+
+/**
+ * Used for autoscan "emulation".
+ * Should be removed when wpa_s autoscan support will be by default.
+ */
+struct autoscan_params {
+       int base;
+       int limit;
+       int interval;
+       unsigned int timeout;
+};
+
+struct wifi_data {
+       char *identifier;
+       struct connman_device *device;
+       struct connman_network *network;
+       struct connman_network *pending_network;
+       GSList *networks;
+       GSupplicantInterface *interface;
+       GSupplicantState state;
+       connman_bool_t connected;
+       connman_bool_t disconnecting;
+       connman_bool_t tethering;
+       connman_bool_t bridged;
+       const char *bridge;
+       int index;
+       unsigned flags;
+       unsigned int watch;
+       int retries;
+       struct hidden_params *hidden;
+       /**
+        * autoscan "emulation".
+        */
+       struct autoscan_params *autoscan;
+};
+
+static GList *iface_list = NULL;
+
+static void start_autoscan(struct connman_device *device);
+
+static void handle_tethering(struct wifi_data *wifi)
+{
+       if (wifi->tethering == FALSE)
+               return;
+
+       if (wifi->bridge == NULL)
+               return;
+
+       if (wifi->bridged == TRUE)
+               return;
+
+       DBG("index %d bridge %s", wifi->index, wifi->bridge);
+
+       if (connman_inet_add_to_bridge(wifi->index, wifi->bridge) < 0)
+               return;
+
+       wifi->bridged = TRUE;
+}
+
+static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       if (wifi == NULL)
+               return;
+
+       DBG("index %d flags %d change %d", wifi->index, flags, change);
+
+       if (!change)
+               return;
+
+       if ((wifi->flags & IFF_UP) != (flags & IFF_UP)) {
+               if (flags & IFF_UP)
+                       DBG("interface up");
+               else
+                       DBG("interface down");
+       }
+
+       if ((wifi->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
+               if (flags & IFF_LOWER_UP) {
+                       DBG("carrier on");
+
+                       handle_tethering(wifi);
+               } else
+                       DBG("carrier off");
+       }
+
+       wifi->flags = flags;
+}
+
+static int wifi_probe(struct connman_device *device)
+{
+       struct wifi_data *wifi;
+
+       DBG("device %p", device);
+
+       wifi = g_try_new0(struct wifi_data, 1);
+       if (wifi == NULL)
+               return -ENOMEM;
+
+       wifi->connected = FALSE;
+       wifi->disconnecting = FALSE;
+       wifi->tethering = FALSE;
+       wifi->bridged = FALSE;
+       wifi->bridge = NULL;
+       wifi->state = G_SUPPLICANT_STATE_INACTIVE;
+
+       connman_device_set_data(device, wifi);
+       wifi->device = connman_device_ref(device);
+
+       wifi->index = connman_device_get_index(device);
+       wifi->flags = 0;
+
+       wifi->watch = connman_rtnl_add_newlink_watch(wifi->index,
+                                                       wifi_newlink, device);
+
+       iface_list = g_list_append(iface_list, wifi);
+
+       return 0;
+}
+
+static void remove_networks(struct connman_device *device,
+                               struct wifi_data *wifi)
+{
+       GSList *list;
+
+       for (list = wifi->networks; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               connman_device_remove_network(device, network);
+               connman_network_unref(network);
+       }
+
+       g_slist_free(wifi->networks);
+       wifi->networks = NULL;
+}
+
+static void reset_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL || wifi->autoscan == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->timeout == 0 && autoscan->interval == 0)
+               return;
+
+       g_source_remove(autoscan->timeout);
+
+       autoscan->timeout = 0;
+       autoscan->interval = 0;
+
+       connman_device_unref(device);
+}
+
+static void stop_autoscan(struct connman_device *device)
+{
+       reset_autoscan(device);
+
+       connman_device_set_scanning(device, FALSE);
+}
+
+static void wifi_remove(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       DBG("device %p wifi %p", device, wifi);
+
+       if (wifi == NULL)
+               return;
+
+       iface_list = g_list_remove(iface_list, wifi);
+
+       remove_networks(device, wifi);
+
+       connman_device_set_powered(device, FALSE);
+       connman_device_set_data(device, NULL);
+       connman_device_unref(wifi->device);
+       connman_rtnl_remove_watch(wifi->watch);
+
+       g_supplicant_interface_set_data(wifi->interface, NULL);
+
+       g_free(wifi->autoscan);
+       g_free(wifi->identifier);
+       g_free(wifi);
+}
+
+static int add_scan_param(gchar *hex_ssid, int freq,
+                       GSupplicantScanParams *scan_data,
+                       int driver_max_scan_ssids)
+{
+       unsigned int i;
+       struct scan_ssid *scan_ssid;
+
+       if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
+               gchar *ssid;
+               unsigned int j = 0, hex;
+               size_t hex_ssid_len = strlen(hex_ssid);
+
+               ssid = g_try_malloc0(hex_ssid_len / 2);
+               if (ssid == NULL)
+                       return -ENOMEM;
+
+               for (i = 0; i < hex_ssid_len; i += 2) {
+                       sscanf(hex_ssid + i, "%02x", &hex);
+                       ssid[j++] = hex;
+               }
+
+               scan_ssid = g_try_new(struct scan_ssid, 1);
+               if (scan_ssid == NULL) {
+                       g_free(ssid);
+                       return -ENOMEM;
+               }
+
+               memcpy(scan_ssid->ssid, ssid, j);
+               scan_ssid->ssid_len = j;
+               scan_data->ssids = g_slist_prepend(scan_data->ssids,
+                                                               scan_ssid);
+
+               scan_data->num_ssids++;
+
+               g_free(ssid);
+       } else
+               return -EINVAL;
+
+       scan_data->ssids = g_slist_reverse(scan_data->ssids);
+
+       if (scan_data->freqs == NULL) {
+               scan_data->freqs = g_try_malloc0(sizeof(uint16_t) *
+                                               scan_data->num_ssids);
+               if (scan_data->freqs == NULL) {
+                       g_slist_free_full(scan_data->ssids, g_free);
+                       return -ENOMEM;
+               }
+       } else {
+               scan_data->freqs = g_try_realloc(scan_data->freqs,
+                               sizeof(uint16_t) * scan_data->num_ssids);
+               if (scan_data->freqs == NULL) {
+                       g_slist_free_full(scan_data->ssids, g_free);
+                       return -ENOMEM;
+               }
+               scan_data->freqs[scan_data->num_ssids - 1] = 0;
+       }
+
+       /* Don't add duplicate entries */
+       for (i = 0; i < scan_data->num_ssids; i++) {
+               if (scan_data->freqs[i] == 0) {
+                       scan_data->freqs[i] = freq;
+                       break;
+               } else if (scan_data->freqs[i] == freq)
+                       break;
+       }
+
+       return 0;
+}
+
+static int get_hidden_connections(int max_ssids,
+                               GSupplicantScanParams *scan_data)
+{
+       GKeyFile *keyfile;
+       gchar **services;
+       char *ssid;
+       gchar *str;
+       int i, freq;
+       gboolean value;
+       int num_ssids = 0, add_param_failed = 0;
+
+       services = connman_storage_get_services();
+       for (i = 0; services && services[i]; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Hidden", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "AutoConnect", NULL);
+               if (value == FALSE) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               ssid = g_key_file_get_string(keyfile,
+                                       services[i], "SSID", NULL);
+
+               freq = g_key_file_get_integer(keyfile, services[i],
+                                       "Frequency", NULL);
+
+               if (add_scan_param(ssid, freq, scan_data, max_ssids) < 0) {
+                       str = g_key_file_get_string(keyfile,
+                                       services[i], "Name", NULL);
+                       DBG("Cannot scan %s (%s)", ssid, str);
+                       g_free(str);
+                       add_param_failed++;
+               }
+
+               num_ssids++;
+
+               g_key_file_free(keyfile);
+       }
+
+       if (add_param_failed > 0)
+               connman_warn("Unable to scan %d out of %d SSIDs (max is %d)",
+                       add_param_failed, num_ssids, max_ssids);
+
+       g_strfreev(services);
+
+       return num_ssids > max_ssids ? max_ssids : num_ssids;
+}
+
+static int throw_wifi_scan(struct connman_device *device,
+                       GSupplicantInterfaceCallback callback)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       DBG("device %p %p", device, wifi->interface);
+
+       if (wifi->tethering == TRUE)
+               return 0;
+
+       if (connman_device_get_scanning(device) == TRUE)
+               return -EALREADY;
+
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, NULL,
+                                               callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else
+               connman_device_unref(device);
+
+       return ret;
+}
+
+static void hidden_free(struct hidden_params *hidden)
+{
+       if (hidden == NULL)
+               return;
+
+       g_free(hidden->identity);
+       g_free(hidden->passphrase);
+       g_free(hidden);
+}
+
+static void scan_callback(int result, GSupplicantInterface *interface,
+                                               void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       DBG("result %d wifi %p", result, wifi);
+
+       if (wifi != NULL && wifi->hidden != NULL) {
+               connman_network_clear_hidden(wifi->hidden->user_data);
+               hidden_free(wifi->hidden);
+               wifi->hidden = NULL;
+       }
+
+       if (result < 0)
+               connman_device_reset_scanning(device);
+
+       connman_device_set_scanning(device, FALSE);
+       start_autoscan(device);
+       connman_device_unref(device);
+}
+
+static void scan_callback_hidden(int result,
+                       GSupplicantInterface *interface, void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int driver_max_ssids;
+
+       DBG("result %d wifi %p", result, wifi);
+
+       if (wifi == NULL)
+               goto out;
+
+       /*
+        * Scan hidden networks so that we can autoconnect to them.
+        */
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       DBG("max ssids %d", driver_max_ssids);
+
+       if (driver_max_ssids > 0) {
+               GSupplicantScanParams *scan_params;
+               int ret;
+
+               scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+               if (scan_params == NULL)
+                       goto out;
+
+               if (get_hidden_connections(driver_max_ssids,
+                                               scan_params) > 0) {
+                       ret = g_supplicant_interface_scan(wifi->interface,
+                                                       scan_params,
+                                                       scan_callback,
+                                                       device);
+                       if (ret == 0)
+                               return;
+               }
+
+               g_supplicant_free_scan_params(scan_params);
+       }
+
+out:
+       scan_callback(result, interface, user_data);
+}
+
+static gboolean autoscan_timeout(gpointer data)
+{
+       struct connman_device *device = data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+       int interval;
+
+       if (wifi == NULL)
+               return FALSE;
+
+       autoscan = wifi->autoscan;
+
+       if (autoscan->interval <= 0) {
+               interval = autoscan->base;
+               goto set_interval;
+       } else
+               interval = autoscan->interval * autoscan->base;
+
+       if (autoscan->interval >= autoscan->limit)
+               interval = autoscan->limit;
+
+       throw_wifi_scan(wifi->device, scan_callback_hidden);
+
+set_interval:
+       DBG("interval %d", interval);
+
+       autoscan->interval = interval;
+
+       autoscan->timeout = g_timeout_add_seconds(interval,
+                                               autoscan_timeout, device);
+
+       return FALSE;
+}
+
+static void start_autoscan(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       struct autoscan_params *autoscan;
+
+       DBG("");
+
+       if (wifi == NULL)
+               return;
+
+       autoscan = wifi->autoscan;
+       if (autoscan == NULL)
+               return;
+
+       if (autoscan->timeout > 0 || autoscan->interval > 0)
+               return;
+
+       connman_device_ref(device);
+
+       autoscan_timeout(device);
+}
+
+static struct autoscan_params *parse_autoscan_params(const char *params)
+{
+       struct autoscan_params *autoscan;
+       char **list_params;
+       int limit;
+       int base;
+
+       DBG("Emulating autoscan");
+
+       list_params = g_strsplit(params, ":", 0);
+       if (list_params == 0)
+               return NULL;
+
+       if (g_strv_length(list_params) < 3) {
+               g_strfreev(list_params);
+               return NULL;
+       }
+
+       base = atoi(list_params[1]);
+       limit = atoi(list_params[2]);
+
+       g_strfreev(list_params);
+
+       autoscan = g_try_malloc0(sizeof(struct autoscan_params));
+       if (autoscan == NULL) {
+               DBG("Could not allocate memory for autoscan");
+               return NULL;
+       }
+
+       DBG("base %d - limit %d", base, limit);
+       autoscan->base = base;
+       autoscan->limit = limit;
+
+       return autoscan;
+}
+
+static void setup_autoscan(struct wifi_data *wifi)
+{
+       if (wifi->autoscan == NULL)
+               wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT);
+
+       start_autoscan(wifi->device);
+}
+
+static void interface_create_callback(int result,
+                                       GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct wifi_data *wifi = user_data;
+
+       DBG("result %d ifname %s, wifi %p", result,
+                               g_supplicant_interface_get_ifname(interface),
+                               wifi);
+
+       if (result < 0 || wifi == NULL)
+               return;
+
+       wifi->interface = interface;
+       g_supplicant_interface_set_data(interface, wifi);
+
+       if (g_supplicant_interface_get_ready(interface) == FALSE)
+               return;
+
+       DBG("interface is ready wifi %p tethering %d", wifi, wifi->tethering);
+
+       if (wifi->device == NULL) {
+               connman_error("WiFi device not set");
+               return;
+       }
+
+       connman_device_set_powered(wifi->device, TRUE);
+
+       if (connman_setting_get_bool("BackgroundScanning") == FALSE)
+               return;
+
+       /* Setting up automatic scanning */
+       setup_autoscan(wifi);
+}
+
+static int wifi_enable(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       const char *interface = connman_device_get_string(device, "Interface");
+       const char *driver = connman_option_get_string("wifi");
+       int ret;
+
+       DBG("device %p %p", device, wifi);
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       ret = g_supplicant_interface_create(interface, driver, NULL,
+                                               interface_create_callback,
+                                                       wifi);
+       if (ret < 0)
+               return ret;
+
+       return -EINPROGRESS;
+}
+
+static int wifi_disable(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       DBG("device %p wifi %p", device, wifi);
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       wifi->connected = FALSE;
+       wifi->disconnecting = FALSE;
+
+       if (wifi->pending_network != NULL)
+               wifi->pending_network = NULL;
+
+       stop_autoscan(device);
+
+       /* In case of a user scan, device is still referenced */
+       if (connman_device_get_scanning(device) == TRUE) {
+               connman_device_set_scanning(device, FALSE);
+               connman_device_unref(wifi->device);
+       }
+
+       remove_networks(device, wifi);
+
+       ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL);
+       if (ret < 0)
+               return ret;
+
+       return -EINPROGRESS;
+}
+
+struct last_connected {
+       GTimeVal modified;
+       gchar *ssid;
+       int freq;
+};
+
+static gint sort_entry(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       GTimeVal *aval = (GTimeVal *)a;
+       GTimeVal *bval = (GTimeVal *)b;
+
+       /* Note that the sort order is descending */
+       if (aval->tv_sec < bval->tv_sec)
+               return 1;
+
+       if (aval->tv_sec > bval->tv_sec)
+               return -1;
+
+       return 0;
+}
+
+static void free_entry(gpointer data)
+{
+       struct last_connected *entry = data;
+
+       g_free(entry->ssid);
+       g_free(entry);
+}
+
+static int get_latest_connections(int max_ssids,
+                               GSupplicantScanParams *scan_data)
+{
+       GSequenceIter *iter;
+       GSequence *latest_list;
+       struct last_connected *entry;
+       GKeyFile *keyfile;
+       GTimeVal modified;
+       gchar **services;
+       gchar *str;
+       char *ssid;
+       int i, freq;
+       int num_ssids = 0;
+
+       latest_list = g_sequence_new(free_entry);
+       if (latest_list == NULL)
+               return -ENOMEM;
+
+       services = connman_storage_get_services();
+       for (i = 0; services && services[i]; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       if (str)
+                               g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "AutoConnect", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       if (str)
+                               g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "Modified", NULL);
+               if (str != NULL) {
+                       g_time_val_from_iso8601(str, &modified);
+                       g_free(str);
+               }
+
+               ssid = g_key_file_get_string(keyfile,
+                                       services[i], "SSID", NULL);
+
+               freq = g_key_file_get_integer(keyfile, services[i],
+                                       "Frequency", NULL);
+               if (freq) {
+                       entry = g_try_new(struct last_connected, 1);
+                       if (entry == NULL) {
+                               g_sequence_free(latest_list);
+                               g_key_file_free(keyfile);
+                               g_free(ssid);
+                               return -ENOMEM;
+                       }
+
+                       entry->ssid = ssid;
+                       entry->modified = modified;
+                       entry->freq = freq;
+
+                       g_sequence_insert_sorted(latest_list, entry,
+                                               sort_entry, NULL);
+                       num_ssids++;
+               } else
+                       g_free(ssid);
+
+               g_key_file_free(keyfile);
+       }
+
+       g_strfreev(services);
+
+       num_ssids = num_ssids > max_ssids ? max_ssids : num_ssids;
+
+       iter = g_sequence_get_begin_iter(latest_list);
+
+       for (i = 0; i < num_ssids; i++) {
+               entry = g_sequence_get(iter);
+
+               DBG("ssid %s freq %d modified %lu", entry->ssid, entry->freq,
+                                               entry->modified.tv_sec);
+
+               add_scan_param(entry->ssid, entry->freq, scan_data, max_ssids);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       g_sequence_free(latest_list);
+       return num_ssids;
+}
+
+static int wifi_scan(struct connman_device *device)
+{
+       reset_autoscan(device);
+
+       return throw_wifi_scan(device, scan_callback_hidden);
+}
+
+static int wifi_scan_fast(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params = NULL;
+       int ret;
+       int driver_max_ssids = 0;
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       DBG("device %p %p", device, wifi->interface);
+
+       if (wifi->tethering == TRUE)
+               return 0;
+
+       if (connman_device_get_scanning(device) == TRUE)
+               return -EALREADY;
+
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       DBG("max ssids %d", driver_max_ssids);
+       if (driver_max_ssids == 0)
+               return wifi_scan(device);
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (scan_params == NULL)
+               return -ENOMEM;
+
+       ret = get_latest_connections(driver_max_ssids, scan_params);
+       if (ret <= 0) {
+               g_supplicant_free_scan_params(scan_params);
+               return wifi_scan(device);
+       }
+
+       connman_device_ref(device);
+       reset_autoscan(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                                               scan_callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else {
+               g_supplicant_free_scan_params(scan_params);
+               connman_device_unref(device);
+       }
+
+       return ret;
+}
+
+/*
+ * This func is only used when connecting to this specific AP first time.
+ * It is not used when system autoconnects to hidden AP.
+ */
+static int wifi_scan_hidden(struct connman_device *device,
+               const char *ssid, unsigned int ssid_len,
+               const char *identity, const char* passphrase,
+               gpointer user_data)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params = NULL;
+       struct scan_ssid *scan_ssid;
+       struct hidden_params *hidden;
+       int ret;
+
+       if (wifi == NULL)
+               return -ENODEV;
+
+       DBG("hidden SSID %s", ssid);
+
+       if (wifi->tethering == TRUE || wifi->hidden != NULL)
+               return -EBUSY;
+
+       if (ssid == NULL || ssid_len == 0 || ssid_len > 32)
+               return -EINVAL;
+
+       if (connman_device_get_scanning(device) == TRUE)
+               return -EALREADY;
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (scan_params == NULL)
+               return -ENOMEM;
+
+       scan_ssid = g_try_new(struct scan_ssid, 1);
+       if (scan_ssid == NULL) {
+               g_free(scan_params);
+               return -ENOMEM;
+       }
+
+       memcpy(scan_ssid->ssid, ssid, ssid_len);
+       scan_ssid->ssid_len = ssid_len;
+       scan_params->ssids = g_slist_prepend(scan_params->ssids, scan_ssid);
+
+       scan_params->num_ssids = 1;
+
+       hidden = g_try_new0(struct hidden_params, 1);
+       if (hidden == NULL) {
+               g_free(scan_params);
+               return -ENOMEM;
+       }
+       memcpy(hidden->ssid, ssid, ssid_len);
+       hidden->ssid_len = ssid_len;
+       hidden->identity = g_strdup(identity);
+       hidden->passphrase = g_strdup(passphrase);
+       hidden->user_data = user_data;
+       wifi->hidden = hidden;
+
+       connman_device_ref(device);
+
+       reset_autoscan(device);
+
+       ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                       scan_callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else {
+               connman_device_unref(device);
+               g_supplicant_free_scan_params(scan_params);
+               hidden_free(wifi->hidden);
+               wifi->hidden = NULL;
+       }
+
+       return ret;
+}
+
+static struct connman_device_driver wifi_ng_driver = {
+       .name           = "wifi",
+       .type           = CONNMAN_DEVICE_TYPE_WIFI,
+       .priority       = CONNMAN_DEVICE_PRIORITY_LOW,
+       .probe          = wifi_probe,
+       .remove         = wifi_remove,
+       .enable         = wifi_enable,
+       .disable        = wifi_disable,
+       .scan           = wifi_scan,
+       .scan_fast      = wifi_scan_fast,
+       .scan_hidden    = wifi_scan_hidden,
+};
+
+static void system_ready(void)
+{
+       DBG("");
+
+       if (connman_device_driver_register(&wifi_ng_driver) < 0)
+               connman_error("Failed to register WiFi driver");
+}
+
+static void system_killed(void)
+{
+       DBG("");
+
+       connman_device_driver_unregister(&wifi_ng_driver);
+}
+
+static int network_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       struct connman_device *device = connman_network_get_device(network);
+       struct wifi_data *wifi;
+
+       DBG("network %p", network);
+
+       wifi = connman_device_get_data(device);
+       if (wifi == NULL)
+               return;
+
+       if (wifi->network != network)
+               return;
+
+       wifi->network = NULL;
+}
+
+static void connect_callback(int result, GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+#if defined TIZEN_EXT
+       GList *list;
+       struct wifi_data *wifi;
+#endif
+       struct connman_network *network = user_data;
+
+       DBG("network %p result %d", network, result);
+
+#if defined TIZEN_EXT
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               if (wifi->networks == NULL)
+                       continue;
+
+               if (g_slist_find(wifi->networks, network) != NULL)
+                       goto seterr;
+       }
+
+       return;
+
+seterr:
+#endif
+       if (result == -ENOKEY) {
+               connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_INVALID_KEY);
+       } else if (result < 0) {
+               connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       }
+}
+
+static GSupplicantSecurity network_security(const char *security)
+{
+       if (g_str_equal(security, "none") == TRUE)
+               return G_SUPPLICANT_SECURITY_NONE;
+       else if (g_str_equal(security, "wep") == TRUE)
+               return G_SUPPLICANT_SECURITY_WEP;
+       else if (g_str_equal(security, "psk") == TRUE)
+               return G_SUPPLICANT_SECURITY_PSK;
+       else if (g_str_equal(security, "wpa") == TRUE)
+               return G_SUPPLICANT_SECURITY_PSK;
+       else if (g_str_equal(security, "rsn") == TRUE)
+               return G_SUPPLICANT_SECURITY_PSK;
+       else if (g_str_equal(security, "ieee8021x") == TRUE)
+               return G_SUPPLICANT_SECURITY_IEEE8021X;
+
+       return G_SUPPLICANT_SECURITY_UNKNOWN;
+}
+
+static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
+{
+       const char *security, *passphrase, *agent_passphrase;
+
+       memset(ssid, 0, sizeof(*ssid));
+       ssid->mode = G_SUPPLICANT_MODE_INFRA;
+       ssid->ssid = connman_network_get_blob(network, "WiFi.SSID",
+                                               &ssid->ssid_len);
+       ssid->scan_ssid = 1;
+       security = connman_network_get_string(network, "WiFi.Security");
+       ssid->security = network_security(security);
+       passphrase = connman_network_get_string(network,
+                                               "WiFi.Passphrase");
+       if (passphrase == NULL || strlen(passphrase) == 0) {
+
+               /* Use agent provided passphrase as a fallback */
+               agent_passphrase = connman_network_get_string(network,
+                                               "WiFi.AgentPassphrase");
+
+               if (agent_passphrase == NULL || strlen(agent_passphrase) == 0)
+                       ssid->passphrase = NULL;
+               else
+                       ssid->passphrase = agent_passphrase;
+       } else
+               ssid->passphrase = passphrase;
+
+       ssid->eap = connman_network_get_string(network, "WiFi.EAP");
+
+       /*
+        * If our private key password is unset,
+        * we use the supplied passphrase. That is needed
+        * for PEAP where 2 passphrases (identity and client
+        * cert may have to be provided.
+        */
+       if (connman_network_get_string(network,
+                                       "WiFi.PrivateKeyPassphrase") == NULL)
+               connman_network_set_string(network,
+                                               "WiFi.PrivateKeyPassphrase",
+                                               ssid->passphrase);
+       /* We must have an identity for both PEAP and TLS */
+       ssid->identity = connman_network_get_string(network, "WiFi.Identity");
+
+       /* Use agent provided identity as a fallback */
+       if (ssid->identity == NULL || strlen(ssid->identity) == 0)
+               ssid->identity = connman_network_get_string(network,
+                                                       "WiFi.AgentIdentity");
+
+       ssid->ca_cert_path = connman_network_get_string(network,
+                                                       "WiFi.CACertFile");
+       ssid->client_cert_path = connman_network_get_string(network,
+                                                       "WiFi.ClientCertFile");
+       ssid->private_key_path = connman_network_get_string(network,
+                                                       "WiFi.PrivateKeyFile");
+       ssid->private_key_passphrase = connman_network_get_string(network,
+                                               "WiFi.PrivateKeyPassphrase");
+       ssid->phase2_auth = connman_network_get_string(network, "WiFi.Phase2");
+
+       ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
+       ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
+
+       if (connman_setting_get_bool("BackgroundScanning") == TRUE)
+               ssid->bgscan = BGSCAN_DEFAULT;
+}
+
+static int network_connect(struct connman_network *network)
+{
+       struct connman_device *device = connman_network_get_device(network);
+       struct wifi_data *wifi;
+       GSupplicantInterface *interface;
+       GSupplicantSSID *ssid;
+
+       DBG("network %p", network);
+
+       if (device == NULL)
+               return -ENODEV;
+
+       wifi = connman_device_get_data(device);
+       if (wifi == NULL)
+               return -ENODEV;
+
+       ssid = g_try_malloc0(sizeof(GSupplicantSSID));
+       if (ssid == NULL)
+               return -ENOMEM;
+
+       interface = wifi->interface;
+
+       ssid_init(ssid, network);
+
+       if (wifi->disconnecting == TRUE)
+               wifi->pending_network = network;
+       else {
+               wifi->network = network;
+               wifi->retries = 0;
+
+               return g_supplicant_interface_connect(interface, ssid,
+                                               connect_callback, network);
+       }
+
+       return -EINPROGRESS;
+}
+
+static void disconnect_callback(int result, GSupplicantInterface *interface,
+                                                               void *user_data)
+{
+       struct wifi_data *wifi = user_data;
+
+       if (wifi->network != NULL) {
+               /*
+                * if result < 0 supplican return an error because
+                * the network is not current.
+                * we wont receive G_SUPPLICANT_STATE_DISCONNECTED since it
+                * failed, call connman_network_set_connected to report
+                * disconnect is completed.
+                */
+               if (result < 0)
+                       connman_network_set_connected(wifi->network, FALSE);
+       }
+
+       wifi->network = NULL;
+
+       wifi->disconnecting = FALSE;
+
+       if (wifi->pending_network != NULL) {
+               network_connect(wifi->pending_network);
+               wifi->pending_network = NULL;
+       }
+
+       start_autoscan(wifi->device);
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       struct connman_device *device = connman_network_get_device(network);
+       struct wifi_data *wifi;
+       int err;
+
+       DBG("network %p", network);
+
+       wifi = connman_device_get_data(device);
+       if (wifi == NULL || wifi->interface == NULL)
+               return -ENODEV;
+
+       connman_network_set_associating(network, FALSE);
+
+       if (wifi->disconnecting == TRUE)
+               return -EALREADY;
+
+       wifi->disconnecting = TRUE;
+
+       err = g_supplicant_interface_disconnect(wifi->interface,
+                                               disconnect_callback, wifi);
+       if (err < 0)
+               wifi->disconnecting = FALSE;
+
+       return err;
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "wifi",
+       .type           = CONNMAN_NETWORK_TYPE_WIFI,
+       .priority       = CONNMAN_NETWORK_PRIORITY_LOW,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static void interface_added(GSupplicantInterface *interface)
+{
+       const char *ifname = g_supplicant_interface_get_ifname(interface);
+       const char *driver = g_supplicant_interface_get_driver(interface);
+       struct wifi_data *wifi;
+
+       wifi = g_supplicant_interface_get_data(interface);
+
+       /*
+        * We can get here with a NULL wifi pointer when
+        * the interface added signal is sent before the
+        * interface creation callback is called.
+        */
+       if (wifi == NULL)
+               return;
+
+       DBG("ifname %s driver %s wifi %p tethering %d",
+                       ifname, driver, wifi, wifi->tethering);
+
+       if (wifi->device == NULL) {
+               connman_error("WiFi device not set");
+               return;
+       }
+
+       connman_device_set_powered(wifi->device, TRUE);
+
+       if (wifi->tethering == TRUE)
+               return;
+}
+
+static connman_bool_t is_idle(struct wifi_data *wifi)
+{
+       DBG("state %d", wifi->state);
+
+       switch (wifi->state) {
+       case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISCONNECTED:
+       case G_SUPPLICANT_STATE_INACTIVE:
+       case G_SUPPLICANT_STATE_SCANNING:
+               return TRUE;
+
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+       case G_SUPPLICANT_STATE_COMPLETED:
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_idle_wps(GSupplicantInterface *interface,
+                                               struct wifi_data *wifi)
+{
+       /* First, let's check if WPS processing did not went wrong */
+       if (g_supplicant_interface_get_wps_state(interface) ==
+               G_SUPPLICANT_WPS_STATE_FAIL)
+               return FALSE;
+
+       /* Unlike normal connection, being associated while processing wps
+        * actually means that we are idling. */
+       switch (wifi->state) {
+       case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISCONNECTED:
+       case G_SUPPLICANT_STATE_INACTIVE:
+       case G_SUPPLICANT_STATE_SCANNING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+               return TRUE;
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+       case G_SUPPLICANT_STATE_COMPLETED:
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
+                                       struct connman_network *network,
+                                       struct connman_device *device,
+                                       struct wifi_data *wifi)
+{
+       connman_bool_t wps;
+
+       wps = connman_network_get_bool(network, "WiFi.UseWPS");
+       if (wps == TRUE) {
+               const unsigned char *ssid, *wps_ssid;
+               unsigned int ssid_len, wps_ssid_len;
+               const char *wps_key;
+
+               /* Checking if we got associated with requested
+                * network */
+               ssid = connman_network_get_blob(network, "WiFi.SSID",
+                                               &ssid_len);
+
+               wps_ssid = g_supplicant_interface_get_wps_ssid(
+                       interface, &wps_ssid_len);
+
+               if (wps_ssid == NULL || wps_ssid_len != ssid_len ||
+                               memcmp(ssid, wps_ssid, ssid_len) != 0) {
+                       connman_network_set_associating(network, FALSE);
+                       g_supplicant_interface_disconnect(wifi->interface,
+                                               disconnect_callback, wifi);
+                       return FALSE;
+               }
+
+               wps_key = g_supplicant_interface_get_wps_key(interface);
+               connman_network_set_string(network, "WiFi.Passphrase",
+                                       wps_key);
+
+               connman_network_set_string(network, "WiFi.PinWPS", NULL);
+       }
+
+       return TRUE;
+}
+
+static connman_bool_t handle_4way_handshake_failure(GSupplicantInterface *interface,
+                                       struct connman_network *network,
+                                       struct wifi_data *wifi)
+{
+       if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
+               return FALSE;
+
+       wifi->retries++;
+
+       if (wifi->retries < MAXIMUM_RETRIES)
+               return TRUE;
+
+       connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+
+       return FALSE;
+}
+
+static void interface_state(GSupplicantInterface *interface)
+{
+       struct connman_network *network;
+       struct connman_device *device;
+       struct wifi_data *wifi;
+       GSupplicantState state = g_supplicant_interface_get_state(interface);
+       connman_bool_t wps;
+
+       wifi = g_supplicant_interface_get_data(interface);
+
+       DBG("wifi %p interface state %d", wifi, state);
+
+       if (wifi == NULL)
+               return;
+
+       network = wifi->network;
+       device = wifi->device;
+
+       if (device == NULL || network == NULL)
+               return;
+
+       switch (state) {
+       case G_SUPPLICANT_STATE_SCANNING:
+               break;
+
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+#if defined TIZEN_EXT
+               reset_autoscan(device);
+#else
+               stop_autoscan(device);
+#endif
+
+               if (wifi->connected == FALSE)
+                       connman_network_set_associating(network, TRUE);
+
+               break;
+
+       case G_SUPPLICANT_STATE_COMPLETED:
+#if defined TIZEN_EXT
+               /* though it should be already resetted: */
+               reset_autoscan(device);
+#else
+               /* though it should be already stopped: */
+               stop_autoscan(device);
+#endif
+
+               if (handle_wps_completion(interface, network, device, wifi) ==
+                                                                       FALSE)
+                       break;
+
+               connman_network_set_connected(network, TRUE);
+               break;
+
+       case G_SUPPLICANT_STATE_DISCONNECTED:
+               /*
+                * If we're in one of the idle modes, we have
+                * not started association yet and thus setting
+                * those ones to FALSE could cancel an association
+                * in progress.
+                */
+               wps = connman_network_get_bool(network, "WiFi.UseWPS");
+               if (wps == TRUE)
+                       if (is_idle_wps(interface, wifi) == TRUE)
+                               break;
+
+               if (is_idle(wifi))
+                       break;
+
+               /* If previous state was 4way-handshake, then
+                * it's either: psk was incorrect and thus we retry
+                * or if we reach the maximum retries we declare the
+                * psk as wrong */
+               if (handle_4way_handshake_failure(interface,
+                                               network, wifi) == TRUE)
+                       break;
+
+               /* We disable the selected network, if not then
+                * wpa_supplicant will loop retrying */
+               if (g_supplicant_interface_enable_selected_network(interface,
+                                               FALSE) != 0)
+                       DBG("Could not disables selected network");
+
+               connman_network_set_connected(network, FALSE);
+               connman_network_set_associating(network, FALSE);
+               wifi->disconnecting = FALSE;
+
+               start_autoscan(device);
+
+               break;
+
+       case G_SUPPLICANT_STATE_INACTIVE:
+               connman_network_set_associating(network, FALSE);
+               start_autoscan(device);
+
+               break;
+
+       case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+               break;
+       }
+
+       wifi->state = state;
+
+       /* Saving wpa_s state policy:
+        * If connected and if the state changes are roaming related:
+        * --> We stay connected
+        * If completed
+        * --> We are connected
+        * All other case:
+        * --> We are not connected
+        * */
+       switch (state) {
+       case G_SUPPLICANT_STATE_AUTHENTICATING:
+       case G_SUPPLICANT_STATE_ASSOCIATING:
+       case G_SUPPLICANT_STATE_ASSOCIATED:
+       case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
+       case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
+               if (wifi->connected == TRUE)
+                       connman_warn("Probably roaming right now!"
+                                               " Staying connected...");
+               else
+                       wifi->connected = FALSE;
+               break;
+       case G_SUPPLICANT_STATE_COMPLETED:
+               wifi->connected = TRUE;
+               break;
+       default:
+               wifi->connected = FALSE;
+               break;
+       }
+
+       DBG("DONE");
+}
+
+static void interface_removed(GSupplicantInterface *interface)
+{
+       const char *ifname = g_supplicant_interface_get_ifname(interface);
+       struct wifi_data *wifi;
+
+       DBG("ifname %s", ifname);
+
+       wifi = g_supplicant_interface_get_data(interface);
+
+       if (wifi != NULL && wifi->tethering == TRUE)
+               return;
+
+       if (wifi == NULL || wifi->device == NULL) {
+               DBG("wifi interface already removed");
+               return;
+       }
+
+       wifi->interface = NULL;
+       connman_device_set_powered(wifi->device, FALSE);
+}
+
+static void scan_started(GSupplicantInterface *interface)
+{
+       DBG("");
+}
+
+static void scan_finished(GSupplicantInterface *interface)
+{
+       DBG("");
+}
+
+static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
+{
+       unsigned char strength;
+
+       strength = 120 + g_supplicant_network_get_signal(supplicant_network);
+       if (strength > 100)
+               strength = 100;
+
+       return strength;
+}
+
+static void network_added(GSupplicantNetwork *supplicant_network)
+{
+       struct connman_network *network;
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       const char *name, *identifier, *security, *group, *mode;
+       const unsigned char *ssid;
+       unsigned int ssid_len;
+       connman_bool_t wps;
+       connman_bool_t wps_pbc;
+       connman_bool_t wps_ready;
+       connman_bool_t wps_advertizing;
+
+       DBG("");
+
+       interface = g_supplicant_network_get_interface(supplicant_network);
+       wifi = g_supplicant_interface_get_data(interface);
+       name = g_supplicant_network_get_name(supplicant_network);
+       identifier = g_supplicant_network_get_identifier(supplicant_network);
+       security = g_supplicant_network_get_security(supplicant_network);
+       group = g_supplicant_network_get_identifier(supplicant_network);
+       wps = g_supplicant_network_get_wps(supplicant_network);
+       wps_pbc = g_supplicant_network_is_wps_pbc(supplicant_network);
+       wps_ready = g_supplicant_network_is_wps_active(supplicant_network);
+       wps_advertizing = g_supplicant_network_is_wps_advertizing(
+                                                       supplicant_network);
+       mode = g_supplicant_network_get_mode(supplicant_network);
+
+       if (wifi == NULL)
+               return;
+
+       ssid = g_supplicant_network_get_ssid(supplicant_network, &ssid_len);
+
+       network = connman_device_get_network(wifi->device, identifier);
+
+       if (network == NULL) {
+               network = connman_network_create(identifier,
+                                               CONNMAN_NETWORK_TYPE_WIFI);
+               if (network == NULL)
+                       return;
+
+               connman_network_set_index(network, wifi->index);
+
+               if (connman_device_add_network(wifi->device, network) < 0) {
+                       connman_network_unref(network);
+                       return;
+               }
+
+               wifi->networks = g_slist_append(wifi->networks, network);
+       }
+
+       if (name != NULL && name[0] != '\0')
+               connman_network_set_name(network, name);
+
+       connman_network_set_blob(network, "WiFi.SSID",
+                                               ssid, ssid_len);
+       connman_network_set_string(network, "WiFi.Security", security);
+       connman_network_set_strength(network,
+                               calculate_strength(supplicant_network));
+       connman_network_set_bool(network, "WiFi.WPS", wps);
+
+       if (wps == TRUE) {
+               /* Is AP advertizing for WPS association?
+                * If so, we decide to use WPS by default */
+               if (wps_ready == TRUE && wps_pbc == TRUE &&
+                                               wps_advertizing == TRUE)
+                       connman_network_set_bool(network, "WiFi.UseWPS", TRUE);
+       }
+
+       connman_network_set_frequency(network,
+                       g_supplicant_network_get_frequency(supplicant_network));
+
+#if defined TIZEN_EXT
+       connman_network_set_bssid(network,
+                       g_supplicant_network_get_bssid(supplicant_network));
+       connman_network_set_maxrate(network,
+                       g_supplicant_network_get_maxrate(supplicant_network));
+       connman_network_set_enc_mode(network,
+                       g_supplicant_network_get_enc_mode(supplicant_network));
+#endif
+
+       connman_network_set_available(network, TRUE);
+       connman_network_set_string(network, "WiFi.Mode", mode);
+
+       if (ssid != NULL)
+               connman_network_set_group(network, group);
+
+       if (wifi->hidden != NULL && ssid != NULL) {
+               if (wifi->hidden->ssid_len == ssid_len &&
+                               memcmp(wifi->hidden->ssid, ssid,
+                                               ssid_len) == 0) {
+                       connman_network_connect_hidden(network,
+                                       wifi->hidden->identity,
+                                       wifi->hidden->passphrase,
+                                       wifi->hidden->user_data);
+                       wifi->hidden->user_data = NULL;
+                       hidden_free(wifi->hidden);
+                       wifi->hidden = NULL;
+               }
+       }
+}
+
+static void network_removed(GSupplicantNetwork *network)
+{
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       const char *name, *identifier;
+       struct connman_network *connman_network;
+
+       interface = g_supplicant_network_get_interface(network);
+       wifi = g_supplicant_interface_get_data(interface);
+       identifier = g_supplicant_network_get_identifier(network);
+       name = g_supplicant_network_get_name(network);
+
+       DBG("name %s", name);
+
+       if (wifi == NULL)
+               return;
+
+       connman_network = connman_device_get_network(wifi->device, identifier);
+       if (connman_network == NULL)
+               return;
+
+       wifi->networks = g_slist_remove(wifi->networks, connman_network);
+
+       connman_device_remove_network(wifi->device, connman_network);
+       connman_network_unref(connman_network);
+}
+
+static void network_changed(GSupplicantNetwork *network, const char *property)
+{
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       const char *name, *identifier;
+       struct connman_network *connman_network;
+#if defined TIZEN_EXT
+       const unsigned char *bssid;
+       unsigned int maxrate;
+       connman_uint16_t frequency;
+#endif
+
+       interface = g_supplicant_network_get_interface(network);
+       wifi = g_supplicant_interface_get_data(interface);
+       identifier = g_supplicant_network_get_identifier(network);
+       name = g_supplicant_network_get_name(network);
+
+       DBG("name %s", name);
+
+       if (wifi == NULL)
+               return;
+
+       connman_network = connman_device_get_network(wifi->device, identifier);
+       if (connman_network == NULL)
+               return;
+
+       if (g_str_equal(property, "Signal") == TRUE) {
+              connman_network_set_strength(connman_network,
+                                       calculate_strength(network));
+              connman_network_update(connman_network);
+       }
+
+#if defined TIZEN_EXT
+       bssid = g_supplicant_network_get_bssid(network);
+       maxrate = g_supplicant_network_get_maxrate(network);
+       frequency = g_supplicant_network_get_frequency(network);
+
+       connman_network_set_bssid(connman_network, bssid);
+       connman_network_set_maxrate(connman_network, maxrate);
+       connman_network_set_frequency(connman_network, frequency);
+#endif
+}
+
+static void debug(const char *str)
+{
+       if (getenv("CONNMAN_SUPPLICANT_DEBUG"))
+               connman_debug("%s", str);
+}
+
+static const GSupplicantCallbacks callbacks = {
+       .system_ready           = system_ready,
+       .system_killed          = system_killed,
+       .interface_added        = interface_added,
+       .interface_state        = interface_state,
+       .interface_removed      = interface_removed,
+       .scan_started           = scan_started,
+       .scan_finished          = scan_finished,
+       .network_added          = network_added,
+       .network_removed        = network_removed,
+       .network_changed        = network_changed,
+       .debug                  = debug,
+};
+
+
+static int tech_probe(struct connman_technology *technology)
+{
+       wifi_technology = technology;
+
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+       wifi_technology = NULL;
+}
+
+struct wifi_tethering_info {
+       struct wifi_data *wifi;
+       struct connman_technology *technology;
+       char *ifname;
+       GSupplicantSSID *ssid;
+};
+
+static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
+{
+       GSupplicantSSID *ap;
+
+       ap = g_try_malloc0(sizeof(GSupplicantSSID));
+       if (ap == NULL)
+               return NULL;
+
+       ap->mode = G_SUPPLICANT_MODE_MASTER;
+       ap->ssid = ssid;
+       ap->ssid_len = strlen(ssid);
+       ap->scan_ssid = 0;
+       ap->freq = 2412;
+
+       if (passphrase == NULL || strlen(passphrase) == 0) {
+               ap->security = G_SUPPLICANT_SECURITY_NONE;
+               ap->passphrase = NULL;
+       } else {
+              ap->security = G_SUPPLICANT_SECURITY_PSK;
+              ap->protocol = G_SUPPLICANT_PROTO_RSN;
+              ap->pairwise_cipher = G_SUPPLICANT_PAIRWISE_CCMP;
+              ap->group_cipher = G_SUPPLICANT_GROUP_CCMP;
+              ap->passphrase = passphrase;
+       }
+
+       return ap;
+}
+
+static void ap_start_callback(int result, GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct wifi_tethering_info *info = user_data;
+
+       DBG("result %d index %d bridge %s",
+               result, info->wifi->index, info->wifi->bridge);
+
+       if (result < 0) {
+               connman_inet_remove_from_bridge(info->wifi->index,
+                                                       info->wifi->bridge);
+               connman_technology_tethering_notify(info->technology, FALSE);
+       }
+
+       g_free(info->ifname);
+       g_free(info);
+}
+
+static void ap_create_callback(int result,
+                               GSupplicantInterface *interface,
+                                       void *user_data)
+{
+       struct wifi_tethering_info *info = user_data;
+
+       DBG("result %d ifname %s", result,
+                               g_supplicant_interface_get_ifname(interface));
+
+       if (result < 0) {
+               connman_inet_remove_from_bridge(info->wifi->index,
+                                                       info->wifi->bridge);
+               connman_technology_tethering_notify(info->technology, FALSE);
+
+               g_free(info->ifname);
+               g_free(info);
+               return;
+       }
+
+       info->wifi->interface = interface;
+       g_supplicant_interface_set_data(interface, info->wifi);
+
+       if (g_supplicant_interface_set_apscan(interface, 2) < 0)
+               connman_error("Failed to set interface ap_scan property");
+
+       g_supplicant_interface_connect(interface, info->ssid,
+                                               ap_start_callback, info);
+}
+
+static void sta_remove_callback(int result,
+                               GSupplicantInterface *interface,
+                                       void *user_data)
+{
+       struct wifi_tethering_info *info = user_data;
+       const char *driver = connman_option_get_string("wifi");
+
+       DBG("ifname %s result %d ", info->ifname, result);
+
+       if (result < 0) {
+               info->wifi->tethering = TRUE;
+
+               g_free(info->ifname);
+               g_free(info);
+               return;
+       }
+
+       info->wifi->interface = NULL;
+
+       connman_technology_tethering_notify(info->technology, TRUE);
+
+       g_supplicant_interface_create(info->ifname, driver, info->wifi->bridge,
+                                               ap_create_callback,
+                                                       info);
+}
+
+static int tech_set_tethering(struct connman_technology *technology,
+                               const char *identifier, const char *passphrase,
+                               const char *bridge, connman_bool_t enabled)
+{
+       GList *list;
+       GSupplicantInterface *interface;
+       struct wifi_data *wifi;
+       struct wifi_tethering_info *info;
+       const char *ifname;
+       unsigned int mode;
+       int err;
+
+       DBG("");
+
+       if (enabled == FALSE) {
+               for (list = iface_list; list; list = list->next) {
+                       wifi = list->data;
+
+                       if (wifi->tethering == TRUE) {
+                               wifi->tethering = FALSE;
+
+                               connman_inet_remove_from_bridge(wifi->index,
+                                                                       bridge);
+                               wifi->bridged = FALSE;
+                       }
+               }
+
+               connman_technology_tethering_notify(technology, FALSE);
+
+               return 0;
+       }
+
+       for (list = iface_list; list; list = list->next) {
+               wifi = list->data;
+
+               interface = wifi->interface;
+
+               if (interface == NULL)
+                       continue;
+
+               ifname = g_supplicant_interface_get_ifname(wifi->interface);
+
+               mode = g_supplicant_interface_get_mode(interface);
+               if ((mode & G_SUPPLICANT_CAPABILITY_MODE_AP) == 0) {
+                       DBG("%s does not support AP mode", ifname);
+                       continue;
+               }
+
+               info = g_try_malloc0(sizeof(struct wifi_tethering_info));
+               if (info == NULL)
+                       return -ENOMEM;
+
+               info->wifi = wifi;
+               info->technology = technology;
+               info->wifi->bridge = bridge;
+               info->ssid = ssid_ap_init(identifier, passphrase);
+               if (info->ssid == NULL) {
+                       g_free(info);
+                       continue;
+               }
+               info->ifname = g_strdup(ifname);
+               if (info->ifname == NULL) {
+                       g_free(info);
+                       continue;
+               }
+
+               info->wifi->tethering = TRUE;
+
+               err = g_supplicant_interface_remove(interface,
+                                               sta_remove_callback,
+                                                       info);
+               if (err == 0)
+                       return err;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static void regdom_callback(void *user_data)
+{
+       char *alpha2 = user_data;
+
+       DBG("");
+
+       if (wifi_technology == NULL)
+               return;
+
+       connman_technology_regdom_notify(wifi_technology, alpha2);
+}
+
+static int tech_set_regdom(struct connman_technology *technology, const char *alpha2)
+{
+       return g_supplicant_set_country(alpha2, regdom_callback, alpha2);
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "wifi",
+       .type           = CONNMAN_SERVICE_TYPE_WIFI,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+       .set_tethering  = tech_set_tethering,
+       .set_regdom     = tech_set_regdom,
+};
+
+static int wifi_init(void)
+{
+       int err;
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               return err;
+
+       err = g_supplicant_register(&callbacks);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               return err;
+       }
+
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               g_supplicant_unregister(&callbacks);
+               connman_network_driver_unregister(&network_driver);
+               return err;
+       }
+
+       return 0;
+}
+
+static void wifi_exit(void)
+{
+       DBG();
+
+       connman_technology_driver_unregister(&tech_driver);
+
+       g_supplicant_unregister(&callbacks);
+
+       connman_network_driver_unregister(&network_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, wifi_init, wifi_exit)
diff --git a/resources/etc/rc.d/init.d/connman b/resources/etc/rc.d/init.d/connman
new file mode 100755 (executable)
index 0000000..00c5df3
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+HARDWARE_MODEL=`grep Hardware /proc/cpuinfo | awk "{print \\$3}"`
+/bin/echo "Hardware Model=${HARDWARE_MODEL}"
+
+case $HARDWARE_MODEL in
+               "SLP_PQ")       /bin/echo "This is PQ"
+                       /usr/sbin/connmand -W nl80211 &
+               ;;
+               "U1SLP" | "U1HD")       /bin/echo "This is U1SLP"
+                       /usr/sbin/connmand -W wext &
+               ;;
+               "SLP7_C210")    /bin/echo "This is C210"
+                       /usr/sbin/connmand -W wext &
+               ;;
+               "SLP10_C210")
+                       /usr/sbin/connmand -W wext -nd > /var/log/connman.log 2>&1 &
+               ;;
+               *)
+                       /usr/sbin/connmand -W nl80211 -nd > /var/log/connman.log 2>&1 &
+               ;;
+esac
diff --git a/resources/usr/share/dbus-1/services/net.connman.service b/resources/usr/share/dbus-1/services/net.connman.service
new file mode 100644 (file)
index 0000000..a1e5581
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=net.connman
+Exec=/etc/rc.d/init.d/connman
+User=root
diff --git a/resources/var/lib/connman/settings b/resources/var/lib/connman/settings
new file mode 100644 (file)
index 0000000..15dd09c
--- /dev/null
@@ -0,0 +1,18 @@
+
+[global]
+OfflineMode=false
+
+[WiFi]
+Enable=false
+
+[Bluetooth]
+Enable=true
+
+[Wired]
+Enable=true
+
+[3G]
+Enable=true
+
+[WiMAX]
+Enable=false
diff --git a/scripts/connman.in b/scripts/connman.in
new file mode 100644 (file)
index 0000000..ec98f39
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+DAEMON=@prefix@/sbin/connmand
+DESC="Connection Manager"
+
+. /lib/lsb/init-functions
+
+if [ -f @sysconfdir@/default/connman ] ; then
+       . @sysconfdir@/default/connman
+fi
+
+set -e
+
+do_start() {
+       start-stop-daemon --start --oknodo --exec $DAEMON -- $DAEMON_OPTS
+}
+
+do_stop() {
+       start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+}
+
+case "$1" in
+  start)
+       log_daemon_msg "Starting $DESC"
+       do_start
+       log_end_msg $?
+       ;;
+  stop)
+       log_daemon_msg "Stopping $DESC"
+       do_stop
+       log_end_msg $?
+       ;;
+  restart|force-reload)
+       log_daemon_msg "Restarting $DESC"
+       do_stop
+       sleep 1
+       do_start
+       log_end_msg $?
+       ;;
+  *)
+       log_success_msg "Usage: $0 {start|stop|restart|force-reload}" >&2
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/scripts/libppp-plugin.c b/scripts/libppp-plugin.c
new file mode 100644 (file)
index 0000000..e77b8cc
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pppd/pppd.h>
+#include <pppd/fsm.h>
+#include <pppd/ipcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <dbus/dbus.h>
+
+#define INET_ADDRES_LEN (INET_ADDRSTRLEN + 5)
+#define INET_DNS_LEN   (2*INET_ADDRSTRLEN + 9)
+
+static char *busname;
+static char *interface;
+static char *path;
+
+static DBusConnection *connection;
+static int prev_phase;
+
+char pppd_version[] = VERSION;
+
+int plugin_init(void);
+
+static void append(DBusMessageIter *dict, const char *key, const char *value)
+{
+       DBusMessageIter entry;
+
+       /* We clean the environment before invoking pppd, 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);
+}
+
+
+static int ppp_have_secret()
+{
+       return 1;
+}
+
+static int ppp_get_secret(char *username, char *password)
+{
+       DBusMessage *msg, *reply;
+       const char *user, *pass;
+       DBusError err;
+
+       if (username == NULL && password == NULL)
+               return -1;
+
+       if (password == NULL)
+               return 1;
+
+       if (connection == NULL)
+               return -1;
+
+       dbus_error_init(&err);
+
+       msg = dbus_message_new_method_call(busname, path, interface, "getsec");
+       if (msg == NULL)
+               return -1;
+
+       dbus_message_append_args(msg, DBUS_TYPE_INVALID, DBUS_TYPE_INVALID);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                               msg, -1, &err);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&err) == TRUE)
+                       dbus_error_free(&err);
+
+               dbus_message_unref(msg);
+               return -1;
+       }
+
+       dbus_message_unref(msg);
+
+       dbus_error_init(&err);
+
+       if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &user,
+                                               DBUS_TYPE_STRING, &pass,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&err) == TRUE)
+                       dbus_error_free(&err);
+
+               dbus_message_unref(reply);
+               return -1;
+       }
+
+       if (username != NULL)
+               strcpy(username, user);
+
+       strcpy(password, pass);
+
+       dbus_message_unref(reply);
+
+       return 1;
+}
+
+static void ppp_up(void *data, int arg)
+{
+       char buf[INET_ADDRES_LEN];
+       char dns[INET_DNS_LEN];
+       const char *reason = "connect";
+       bool add_blank = FALSE;
+       DBusMessageIter iter, dict;
+       DBusMessage *msg;
+
+       if (connection == NULL)
+               return;
+
+       if (ipcp_gotoptions[0].ouraddr == 0)
+               return;
+
+       msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+       if (msg == NULL)
+               return;
+
+       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);
+
+       append(&dict, "INTERNAL_IFNAME", ifname);
+
+       inet_ntop(AF_INET, &ipcp_gotoptions[0].ouraddr, buf, INET_ADDRSTRLEN);
+       append(&dict, "INTERNAL_IP4_ADDRESS", buf);
+
+       strcpy(buf, "255.255.255.255");
+       append(&dict, "INTERNAL_IP4_NETMASK", buf);
+
+       if (ipcp_gotoptions[0].dnsaddr[0] || ipcp_gotoptions[0].dnsaddr[1]) {
+               memset(dns, 0, sizeof(dns));
+               dns[0] = '\0';
+
+               if (ipcp_gotoptions[0].dnsaddr[0]) {
+                       inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[0],
+                                                       buf, INET_ADDRSTRLEN);
+                       strcat(dns, buf);
+
+                       add_blank = TRUE;
+               }
+
+               if (ipcp_gotoptions[0].dnsaddr[1]) {
+                       inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[1],
+                                                       buf, INET_ADDRSTRLEN);
+                       if (add_blank == TRUE)
+                               strcat(dns, " ");
+
+                       strcat(dns, buf);
+               }
+               append(&dict, "INTERNAL_IP4_DNS", dns);
+       }
+
+       append(&dict, "MTU", "1400");
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_connection_send(connection, msg, NULL);
+
+       dbus_connection_flush(connection);
+
+       dbus_message_unref(msg);
+}
+
+static void ppp_exit(void *data, int arg)
+{
+       if (connection != NULL) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+       }
+
+       if (busname != NULL) {
+               free(busname);
+               busname = NULL;
+       }
+
+       if (interface != NULL) {
+               free(interface);
+               interface = NULL;
+       }
+
+       if (path != NULL) {
+               free(path);
+               path = NULL;
+       }
+}
+
+static void ppp_phase_change(void *data, int arg)
+{
+       const char *reason = "disconnect";
+       DBusMessage *msg;
+       int send_msg = 0;
+
+       if (connection == NULL)
+               return;
+
+       if (prev_phase == PHASE_AUTHENTICATE &&
+                               arg == PHASE_TERMINATE) {
+               reason = "auth failed";
+               send_msg = 1;
+       }
+
+       if (send_msg > 0 || arg == PHASE_DEAD || arg == PHASE_DISCONNECT) {
+               msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+               if (msg == NULL)
+                       return;
+
+               dbus_message_set_no_reply(msg, TRUE);
+
+               dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
+
+               dbus_connection_send(connection, msg, NULL);
+
+               dbus_connection_flush(connection);
+
+               dbus_message_unref(msg);
+       }
+
+       prev_phase = arg;
+}
+
+int plugin_init(void)
+{
+       DBusError error;
+       static const char *bus, *inter, *p;
+
+       dbus_error_init(&error);
+
+       bus = getenv("CONNMAN_BUSNAME");
+       inter = getenv("CONNMAN_INTERFACE");
+       p = getenv("CONNMAN_PATH");
+
+       if (bus == NULL || inter == NULL || p == NULL)
+               return -1;
+
+       busname = strdup(bus);
+       interface = strdup(inter);
+       path = strdup(p);
+
+       if (busname == NULL || interface == NULL || path == NULL) {
+               ppp_exit(NULL, 0);
+               return -1;
+       }
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (connection == NULL) {
+               if (dbus_error_is_set(&error) == TRUE)
+                       dbus_error_free(&error);
+
+               ppp_exit(NULL, 0);
+               return -1;
+       }
+
+       pap_passwd_hook = ppp_get_secret;
+       chap_passwd_hook = ppp_get_secret;
+
+       chap_check_hook = ppp_have_secret;
+       pap_check_hook = ppp_have_secret;
+
+       add_notifier(&ip_up_notifier, ppp_up, NULL);
+       add_notifier(&phasechange, ppp_phase_change, NULL);
+       add_notifier(&exitnotify, ppp_exit, connection);
+
+       return 0;
+}
diff --git a/scripts/openconnect-script.c b/scripts/openconnect-script.c
new file mode 100644 (file)
index 0000000..ef81c94
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  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 <dbus/dbus.h>
+
+extern char **environ;
+
+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;
+
+       busname = getenv("CONNMAN_BUSNAME");
+       interface = getenv("CONNMAN_INTERFACE");
+       path = getenv("CONNMAN_PATH");
+
+       reason = getenv("reason");
+
+       if (!busname || !interface || !path || !reason) {
+               fprintf(stderr, "Required environment variables not set\n");
+               return 1;
+       }
+
+       if (strcmp(reason, "pre-init") == 0)
+               return 0;
+
+       dbus_error_init(&error);
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get on system bus\n");
+               return 0;
+       }
+
+       msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+       if (msg == NULL) {
+               dbus_connection_unref(conn);
+               fprintf(stderr, "Failed to allocate method call\n");
+               return 0;
+       }
+
+       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) == FALSE)
+               fprintf(stderr, "Failed to send message\n");
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(msg);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/scripts/openvpn-script.c b/scripts/openvpn-script.c
new file mode 100644 (file)
index 0000000..3d46384
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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 <dbus/dbus.h>
+
+extern char **environ;
+
+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;
+
+       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, *interface, *path, *reason;
+
+       busname = getenv("CONNMAN_BUSNAME");
+       interface = getenv("CONNMAN_INTERFACE");
+       path = getenv("CONNMAN_PATH");
+
+       reason = getenv("script_type");
+
+       if (!busname || !interface || !path || !reason) {
+               fprintf(stderr, "Required environment variables not set\n");
+               return 1;
+       }
+       dbus_error_init(&error);
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get on system bus\n");
+               return 0;
+       }
+
+       msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+       if (msg == NULL) {
+               dbus_connection_unref(conn);
+               fprintf(stderr, "Failed to allocate method call\n");
+               return 0;
+       }
+
+       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) == FALSE)
+               fprintf(stderr, "Failed to send message\n");
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(msg);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/src/6to4.c b/src/6to4.c
new file mode 100644 (file)
index 0000000..340b69e
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  Nokia Corporation. All rights reserved.
+ *  Copyright (C) Alexey Kuznetsov et al. from iproute2 package.
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "connman.h"
+#include <connman/log.h>
+#include <connman/ipconfig.h>
+#include "gweb/gweb.h"
+
+static int tunnel_created;
+static int tunnel_pending;
+static char *tunnel_ip_address;
+static GWeb *web;
+static guint web_request_id;
+static unsigned int newlink_watch;
+static unsigned int newlink_flags;
+static int newlink_timeout_id;
+
+#define STATUS_URL "http://ipv6.connman.net/online/status.html"
+
+#ifndef IP_DF
+#define IP_DF          0x4000          /* Flag: "Don't Fragment"       */
+#endif
+
+static int tunnel_create(struct in_addr *addr)
+{
+       struct ip_tunnel_parm p;
+       struct ifreq ifr;
+       int fd = -1;
+       int ret;
+
+       /* ip tunnel add tun6to4 mode sit remote any local 1.2.3.4 ttl 64 */
+
+       memset(&p, 0, sizeof(struct ip_tunnel_parm));
+       memset(&ifr, 0, sizeof(struct ifreq));
+
+       p.iph.version = 4;
+       p.iph.ihl = 5;
+       p.iph.frag_off = htons(IP_DF);
+       p.iph.protocol = IPPROTO_IPV6;
+       p.iph.saddr = addr->s_addr;
+       p.iph.ttl = 64;
+       strncpy(p.name, "tun6to4", IFNAMSIZ);
+
+       strncpy(ifr.ifr_name, "sit0", IFNAMSIZ);
+       ifr.ifr_ifru.ifru_data = (void *)&p;
+       fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       ret = ioctl(fd, SIOCADDTUNNEL, &ifr);
+       if (ret)
+               connman_error("add tunnel %s failed: %s", ifr.ifr_name,
+                                                       strerror(errno));
+       close(fd);
+
+       return ret;
+}
+
+static void tunnel_destroy()
+{
+       struct ip_tunnel_parm p;
+       struct ifreq ifr;
+       int fd = -1;
+       int ret;
+
+       if (tunnel_created == 0)
+               return;
+
+       /* ip tunnel del tun6to4 */
+
+       memset(&p, 0, sizeof(struct ip_tunnel_parm));
+       memset(&ifr, 0, sizeof(struct ifreq));
+
+       p.iph.version = 4;
+       p.iph.ihl = 5;
+       p.iph.protocol = IPPROTO_IPV6;
+       strncpy(p.name, "tun6to4", IFNAMSIZ);
+
+       strncpy(ifr.ifr_name, "tun6to4", IFNAMSIZ);
+       ifr.ifr_ifru.ifru_data = (void *)&p;
+       fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               connman_error("socket failed: %s", strerror(errno));
+               return;
+       }
+
+       ret = ioctl(fd, SIOCDELTUNNEL, &ifr);
+       if (ret)
+               connman_error("del tunnel %s failed: %s", ifr.ifr_name,
+                                                       strerror(errno));
+       else
+               tunnel_created = 0;
+
+       tunnel_pending = 0;
+       close(fd);
+
+       g_free(tunnel_ip_address);
+       tunnel_ip_address = NULL;
+}
+
+static int tunnel_add_route()
+{
+       struct __connman_inet_rtnl_handle rth;
+       struct in6_addr addr6;
+       int index;
+       int ret = 0;
+
+       /* ip -6 route add ::/0 via ::192.88.99.1 dev tun6to4 metric 1 */
+
+       index = if_nametoindex("tun6to4");
+       if (index == 0) {
+               DBG("Can not find device tun6to4");
+               return -1;
+       }
+
+       memset(&rth, 0, sizeof(rth));
+
+       rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+       rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+       rth.req.n.nlmsg_type = RTM_NEWROUTE;
+       rth.req.u.r.rt.rtm_family = AF_INET6;
+       rth.req.u.r.rt.rtm_table = RT_TABLE_MAIN;
+       rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT;
+       rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+       rth.req.u.r.rt.rtm_type = RTN_UNICAST;
+       rth.req.u.r.rt.rtm_dst_len = 0;
+
+       inet_pton(AF_INET6, "::192.88.99.1", &addr6);
+
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY,
+                                       &addr6.s6_addr, 16);
+       __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req), RTA_OIF,
+                                       index);
+       __connman_inet_rtnl_addattr32(&rth.req.n, sizeof(rth.req),
+                                       RTA_PRIORITY, 1);
+
+       ret = __connman_inet_rtnl_open(&rth);
+       if (ret < 0)
+               goto done;
+
+       ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
+
+done:
+       __connman_inet_rtnl_close(&rth);
+       return ret;
+}
+
+static int tunnel_set_addr(unsigned int a, unsigned int b,
+                       unsigned int c, unsigned int d)
+{
+       struct __connman_inet_rtnl_handle rth;
+       struct in6_addr addr6;
+       char *ip6addr;
+       int ret;
+
+       /* ip -6 addr add dev tun6to4 2002:0102:0304::1/64 */
+
+       memset(&rth, 0, sizeof(rth));
+
+       rth.req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       rth.req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+       rth.req.n.nlmsg_type = RTM_NEWADDR;
+       rth.req.u.i.ifa.ifa_family = AF_INET6;
+       rth.req.u.i.ifa.ifa_prefixlen = 64;
+       rth.req.u.i.ifa.ifa_index = if_nametoindex("tun6to4");
+       if (rth.req.u.i.ifa.ifa_index == 0) {
+               connman_error("Can not find device tun6to4");
+               ret = -1;
+               goto done;
+       }
+
+       ip6addr = g_strdup_printf("2002:%02x%02x:%02x%02x::1", a, b, c, d);
+       inet_pton(AF_INET6, ip6addr, &addr6);
+       DBG("ipv6 address %s", ip6addr);
+       g_free(ip6addr);
+
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_LOCAL,
+                                       &addr6.s6_addr, 16);
+       __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), IFA_ADDRESS,
+                                       &addr6.s6_addr, 16);
+
+       ret = __connman_inet_rtnl_open(&rth);
+       if (ret < 0)
+               goto done;
+
+       ret = __connman_inet_rtnl_send(&rth, &rth.req.n);
+
+done:
+       __connman_inet_rtnl_close(&rth);
+       return ret;
+}
+
+static gboolean unref_web(gpointer user_data)
+{
+       g_web_unref(web);
+       return FALSE;
+}
+
+static gboolean web_result(GWebResult *result, gpointer user_data)
+{
+       guint16 status;
+
+       if (web_request_id == 0)
+               return FALSE;
+
+       status = g_web_result_get_status(result);
+
+       DBG("status %u", status);
+
+       if (status >= 400 && status < 500)
+               tunnel_destroy();
+       else
+               tunnel_pending = 0;
+
+       web_request_id = 0;
+
+       g_timeout_add_seconds(1, unref_web, NULL);
+
+       return FALSE;
+}
+
+static void web_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static gboolean newlink_timeout(gpointer user_data)
+{
+       /*
+        * Stop if the timeout has been cancelled already by tun_newlink()
+        */
+       if (newlink_timeout_id == 0)
+               return FALSE;
+
+       DBG("");
+
+       if (newlink_watch != 0) {
+               connman_rtnl_remove_watch(newlink_watch);
+               newlink_watch = 0;
+       }
+
+       newlink_flags = 0;
+
+       if (web_request_id == 0)
+               tunnel_destroy();
+
+       newlink_timeout_id = 0;
+
+       return FALSE;
+}
+
+static void tun_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       int index = GPOINTER_TO_INT(user_data);
+
+       if ((newlink_flags & IFF_UP) == (flags & IFF_UP)) {
+               newlink_flags = flags;
+               return;
+       }
+
+       if (flags & IFF_UP) {
+               /*
+                * We try to verify that connectivity through tunnel works ok.
+                */
+               if (newlink_timeout_id > 0) {
+                       g_source_remove(newlink_timeout_id);
+                       newlink_timeout_id = 0;
+               }
+
+               web = g_web_new(index);
+               if (web == NULL) {
+                       tunnel_destroy();
+                       return;
+               }
+
+               g_web_set_accept(web, NULL);
+               g_web_set_user_agent(web, "ConnMan/%s", VERSION);
+               g_web_set_close_connection(web, TRUE);
+
+               if (getenv("CONNMAN_WEB_DEBUG"))
+                       g_web_set_debug(web, web_debug, "6to4");
+
+               web_request_id = g_web_request_get(web, STATUS_URL,
+                               web_result, NULL,  NULL);
+
+               newlink_timeout(NULL);
+       }
+
+       newlink_flags = flags;
+}
+
+static int init_6to4(struct in_addr *ip4addr)
+{
+       unsigned int a, b, c, d;
+       int ret, if_index;
+       in_addr_t addr;
+
+       DBG("");
+
+       addr = ntohl(ip4addr->s_addr);
+
+       a = (addr & 0xff000000) >> 24;
+       b = (addr & 0x00ff0000) >> 16;
+       c = (addr & 0x0000ff00) >> 8;
+       d = addr & 0x000000ff;
+
+       ret = tunnel_create(ip4addr);
+       if (ret)
+               return -1;
+
+       tunnel_created = 1;
+
+       ret = connman_inet_setup_tunnel("tun6to4", 1472);
+       if (ret)
+               goto error;
+
+       ret = tunnel_set_addr(a, b, c, d);
+       if (ret)
+               goto error;
+
+       ret = tunnel_add_route();
+       if (ret)
+               goto error;
+
+       if_index = connman_inet_ifindex("tun6to4");
+       if (if_index < 0)
+               goto error;
+
+       newlink_watch = connman_rtnl_add_newlink_watch(if_index,
+                               tun_newlink, GINT_TO_POINTER(if_index));
+
+       newlink_timeout_id = g_timeout_add_seconds(1, newlink_timeout, NULL);
+
+       return 0;
+
+error:
+       tunnel_destroy();
+       return -1;
+}
+
+static void receive_rs_reply(struct nd_router_advert *reply,
+                       unsigned int length, void *user_data)
+{
+       char *address = user_data;
+       struct in_addr ip4addr;
+
+       DBG("reply %p len %d address %s", reply, length, address);
+
+       /* We try to create tunnel if autoconfiguration did not work i.e.,
+        * we did not receive any reply to router solicitation message.
+        */
+       if (reply == NULL && inet_aton(address, &ip4addr) != 0)
+               init_6to4(&ip4addr);
+
+       g_free(address);
+}
+
+int __connman_6to4_probe(struct connman_service *service)
+{
+       struct connman_ipconfig *ip4config, *ip6config;
+       enum connman_ipconfig_method method;
+       unsigned int a, b;
+       struct in_addr ip4addr;
+       in_addr_t addr;
+       const char *address;
+       char *ip_address;
+       int index;
+
+       DBG("service %p", service);
+
+       if (tunnel_created || tunnel_pending)
+               return 0;
+
+       if (service == NULL)
+               return -1;
+
+       ip4config = __connman_service_get_ip4config(service);
+       if (ip4config == NULL)
+               return -1;
+
+       ip6config = __connman_service_get_ip6config(service);
+       if (ip6config == NULL)
+               return -1;
+
+       method = __connman_ipconfig_get_method(ip6config);
+       if (method != CONNMAN_IPCONFIG_METHOD_AUTO)
+               return -1;
+
+       address = __connman_ipconfig_get_local(ip4config);
+       if (address == NULL)
+               return -1;
+
+       if (inet_aton(address, &ip4addr) == 0)
+               return -1;
+
+       addr = ntohl(ip4addr.s_addr);
+
+       a = (addr & 0xff000000) >> 24;
+       b = (addr & 0x00ff0000) >> 16;
+
+       /* 6to4 tunnel is only usable if we have a public IPv4 address */
+       if (a == 10 || (a == 192 && b == 168) ||
+                                       (a == 172 && (b >= 16 && b <= 31)))
+               return -1;
+
+       index = __connman_ipconfig_get_index(ip4config);
+       ip_address = g_strdup(address);
+       tunnel_pending = 1;
+
+       g_free(tunnel_ip_address);
+       tunnel_ip_address = g_strdup(address);
+
+       return __connman_inet_ipv6_send_rs(index, 2, receive_rs_reply,
+                                                       ip_address);
+}
+
+void __connman_6to4_remove(struct connman_ipconfig *ip4config)
+{
+       const char *address;
+
+       DBG("tunnel ip address %s", tunnel_ip_address);
+
+       if (ip4config == NULL)
+               return;
+
+       address = __connman_ipconfig_get_local(ip4config);
+       if (address == NULL)
+               return;
+
+       if (g_strcmp0(address, tunnel_ip_address) != 0)
+               return;
+
+       if (tunnel_created)
+               tunnel_destroy();
+}
+
+int __connman_6to4_check(struct connman_ipconfig *ip4config)
+{
+       const char *address;
+
+       if (ip4config == NULL || tunnel_created == 0 ||
+                                       tunnel_pending == 1)
+               return -1;
+
+       DBG("tunnel ip address %s", tunnel_ip_address);
+
+       address = __connman_ipconfig_get_local(ip4config);
+       if (address == NULL)
+               return -1;
+
+       if (g_strcmp0(address, tunnel_ip_address) == 0)
+               return 1;
+
+       return 0;
+}
diff --git a/src/agent.c b/src/agent.c
new file mode 100644 (file)
index 0000000..7aaf304
--- /dev/null
@@ -0,0 +1,806 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection = NULL;
+static guint agent_watch = 0;
+static gchar *agent_path = NULL;
+static gchar *agent_sender = NULL;
+
+static void agent_free(void)
+{
+       agent_watch = 0;
+
+       g_free(agent_sender);
+       agent_sender = NULL;
+
+       g_free(agent_path);
+       agent_path = NULL;
+}
+
+static void agent_disconnect(DBusConnection *connection, void *data)
+{
+       DBG("data %p", data);
+
+       agent_free();
+}
+
+int __connman_agent_register(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (agent_path != NULL)
+               return -EEXIST;
+
+       agent_sender = g_strdup(sender);
+       agent_path = g_strdup(path);
+
+       agent_watch = g_dbus_add_disconnect_watch(connection, sender,
+                                               agent_disconnect, NULL, NULL);
+
+       return 0;
+}
+
+int __connman_agent_unregister(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (agent_path == NULL)
+               return -ESRCH;
+
+       if (agent_watch > 0)
+               g_dbus_remove_watch(connection, agent_watch);
+
+       agent_free();
+
+       return 0;
+}
+
+static connman_bool_t check_reply_has_dict(DBusMessage *reply)
+{
+       const char *signature = DBUS_TYPE_ARRAY_AS_STRING
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+
+       if (dbus_message_has_signature(reply, signature) == TRUE)
+               return TRUE;
+
+       connman_warn("Reply %s to %s from %s has wrong signature %s",
+                       signature,
+                       dbus_message_get_interface(reply),
+                       dbus_message_get_sender(reply),
+                       dbus_message_get_signature(reply));
+
+       return FALSE;
+}
+
+struct request_input_reply {
+       struct connman_service *service;
+       authentication_cb_t callback;
+       void *user_data;
+};
+
+static void request_input_passphrase_reply(DBusPendingCall *call, void *user_data)
+{
+       struct request_input_reply *passphrase_reply = user_data;
+       connman_bool_t values_received = FALSE;
+       connman_bool_t wps = FALSE;
+       const char *error = NULL;
+       char *identity = NULL;
+       char *passphrase = NULL;
+       char *wpspin = NULL;
+       char *key;
+       char *name = NULL;
+       int name_len = 0;
+       DBusMessageIter iter, dict;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
+               goto done;
+       }
+
+       if (check_reply_has_dict(reply) == FALSE)
+               goto done;
+
+       values_received = TRUE;
+
+       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, "Identity")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &identity);
+
+               } else if (g_str_equal(key, "Passphrase")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &passphrase);
+
+               } else if (g_str_equal(key, "WPS")) {
+                       wps = TRUE;
+
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &wpspin);
+                       break;
+               } else if (g_str_equal(key, "Name")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &name);
+                       name_len = strlen(name);
+               } else if (g_str_equal(key, "SSID")) {
+                       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_VARIANT)
+                               break;
+                       if (dbus_message_iter_get_element_type(&value)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_get_fixed_array(&value, &name,
+                                                       &name_len);
+               }
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       passphrase_reply->callback(passphrase_reply->service, values_received,
+                               name, name_len,
+                               identity, passphrase,
+                               wps, wpspin, error,
+                               passphrase_reply->user_data);
+       connman_service_unref(passphrase_reply->service);
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+       g_free(passphrase_reply);
+}
+
+static void request_input_append_alternates(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       const char *str = user_data;
+       char **alternates, **alternative;
+
+       if (str == NULL)
+               return;
+
+       alternates = g_strsplit(str, ",", 0);
+       if (alternates == NULL)
+               return;
+
+       for (alternative = alternates; *alternative != NULL; alternative++)
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                                               alternative);
+
+       g_strfreev(alternates);
+}
+
+static void request_input_append_identity(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_passphrase(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct connman_service *service = user_data;
+       char *value;
+       const char *phase2;
+
+       switch (__connman_service_get_security(service)) {
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               value = "wep";
+               break;
+       case CONNMAN_SERVICE_SECURITY_PSK:
+               value = "psk";
+               break;
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               phase2 = __connman_service_get_phase2(service);
+
+               if (phase2 != NULL && (
+                               g_str_has_suffix(phase2, "GTC") == TRUE ||
+                               g_str_has_suffix(phase2, "OTP") == TRUE))
+                       value = "response";
+               else
+                       value = "passphrase";
+
+               break;
+       default:
+               value = "string";
+               break;
+       }
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &value);
+       value = "mandatory";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &value);
+
+       if (__connman_service_wps_enabled(service) == TRUE) {
+               connman_dbus_dict_append_array(iter, "Alternates",
+                                       DBUS_TYPE_STRING,
+                                       request_input_append_alternates,
+                                       "WPS");
+       }
+}
+
+static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = "wpspin";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "alternate";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_name(DBusMessageIter *iter, void *user_data)
+{
+       const 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);
+       connman_dbus_dict_append_array(iter, "Alternates",
+                               DBUS_TYPE_STRING,
+                               request_input_append_alternates,
+                               "SSID");
+}
+
+static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = "ssid";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "alternate";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_append_password(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       char *str = "passphrase";
+
+       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_previouspassphrase(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct connman_service *service = user_data;
+       enum connman_service_security security;
+       const char *passphrase, *str = NULL;
+
+       passphrase = __connman_service_get_passphrase(service);
+
+       security = __connman_service_get_security(service);
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               str = "wep";
+               break;
+       case CONNMAN_SERVICE_SECURITY_PSK:
+               str  = "psk";
+               break;
+       /*
+        * This should never happen: no passphrase is set if security is not
+        * one of the above.*/
+       default:
+               break;
+       }
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+
+       str = "informational";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+
+       connman_dbus_dict_append_basic(iter, "Value",
+                               DBUS_TYPE_STRING, &passphrase);
+}
+
+static void request_input_login_reply(DBusPendingCall *call, void *user_data)
+{
+       struct request_input_reply *username_password_reply = user_data;
+       const char *error = NULL;
+       connman_bool_t values_received = FALSE;
+       char *username = NULL;
+       char *password = NULL;
+       char *key;
+       DBusMessageIter iter, dict;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
+               goto done;
+       }
+
+       if (check_reply_has_dict(reply) == FALSE)
+               goto done;
+
+       values_received = TRUE;
+
+       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, "Username")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &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);
+                       dbus_message_iter_get_basic(&value, &password);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       username_password_reply->callback(username_password_reply->service,
+                                       values_received, NULL, 0,
+                                       username, password,
+                                       FALSE, NULL, error,
+                                       username_password_reply->user_data);
+       connman_service_unref(username_password_reply->service);
+       dbus_message_unref(reply);
+       g_free(username_password_reply);
+}
+
+int __connman_agent_request_passphrase_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data)
+{
+       DBusMessage *message;
+       const char *path;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusPendingCall *call;
+       struct request_input_reply *passphrase_reply;
+
+       if (service == NULL || agent_path == NULL || callback == NULL)
+               return -ESRCH;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "RequestInput");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+
+       connman_dbus_dict_open(&iter, &dict);
+
+       if (__connman_service_is_hidden(service)) {
+               connman_dbus_dict_append_dict(&dict, "Name",
+                                       request_input_append_name, NULL);
+               connman_dbus_dict_append_dict(&dict, "SSID",
+                                       request_input_append_ssid, NULL);
+       }
+
+       if (__connman_service_get_security(service) ==
+                       CONNMAN_SERVICE_SECURITY_8021X) {
+               connman_dbus_dict_append_dict(&dict, "Identity",
+                                       request_input_append_identity, service);
+       }
+
+       if (__connman_service_get_security(service) !=
+                       CONNMAN_SERVICE_SECURITY_NONE) {
+               connman_dbus_dict_append_dict(&dict, "Passphrase",
+                                       request_input_append_passphrase, service);
+
+               if (__connman_service_get_passphrase(service) != NULL)
+                       connman_dbus_dict_append_dict(&dict, "PreviousPassphrase",
+                                       request_input_append_previouspassphrase,
+                                       service);
+       }
+
+       if (__connman_service_wps_enabled(service) == TRUE) {
+           connman_dbus_dict_append_dict(&dict, "WPS",
+                               request_input_append_wps, NULL);
+       }
+
+       connman_dbus_dict_close(&iter, &dict);
+
+       passphrase_reply = g_try_new0(struct request_input_reply, 1);
+       if (passphrase_reply == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
+               dbus_message_unref(message);
+               g_free(passphrase_reply);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(passphrase_reply);
+               return -ESRCH;
+       }
+
+       passphrase_reply->service = connman_service_ref(service);
+       passphrase_reply->callback = callback;
+       passphrase_reply->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, request_input_passphrase_reply,
+                               passphrase_reply, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+int __connman_agent_request_login_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data)
+{
+       DBusMessage *message;
+       const char *path;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusPendingCall *call;
+       struct request_input_reply *username_password_reply;
+
+       if (service == NULL || agent_path == NULL || callback == NULL)
+               return -ESRCH;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "RequestInput");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+
+       connman_dbus_dict_open(&iter, &dict);
+
+       connman_dbus_dict_append_dict(&dict, "Username",
+                               request_input_append_identity, service);
+
+       connman_dbus_dict_append_dict(&dict, "Password",
+                               request_input_append_password, service);
+
+       connman_dbus_dict_close(&iter, &dict);
+
+       username_password_reply = g_try_new0(struct request_input_reply, 1);
+       if (username_password_reply == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
+               dbus_message_unref(message);
+               g_free(username_password_reply);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(username_password_reply);
+               return -ESRCH;
+       }
+
+       username_password_reply->service = connman_service_ref(service);
+       username_password_reply->callback = callback;
+       username_password_reply->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, request_input_login_reply,
+                                               username_password_reply, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+struct request_browser_reply_data {
+       struct connman_service *service;
+       browser_authentication_cb_t callback;
+       void *user_data;
+};
+
+static void request_browser_reply(DBusPendingCall *call, void *user_data)
+{
+       struct request_browser_reply_data *browser_reply_data = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       connman_bool_t result = FALSE;
+       const char *error = NULL;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               error = dbus_message_get_error_name(reply);
+               goto done;
+       }
+
+       result = TRUE;
+
+done:
+       browser_reply_data->callback(browser_reply_data->service, result,
+                                       error, browser_reply_data->user_data);
+       connman_service_unref(browser_reply_data->service);
+       dbus_message_unref(reply);
+       g_free(browser_reply_data);
+}
+
+int __connman_agent_request_browser(struct connman_service *service,
+                               browser_authentication_cb_t callback,
+                               const char *url, void *user_data)
+{
+       struct request_browser_reply_data *browser_reply_data;
+       DBusPendingCall *call;
+       DBusMessage *message;
+       DBusMessageIter iter;
+       const char *path;
+
+       if (service == NULL || agent_path == NULL || callback == NULL)
+               return -ESRCH;
+
+       if (url == NULL)
+               url = "";
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "RequestBrowser");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
+
+       browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
+       if (browser_reply_data == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_browser_launch())
+                       == FALSE) {
+               dbus_message_unref(message);
+               g_free(browser_reply_data);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(browser_reply_data);
+               return -ESRCH;
+       }
+
+       browser_reply_data->service = connman_service_ref(service);
+       browser_reply_data->callback = callback;
+       browser_reply_data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, request_browser_reply,
+                                               browser_reply_data, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+struct report_error_data {
+       struct connman_service *service;
+       report_error_cb_t callback;
+       void *user_data;
+};
+
+static void report_error_reply(DBusPendingCall *call, void *user_data)
+{
+       struct report_error_data *report_error = user_data;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       gboolean retry = FALSE;
+       const char *dbus_err;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               dbus_err = dbus_message_get_error_name(reply);
+               if (dbus_err != NULL &&
+                       strcmp(dbus_err,
+                               CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
+                       retry = TRUE;
+       }
+
+       report_error->callback(report_error->service, retry,
+                       report_error->user_data);
+       connman_service_unref(report_error->service);
+       g_free(report_error);
+       dbus_message_unref(reply);
+       dbus_pending_call_unref(call);
+}
+
+int __connman_agent_report_error(struct connman_service *service,
+                               const char *error,
+                               report_error_cb_t callback, void *user_data)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       const char *path;
+       struct report_error_data *report_error;
+       DBusPendingCall *call;
+
+       if (service == NULL || agent_path == NULL || error == NULL ||
+               callback == NULL)
+               return -ESRCH;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "ReportError");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+       dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_STRING, &error);
+
+       report_error = g_try_new0(struct report_error_data, 1);
+       if (report_error == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message, &call,
+                                       connman_timeout_input_request())
+                       == FALSE) {
+               dbus_message_unref(message);
+               g_free(report_error);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(report_error);
+               return -ESRCH;
+       }
+
+       report_error->service = connman_service_ref(service);
+       report_error->callback = callback;
+       report_error->user_data = user_data;
+       dbus_pending_call_set_notify(call, report_error_reply,
+                               report_error, NULL);
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+int __connman_agent_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       return 0;
+}
+
+void __connman_agent_cleanup(void)
+{
+       DBusMessage *message;
+
+       DBG("");
+
+       if (connection == NULL)
+               return;
+
+       if (agent_watch > 0)
+               g_dbus_remove_watch(connection, agent_watch);
+
+       if (agent_path == NULL)
+               return;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE, "Release");
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+
+       agent_free();
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/bridge.c b/src/bridge.c
new file mode 100644 (file)
index 0000000..2849ef7
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. 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 <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <string.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+
+#include "connman.h"
+
+static int set_forward_delay(const char *name, unsigned int delay)
+{
+       FILE *f;
+       char *forward_delay_path;
+
+       forward_delay_path =
+               g_strdup_printf("/sys/class/net/%s/bridge/forward_delay", name);
+
+       if (forward_delay_path == NULL)
+               return -ENOMEM;
+
+       f = fopen(forward_delay_path, "r+");
+
+       g_free(forward_delay_path);
+
+       if (f == NULL)
+               return -errno;
+
+       fprintf(f, "%d", delay);
+
+       fclose(f);
+
+       return 0;
+}
+
+int __connman_bridge_create(const char *name)
+{
+       int sk, err;
+
+       DBG("name %s", name);
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       if (ioctl(sk, SIOCBRADDBR, name) == -1) {
+               err = -errno;
+               if (err != -EEXIST)
+                       return -EOPNOTSUPP;
+       }
+
+       err = set_forward_delay(name, 0);
+
+       if (err < 0)
+               ioctl(sk, SIOCBRDELBR, name);
+
+       close(sk);
+
+       return err;
+}
+
+int __connman_bridge_remove(const char *name)
+{
+       int sk, err;
+
+       DBG("name %s", name);
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       err = ioctl(sk, SIOCBRDELBR, name);
+
+       close(sk);
+
+       if (err < 0)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+int __connman_bridge_enable(const char *name, const char *gateway,
+                               const char *broadcast)
+{
+       int err, index;
+
+       index = connman_inet_ifindex(name);
+       if (index < 0)
+               return index;
+
+       err = __connman_inet_modify_address(RTM_NEWADDR,
+                       NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+                                       gateway, NULL, 24, broadcast);
+       if (err < 0)
+               return err;
+
+       return connman_inet_ifup(index);
+}
+
+int __connman_bridge_disable(const char *name)
+{
+       int index;
+
+       index = connman_inet_ifindex(name);
+       if (index < 0)
+               return index;
+
+       return connman_inet_ifdown(index);
+}
diff --git a/src/clock.c b/src/clock.c
new file mode 100644 (file)
index 0000000..e2cc19f
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *
+ *  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 <sys/time.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+enum time_updates {
+       TIME_UPDATES_UNKNOWN = 0,
+       TIME_UPDATES_MANUAL  = 1,
+       TIME_UPDATES_AUTO    = 2,
+};
+
+enum timezone_updates {
+       TIMEZONE_UPDATES_UNKNOWN = 0,
+       TIMEZONE_UPDATES_MANUAL  = 1,
+       TIMEZONE_UPDATES_AUTO    = 2,
+};
+
+static enum time_updates time_updates_config = TIME_UPDATES_AUTO;
+static enum timezone_updates timezone_updates_config = TIMEZONE_UPDATES_AUTO;
+
+static char *timezone_config = NULL;
+
+static const char *time_updates2string(enum time_updates value)
+{
+       switch (value) {
+       case TIME_UPDATES_UNKNOWN:
+               break;
+       case TIME_UPDATES_MANUAL:
+               return "manual";
+       case TIME_UPDATES_AUTO:
+               return "auto";
+       }
+
+       return NULL;
+}
+
+static enum time_updates string2time_updates(const char *value)
+{
+       if (g_strcmp0(value, "manual") == 0)
+               return TIME_UPDATES_MANUAL;
+       else if (g_strcmp0(value, "auto") == 0)
+               return TIME_UPDATES_AUTO;
+
+       return TIME_UPDATES_UNKNOWN;
+}
+
+static const char *timezone_updates2string(enum timezone_updates value)
+{
+       switch (value) {
+       case TIMEZONE_UPDATES_UNKNOWN:
+               break;
+       case TIMEZONE_UPDATES_MANUAL:
+               return "manual";
+       case TIMEZONE_UPDATES_AUTO:
+               return "auto";
+       }
+
+       return NULL;
+}
+
+static enum timezone_updates string2timezone_updates(const char *value)
+{
+       if (g_strcmp0(value, "manual") == 0)
+               return TIMEZONE_UPDATES_MANUAL;
+       else if (g_strcmp0(value, "auto") == 0)
+               return TIMEZONE_UPDATES_AUTO;
+
+        return TIMEZONE_UPDATES_UNKNOWN;
+}
+
+static void append_timeservers(DBusMessageIter *iter, void *user_data)
+{
+       int i;
+       char **timeservers = __connman_timeserver_system_get();
+
+       if (timeservers == NULL)
+               return;
+
+       for (i = 0; timeservers[i] != NULL; i++) {
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &timeservers[i]);
+       }
+
+       g_strfreev(timeservers);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       struct timeval tv;
+       const char *str;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       connman_dbus_dict_open(&array, &dict);
+
+       if (gettimeofday(&tv, NULL) == 0) {
+               dbus_uint64_t val = tv.tv_sec;
+
+               connman_dbus_dict_append_basic(&dict, "Time",
+                                               DBUS_TYPE_UINT64, &val);
+       }
+
+       str = time_updates2string(time_updates_config);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(&dict, "TimeUpdates",
+                                               DBUS_TYPE_STRING, &str);
+
+       if (timezone_config != NULL)
+               connman_dbus_dict_append_basic(&dict, "Timezone",
+                                       DBUS_TYPE_STRING, &timezone_config);
+
+       str = timezone_updates2string(timezone_updates_config);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(&dict, "TimezoneUpdates",
+                                               DBUS_TYPE_STRING, &str);
+
+       connman_dbus_dict_append_array(&dict, "Timeservers",
+                               DBUS_TYPE_STRING, append_timeservers, NULL);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "Time") == TRUE) {
+               struct timeval tv;
+               dbus_uint64_t newval;
+
+               if (type != DBUS_TYPE_UINT64)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (time_updates_config != TIME_UPDATES_MANUAL)
+                       return __connman_error_permission_denied(msg);
+
+               dbus_message_iter_get_basic(&value, &newval);
+
+               tv.tv_sec = newval;
+               tv.tv_usec = 0;
+
+               if (settimeofday(&tv, NULL) < 0)
+                       return __connman_error_invalid_arguments(msg);
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "Time",
+                               DBUS_TYPE_UINT64, &newval);
+       } else if (g_str_equal(name, "TimeUpdates") == TRUE) {
+               const char *strval;
+               enum time_updates newval;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &strval);
+               newval = string2time_updates(strval);
+
+               if (newval == TIME_UPDATES_UNKNOWN)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (newval == time_updates_config)
+                       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+               time_updates_config = newval;
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
+                               DBUS_TYPE_STRING, &strval);
+       } else if (g_str_equal(name, "Timezone") == TRUE) {
+               const char *strval;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (timezone_updates_config != TIMEZONE_UPDATES_MANUAL)
+                       return __connman_error_permission_denied(msg);
+
+               dbus_message_iter_get_basic(&value, &strval);
+
+               if (__connman_timezone_change(strval) < 0)
+                        return __connman_error_invalid_arguments(msg);
+       } else if (g_str_equal(name, "TimezoneUpdates") == TRUE) {
+               const char *strval;
+               enum timezone_updates newval;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &strval);
+               newval = string2timezone_updates(strval);
+
+               if (newval == TIMEZONE_UPDATES_UNKNOWN)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (newval == timezone_updates_config)
+                       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+               timezone_updates_config = newval;
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "TimezoneUpdates",
+                               DBUS_TYPE_STRING, &strval);
+       } else if (g_str_equal(name, "Timeservers") == TRUE) {
+               DBusMessageIter entry;
+               char **str = NULL;
+               GSList *list = NULL;
+               int count = 0;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_recurse(&value, &entry);
+
+               while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+                       const char *val;
+                       GSList *new_head;
+
+                       dbus_message_iter_get_basic(&entry, &val);
+
+                       new_head = __connman_timeserver_add_list(list, val);
+                       if (list != new_head) {
+                               count++;
+                               list = new_head;
+                       }
+
+                       dbus_message_iter_next(&entry);
+               }
+
+               if (list != NULL) {
+                       str = g_new0(char *, count+1);
+
+                       while (list != NULL) {
+                               count--;
+                               str[count] = list->data;
+                               list = g_slist_delete_link(list, list);
+                       };
+               }
+
+               __connman_timeserver_system_set(str);
+
+               if (str != NULL)
+                       g_strfreev(str);
+
+               connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "Timeservers",
+                               DBUS_TYPE_STRING, append_timeservers, NULL);
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable clock_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property)   },
+       { },
+};
+
+static const GDBusSignalTable clock_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { },
+};
+
+static DBusConnection *connection = NULL;
+
+void __connman_clock_update_timezone(void)
+{
+       DBG("");
+
+       g_free(timezone_config);
+       timezone_config = __connman_timezone_lookup();
+
+       if (timezone_config == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "Timezone",
+                               DBUS_TYPE_STRING, &timezone_config);
+}
+
+int __connman_clock_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       __connman_timezone_init();
+
+       timezone_config = __connman_timezone_lookup();
+
+       g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
+                                               CONNMAN_CLOCK_INTERFACE,
+                                               clock_methods, clock_signals,
+                                               NULL, NULL, NULL);
+
+       return 0;
+}
+
+void __connman_clock_cleanup(void)
+{
+       DBG("");
+
+       if (connection == NULL)
+               return;
+
+       g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
+                                               CONNMAN_CLOCK_INTERFACE);
+
+       dbus_connection_unref(connection);
+
+       __connman_timezone_cleanup();
+
+       g_free(timezone_config);
+}
diff --git a/src/config.c b/src/config.c
new file mode 100644 (file)
index 0000000..8d42bcc
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/vfs.h>
+#include <sys/inotify.h>
+#include <glib.h>
+
+#include "connman.h"
+
+struct connman_config_service {
+       char *ident;
+       char *name;
+       char *type;
+       void *ssid;
+       unsigned int ssid_len;
+       char *eap;
+       char *identity;
+       char *ca_cert_file;
+       char *client_cert_file;
+       char *private_key_file;
+       char *private_key_passphrase;
+       char *private_key_passphrase_type;
+       char *phase2;
+       char *passphrase;
+       GSList *service_identifiers;
+       char *config_ident; /* file prefix */
+       char *config_entry; /* entry name */
+};
+
+struct connman_config {
+       char *ident;
+       char *name;
+       char *description;
+       connman_bool_t protected;
+       GHashTable *service_table;
+};
+
+static GHashTable *config_table = NULL;
+static GSList *protected_services = NULL;
+
+static int inotify_wd = -1;
+
+static GIOChannel *inotify_channel = NULL;
+static uint inotify_watch = 0;
+static connman_bool_t cleanup = FALSE;
+
+#define INTERNAL_CONFIG_PREFIX           "__internal"
+
+/* Definition of possible strings in the .config files */
+#define CONFIG_KEY_NAME                "Name"
+#define CONFIG_KEY_DESC                "Description"
+#define CONFIG_KEY_PROT                "Protected"
+
+#define SERVICE_KEY_TYPE               "Type"
+#define SERVICE_KEY_NAME               "Name"
+#define SERVICE_KEY_SSID               "SSID"
+#define SERVICE_KEY_EAP                "EAP"
+#define SERVICE_KEY_CA_CERT            "CACertFile"
+#define SERVICE_KEY_CL_CERT            "ClientCertFile"
+#define SERVICE_KEY_PRV_KEY            "PrivateKeyFile"
+#define SERVICE_KEY_PRV_KEY_PASS       "PrivateKeyPassphrase"
+#define SERVICE_KEY_PRV_KEY_PASS_TYPE  "PrivateKeyPassphraseType"
+#define SERVICE_KEY_IDENTITY           "Identity"
+#define SERVICE_KEY_PHASE2             "Phase2"
+#define SERVICE_KEY_PASSPHRASE         "Passphrase"
+
+static const char *config_possible_keys[] = {
+       CONFIG_KEY_NAME,
+       CONFIG_KEY_DESC,
+       CONFIG_KEY_PROT,
+       NULL,
+};
+
+static const char *service_possible_keys[] = {
+       SERVICE_KEY_TYPE,
+       SERVICE_KEY_NAME,
+       SERVICE_KEY_SSID,
+       SERVICE_KEY_EAP,
+       SERVICE_KEY_CA_CERT,
+       SERVICE_KEY_CL_CERT,
+       SERVICE_KEY_PRV_KEY,
+       SERVICE_KEY_PRV_KEY_PASS,
+       SERVICE_KEY_PRV_KEY_PASS_TYPE,
+       SERVICE_KEY_IDENTITY,
+       SERVICE_KEY_PHASE2,
+       SERVICE_KEY_PASSPHRASE,
+       NULL,
+};
+
+static void unregister_config(gpointer data)
+{
+       struct connman_config *config = data;
+
+       connman_info("Removing configuration %s", config->ident);
+
+       g_hash_table_destroy(config->service_table);
+
+       g_free(config->description);
+       g_free(config->name);
+       g_free(config->ident);
+       g_free(config);
+}
+
+static void unregister_service(gpointer data)
+{
+       struct connman_config_service *config_service = data;
+       struct connman_service *service;
+       char *service_id;
+       GSList *list;
+
+       if (cleanup == TRUE)
+               goto free_only;
+
+       connman_info("Removing service configuration %s",
+                                               config_service->ident);
+
+       protected_services = g_slist_remove(protected_services,
+                                               config_service);
+
+       for (list = config_service->service_identifiers; list != NULL;
+                                                       list = list->next) {
+               service_id = list->data;
+
+               service = __connman_service_lookup_from_ident(service_id);
+               if (service != NULL) {
+                       __connman_service_set_immutable(service, FALSE);
+                       __connman_service_remove(service);
+               }
+
+               if (__connman_storage_remove_service(service_id) == FALSE)
+                       DBG("Could not remove all files for service %s",
+                                                               service_id);
+       }
+
+free_only:
+       g_free(config_service->ident);
+       g_free(config_service->type);
+       g_free(config_service->name);
+       g_free(config_service->ssid);
+       g_free(config_service->eap);
+       g_free(config_service->identity);
+       g_free(config_service->ca_cert_file);
+       g_free(config_service->client_cert_file);
+       g_free(config_service->private_key_file);
+       g_free(config_service->private_key_passphrase);
+       g_free(config_service->private_key_passphrase_type);
+       g_free(config_service->phase2);
+       g_free(config_service->passphrase);
+       g_slist_free_full(config_service->service_identifiers, g_free);
+       g_free(config_service->config_ident);
+       g_free(config_service->config_entry);
+       g_free(config_service);
+}
+
+static void check_keys(GKeyFile *keyfile, const char *group,
+                       const char **possible_keys)
+{
+       char **avail_keys;
+       gsize nb_avail_keys, i, j;
+
+       avail_keys = g_key_file_get_keys(keyfile, group, &nb_avail_keys, NULL);
+       if (avail_keys == NULL)
+               return;
+
+       /*
+        * For each key in the configuration file,
+        * verify it is understood by connman
+        */
+       for (i = 0 ; i < nb_avail_keys; i++) {
+               for (j = 0; possible_keys[j] ; j++)
+                       if (g_strcmp0(avail_keys[i], possible_keys[j]) == 0)
+                               break;
+
+               if (possible_keys[j] == NULL)
+                       connman_warn("Unknown configuration key %s in [%s]",
+                                       avail_keys[i], group);
+       }
+
+       g_strfreev(avail_keys);
+}
+
+static connman_bool_t
+is_protected_service(struct connman_config_service *service)
+{
+       GSList *list;
+
+       DBG("ident %s", service->ident);
+
+       for (list = protected_services; list; list = list->next) {
+               struct connman_config_service *s = list->data;
+
+               if (g_strcmp0(s->type, service->type) != 0)
+                       continue;
+
+               if (s->ssid == NULL || service->ssid == NULL)
+                       continue;
+
+               if (s->ssid_len != service->ssid_len)
+                       continue;
+
+               if (g_strcmp0(service->type, "wifi") == 0 &&
+                       strncmp(s->ssid, service->ssid, s->ssid_len) == 0) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static int load_service(GKeyFile *keyfile, const char *group,
+                                               struct connman_config *config)
+{
+       struct connman_config_service *service;
+       const char *ident;
+       char *str, *hex_ssid;
+       gboolean service_created = FALSE;
+       int err;
+
+       /* Strip off "service_" prefix */
+       ident = group + 8;
+
+       if (strlen(ident) < 1)
+               return -EINVAL;
+
+       /* Verify that provided keys are good */
+       check_keys(keyfile, group, service_possible_keys);
+
+       service = g_hash_table_lookup(config->service_table, ident);
+       if (service == NULL) {
+               service = g_try_new0(struct connman_config_service, 1);
+               if (service == NULL)
+                       return -ENOMEM;
+
+               service->ident = g_strdup(ident);
+
+               service_created = TRUE;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_TYPE, NULL);
+       if (str != NULL) {
+               g_free(service->type);
+               service->type = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_NAME, NULL);
+       if (str != NULL) {
+               g_free(service->name);
+               service->name = str;
+       }
+
+       hex_ssid = g_key_file_get_string(keyfile, group, SERVICE_KEY_SSID,
+                                        NULL);
+       if (hex_ssid != NULL) {
+               char *ssid;
+               unsigned int i, j = 0, hex;
+               size_t hex_ssid_len = strlen(hex_ssid);
+
+               ssid = g_try_malloc0(hex_ssid_len / 2);
+               if (ssid == NULL) {
+                       err = -ENOMEM;
+                       g_free(hex_ssid);
+                       goto err;
+               }
+
+               for (i = 0; i < hex_ssid_len; i += 2) {
+                       if (sscanf(hex_ssid + i, "%02x", &hex) <= 0) {
+                               connman_warn("Invalid SSID %s", hex_ssid);
+                               g_free(ssid);
+                               g_free(hex_ssid);
+                               err = -EILSEQ;
+                               goto err;
+                       }
+                       ssid[j++] = hex;
+               }
+
+               g_free(hex_ssid);
+
+               g_free(service->ssid);
+               service->ssid = ssid;
+               service->ssid_len = hex_ssid_len / 2;
+       } else if (service->name != NULL) {
+               char *ssid;
+               unsigned int ssid_len;
+
+               ssid_len = strlen(service->name);
+               ssid = g_try_malloc0(ssid_len);
+               if (ssid == NULL) {
+                       err = -ENOMEM;
+                       goto err;
+               }
+
+               memcpy(ssid, service->name, ssid_len);
+               g_free(service->ssid);
+               service->ssid = ssid;
+               service->ssid_len = ssid_len;
+       }
+
+       if (is_protected_service(service) == TRUE) {
+               connman_error("Trying to provision a protected service");
+               err = -EACCES;
+               goto err;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_EAP, NULL);
+       if (str != NULL) {
+               g_free(service->eap);
+               service->eap = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CA_CERT, NULL);
+       if (str != NULL) {
+               g_free(service->ca_cert_file);
+               service->ca_cert_file = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_CL_CERT, NULL);
+       if (str != NULL) {
+               g_free(service->client_cert_file);
+               service->client_cert_file = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PRV_KEY, NULL);
+       if (str != NULL) {
+               g_free(service->private_key_file);
+               service->private_key_file = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group,
+                                               SERVICE_KEY_PRV_KEY_PASS, NULL);
+       if (str != NULL) {
+               g_free(service->private_key_passphrase);
+               service->private_key_passphrase = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group,
+                                       SERVICE_KEY_PRV_KEY_PASS_TYPE, NULL);
+       if (str != NULL) {
+               g_free(service->private_key_passphrase_type);
+               service->private_key_passphrase_type = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_IDENTITY, NULL);
+       if (str != NULL) {
+               g_free(service->identity);
+               service->identity = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PHASE2, NULL);
+       if (str != NULL) {
+               g_free(service->phase2);
+               service->phase2 = str;
+       }
+
+       str = g_key_file_get_string(keyfile, group, SERVICE_KEY_PASSPHRASE,
+                                       NULL);
+       if (str != NULL) {
+               g_free(service->passphrase);
+               service->passphrase = str;
+       }
+
+       service->config_ident = g_strdup(config->ident);
+       service->config_entry = g_strdup_printf("service_%s", service->ident);
+
+       if (service_created)
+               g_hash_table_insert(config->service_table, service->ident,
+                                       service);
+
+       if (config->protected == TRUE)
+               protected_services =
+                       g_slist_append(protected_services, service);
+
+       connman_info("Adding service configuration %s", service->ident);
+
+       return 0;
+
+err:
+       if (service_created == TRUE) {
+               g_free(service->ident);
+               g_free(service->type);
+               g_free(service->name);
+               g_free(service->ssid);
+               g_free(service);
+       }
+
+       return err;
+}
+
+static int load_config(struct connman_config *config)
+{
+       GKeyFile *keyfile;
+       GError *error = NULL;
+       gsize length;
+       char **groups;
+       char *str;
+       gboolean protected, found = FALSE;
+       int i;
+
+       DBG("config %p", config);
+
+       keyfile = __connman_storage_load_config(config->ident);
+       if (keyfile == NULL)
+               return -EIO;
+
+       /* Verify keys validity of the global section */
+       check_keys(keyfile, "global", config_possible_keys);
+
+       str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_NAME, NULL);
+       if (str != NULL) {
+               g_free(config->name);
+               config->name = str;
+       }
+
+       str = g_key_file_get_string(keyfile, "global", CONFIG_KEY_DESC, NULL);
+       if (str != NULL) {
+               g_free(config->description);
+               config->description = str;
+       }
+
+       protected = g_key_file_get_boolean(keyfile, "global",
+                                       CONFIG_KEY_PROT, &error);
+       if (error == NULL)
+               config->protected = protected;
+       else
+               config->protected = TRUE;
+#if defined TIZEN_EXT
+       g_clear_error(&error);
+#endif
+
+       groups = g_key_file_get_groups(keyfile, &length);
+
+       for (i = 0; groups[i] != NULL; i++) {
+               if (g_str_has_prefix(groups[i], "service_") == TRUE) {
+                       if (load_service(keyfile, groups[i], config) == 0)
+                               found = TRUE;
+               }
+       }
+
+       if (found == FALSE)
+               connman_warn("Config file %s/%s.config does not contain any "
+                       "configuration that can be provisioned!",
+                       STORAGEDIR, config->ident);
+
+       g_strfreev(groups);
+
+       g_key_file_free(keyfile);
+
+       return 0;
+}
+
+static struct connman_config *create_config(const char *ident)
+{
+       struct connman_config *config;
+
+       DBG("ident %s", ident);
+
+       if (g_hash_table_lookup(config_table, ident) != NULL)
+               return NULL;
+
+       config = g_try_new0(struct connman_config, 1);
+       if (config == NULL)
+               return NULL;
+
+       config->ident = g_strdup(ident);
+
+       config->service_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, unregister_service);
+
+       g_hash_table_insert(config_table, config->ident, config);
+
+       connman_info("Adding configuration %s", config->ident);
+
+       return config;
+}
+
+static connman_bool_t validate_ident(const char *ident)
+{
+       unsigned int i;
+
+       if (ident == NULL)
+               return FALSE;
+
+       for (i = 0; i < strlen(ident); i++)
+               if (g_ascii_isprint(ident[i]) == FALSE)
+                       return FALSE;
+
+       return TRUE;
+}
+
+static int read_configs(void)
+{
+       GDir *dir;
+
+       DBG("");
+
+       dir = g_dir_open(STORAGEDIR, 0, NULL);
+       if (dir != NULL) {
+               const gchar *file;
+
+               while ((file = g_dir_read_name(dir)) != NULL) {
+                       GString *str;
+                       gchar *ident;
+
+                       if (g_str_has_suffix(file, ".config") == FALSE)
+                               continue;
+
+                       ident = g_strrstr(file, ".config");
+                       if (ident == NULL)
+                               continue;
+
+                       str = g_string_new_len(file, ident - file);
+                       if (str == NULL)
+                               continue;
+
+                       ident = g_string_free(str, FALSE);
+
+                       if (validate_ident(ident) == TRUE) {
+                               struct connman_config *config;
+
+                               config = create_config(ident);
+                               if (config != NULL)
+                                       load_config(config);
+                       } else {
+                               connman_error("Invalid config ident %s", ident);
+                       }
+                       g_free(ident);
+               }
+
+               g_dir_close(dir);
+       }
+
+       return 0;
+}
+
+static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       char buffer[256];
+       char *next_event;
+       gsize bytes_read;
+       GIOStatus status;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               inotify_watch = 0;
+               return FALSE;
+       }
+
+       status = g_io_channel_read_chars(channel, buffer,
+                                       sizeof(buffer) -1, &bytes_read, NULL);
+
+       switch (status) {
+       case G_IO_STATUS_NORMAL:
+               break;
+       case G_IO_STATUS_AGAIN:
+               return TRUE;
+       default:
+               connman_error("Reading from inotify channel failed");
+               inotify_watch = 0;
+               return FALSE;
+       }
+
+       next_event = buffer;
+
+       while (bytes_read > 0) {
+               struct inotify_event *event;
+               gchar *ext;
+               gchar *ident;
+               gsize len;
+
+               event = (struct inotify_event *) next_event;
+               if (event->len)
+                       ident = next_event + sizeof(struct inotify_event);
+               else
+                       ident = NULL;
+
+               len = sizeof(struct inotify_event) + event->len;
+
+               /* check if inotify_event block fit */
+               if (len > bytes_read)
+                       break;
+
+               next_event += len;
+               bytes_read -= len;
+
+               if (ident == NULL)
+                       continue;
+
+               if (g_str_has_suffix(ident, ".config") == FALSE)
+                       continue;
+
+               ext = g_strrstr(ident, ".config");
+               if (ext == NULL)
+                       continue;
+
+               *ext = '\0';
+
+               if (validate_ident(ident) == FALSE) {
+                       connman_error("Invalid config ident %s", ident);
+                       continue;
+               }
+
+               if (event->mask & IN_CREATE)
+                       create_config(ident);
+
+               if (event->mask & IN_MODIFY) {
+                       struct connman_config *config;
+
+                       config = g_hash_table_lookup(config_table, ident);
+                       if (config != NULL) {
+                               int ret;
+
+                               g_hash_table_remove_all(config->service_table);
+                               load_config(config);
+                               ret = __connman_service_provision_changed(ident);
+                               if (ret > 0) {
+                                       /*
+                                        * Re-scan the config file for any
+                                        * changes
+                                        */
+                                       g_hash_table_remove_all(config->service_table);
+                                       load_config(config);
+                                       __connman_service_provision_changed(ident);
+                               }
+#if defined TIZEN_EXT
+                               __connman_service_auto_connect();
+#endif
+                       }
+               }
+
+               if (event->mask & IN_DELETE)
+                       g_hash_table_remove(config_table, ident);
+       }
+
+       return TRUE;
+}
+
+static int create_watch(void)
+{
+       int fd;
+
+       fd = inotify_init();
+       if (fd < 0)
+               return -EIO;
+
+       inotify_wd = inotify_add_watch(fd, STORAGEDIR,
+                                       IN_MODIFY | IN_CREATE | IN_DELETE);
+       if (inotify_wd < 0) {
+               connman_error("Creation of STORAGEDIR  watch failed");
+               close(fd);
+               return -EIO;
+       }
+
+       inotify_channel = g_io_channel_unix_new(fd);
+       if (inotify_channel == NULL) {
+               connman_error("Creation of inotify channel failed");
+               inotify_rm_watch(fd, inotify_wd);
+               inotify_wd = 0;
+
+               close(fd);
+               return -EIO;
+       }
+
+       g_io_channel_set_close_on_unref(inotify_channel, TRUE);
+       g_io_channel_set_encoding(inotify_channel, NULL, NULL);
+       g_io_channel_set_buffered(inotify_channel, FALSE);
+
+       inotify_watch = g_io_add_watch(inotify_channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                               inotify_data, NULL);
+
+       return 0;
+}
+
+static void remove_watch(void)
+{
+       int fd;
+
+       if (inotify_channel == NULL)
+               return;
+
+       if (inotify_watch > 0) {
+               g_source_remove(inotify_watch);
+               inotify_watch = 0;
+       }
+
+       fd = g_io_channel_unix_get_fd(inotify_channel);
+
+       if (inotify_wd >= 0) {
+               inotify_rm_watch(fd, inotify_wd);
+               inotify_wd = 0;
+       }
+
+       g_io_channel_unref(inotify_channel);
+}
+
+int __connman_config_init(void)
+{
+       DBG("");
+
+       config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, unregister_config);
+
+       create_watch();
+
+       return read_configs();
+}
+
+void __connman_config_cleanup(void)
+{
+       DBG("");
+
+       cleanup = TRUE;
+
+       remove_watch();
+
+       g_hash_table_destroy(config_table);
+       config_table = NULL;
+
+       cleanup = FALSE;
+}
+
+static char *config_pem_fsid(const char *pem_file)
+{
+       struct statfs buf;
+       unsigned *fsid = (unsigned *) &buf.f_fsid;
+       unsigned long long fsid64;
+
+       if (pem_file == NULL)
+               return NULL;
+
+       if (statfs(pem_file, &buf) < 0) {
+               connman_error("statfs error %s for %s",
+                                               strerror(errno), pem_file);
+               return NULL;
+       }
+
+       fsid64 = ((unsigned long long) fsid[0] << 32) | fsid[1];
+
+       return g_strdup_printf("%llx", fsid64);
+}
+
+static void provision_service(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_service *service = user_data;
+       struct connman_config_service *config = value;
+       struct connman_network *network;
+       const void *ssid, *service_id;
+       unsigned int ssid_len;
+
+       /* For now only WiFi service entries are supported */
+       if (g_strcmp0(config->type, "wifi") != 0)
+               return;
+
+       network = __connman_service_get_network(service);
+       if (network == NULL) {
+               connman_error("Service has no network set");
+               return;
+       }
+
+       ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len);
+       if (ssid == NULL) {
+               connman_error("Network SSID not set");
+               return;
+       }
+
+       if (config->ssid == NULL || ssid_len != config->ssid_len)
+               return;
+
+       if (memcmp(config->ssid, ssid, ssid_len) != 0)
+               return;
+
+       service_id = __connman_service_get_ident(service);
+       config->service_identifiers =
+               g_slist_prepend(config->service_identifiers,
+                               g_strdup(service_id));
+
+       __connman_service_set_immutable(service, TRUE);
+
+       __connman_service_set_favorite_delayed(service, TRUE, TRUE);
+
+       __connman_service_set_config(service, config->config_ident,
+                                               config->config_entry);
+
+       if (config->eap != NULL)
+               __connman_service_set_string(service, "EAP", config->eap);
+
+       if (config->identity != NULL)
+               __connman_service_set_string(service, "Identity",
+                                                       config->identity);
+
+       if (config->ca_cert_file != NULL)
+               __connman_service_set_string(service, "CACertFile",
+                                                       config->ca_cert_file);
+
+       if (config->client_cert_file != NULL)
+               __connman_service_set_string(service, "ClientCertFile",
+                                               config->client_cert_file);
+
+       if (config->private_key_file != NULL)
+               __connman_service_set_string(service, "PrivateKeyFile",
+                                               config->private_key_file);
+
+       if (g_strcmp0(config->private_key_passphrase_type, "fsid") == 0 &&
+                                       config->private_key_file != NULL) {
+               char *fsid;
+
+               fsid = config_pem_fsid(config->private_key_file);
+               if (fsid == NULL)
+                       return;
+
+               g_free(config->private_key_passphrase);
+               config->private_key_passphrase = fsid;
+       }
+
+       if (config->private_key_passphrase != NULL) {
+               __connman_service_set_string(service, "PrivateKeyPassphrase",
+                                               config->private_key_passphrase);
+               /*
+                * TODO: Support for PEAP with both identity and key passwd.
+                * In that case, we should check if both of them are found
+                * from the config file. If not, we should not set the
+                * service passphrase in order for the UI to request for an
+                * additional passphrase.
+                */
+       }
+
+       if (config->phase2 != NULL)
+               __connman_service_set_string(service, "Phase2", config->phase2);
+
+       if (config->passphrase != NULL)
+               __connman_service_set_string(service, "Passphrase", config->passphrase);
+
+       __connman_service_mark_dirty();
+
+       __connman_service_save(service);
+}
+
+int __connman_config_provision_service(struct connman_service *service)
+{
+       enum connman_service_type type;
+       GHashTableIter iter;
+       gpointer value, key;
+
+       DBG("service %p", service);
+
+       /* For now only WiFi services are supported */
+       type = connman_service_get_type(service);
+       if (type != CONNMAN_SERVICE_TYPE_WIFI)
+               return -ENOSYS;
+
+       g_hash_table_iter_init(&iter, config_table);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_config *config = value;
+
+               g_hash_table_foreach(config->service_table,
+                                               provision_service, service);
+       }
+
+       return 0;
+}
+
+int __connman_config_provision_service_ident(struct connman_service *service,
+                       const char *ident, const char *file, const char *entry)
+{
+       enum connman_service_type type;
+       struct connman_config *config;
+       int ret = 0;
+
+       DBG("service %p", service);
+
+       /* For now only WiFi services are supported */
+       type = connman_service_get_type(service);
+       if (type != CONNMAN_SERVICE_TYPE_WIFI)
+               return -ENOSYS;
+
+       config = g_hash_table_lookup(config_table, ident);
+       if(config != NULL) {
+               GHashTableIter iter;
+               gpointer value, key;
+               gboolean found = FALSE;
+
+               g_hash_table_iter_init(&iter, config->service_table);
+
+               /*
+                * Check if we need to remove individual service if it
+                * is missing from config file.
+                */
+               if (file != NULL && entry != NULL) {
+                       while (g_hash_table_iter_next(&iter, &key,
+                                                       &value) == TRUE) {
+                               struct connman_config_service *config = value;
+
+                               if (g_strcmp0(config->config_ident,
+                                                               file) == 0 &&
+                                               g_strcmp0(config->config_entry,
+                                                               entry) == 0) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+
+                       DBG("found %d ident %s file %s entry %s", found, ident,
+                                                               file, entry);
+
+                       if (found == FALSE) {
+                               /*
+                                * The entry+8 will skip "service_" prefix
+                                */
+                               g_hash_table_remove(config->service_table,
+                                               entry + 8);
+                               ret = 1;
+                       }
+               }
+
+               g_hash_table_foreach(config->service_table,
+                                               provision_service, service);
+       }
+
+       return ret;
+}
diff --git a/src/connection.c b/src/connection.c
new file mode 100644 (file)
index 0000000..d40f428
--- /dev/null
@@ -0,0 +1,1124 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BMW Car IT GmbH. 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 <errno.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+struct gateway_config {
+       gboolean active;
+       char *gateway;
+
+       /* VPN extra data */
+       gboolean vpn;
+       char *vpn_ip;
+       int vpn_phy_index;
+       char *vpn_phy_ip;
+};
+
+struct gateway_data {
+       int index;
+       struct connman_service *service;
+       unsigned int order;
+       struct gateway_config *ipv4_gateway;
+       struct gateway_config *ipv6_gateway;
+       connman_bool_t default_checked;
+};
+
+static GHashTable *gateway_hash = NULL;
+
+static struct gateway_config *find_gateway(int index, const char *gateway)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv4_gateway;
+
+               if (data->ipv6_gateway != NULL && data->index == index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data->ipv6_gateway;
+       }
+
+       return NULL;
+}
+
+static struct gateway_data *lookup_gateway_data(struct gateway_config *config)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (config == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway == config)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway == config)
+                       return data;
+       }
+
+       return NULL;
+}
+
+/*
+ * Find the gateway that is serving the VPN link
+ */
+static struct gateway_data *find_phy_gateway(int index, const char *gateway)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway == NULL)
+               return NULL;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv4_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+
+               if (data->ipv6_gateway != NULL && data->index != index &&
+                               g_str_equal(data->ipv6_gateway->gateway,
+                                       gateway) == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static void set_vpn_routes(struct gateway_data *new_gateway,
+                       struct connman_service *service,
+                       const char *gateway,
+                       enum connman_ipconfig_type type,
+                       const char *peer,
+                       struct gateway_data *active_gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       struct connman_ipconfig *ipconfig;
+       char *dest;
+       int index;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               ipconfig = __connman_service_get_ip4config(service);
+               config = new_gateway->ipv4_gateway;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               ipconfig = __connman_service_get_ip6config(service);
+               config = new_gateway->ipv6_gateway;
+       } else
+               return;
+
+       if (config == NULL)
+               goto done;
+
+       config->vpn = TRUE;
+       if (peer != NULL)
+               config->vpn_ip = g_strdup(peer);
+       else if (gateway != NULL)
+               config->vpn_ip = g_strdup(gateway);
+
+       index = __connman_ipconfig_get_index(ipconfig);
+       data = find_phy_gateway(index, gateway);
+
+       if (data == NULL)
+               goto done;
+
+       /*
+        * data->service points now to original
+        * service that is serving the VPN link
+        */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               ipconfig = __connman_service_get_ip4config(data->service);
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               ipconfig = __connman_service_get_ip6config(data->service);
+       else
+               return;
+
+       if (ipconfig != NULL) {
+               const char *address;
+
+               address = __connman_ipconfig_get_local(ipconfig);
+               config->vpn_phy_ip = g_strdup(address);
+       }
+
+       config->vpn_phy_index = data->index;
+
+       DBG("vpn %s phy %s index %d", config->vpn_ip,
+               config->vpn_phy_ip, config->vpn_phy_index);
+
+done:
+       if (active_gateway == NULL)
+               return;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               /*
+                * Special route to VPN server via gateway. This
+                * is needed so that we can access hosts behind
+                * the VPN. The route might already exist depending
+                * on network topology.
+                */
+               if (active_gateway->ipv4_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv4_gateway->gateway,
+                                                       "0.0.0.0") != 0)
+                       dest = active_gateway->ipv4_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_host_route(active_gateway->index, gateway,
+                                                                       dest);
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+
+               if (active_gateway->ipv6_gateway == NULL)
+                       return;
+
+               if (g_strcmp0(active_gateway->ipv6_gateway->gateway,
+                                                               "::") != 0)
+                       dest = active_gateway->ipv6_gateway->gateway;
+               else
+                       dest = NULL;
+
+               connman_inet_add_ipv6_host_route(active_gateway->index,
+                                                               gateway, dest);
+       }
+}
+
+static int del_routes(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
+{
+       int status4 = 0, status6 = 0;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL) {
+               if (data->ipv4_gateway->vpn == TRUE) {
+                       status4 = connman_inet_clear_gateway_address(
+                                               data->index,
+                                               data->ipv4_gateway->vpn_ip);
+
+               } else if (g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
+                       status4 = connman_inet_clear_gateway_interface(
+                                                               data->index);
+               } else {
+                       connman_inet_del_host_route(data->index,
+                                               data->ipv4_gateway->gateway);
+                       status4 = connman_inet_clear_gateway_address(
+                                               data->index,
+                                               data->ipv4_gateway->gateway);
+               }
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL) {
+               if (data->ipv6_gateway->vpn == TRUE) {
+                       status6 = connman_inet_clear_ipv6_gateway_address(
+                                               data->index,
+                                               data->ipv6_gateway->vpn_ip);
+
+               } else if (g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) {
+                       status6 = connman_inet_clear_ipv6_gateway_interface(
+                                                               data->index);
+               } else {
+                       connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->gateway);
+                       status6 = connman_inet_clear_ipv6_gateway_address(
+                                               data->index,
+                                               data->ipv6_gateway->gateway);
+               }
+       }
+
+       return (status4 < 0 ? status4 : status6);
+}
+
+static int disable_gateway(struct gateway_data *data,
+                       enum connman_ipconfig_type type)
+{
+       gboolean active = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (data->ipv4_gateway != NULL)
+                       active = data->ipv4_gateway->active;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (data->ipv6_gateway != NULL)
+                       active = data->ipv6_gateway->active;
+       } else
+               active = TRUE;
+
+       DBG("type %d active %d", type, active);
+
+       if (active == TRUE)
+               return del_routes(data, type);
+
+       return 0;
+}
+
+static struct gateway_data *add_gateway(struct connman_service *service,
+                                       int index, const char *gateway,
+                                       enum connman_ipconfig_type type)
+{
+       struct gateway_data *data, *old;
+       struct gateway_config *config;
+
+       if (gateway == NULL || strlen(gateway) == 0)
+               return NULL;
+
+       data = g_try_new0(struct gateway_data, 1);
+       if (data == NULL)
+               return NULL;
+
+       data->index = index;
+
+       config = g_try_new0(struct gateway_config, 1);
+       if (config == NULL) {
+               g_free(data);
+               return NULL;
+       }
+
+       config->gateway = g_strdup(gateway);
+       config->vpn_ip = NULL;
+       config->vpn_phy_ip = NULL;
+       config->vpn = FALSE;
+       config->vpn_phy_index = -1;
+       config->active = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               data->ipv4_gateway = config;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               data->ipv6_gateway = config;
+       else {
+               g_free(config->gateway);
+               g_free(config);
+               g_free(data);
+               return NULL;
+       }
+
+       data->service = service;
+
+       data->order = __connman_service_get_order(service);
+
+       /*
+        * If the service is already in the hash, then we
+        * must not replace it blindly but disable the gateway
+        * of the type we are replacing and take the other type
+        * from old gateway settings.
+        */
+       old = g_hash_table_lookup(gateway_hash, service);
+       if (old != NULL) {
+               DBG("Replacing gw %p ipv4 %p ipv6 %p", old,
+                       old->ipv4_gateway, old->ipv6_gateway);
+               disable_gateway(old, type);
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       data->ipv6_gateway = old->ipv6_gateway;
+                       old->ipv6_gateway = NULL;
+               } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+                       data->ipv4_gateway = old->ipv4_gateway;
+                       old->ipv4_gateway = NULL;
+               }
+       } else {
+               /*
+                * Only take a ref if we are adding new stuff to hash.
+                */
+               connman_service_ref(service);
+       }
+
+       g_hash_table_replace(gateway_hash, service, data);
+
+       return data;
+}
+
+static void set_default_gateway(struct gateway_data *data,
+                               enum connman_ipconfig_type type)
+{
+       int index;
+       int status4 = 0, status6 = 0;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
+
+       DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
+                                               data->ipv6_gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                                       data->ipv4_gateway->vpn == TRUE) {
+               connman_inet_set_gateway_address(data->index,
+                                               data->ipv4_gateway->vpn_ip);
+               connman_inet_add_host_route(data->index,
+                                       data->ipv4_gateway->vpn_ip, NULL);
+               data->ipv4_gateway->active = TRUE;
+
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
+               __connman_service_indicate_default(data->service);
+
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                                       data->ipv6_gateway->vpn == TRUE) {
+               connman_inet_set_ipv6_gateway_address(data->index,
+                                               data->ipv6_gateway->vpn_ip);
+               connman_inet_add_ipv6_host_route(data->index,
+                                       data->ipv6_gateway->vpn_ip, NULL);
+               data->ipv6_gateway->active = TRUE;
+
+               DBG("set %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
+               __connman_service_indicate_default(data->service);
+
+               return;
+       }
+
+       index = __connman_service_get_index(data->service);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
+               if (connman_inet_set_gateway_interface(index) < 0)
+                       return;
+               goto done;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       g_strcmp0(data->ipv6_gateway->gateway,
+                                                       "::") == 0) {
+               if (connman_inet_set_ipv6_gateway_interface(index) < 0)
+                       return;
+               goto done;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               status6 = connman_inet_set_ipv6_gateway_address(index,
+                                               data->ipv6_gateway->gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               status4 = connman_inet_set_gateway_address(index,
+                                               data->ipv4_gateway->gateway);
+
+       if (status4 < 0 || status6 < 0)
+               return;
+
+done:
+       __connman_service_indicate_default(data->service);
+}
+
+static void unset_default_gateway(struct gateway_data *data,
+                               enum connman_ipconfig_type type)
+{
+       int index;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
+
+       DBG("type %d gateway ipv4 %p ipv6 %p", type, data->ipv4_gateway,
+                                               data->ipv6_gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                                       data->ipv4_gateway->vpn == TRUE) {
+               connman_inet_del_host_route(data->index,
+                                               data->ipv4_gateway->vpn_ip);
+               connman_inet_clear_gateway_address(data->index,
+                                               data->ipv4_gateway->vpn_ip);
+               data->ipv4_gateway->active = FALSE;
+
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv4_gateway->vpn_ip,
+                       data->ipv4_gateway->vpn_phy_index,
+                       data->ipv4_gateway->vpn_phy_ip);
+
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                                       data->ipv6_gateway->vpn == TRUE) {
+               connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->vpn_ip);
+               connman_inet_clear_ipv6_gateway_address(data->index,
+                                               data->ipv6_gateway->vpn_ip);
+               data->ipv6_gateway->active = FALSE;
+
+               DBG("unset %p index %d vpn %s index %d phy %s",
+                       data, data->index, data->ipv6_gateway->vpn_ip,
+                       data->ipv6_gateway->vpn_phy_index,
+                       data->ipv6_gateway->vpn_phy_ip);
+
+               return;
+       }
+
+       index = __connman_service_get_index(data->service);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       g_strcmp0(data->ipv4_gateway->gateway,
+                                                       "0.0.0.0") == 0) {
+               connman_inet_clear_gateway_interface(index);
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       g_strcmp0(data->ipv6_gateway->gateway,
+                                                       "::") == 0) {
+               connman_inet_clear_ipv6_gateway_interface(index);
+               return;
+       }
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               connman_inet_clear_ipv6_gateway_address(index,
+                                               data->ipv6_gateway->gateway);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               connman_inet_clear_gateway_address(index,
+                                               data->ipv4_gateway->gateway);
+}
+
+static struct gateway_data *find_default_gateway(void)
+{
+       struct gateway_data *found = NULL;
+       unsigned int order = 0;
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (found == NULL || data->order > order) {
+                       found = data;
+                       order = data->order;
+
+                       DBG("default %p order %d", found, order);
+               }
+       }
+
+       return found;
+}
+
+static gboolean choose_default_gateway(struct gateway_data *data,
+                                       struct gateway_data *candidate)
+{
+       gboolean downgraded = FALSE;
+
+       /*
+        * If the current default is not active, then we mark
+        * this one as default. If the other one is already active
+        * we mark this one as non default.
+        */
+       if (data->ipv4_gateway != NULL) {
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == FALSE) {
+                       DBG("ipv4 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               }
+               if (candidate->ipv4_gateway != NULL &&
+                               candidate->ipv4_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv4 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+                       downgraded = TRUE;
+               }
+       }
+
+       if (data->ipv6_gateway != NULL) {
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == FALSE) {
+                       DBG("ipv6 downgrading %p", candidate);
+                       unset_default_gateway(candidate,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               }
+
+               if (candidate->ipv6_gateway != NULL &&
+                               candidate->ipv6_gateway->active == TRUE &&
+                               candidate->order > data->order) {
+                       DBG("ipv6 downgrading this %p", data);
+                       unset_default_gateway(data,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+                       downgraded = TRUE;
+               }
+       }
+
+       return downgraded;
+}
+
+static void connection_newgateway(int index, const char *gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+       GHashTableIter iter;
+       gpointer value, key;
+       gboolean found = FALSE;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       config = find_gateway(index, gateway);
+       if (config == NULL)
+               return;
+
+       config->active = TRUE;
+
+       /*
+        * It is possible that we have two default routes atm
+        * if there are two gateways waiting rtnl activation at the
+        * same time.
+        */
+       data = lookup_gateway_data(config);
+       if (data == NULL)
+               return;
+
+       if (data->default_checked == TRUE)
+               return;
+
+       /*
+        * The next checks are only done once, otherwise setting
+        * the default gateway could lead into rtnl forever loop.
+        */
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *candidate = value;
+
+               if (candidate == data)
+                       continue;
+
+               found = choose_default_gateway(data, candidate);
+               if (found == TRUE)
+                       break;
+       }
+
+       if (found == FALSE) {
+               if (data->ipv4_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (data->ipv6_gateway != NULL)
+                       set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
+       data->default_checked = TRUE;
+}
+
+static void remove_gateway(gpointer user_data)
+{
+       struct gateway_data *data = user_data;
+
+       DBG("gateway ipv4 %p ipv6 %p", data->ipv4_gateway, data->ipv6_gateway);
+
+       if (data->ipv4_gateway != NULL) {
+               g_free(data->ipv4_gateway->gateway);
+               g_free(data->ipv4_gateway->vpn_ip);
+               g_free(data->ipv4_gateway->vpn_phy_ip);
+               g_free(data->ipv4_gateway);
+       }
+
+       if (data->ipv6_gateway != NULL) {
+               g_free(data->ipv6_gateway->gateway);
+               g_free(data->ipv6_gateway->vpn_ip);
+               g_free(data->ipv6_gateway->vpn_phy_ip);
+               g_free(data->ipv6_gateway);
+       }
+
+       g_free(data);
+}
+
+static void connection_delgateway(int index, const char *gateway)
+{
+       struct gateway_config *config;
+       struct gateway_data *data;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       config = find_gateway(index, gateway);
+       if (config != NULL)
+               config->active = FALSE;
+
+       data = find_default_gateway();
+       if (data != NULL)
+               set_default_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
+}
+
+static struct connman_rtnl connection_rtnl = {
+       .name           = "connection",
+       .newgateway     = connection_newgateway,
+       .delgateway     = connection_delgateway,
+};
+
+static struct gateway_data *find_active_gateway(void)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       DBG("");
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->active == TRUE)
+                       return data;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->active == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static void update_order(void)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       DBG("");
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               data->order = __connman_service_get_order(data->service);
+       }
+}
+
+void __connman_connection_gateway_activate(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct gateway_data *data = NULL;
+
+       data = g_hash_table_lookup(gateway_hash, service);
+       if (data == NULL)
+               return;
+
+       DBG("gateway %p/%p type %d", data->ipv4_gateway,
+                                       data->ipv6_gateway, type);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               data->ipv4_gateway->active = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               data->ipv6_gateway->active = TRUE;
+}
+
+static void add_host_route(int family, int index, const char *gateway,
+                       enum connman_service_type service_type)
+{
+       switch (family) {
+       case AF_INET:
+               if (g_strcmp0(gateway, "0.0.0.0") != 0) {
+                       /*
+                        * We must not set route to the phy dev gateway in
+                        * VPN link. The packets to VPN link might be routed
+                        * back to itself and not routed into phy link gateway.
+                        */
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_host_route(index, gateway,
+                                                                       NULL);
+               } else {
+                       /*
+                        * Add host route to P-t-P link so that services can
+                        * be moved around and we can have some link to P-t-P
+                        * network (although those P-t-P links have limited
+                        * usage if default route is not directed to them)
+                        */
+                       char *dest;
+                       if (connman_inet_get_dest_addr(index, &dest) == 0) {
+                               connman_inet_add_host_route(index, dest, NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+
+       case AF_INET6:
+               if (g_strcmp0(gateway, "::") != 0) {
+                       if (service_type != CONNMAN_SERVICE_TYPE_VPN)
+                               connman_inet_add_ipv6_host_route(index,
+                                                               gateway, NULL);
+               } else {
+                       /* P-t-P link, add route to destination */
+                       char *dest;
+                       if (connman_inet_ipv6_get_dest_addr(index,
+                                                               &dest) == 0) {
+                               connman_inet_add_ipv6_host_route(index, dest,
+                                                               NULL);
+                               g_free(dest);
+                       }
+               }
+               break;
+       }
+}
+
+int __connman_connection_gateway_add(struct connman_service *service,
+                                       const char *gateway,
+                                       enum connman_ipconfig_type type,
+                                       const char *peer)
+{
+       struct gateway_data *active_gateway = NULL;
+       struct gateway_data *new_gateway = NULL;
+       enum connman_ipconfig_type type4 = CONNMAN_IPCONFIG_TYPE_UNKNOWN,
+               type6 = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+       enum connman_service_type service_type =
+                                       connman_service_get_type(service);
+       int index;
+
+       index = __connman_service_get_index(service);
+
+       /*
+        * If gateway is NULL, it's a point to point link and the default
+        * gateway for ipv4 is 0.0.0.0 and for ipv6 is ::, meaning the
+        * interface
+        */
+       if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               gateway = "0.0.0.0";
+
+       if (gateway == NULL && type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               gateway = "::";
+
+       DBG("service %p index %d gateway %s vpn ip %s type %d",
+               service, index, gateway, peer, type);
+
+       new_gateway = add_gateway(service, index, gateway, type);
+       if (new_gateway == NULL)
+               return -EINVAL;
+
+       active_gateway = find_active_gateway();
+
+       DBG("active %p index %d new %p", active_gateway,
+               active_gateway ? active_gateway->index : -1, new_gateway);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                               new_gateway->ipv4_gateway != NULL) {
+               add_host_route(AF_INET, index, gateway, service_type);
+               __connman_service_nameserver_add_routes(service,
+                                       new_gateway->ipv4_gateway->gateway);
+               type4 = CONNMAN_IPCONFIG_TYPE_IPV4;
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               new_gateway->ipv6_gateway != NULL) {
+               add_host_route(AF_INET6, index, gateway, service_type);
+               __connman_service_nameserver_add_routes(service,
+                                       new_gateway->ipv6_gateway->gateway);
+               type6 = CONNMAN_IPCONFIG_TYPE_IPV6;
+       }
+
+       if (service_type == CONNMAN_SERVICE_TYPE_VPN) {
+
+               set_vpn_routes(new_gateway, service, gateway, type, peer,
+                                                       active_gateway);
+
+       } else {
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                                       new_gateway->ipv4_gateway != NULL)
+                       new_gateway->ipv4_gateway->vpn = FALSE;
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                                       new_gateway->ipv6_gateway != NULL)
+                       new_gateway->ipv6_gateway->vpn = FALSE;
+       }
+
+       if (active_gateway == NULL) {
+               set_default_gateway(new_gateway, type);
+               goto done;
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                               new_gateway->ipv4_gateway != NULL &&
+                               new_gateway->ipv4_gateway->vpn == TRUE) {
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_gateway_address(
+                                       active_gateway->index,
+                                       active_gateway->ipv4_gateway->gateway);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                               new_gateway->ipv6_gateway != NULL &&
+                               new_gateway->ipv6_gateway->vpn == TRUE) {
+               if (__connman_service_is_split_routing(new_gateway->service) ==
+                                                                       FALSE)
+                       connman_inet_clear_ipv6_gateway_address(
+                                       active_gateway->index,
+                                       active_gateway->ipv6_gateway->gateway);
+       }
+
+done:
+       if (type4 == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (type6 == CONNMAN_IPCONFIG_TYPE_IPV6)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       return 0;
+}
+
+void __connman_connection_gateway_remove(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct gateway_data *data = NULL;
+       gboolean set_default4 = FALSE, set_default6 = FALSE;
+       int do_ipv4 = FALSE, do_ipv6 = FALSE;
+       int err;
+
+       DBG("service %p type %d", service, type);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               do_ipv4 = TRUE;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               do_ipv6 = TRUE;
+       else
+               do_ipv4 = do_ipv6 = TRUE;
+
+       __connman_service_nameserver_del_routes(service, type);
+
+       data = g_hash_table_lookup(gateway_hash, service);
+       if (data == NULL)
+               return;
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL)
+               set_default4 = data->ipv4_gateway->vpn;
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL)
+               set_default6 = data->ipv6_gateway->vpn;
+
+       DBG("ipv4 gateway %s ipv6 gateway %s vpn %d/%d",
+               data->ipv4_gateway ? data->ipv4_gateway->gateway : "<null>",
+               data->ipv6_gateway ? data->ipv6_gateway->gateway : "<null>",
+               set_default4, set_default6);
+
+       if (do_ipv4 == TRUE && data->ipv4_gateway != NULL &&
+                       data->ipv4_gateway->vpn == TRUE && data->index >= 0)
+               connman_inet_del_host_route(data->index,
+                                               data->ipv4_gateway->gateway);
+
+       if (do_ipv6 == TRUE && data->ipv6_gateway != NULL &&
+                       data->ipv6_gateway->vpn == TRUE && data->index >= 0)
+               connman_inet_del_ipv6_host_route(data->index,
+                                               data->ipv6_gateway->gateway);
+
+       err = disable_gateway(data, type);
+
+       /*
+        * We remove the service from the hash only if all the gateway
+        * settings are to be removed.
+        */
+       if (do_ipv4 == do_ipv6 ||
+               (data->ipv4_gateway != NULL && data->ipv6_gateway == NULL
+                       && do_ipv4 == TRUE) ||
+               (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
+                       && do_ipv6 == TRUE)
+               ) {
+               connman_service_unref(service);
+               g_hash_table_remove(gateway_hash, service);
+       } else
+               DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
+                       data->ipv4_gateway, do_ipv4,
+                       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
+        * gateway delete notification.
+        * We hit the same issue if remove_gateway() fails.
+        */
+       if (set_default4 || set_default6 || err < 0) {
+               data = find_default_gateway();
+               if (data != NULL)
+                       set_default_gateway(data, type);
+       }
+}
+
+gboolean __connman_connection_update_gateway(void)
+{
+       struct gateway_data *default_gateway;
+       gboolean updated = FALSE;
+       GHashTableIter iter;
+       gpointer value, key;
+
+       if (gateway_hash == NULL)
+               return updated;
+
+       update_order();
+
+       default_gateway = find_default_gateway();
+
+       __connman_service_update_ordering();
+
+       DBG("default %p", default_gateway);
+
+       /*
+        * There can be multiple active gateways so we need to
+        * check them all.
+        */
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *active_gateway = value;
+
+               if (active_gateway == default_gateway)
+                       continue;
+
+               if (active_gateway->ipv4_gateway != NULL &&
+                               active_gateway->ipv4_gateway->active == TRUE) {
+
+                       unset_default_gateway(active_gateway,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+                       updated = TRUE;
+               }
+
+               if (active_gateway->ipv6_gateway != NULL &&
+                               active_gateway->ipv6_gateway->active == TRUE) {
+
+                       unset_default_gateway(active_gateway,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+                       updated = TRUE;
+               }
+       }
+
+       if (updated && default_gateway != NULL) {
+               if (default_gateway->ipv4_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               if (default_gateway->ipv6_gateway)
+                       set_default_gateway(default_gateway,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
+       return updated;
+}
+
+int __connman_connection_get_vpn_index(int phy_index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               if (data->ipv4_gateway != NULL &&
+                               data->ipv4_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+
+               if (data->ipv6_gateway != NULL &&
+                               data->ipv6_gateway->vpn_phy_index == phy_index)
+                       return data->index;
+       }
+
+       return -1;
+}
+
+int __connman_connection_init(void)
+{
+       int err;
+
+       DBG("");
+
+       gateway_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, remove_gateway);
+
+       err = connman_rtnl_register(&connection_rtnl);
+       if (err < 0)
+               connman_error("Failed to setup RTNL gateway driver");
+
+       return err;
+}
+
+void __connman_connection_cleanup(void)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       DBG("");
+
+       connman_rtnl_unregister(&connection_rtnl);
+
+       g_hash_table_iter_init(&iter, gateway_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct gateway_data *data = value;
+
+               disable_gateway(data, CONNMAN_IPCONFIG_TYPE_ALL);
+       }
+
+       g_hash_table_destroy(gateway_hash);
+       gateway_hash = NULL;
+}
diff --git a/src/connman-dbus.conf b/src/connman-dbus.conf
new file mode 100644 (file)
index 0000000..42e473c
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="0">
+        <allow own="net.connman"/>
+        <allow send_destination="net.connman"/>
+        <allow send_interface="net.connman.Agent"/>
+        <allow send_interface="net.connman.Counter"/>
+        <allow send_interface="net.connman.Notification"/>
+    </policy>
+    <policy user="5000">
+        <allow own="net.connman"/>
+        <allow send_destination="net.connman"/>
+        <allow send_interface="net.connman.Agent"/>
+        <allow send_interface="net.connman.Counter"/>
+        <allow send_interface="net.connman.Notification"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="net.connman"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="net.connman"/>
+    </policy>
+</busconfig>
diff --git a/src/connman-polkit.conf b/src/connman-polkit.conf
new file mode 100644 (file)
index 0000000..fdddb29
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="net.connman"/>
+        <allow send_interface="net.connman.Agent"/>
+    </policy>
+    <policy context="default">
+        <allow send_destination="net.connman"/>
+    </policy>
+</busconfig>
diff --git a/src/connman.h b/src/connman.h
new file mode 100644 (file)
index 0000000..5d14ea9
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+
+#include <connman/dbus.h>
+
+dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
+               connman_dbus_append_cb_t function, void *user_data);
+int __connman_dbus_init(DBusConnection *conn);
+void __connman_dbus_cleanup(void);
+
+DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum);
+DBusMessage *__connman_error_invalid_arguments(DBusMessage *msg);
+DBusMessage *__connman_error_permission_denied(DBusMessage *msg);
+DBusMessage *__connman_error_passphrase_required(DBusMessage *msg);
+DBusMessage *__connman_error_not_registered(DBusMessage *msg);
+DBusMessage *__connman_error_not_unique(DBusMessage *msg);
+DBusMessage *__connman_error_not_supported(DBusMessage *msg);
+DBusMessage *__connman_error_not_implemented(DBusMessage *msg);
+DBusMessage *__connman_error_not_found(DBusMessage *msg);
+DBusMessage *__connman_error_no_carrier(DBusMessage *msg);
+DBusMessage *__connman_error_in_progress(DBusMessage *msg);
+DBusMessage *__connman_error_already_exists(DBusMessage *msg);
+DBusMessage *__connman_error_already_enabled(DBusMessage *msg);
+DBusMessage *__connman_error_already_disabled(DBusMessage *msg);
+DBusMessage *__connman_error_already_connected(DBusMessage *msg);
+DBusMessage *__connman_error_not_connected(DBusMessage *msg);
+DBusMessage *__connman_error_operation_aborted(DBusMessage *msg);
+DBusMessage *__connman_error_operation_timeout(DBusMessage *msg);
+DBusMessage *__connman_error_invalid_service(DBusMessage *msg);
+DBusMessage *__connman_error_invalid_property(DBusMessage *msg);
+
+#include <connman/types.h>
+
+int __connman_manager_init(void);
+void __connman_manager_cleanup(void);
+
+int __connman_clock_init(void);
+void __connman_clock_cleanup(void);
+
+void __connman_clock_update_timezone(void);
+
+int __connman_timezone_init(void);
+void __connman_timezone_cleanup(void);
+
+char *__connman_timezone_lookup(void);
+int __connman_timezone_change(const char *zone);
+
+int __connman_agent_init(void);
+void __connman_agent_cleanup(void);
+
+int __connman_agent_register(const char *sender, const char *path);
+int __connman_agent_unregister(const char *sender, const char *path);
+
+void __connman_counter_send_usage(const char *path,
+                                       DBusMessage *message);
+int __connman_counter_register(const char *owner, const char *path,
+                                               unsigned int interval);
+int __connman_counter_unregister(const char *owner, const char *path);
+
+int __connman_counter_init(void);
+void __connman_counter_cleanup(void);
+
+struct connman_service;
+
+int __connman_service_add_passphrase(struct connman_service *service,
+                                       const gchar *passphrase);
+typedef void (* authentication_cb_t) (struct connman_service *service,
+                               connman_bool_t values_received,
+                               const char *name, int name_len,
+                               const char *identifier, const char *secret,
+                               gboolean wps, const char *wpspin,
+                               const char *error, void *user_data);
+typedef void (* browser_authentication_cb_t) (struct connman_service *service,
+                               connman_bool_t authentication_done,
+                               const char *error, void *user_data);
+typedef void (* report_error_cb_t) (struct connman_service *service,
+                               gboolean retry, void *user_data);
+int __connman_agent_request_passphrase_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data);
+int __connman_agent_request_login_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data);
+int __connman_agent_request_browser(struct connman_service *service,
+                               browser_authentication_cb_t callback,
+                               const char *url, void *user_data);
+int __connman_agent_report_error(struct connman_service *service,
+                               const char *error,
+                               report_error_cb_t callback, void *user_data);
+
+
+#include <connman/log.h>
+
+int __connman_log_init(const char *program, const char *debug,
+                                               connman_bool_t detach);
+void __connman_log_cleanup(void);
+void __connman_log_enable(struct connman_debug_desc *start,
+                                       struct connman_debug_desc *stop);
+
+#include <connman/option.h>
+
+#include <connman/setting.h>
+
+#include <connman/plugin.h>
+
+int __connman_plugin_init(const char *pattern, const char *exclude);
+void __connman_plugin_cleanup(void);
+
+#include <connman/task.h>
+
+int __connman_task_init(void);
+void __connman_task_cleanup(void);
+
+#include <connman/inet.h>
+
+int __connman_inet_modify_address(int cmd, int flags, int index, int family,
+                               const char *address,
+                               const char *peer,
+                               unsigned char prefixlen,
+                               const char *broadcast);
+
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+typedef void (*__connman_inet_rs_cb_t) (struct nd_router_advert *reply,
+                                       unsigned int length, void *user_data);
+
+int __connman_inet_ipv6_send_rs(int index, int timeout,
+                       __connman_inet_rs_cb_t callback, void *user_data);
+
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index);
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+                                       unsigned int length);
+
+struct __connman_inet_rtnl_handle {
+       int                     fd;
+       struct sockaddr_nl      local;
+       struct sockaddr_nl      peer;
+       __u32                   seq;
+       __u32                   dump;
+
+       struct {
+               struct nlmsghdr n;
+               union {
+                       struct {
+                               struct rtmsg rt;
+                       } r;
+                       struct {
+                               struct ifaddrmsg ifa;
+                       } i;
+               } u;
+               char buf[1024];
+       } req;
+};
+
+int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth);
+typedef void (*__connman_inet_rtnl_cb_t) (struct nlmsghdr *answer,
+                                       void *user_data);
+int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
+                       struct nlmsghdr *n, int timeout,
+                       __connman_inet_rtnl_cb_t callback, void *user_data);
+static inline
+int __connman_inet_rtnl_send(struct __connman_inet_rtnl_handle *rtnl,
+                                               struct nlmsghdr *n)
+{
+       return __connman_inet_rtnl_talk(rtnl, n, 0, NULL, NULL);
+}
+
+void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth);
+int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length,
+                       int type, const void *data, size_t data_length);
+int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen,
+                       int type, __u32 data);
+
+#include <connman/resolver.h>
+
+int __connman_resolver_init(connman_bool_t dnsproxy);
+void __connman_resolver_cleanup(void);
+int __connman_resolvfile_append(const char *interface, const char *domain, const char *server);
+int __connman_resolvfile_remove(const char *interface, const char *domain, const char *server);
+int __connman_resolver_redo_servers(const char *interface);
+
+void __connman_storage_migrate(void);
+GKeyFile *__connman_storage_open_global();
+GKeyFile *__connman_storage_load_global();
+int __connman_storage_save_global(GKeyFile *keyfile);
+void __connman_storage_delete_global();
+
+GKeyFile *__connman_storage_load_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);
+char **__connman_storage_get_providers(void);
+gboolean __connman_storage_remove_service(const char *service_id);
+
+int __connman_detect_init(void);
+void __connman_detect_cleanup(void);
+
+#include <connman/proxy.h>
+
+int __connman_proxy_init(void);
+void __connman_proxy_cleanup(void);
+
+#include <connman/ipconfig.h>
+
+struct connman_ipaddress {
+       int family;
+       unsigned char prefixlen;
+       char *local;
+       char *peer;
+       char *broadcast;
+       char *gateway;
+};
+
+struct connman_ipconfig_ops {
+       void (*up) (struct connman_ipconfig *ipconfig);
+       void (*down) (struct connman_ipconfig *ipconfig);
+       void (*lower_up) (struct connman_ipconfig *ipconfig);
+       void (*lower_down) (struct connman_ipconfig *ipconfig);
+       void (*ip_bound) (struct connman_ipconfig *ipconfig);
+       void (*ip_release) (struct connman_ipconfig *ipconfig);
+       void (*route_set) (struct connman_ipconfig *ipconfig);
+       void (*route_unset) (struct connman_ipconfig *ipconfig);
+};
+
+struct connman_ipconfig *__connman_ipconfig_create(int index,
+                                       enum connman_ipconfig_type type);
+
+#define __connman_ipconfig_ref(ipconfig) \
+       __connman_ipconfig_ref_debug(ipconfig, __FILE__, __LINE__, __func__)
+#define __connman_ipconfig_unref(ipconfig) \
+       __connman_ipconfig_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
+
+struct connman_ipconfig *
+__connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig,
+                       const char *file, int line, const char *caller);
+void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig,
+                       const char *file, int line, const char *caller);
+
+void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data);
+
+int __connman_ipconfig_get_index(struct connman_ipconfig *ipconfig);
+const char *__connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig);
+
+void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
+                               const struct connman_ipconfig_ops *ops);
+int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
+                                       enum connman_ipconfig_method method);
+void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
+
+int __connman_ipconfig_init(void);
+void __connman_ipconfig_cleanup(void);
+
+struct rtnl_link_stats;
+
+void __connman_ipconfig_newlink(int index, unsigned short type,
+                               unsigned int flags, const char *address,
+                                                       unsigned short mtu,
+                                               struct rtnl_link_stats *stats);
+void __connman_ipconfig_dellink(int index, struct rtnl_link_stats *stats);
+void __connman_ipconfig_newaddr(int index, int family, const char *label,
+                               unsigned char prefixlen, const char *address);
+void __connman_ipconfig_deladdr(int index, int family, const char *label,
+                               unsigned char prefixlen, const char *address);
+void __connman_ipconfig_newroute(int index, int family, unsigned char scope,
+                                       const char *dst, const char *gateway);
+void __connman_ipconfig_delroute(int index, int family, unsigned char scope,
+                                       const char *dst, const char *gateway);
+
+void __connman_ipconfig_foreach(void (*function) (int index, void *user_data),
+                                                       void *user_data);
+enum connman_ipconfig_type __connman_ipconfig_get_config_type(
+                                       struct connman_ipconfig *ipconfig);
+unsigned short __connman_ipconfig_get_type_from_index(int index);
+unsigned int __connman_ipconfig_get_flags_from_index(int index);
+const char *__connman_ipconfig_get_gateway_from_index(int index,
+       enum connman_ipconfig_type type);
+void __connman_ipconfig_set_index(struct connman_ipconfig *ipconfig, int index);
+
+const char *__connman_ipconfig_get_local(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_local(struct connman_ipconfig *ipconfig, const char *address);
+const char *__connman_ipconfig_get_peer(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_peer(struct connman_ipconfig *ipconfig, const char *address);
+const char *__connman_ipconfig_get_broadcast(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_broadcast(struct connman_ipconfig *ipconfig, const char *broadcast);
+const char *__connman_ipconfig_get_gateway(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig, const char *gateway);
+unsigned char __connman_ipconfig_get_prefixlen(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig, unsigned char prefixlen);
+
+int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig);
+
+const char *__connman_ipconfig_method2string(enum connman_ipconfig_method method);
+const char *__connman_ipconfig_type2string(enum connman_ipconfig_type type);
+enum connman_ipconfig_method __connman_ipconfig_string2method(const char *method);
+
+void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter);
+void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter);
+void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
+                                       DBusMessageIter *iter,
+                                       struct connman_ipconfig *ip4config);
+void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter);
+int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *array);
+void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter);
+enum connman_ipconfig_method __connman_ipconfig_get_method(
+                               struct connman_ipconfig *ipconfig);
+
+int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig);
+int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_gateway_remove(struct connman_ipconfig *ipconfig);
+unsigned char __connman_ipconfig_netmask_prefix_len(const char *netmask);
+
+int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig,
+                                                       const char *url);
+const char *__connman_ipconfig_get_proxy_autoconfig(struct connman_ipconfig *ipconfig);
+void __connman_ipconfig_set_dhcp_address(struct connman_ipconfig *ipconfig,
+                                       const char *address);
+char *__connman_ipconfig_get_dhcp_address(struct connman_ipconfig *ipconfig);
+
+int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
+               GKeyFile *keyfile, const char *identifier, const char *prefix);
+int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
+               GKeyFile *keyfile, const char *identifier, const char *prefix);
+gboolean __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig);
+
+int __connman_ipconfig_set_rp_filter();
+void __connman_ipconfig_unset_rp_filter(int old_value);
+
+#include <connman/utsname.h>
+
+int __connman_utsname_set_hostname(const char *hostname);
+int __connman_utsname_set_domainname(const char *domainname);
+
+#include <connman/timeserver.h>
+
+int __connman_timeserver_init(void);
+void __connman_timeserver_cleanup(void);
+
+char **__connman_timeserver_system_get();
+
+GSList *__connman_timeserver_add_list(GSList *server_list,
+               const char *timeserver);
+GSList *__connman_timeserver_get_all(struct connman_service *service);
+int __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_sync_next();
+
+typedef void (* dhcp_cb) (struct connman_network *network,
+                               connman_bool_t success);
+int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback);
+void __connman_dhcp_stop(struct connman_network *network);
+int __connman_dhcp_init(void);
+void __connman_dhcp_cleanup(void);
+int __connman_dhcpv6_init(void);
+void __connman_dhcpv6_cleanup(void);
+int __connman_dhcpv6_start_info(struct connman_network *network,
+                               dhcp_cb callback);
+void __connman_dhcpv6_stop(struct connman_network *network);
+int __connman_dhcpv6_start(struct connman_network *network,
+                               GSList *prefixes, dhcp_cb callback);
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                               dhcp_cb callback);
+int __connman_dhcpv6_start_release(struct connman_network *network,
+                               dhcp_cb callback);
+
+int __connman_ipv4_init(void);
+void __connman_ipv4_cleanup(void);
+
+int __connman_connection_init(void);
+void __connman_connection_cleanup(void);
+
+int __connman_connection_gateway_add(struct connman_service *service,
+                                       const char *gateway,
+                                       enum connman_ipconfig_type type,
+                                       const char *peer);
+void __connman_connection_gateway_remove(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+int __connman_connection_get_vpn_index(int phy_index);
+
+gboolean __connman_connection_update_gateway(void);
+void __connman_connection_gateway_activate(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+
+int __connman_ntp_start(char *server);
+void __connman_ntp_stop();
+
+int __connman_wpad_init(void);
+void __connman_wpad_cleanup(void);
+int __connman_wpad_start(struct connman_service *service);
+void __connman_wpad_stop(struct connman_service *service);
+
+int __connman_wispr_init(void);
+void __connman_wispr_cleanup(void);
+int __connman_wispr_start(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+void __connman_wispr_stop(struct connman_service *service);
+
+#include <connman/technology.h>
+
+void __connman_technology_list_struct(DBusMessageIter *array);
+
+int __connman_technology_add_device(struct connman_device *device);
+int __connman_technology_remove_device(struct connman_device *device);
+int __connman_technology_enabled(enum connman_service_type type);
+int __connman_technology_disabled(enum connman_service_type type);
+int __connman_technology_set_offlinemode(connman_bool_t offlinemode);
+connman_bool_t __connman_technology_get_offlinemode(void);
+void __connman_technology_set_connected(enum connman_service_type type,
+                                       connman_bool_t connected);
+
+int __connman_technology_add_rfkill(unsigned int index,
+                                       enum connman_service_type type,
+                                               connman_bool_t softblock,
+                                               connman_bool_t hardblock);
+int __connman_technology_update_rfkill(unsigned int index,
+                                       enum connman_service_type type,
+                                               connman_bool_t softblock,
+                                               connman_bool_t hardblock);
+int __connman_technology_remove_rfkill(unsigned int index,
+                                       enum connman_service_type type);
+
+void __connman_technology_scan_started(struct connman_device *device);
+void __connman_technology_scan_stopped(struct connman_device *device);
+void __connman_technology_add_interface(enum connman_service_type type,
+                               int index, const char *name, const char *ident);
+void __connman_technology_remove_interface(enum connman_service_type type,
+                               int index, const char *name, const char *ident);
+
+#include <connman/device.h>
+
+int __connman_device_init(const char *device, const char *nodevice);
+void __connman_device_cleanup(void);
+
+void __connman_device_list(DBusMessageIter *iter, void *user_data);
+
+enum connman_service_type __connman_device_get_service_type(struct connman_device *device);
+struct connman_device *__connman_device_find_device(enum connman_service_type type);
+int __connman_device_request_scan(enum connman_service_type type);
+int __connman_device_request_hidden_scan(struct connman_device *device,
+                               const char *ssid, unsigned int ssid_len,
+                               const char *identity, const char *passphrase,
+                               gpointer user_data);
+
+connman_bool_t __connman_device_isfiltered(const char *devname);
+
+int __connman_device_get_phyindex(struct connman_device *device);
+void __connman_device_set_phyindex(struct connman_device *device,
+                                                       int phyindex);
+
+void __connman_device_set_network(struct connman_device *device,
+                                       struct connman_network *network);
+void __connman_device_cleanup_networks(struct connman_device *device);
+
+int __connman_device_enable(struct connman_device *device);
+int __connman_device_disable(struct connman_device *device);
+int __connman_device_disconnect(struct connman_device *device);
+
+connman_bool_t __connman_device_has_driver(struct connman_device *device);
+
+void __connman_device_set_reconnect(struct connman_device *device,
+                                               connman_bool_t reconnect);
+connman_bool_t __connman_device_get_reconnect(struct connman_device *device);
+
+const char *__connman_device_get_type(struct connman_device *device);
+
+int __connman_rfkill_init(void);
+void __connman_rfkill_cleanup(void);
+int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block);
+
+#include <connman/network.h>
+
+int __connman_network_init(void);
+void __connman_network_cleanup(void);
+
+void __connman_network_set_device(struct connman_network *network,
+                                       struct connman_device *device);
+
+int __connman_network_connect(struct connman_network *network);
+int __connman_network_disconnect(struct connman_network *network);
+int __connman_network_clear_ipconfig(struct connman_network *network,
+                                       struct connman_ipconfig *ipconfig);
+int __connman_network_set_ipconfig(struct connman_network *network,
+                               struct connman_ipconfig *ipconfig_ipv4,
+                               struct connman_ipconfig *ipconfig_ipv6);
+
+const char *__connman_network_get_type(struct connman_network *network);
+const char *__connman_network_get_group(struct connman_network *network);
+const char *__connman_network_get_ident(struct connman_network *network);
+connman_bool_t __connman_network_get_weakness(struct connman_network *network);
+
+int __connman_config_init();
+void __connman_config_cleanup(void);
+
+int __connman_config_load_service(GKeyFile *keyfile, const char *group, connman_bool_t persistent);
+int __connman_config_provision_service(struct connman_service *service);
+int __connman_config_provision_service_ident(struct connman_service *service,
+               const char *ident, const char *file, const char *entry);
+
+int __connman_tethering_init(void);
+void __connman_tethering_cleanup(void);
+
+const char *__connman_tethering_get_bridge(void);
+void __connman_tethering_set_enabled(void);
+void __connman_tethering_set_disabled(void);
+
+int __connman_private_network_request(DBusMessage *msg, const char *owner);
+int __connman_private_network_release(const char *path);
+
+#include <connman/provider.h>
+
+connman_bool_t __connman_provider_check_routes(struct connman_provider *provider);
+int __connman_provider_append_user_route(struct connman_provider *provider,
+                       int family, const char *network, const char *netmask);
+void __connman_provider_append_properties(struct connman_provider *provider, DBusMessageIter *iter);
+void __connman_provider_list(DBusMessageIter *iter, void *user_data);
+int __connman_provider_create_and_connect(DBusMessage *msg);
+const char * __connman_provider_get_ident(struct connman_provider *provider);
+int __connman_provider_indicate_state(struct connman_provider *provider,
+                                       enum connman_provider_state state);
+int __connman_provider_indicate_error(struct connman_provider *provider,
+                                       enum connman_provider_error error);
+int __connman_provider_connect(struct connman_provider *provider);
+int __connman_provider_disconnect(struct connman_provider *provider);
+int __connman_provider_remove(const char *path);
+void __connman_provider_cleanup(void);
+int __connman_provider_init(void);
+
+#include <connman/service.h>
+
+int __connman_service_init(void);
+void __connman_service_cleanup(void);
+
+void __connman_service_list_struct(DBusMessageIter *iter);
+
+struct connman_service *__connman_service_lookup_from_network(struct connman_network *network);
+struct connman_service *__connman_service_lookup_from_index(int index);
+struct connman_service *__connman_service_lookup_from_ident(const char *identifier);
+struct connman_service *__connman_service_create_from_network(struct connman_network *network);
+struct connman_service *__connman_service_create_from_provider(struct connman_provider *provider);
+struct connman_service *__connman_service_get_default(void);
+void __connman_service_update_from_network(struct connman_network *network);
+void __connman_service_remove_from_network(struct connman_network *network);
+void __connman_service_read_ip4config(struct connman_service *service);
+void __connman_service_create_ip4config(struct connman_service *service,
+                                                               int index);
+void __connman_service_read_ip6config(struct connman_service *service);
+void __connman_service_create_ip6config(struct connman_service *service,
+                                                               int index);
+struct connman_ipconfig *__connman_service_get_ip4config(
+                               struct connman_service *service);
+struct connman_ipconfig *__connman_service_get_ip6config(
+                               struct connman_service *service);
+struct connman_ipconfig *__connman_service_get_ipconfig(
+                               struct connman_service *service, int family);
+connman_bool_t __connman_service_is_connected_state(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+const char *__connman_service_get_ident(struct connman_service *service);
+const char *__connman_service_get_path(struct connman_service *service);
+unsigned int __connman_service_get_order(struct connman_service *service);
+void __connman_service_update_ordering(void);
+struct connman_network *__connman_service_get_network(struct connman_service *service);
+enum connman_service_security __connman_service_get_security(struct connman_service *service);
+const char *__connman_service_get_phase2(struct connman_service *service);
+connman_bool_t __connman_service_wps_enabled(struct connman_service *service);
+#if defined TIZEN_EXT
+/*
+ * Returns profile count if there is any connected profiles
+ * that use same interface
+ */
+int __connman_service_get_connected_count_of_iface(struct connman_service *service);
+#endif
+int __connman_service_set_favorite(struct connman_service *service,
+                                               connman_bool_t favorite);
+int __connman_service_set_favorite_delayed(struct connman_service *service,
+                                       connman_bool_t favorite,
+                                       gboolean delay_ordering);
+int __connman_service_set_immutable(struct connman_service *service,
+                                               connman_bool_t immutable);
+void __connman_service_set_userconnect(struct connman_service *service,
+                                               connman_bool_t userconnect);
+
+void __connman_service_set_string(struct connman_service *service,
+                                       const char *key, const char *value);
+int __connman_service_online_check_failed(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+int __connman_service_ipconfig_indicate_state(struct connman_service *service,
+                                       enum connman_service_state new_state,
+                                       enum connman_ipconfig_type type);
+enum connman_service_state __connman_service_ipconfig_get_state(
+                                       struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+
+int __connman_service_indicate_error(struct connman_service *service,
+                                       enum connman_service_error error);
+int __connman_service_clear_error(struct connman_service *service);
+int __connman_service_indicate_default(struct connman_service *service);
+
+int __connman_service_connect(struct connman_service *service);
+int __connman_service_disconnect(struct connman_service *service);
+int __connman_service_disconnect_all(void);
+void __connman_service_auto_connect(void);
+gboolean __connman_service_remove(struct connman_service *service);
+void __connman_service_set_hidden_data(struct connman_service *service,
+                               gpointer user_data);
+void __connman_service_return_error(struct connman_service *service,
+                               int error, gpointer user_data);
+void __connman_service_reply_dbus_pending(DBusMessage *pending, int error);
+
+int __connman_service_provision_changed(const char *ident);
+void __connman_service_set_config(struct connman_service *service,
+                               const char *file_id, const char *section);
+
+const char *__connman_service_type2string(enum connman_service_type type);
+enum connman_service_type __connman_service_string2type(const char *str);
+
+int __connman_service_nameserver_append(struct connman_service *service,
+                               const char *nameserver, gboolean is_auto);
+int __connman_service_nameserver_remove(struct connman_service *service,
+                               const char *nameserver, gboolean is_auto);
+void __connman_service_nameserver_clear(struct connman_service *service);
+void __connman_service_nameserver_add_routes(struct connman_service *service,
+                                               const char *gw);
+void __connman_service_nameserver_del_routes(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+int __connman_service_timeserver_append(struct connman_service *service,
+                                               const char *timeserver);
+int __connman_service_timeserver_remove(struct connman_service *service,
+                                               const char *timeserver);
+void __connman_service_timeserver_changed(struct connman_service *service,
+               GSList *ts_list);
+void __connman_service_set_pac(struct connman_service *service,
+                                       const char *pac);
+#if defined TIZEN_EXT
+void __connman_service_set_proxy(struct connman_service *service,
+                                       const char *proxies);
+#endif
+connman_bool_t __connman_service_is_hidden(struct connman_service *service);
+connman_bool_t __connman_service_is_split_routing(struct connman_service *service);
+int __connman_service_get_index(struct connman_service *service);
+void __connman_service_set_hidden(struct connman_service *service);
+void __connman_service_set_domainname(struct connman_service *service,
+                                               const char *domainname);
+const char *__connman_service_get_domainname(struct connman_service *service);
+const char *__connman_service_get_nameserver(struct connman_service *service);
+void __connman_service_set_proxy_autoconfig(struct connman_service *service,
+                                                       const char *url);
+
+void __connman_service_set_identity(struct connman_service *service,
+                                       const char *identity);
+void __connman_service_set_agent_identity(struct connman_service *service,
+                                               const char *agent_identity);
+int __connman_service_set_passphrase(struct connman_service *service,
+                                       const char *passphrase);
+const char *__connman_service_get_passphrase(struct connman_service *service);
+void __connman_service_set_agent_passphrase(struct connman_service *service,
+                                               const char *agent_passphrase);
+
+void __connman_service_notify(struct connman_service *service,
+                       unsigned int rx_packets, unsigned int tx_packets,
+                       unsigned int rx_bytes, unsigned int tx_bytes,
+                       unsigned int rx_error, unsigned int tx_error,
+                       unsigned int rx_dropped, unsigned int tx_dropped);
+
+int __connman_service_counter_register(const char *counter);
+void __connman_service_counter_unregister(const char *counter);
+
+struct connman_session;
+struct service_entry;
+typedef connman_bool_t (* service_match_cb) (struct connman_session *session,
+                                       struct connman_service *service);
+typedef struct service_entry* (* create_service_entry_cb) (
+                                       struct connman_service *service,
+                                       const char *name,
+                                       enum connman_service_state state);
+
+GSequence *__connman_service_get_list(struct connman_session *session,
+                               service_match_cb service_match,
+                               create_service_entry_cb create_service_entry,
+                               GDestroyNotify destroy_service_entry);
+
+void __connman_service_session_inc(struct connman_service *service);
+connman_bool_t __connman_service_session_dec(struct connman_service *service);
+void __connman_service_mark_dirty();
+void __connman_service_save(struct connman_service *service);
+
+#include <connman/notifier.h>
+
+int __connman_technology_init(void);
+void __connman_technology_cleanup(void);
+
+int __connman_notifier_init(void);
+void __connman_notifier_cleanup(void);
+
+void __connman_notifier_service_add(struct connman_service *service,
+                                       const char *name);
+void __connman_notifier_service_remove(struct connman_service *service);
+void __connman_notifier_enter_online(enum connman_service_type type);
+void __connman_notifier_leave_online(enum connman_service_type type);
+void __connman_notifier_connect(enum connman_service_type type);
+void __connman_notifier_disconnect(enum connman_service_type type);
+void __connman_notifier_offlinemode(connman_bool_t enabled);
+void __connman_notifier_default_changed(struct connman_service *service);
+void __connman_notifier_proxy_changed(struct connman_service *service);
+void __connman_notifier_service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state);
+void __connman_notifier_ipconfig_changed(struct connman_service *service,
+                                       struct connman_ipconfig *ipconfig);
+
+connman_bool_t __connman_notifier_is_connected(void);
+const char *__connman_notifier_get_state(void);
+
+#include <connman/rtnl.h>
+
+int __connman_rtnl_init(void);
+void __connman_rtnl_start(void);
+void __connman_rtnl_cleanup(void);
+
+enum connman_device_type __connman_rtnl_get_device_type(int index);
+unsigned int __connman_rtnl_update_interval_add(unsigned int interval);
+unsigned int __connman_rtnl_update_interval_remove(unsigned int interval);
+int __connman_rtnl_request_update(void);
+int __connman_rtnl_send(const void *buf, size_t len);
+
+connman_bool_t __connman_session_mode();
+void __connman_session_set_mode(connman_bool_t enable);
+
+int __connman_session_create(DBusMessage *msg);
+int __connman_session_destroy(DBusMessage *msg);
+
+int __connman_session_init(void);
+void __connman_session_cleanup(void);
+
+struct connman_stats_data {
+       unsigned int rx_packets;
+       unsigned int tx_packets;
+       unsigned int rx_bytes;
+       unsigned int tx_bytes;
+       unsigned int rx_errors;
+       unsigned int tx_errors;
+       unsigned int rx_dropped;
+       unsigned int tx_dropped;
+       unsigned int time;
+};
+
+int __connman_stats_init(void);
+void __connman_stats_cleanup(void);
+int __connman_stats_service_register(struct connman_service *service);
+void __connman_stats_service_unregister(struct connman_service *service);
+int  __connman_stats_update(struct connman_service *service,
+                               connman_bool_t roaming,
+                               struct connman_stats_data *data);
+int __connman_stats_get(struct connman_service *service,
+                               connman_bool_t roaming,
+                               struct connman_stats_data *data);
+
+int __connman_iptables_init(void);
+void __connman_iptables_cleanup(void);
+int __connman_iptables_command(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+int __connman_iptables_commit(const char *table_name);
+
+int __connman_dnsproxy_init(void);
+void __connman_dnsproxy_cleanup(void);
+int __connman_dnsproxy_add_listener(const char *interface);
+void __connman_dnsproxy_remove_listener(const char *interface);
+int __connman_dnsproxy_append(const char *interface, const char *domain, const char *server);
+int __connman_dnsproxy_remove(const char *interface, const char *domain, const char *server);
+void __connman_dnsproxy_flush(void);
+
+int __connman_6to4_probe(struct connman_service *service);
+void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
+int __connman_6to4_check(struct connman_ipconfig *ipconfig);
+
+struct connman_ippool;
+
+typedef void (*ippool_collision_cb_t) (struct connman_ippool *pool,
+                                       void *user_data);
+
+int __connman_ippool_init(void);
+void __connman_ippool_cleanup(void);
+
+#define __connman_ippool_ref(ipconfig) \
+       __connman_ippool_ref_debug(ipconfig, __FILE__, __LINE__, __func__)
+#define __connman_ippool_unref(ipconfig) \
+       __connman_ippool_unref_debug(ipconfig, __FILE__, __LINE__, __func__)
+
+struct connman_ippool *__connman_ippool_ref_debug(struct connman_ippool *pool,
+                       const char *file, int line, const char *caller);
+void __connman_ippool_unref_debug(struct connman_ippool *pool,
+                       const char *file, int line, const char *caller);
+
+struct connman_ippool *__connman_ippool_create(int index,
+                                       unsigned int start,
+                                       unsigned int range,
+                                       ippool_collision_cb_t collision_cb,
+                                       void *user_data);
+
+const char *__connman_ippool_get_gateway(struct connman_ippool *pool);
+const char *__connman_ippool_get_broadcast(struct connman_ippool *pool);
+const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool);
+const char *__connman_ippool_get_start_ip(struct connman_ippool *pool);
+const char *__connman_ippool_get_end_ip(struct connman_ippool *pool);
+
+void __connman_ippool_newaddr(int index, const char *address,
+                               unsigned char prefixlen);
+void __connman_ippool_deladdr(int index, const char *address,
+                               unsigned char prefixlen);
+
+int __connman_bridge_create(const char *name);
+int __connman_bridge_remove(const char *name);
+int __connman_bridge_enable(const char *name, const char *gateway,
+                               const char *broadcast);
+int __connman_bridge_disable(const char *name);
+
+int __connman_nat_init(void);
+void __connman_nat_cleanup(void);
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen);
+void __connman_nat_disable(const char *name);
diff --git a/src/connman.service.in b/src/connman.service.in
new file mode 100644 (file)
index 0000000..c155955
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Connection service
+After=syslog.target
+
+[Service]
+EnvironmentFile=-/etc/sysconfig/connman
+Type=dbus
+BusName=net.connman
+ExecStart=@prefix@/sbin/connmand -n $OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/connman.ver b/src/connman.ver
new file mode 100644 (file)
index 0000000..03a0eec
--- /dev/null
@@ -0,0 +1,7 @@
+{
+       global:
+               connman_*;
+               g_dbus_*;
+       local:
+               *;
+};
diff --git a/src/counter.c b/src/counter.c
new file mode 100644 (file)
index 0000000..1dd7d6d
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *
+ *  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 <errno.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+
+static GHashTable *counter_table;
+static GHashTable *owner_mapping;
+
+struct connman_counter {
+       char *owner;
+       char *path;
+       unsigned int interval;
+       guint watch;
+};
+
+static void remove_counter(gpointer user_data)
+{
+       struct connman_counter *counter = user_data;
+
+       DBG("owner %s path %s", counter->owner, counter->path);
+
+       __connman_rtnl_update_interval_remove(counter->interval);
+
+       __connman_service_counter_unregister(counter->path);
+
+       g_free(counter->owner);
+       g_free(counter->path);
+       g_free(counter);
+}
+
+static void owner_disconnect(DBusConnection *connection, void *user_data)
+{
+       struct connman_counter *counter = user_data;
+
+       DBG("owner %s path %s", counter->owner, counter->path);
+
+       g_hash_table_remove(owner_mapping, counter->owner);
+       g_hash_table_remove(counter_table, counter->path);
+}
+
+int __connman_counter_register(const char *owner, const char *path,
+                                               unsigned int interval)
+{
+       struct connman_counter *counter;
+       int err;
+
+       DBG("owner %s path %s interval %u", owner, path, interval);
+
+       counter = g_hash_table_lookup(counter_table, path);
+       if (counter != NULL)
+               return -EEXIST;
+
+       counter = g_try_new0(struct connman_counter, 1);
+       if (counter == NULL)
+               return -ENOMEM;
+
+       counter->owner = g_strdup(owner);
+       counter->path = g_strdup(path);
+
+       err = __connman_service_counter_register(counter->path);
+       if (err < 0) {
+               g_free(counter->owner);
+               g_free(counter->path);
+               g_free(counter);
+               return err;
+       }
+
+       g_hash_table_replace(counter_table, counter->path, counter);
+       g_hash_table_replace(owner_mapping, counter->owner, counter);
+
+       counter->interval = interval;
+       __connman_rtnl_update_interval_add(counter->interval);
+
+       counter->watch = g_dbus_add_disconnect_watch(connection, owner,
+                                       owner_disconnect, counter, NULL);
+
+       return 0;
+}
+
+int __connman_counter_unregister(const char *owner, const char *path)
+{
+       struct connman_counter *counter;
+
+       DBG("owner %s path %s", owner, path);
+
+       counter = g_hash_table_lookup(counter_table, path);
+       if (counter == NULL)
+               return -ESRCH;
+
+       if (g_strcmp0(owner, counter->owner) != 0)
+               return -EACCES;
+
+       if (counter->watch > 0)
+               g_dbus_remove_watch(connection, counter->watch);
+
+       g_hash_table_remove(owner_mapping, counter->owner);
+       g_hash_table_remove(counter_table, counter->path);
+
+       return 0;
+}
+
+void __connman_counter_send_usage(const char *path,
+                                       DBusMessage *message)
+{
+       struct connman_counter *counter;
+
+       counter = g_hash_table_lookup(counter_table, path);
+       if (counter == NULL)
+               return;
+
+       dbus_message_set_destination(message, counter->owner);
+       dbus_message_set_path(message, counter->path);
+       dbus_message_set_interface(message, CONNMAN_COUNTER_INTERFACE);
+       dbus_message_set_member(message, "Usage");
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+}
+
+static void release_counter(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_counter *counter = value;
+       DBusMessage *message;
+
+       DBG("owner %s path %s", counter->owner, counter->path);
+
+       if (counter->watch > 0)
+               g_dbus_remove_watch(connection, counter->watch);
+
+       message = dbus_message_new_method_call(counter->owner, counter->path,
+                                       CONNMAN_COUNTER_INTERFACE, "Release");
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+}
+
+int __connman_counter_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       counter_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, remove_counter);
+       owner_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       return 0;
+}
+
+void __connman_counter_cleanup(void)
+{
+       DBG("");
+
+       if (connection == NULL)
+               return;
+
+       g_hash_table_foreach(counter_table, release_counter, NULL);
+
+       g_hash_table_destroy(owner_mapping);
+       g_hash_table_destroy(counter_table);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..38a6b92
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ *
+ *  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 <string.h>
+#include <gdbus.h>
+
+#include "connman.h"
+
+dbus_bool_t connman_dbus_validate_ident(const char *ident)
+{
+       unsigned int i;
+
+       if (ident == NULL)
+               return FALSE;
+
+       for (i = 0; i < strlen(ident); i++) {
+               if (ident[i] >= '0' && ident[i] <= '9')
+                       continue;
+               if (ident[i] >= 'a' && ident[i] <= 'z')
+                       continue;
+               if (ident[i] >= 'A' && ident[i] <= 'Z')
+                       continue;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+char *connman_dbus_encode_string(const char *value)
+{
+       GString *str;
+       unsigned int i, size;
+
+       if (value == NULL)
+               return NULL;
+
+       size = strlen(value);
+
+       str = g_string_new(NULL);
+       if (str == NULL)
+               return NULL;
+
+       for (i = 0; i < size; i++) {
+               const char tmp = value[i];
+               if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
+                                               (tmp < 'a' || tmp > 'z'))
+                       g_string_append_printf(str, "_%02x", tmp);
+               else
+                       str = g_string_append_c(str, tmp);
+       }
+
+       return g_string_free(str, FALSE);
+}
+
+void connman_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter value;
+       const char *signature;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       case DBUS_TYPE_STRING:
+               signature = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_BYTE:
+               signature = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT16:
+               signature = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+       case DBUS_TYPE_INT16:
+               signature = DBUS_TYPE_INT16_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT32:
+               signature = DBUS_TYPE_UINT32_AS_STRING;
+               break;
+       case DBUS_TYPE_INT32:
+               signature = DBUS_TYPE_INT32_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT64:
+               signature = DBUS_TYPE_UINT64_AS_STRING;
+               break;
+       case DBUS_TYPE_INT64:
+               signature = DBUS_TYPE_INT64_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       default:
+               signature = DBUS_TYPE_VARIANT_AS_STRING;
+               break;
+       }
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void connman_dbus_property_append_dict(DBusMessageIter *iter, const char *key,
+                       connman_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessageIter value, dict;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                       DBUS_TYPE_ARRAY_AS_STRING
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &value);
+
+       connman_dbus_dict_open(&value, &dict);
+       if (function)
+               function(&dict, user_data);
+       connman_dbus_dict_close(&value, &dict);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void connman_dbus_property_append_fixed_array(DBusMessageIter *iter,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_BYTE:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       dbus_message_iter_append_fixed_array(&array, type, val, len);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+void connman_dbus_property_append_array(DBusMessageIter *iter,
+                                               const char *key, int type,
+                       connman_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
+               array_sig = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               array_sig = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       if (function)
+               function(&array, user_data);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+static DBusConnection *connection = NULL;
+
+dbus_bool_t connman_dbus_property_changed_basic(const char *path,
+                               const char *interface, const char *key,
+                                                       int type, void *val)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       connman_dbus_property_append_basic(&iter, key, type, val);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+dbus_bool_t connman_dbus_property_changed_dict(const char *path,
+                               const char *interface, const char *key,
+                       connman_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       connman_dbus_property_append_dict(&iter, key, function, user_data);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+dbus_bool_t connman_dbus_property_changed_array(const char *path,
+                       const char *interface, const char *key, int type,
+                       connman_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       if (path == NULL)
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(signal, &iter);
+       connman_dbus_property_append_array(&iter, key, type,
+                                               function, user_data);
+
+       g_dbus_send_message(connection, signal);
+
+       return TRUE;
+}
+
+dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
+                               const char *path, const char *key,
+                               int type, void *val)
+{
+       DBusMessage *msg;
+       DBusMessageIter array, dict;
+
+       if (owner == NULL || path == NULL)
+               return FALSE;
+
+       msg = dbus_message_new_method_call(owner, path,
+                                               CONNMAN_NOTIFICATION_INTERFACE,
+                                               "Update");
+       if (msg == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(msg, &array);
+       connman_dbus_dict_open(&array, &dict);
+
+       connman_dbus_dict_append_basic(&dict, key, type, val);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       g_dbus_send_message(connection, msg);
+
+       return TRUE;
+}
+
+dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
+                               const char *path, const char *key,
+                               connman_dbus_append_cb_t function,
+                               void *user_data)
+{
+       DBusMessage *msg;
+       DBusMessageIter array, dict;
+
+       if (owner == NULL || path == NULL)
+               return FALSE;
+
+       msg = dbus_message_new_method_call(owner, path,
+                                               CONNMAN_NOTIFICATION_INTERFACE,
+                                               "Update");
+       if (msg == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(msg, &array);
+       connman_dbus_dict_open(&array, &dict);
+
+       connman_dbus_dict_append_dict(&dict, key, function, user_data);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       g_dbus_send_message(connection, msg);
+
+       return TRUE;
+}
+
+dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
+                               const char *path, const char *key, int type,
+                               connman_dbus_append_cb_t function,
+                               void *user_data)
+{
+       DBusMessage *msg;
+       DBusMessageIter array, dict;
+
+       if (owner == NULL || path == NULL)
+               return FALSE;
+
+       msg = dbus_message_new_method_call(owner, path,
+                                               CONNMAN_NOTIFICATION_INTERFACE,
+                                               "Update");
+       if (msg == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(msg, &array);
+       connman_dbus_dict_open(&array, &dict);
+
+       connman_dbus_dict_append_array(&dict, key, type, function, user_data);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       g_dbus_send_message(connection, msg);
+
+       return TRUE;
+}
+
+dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
+               connman_dbus_append_cb_t function, void *user_data)
+{
+       DBusMessageIter iter, array;
+
+       if (msg == NULL || function == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_OBJECT_PATH_AS_STRING
+                       DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                               DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+                       DBUS_STRUCT_END_CHAR_AS_STRING, &array);
+
+       function(&array, user_data);
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       return TRUE;
+}
+
+DBusConnection *connman_dbus_get_connection(void)
+{
+       if (connection == NULL)
+               return NULL;
+
+       return dbus_connection_ref(connection);
+}
+
+int __connman_dbus_init(DBusConnection *conn)
+{
+       DBG("");
+
+       connection = conn;
+
+       return 0;
+}
+
+void __connman_dbus_cleanup(void)
+{
+       DBG("");
+
+       connection = NULL;
+}
diff --git a/src/detect.c b/src/detect.c
new file mode 100644 (file)
index 0000000..f4b5d29
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "connman.h"
+
+static GSList *device_list = NULL;
+
+static struct connman_device *find_device(int index)
+{
+       GSList *list;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (connman_device_get_index(device) == index)
+                       return device;
+       }
+
+       return NULL;
+}
+
+static void detect_newlink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       struct connman_device *device;
+       enum connman_device_type devtype;
+
+       DBG("type %d index %d", type, index);
+
+       devtype = __connman_rtnl_get_device_type(index);
+
+       switch (devtype) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               return;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               break;
+       }
+
+       device = find_device(index);
+       if (device != NULL)
+               return;
+
+       device = connman_inet_create_device(index);
+       if (device == NULL)
+               return;
+
+       if (connman_device_register(device) < 0) {
+               connman_device_unref(device);
+               return;
+       }
+
+       device_list = g_slist_append(device_list, device);
+}
+
+static void detect_dellink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       struct connman_device *device;
+
+       DBG("type %d index %d", type, index);
+
+       device = find_device(index);
+       if (device == NULL)
+               return;
+
+       device_list = g_slist_remove(device_list, device);
+
+       connman_device_unregister(device);
+       connman_device_unref(device);
+}
+
+static struct connman_rtnl detect_rtnl = {
+       .name           = "detect",
+       .priority       = CONNMAN_RTNL_PRIORITY_LOW,
+       .newlink        = detect_newlink,
+       .dellink        = detect_dellink,
+};
+
+int __connman_detect_init(void)
+{
+       DBG("");
+
+       return connman_rtnl_register(&detect_rtnl);
+}
+
+void __connman_detect_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       connman_rtnl_unregister(&detect_rtnl);
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               connman_device_unregister(device);
+               connman_device_unref(device);
+       }
+
+       g_slist_free(device_list);
+       device_list = NULL;
+}
diff --git a/src/device.c b/src/device.c
new file mode 100644 (file)
index 0000000..3ac4328
--- /dev/null
@@ -0,0 +1,1293 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include "connman.h"
+
+#if defined TIZEN_EXT
+#define SIG_WIFI_REFCOUNT_NAME "connman_significant_wifi_profile_refcount"
+#endif
+
+static GSList *device_list = NULL;
+static gchar **device_filter = NULL;
+static gchar **nodevice_filter = NULL;
+
+enum connman_pending_type {
+       PENDING_NONE    = 0,
+       PENDING_ENABLE  = 1,
+       PENDING_DISABLE = 2,
+};
+
+struct connman_device {
+       int refcount;
+       enum connman_device_type type;
+       enum connman_pending_type powered_pending;      /* Indicates a pending
+                                                       enable/disable request */
+       connman_bool_t powered;
+       connman_bool_t scanning;
+       connman_bool_t disconnected;
+       connman_bool_t reconnect;
+       char *name;
+       char *node;
+       char *address;
+       char *interface;
+       char *ident;
+       char *path;
+       char *devname;
+       int phyindex;
+       int index;
+       guint pending_timeout;
+
+       struct connman_device_driver *driver;
+       void *driver_data;
+
+       char *last_network;
+       struct connman_network *network;
+       GHashTable *networks;
+
+#if defined TIZEN_EXT
+        /* It contains number of favorite Wi-Fi profile.
+         * If significant wifi profile is 0,Wi-Fi device does not trigger scan.
+        */
+        gint significant_wifi_profile_refcount;
+#endif
+};
+
+#if defined TIZEN_EXT
+static void __connman_device_set_sig_wifi_profile_refcount(
+                               struct connman_device *device, gint refcount)
+{
+        g_atomic_int_set(&device->significant_wifi_profile_refcount, refcount);
+}
+
+void connman_device_sig_wifi_profile_ref(struct connman_device *device)
+{
+        g_atomic_int_inc(&device->significant_wifi_profile_refcount);
+}
+
+connman_bool_t connman_device_sig_wifi_profile_unref_and_test(
+                                               struct connman_device *device)
+{
+        if (device->significant_wifi_profile_refcount > 0)
+                return (connman_bool_t)g_atomic_int_dec_and_test(
+                               &device->significant_wifi_profile_refcount);
+
+        return FALSE;
+}
+
+#endif
+
+static void clear_pending_trigger(struct connman_device *device)
+{
+       if (device->pending_timeout > 0) {
+               g_source_remove(device->pending_timeout);
+               device->pending_timeout = 0;
+       }
+}
+
+static const char *type2description(enum connman_device_type type)
+{
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return "Ethernet";
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               return "Wireless";
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               return "WiMAX";
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               return "Bluetooth";
+       case CONNMAN_DEVICE_TYPE_GPS:
+               return "GPS";
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+               return "Cellular";
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               return "Gadget";
+
+       }
+
+       return NULL;
+}
+
+static const char *type2string(enum connman_device_type type)
+{
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               return "bluetooth";
+       case CONNMAN_DEVICE_TYPE_GPS:
+               return "gps";
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+               return "cellular";
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               return "gadget";
+
+       }
+
+       return NULL;
+}
+
+enum connman_service_type __connman_device_get_service_type(struct connman_device *device)
+{
+       enum connman_device_type type = connman_device_get_type(device);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               return CONNMAN_SERVICE_TYPE_GADGET;
+
+       }
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static gboolean device_pending_reset(gpointer user_data)
+{
+       struct connman_device *device = user_data;
+
+       DBG("device %p", device);
+
+       /* Power request timedout, reset power pending state. */
+       device->pending_timeout = 0;
+       device->powered_pending = PENDING_NONE;
+
+       return FALSE;
+}
+
+int __connman_device_enable(struct connman_device *device)
+{
+       int err;
+
+       DBG("device %p", device);
+
+       if (!device->driver || !device->driver->enable)
+               return -EOPNOTSUPP;
+
+       /* There is an ongoing power disable request. */
+       if (device->powered_pending == PENDING_DISABLE)
+               return -EBUSY;
+
+       if (device->powered_pending == PENDING_ENABLE)
+               return -EALREADY;
+
+       if (device->powered_pending == PENDING_NONE && device->powered == TRUE)
+               return -EALREADY;
+
+       device->powered_pending = PENDING_ENABLE;
+
+       err = device->driver->enable(device);
+       /*
+        * device gets enabled right away.
+        * Invoke the callback
+        */
+       if (err == 0) {
+               connman_device_set_powered(device, TRUE);
+               goto done;
+       }
+
+       if (err == -EALREADY) {
+               /* If device is already powered, but connman is not updated */
+               connman_device_set_powered(device, TRUE);
+               goto done;
+       }
+       /*
+        * if err == -EINPROGRESS, then the DBus call to the respective daemon
+        * was successful. We set a 4 sec timeout so if the daemon never
+        * returns a reply, we would reset the pending request.
+        */
+       if (err == -EINPROGRESS)
+               device->pending_timeout = g_timeout_add_seconds(4,
+                                       device_pending_reset, device);
+done:
+       return err;
+}
+
+int __connman_device_disable(struct connman_device *device)
+{
+       int err;
+
+       DBG("device %p", device);
+
+       if (!device->driver || !device->driver->disable)
+               return -EOPNOTSUPP;
+
+       /* Ongoing power enable request */
+       if (device->powered_pending == PENDING_ENABLE)
+               return -EBUSY;
+
+       if (device->powered_pending == PENDING_DISABLE)
+               return -EALREADY;
+
+       if (device->powered_pending == PENDING_NONE && device->powered == FALSE)
+               return -EALREADY;
+
+       device->powered_pending = PENDING_DISABLE;
+       device->reconnect = FALSE;
+
+       if (device->network) {
+               struct connman_service *service =
+                       __connman_service_lookup_from_network(device->network);
+
+               if (service != NULL)
+                       __connman_service_disconnect(service);
+               else
+                       connman_network_set_connected(device->network, FALSE);
+       }
+
+       err = device->driver->disable(device);
+       if (err == 0 || err == -EALREADY) {
+               connman_device_set_powered(device, FALSE);
+               goto done;
+       }
+
+       if (err == -EINPROGRESS)
+               device->pending_timeout = g_timeout_add_seconds(4,
+                                       device_pending_reset, device);
+done:
+       return err;
+}
+
+static void probe_driver(struct connman_device_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = device_list; list != NULL; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (device->driver != NULL)
+                       continue;
+
+               if (driver->type != device->type)
+                       continue;
+
+               if (driver->probe(device) < 0)
+                       continue;
+
+               device->driver = driver;
+
+               __connman_technology_add_device(device);
+       }
+}
+
+static void remove_device(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       __connman_device_disable(device);
+
+       __connman_technology_remove_device(device);
+
+       if (device->driver->remove)
+               device->driver->remove(device);
+
+       device->driver = NULL;
+}
+
+static void remove_driver(struct connman_device_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = device_list; list != NULL; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (device->driver == driver)
+                       remove_device(device);
+       }
+}
+
+connman_bool_t __connman_device_has_driver(struct connman_device *device)
+{
+       if (device == NULL || device->driver == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+static GSList *driver_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_device_driver *driver1 = a;
+       const struct connman_device_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_device_driver_register:
+ * @driver: device driver definition
+ *
+ * Register a new device driver
+ *
+ * Returns: %0 on success
+ */
+int connman_device_driver_register(struct connman_device_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+       probe_driver(driver);
+
+       return 0;
+}
+
+/**
+ * connman_device_driver_unregister:
+ * @driver: device driver definition
+ *
+ * Remove a previously registered device driver
+ */
+void connman_device_driver_unregister(struct connman_device_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       remove_driver(driver);
+}
+
+static void free_network(gpointer data)
+{
+       struct connman_network *network = data;
+
+       DBG("network %p", network);
+
+       __connman_network_set_device(network, NULL);
+
+       connman_network_unref(network);
+}
+
+static void device_destruct(struct connman_device *device)
+{
+       DBG("device %p name %s", device, device->name);
+
+       clear_pending_trigger(device);
+
+       g_free(device->ident);
+       g_free(device->node);
+       g_free(device->name);
+       g_free(device->address);
+       g_free(device->interface);
+       g_free(device->path);
+       g_free(device->devname);
+
+       g_free(device->last_network);
+
+       g_hash_table_destroy(device->networks);
+       device->networks = NULL;
+
+       g_free(device);
+}
+
+#if defined TIZEN_EXT
+connman_bool_t connman_device_load_sig_wifi_profile_refcount_from_storage(
+                                               struct connman_device *device)
+{
+       GKeyFile *keyfile = NULL;
+
+       keyfile = __connman_storage_load_global();
+
+       if (g_key_file_has_group(keyfile, SIG_WIFI_REFCOUNT_NAME) == FALSE) {
+               g_key_file_free(keyfile);
+               return FALSE;
+       }
+
+       __connman_device_set_sig_wifi_profile_refcount(device,
+                       g_key_file_get_integer(keyfile, SIG_WIFI_REFCOUNT_NAME,
+                       "ReferenceCount", NULL));
+
+       g_key_file_free(keyfile);
+       return TRUE;
+}
+
+connman_bool_t connman_device_save_sig_wifi_profile_refcount2storage(
+                                               struct connman_device *device)
+{
+       GKeyFile *keyfile = NULL;
+
+       keyfile = __connman_storage_load_global();
+
+       g_key_file_set_integer(keyfile, SIG_WIFI_REFCOUNT_NAME,
+                       "ReferenceCount",
+                       device->significant_wifi_profile_refcount);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+       return TRUE;
+}
+#endif
+
+/**
+ * connman_device_create:
+ * @node: device node name (for example an address)
+ * @type: device type
+ *
+ * Allocate a new device of given #type and assign the #node name to it.
+ *
+ * Returns: a newly-allocated #connman_device structure
+ */
+struct connman_device *connman_device_create(const char *node,
+                                               enum connman_device_type type)
+{
+       struct connman_device *device;
+
+       DBG("node %s type %d", node, type);
+
+       device = g_try_new0(struct connman_device, 1);
+       if (device == NULL)
+               return NULL;
+
+       DBG("device %p", device);
+
+       device->refcount = 1;
+
+#if defined TIZEN_EXT
+       if (type == CONNMAN_DEVICE_TYPE_WIFI) {
+               /* Load significant_wifi_profile_refcount */
+               if (connman_device_load_sig_wifi_profile_refcount_from_storage
+                                                       (device) == FALSE) {
+                       __connman_device_set_sig_wifi_profile_refcount(
+                                                               device, 0);
+                       connman_device_save_sig_wifi_profile_refcount2storage(
+                                                               device);
+               }
+       } else
+               __connman_device_set_sig_wifi_profile_refcount(device, 0);
+#endif
+       device->type = type;
+       device->name = g_strdup(type2description(device->type));
+
+       device->phyindex = -1;
+
+       device->networks = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, free_network);
+
+       device_list = g_slist_append(device_list, device);
+
+       return device;
+}
+
+/**
+ * connman_device_ref:
+ * @device: device structure
+ *
+ * Increase reference counter of device
+ */
+struct connman_device *connman_device_ref_debug(struct connman_device *device,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", device, device->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&device->refcount, 1);
+
+       return device;
+}
+
+/**
+ * connman_device_unref:
+ * @device: device structure
+ *
+ * Decrease reference counter of device
+ */
+void connman_device_unref_debug(struct connman_device *device,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", device, device->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&device->refcount, 1) != 1)
+               return;
+
+       if (device->driver) {
+               device->driver->remove(device);
+               device->driver = NULL;
+       }
+
+       device_list = g_slist_remove(device_list, device);
+
+       device_destruct(device);
+}
+
+const char *__connman_device_get_type(struct connman_device *device)
+{
+       return type2string(device->type);
+}
+
+/**
+ * connman_device_get_type:
+ * @device: device structure
+ *
+ * Get type of device
+ */
+enum connman_device_type connman_device_get_type(struct connman_device *device)
+{
+       return device->type;
+}
+
+/**
+ * connman_device_set_index:
+ * @device: device structure
+ * @index: index number
+ *
+ * Set index number of device
+ */
+void connman_device_set_index(struct connman_device *device, int index)
+{
+       device->index = index;
+}
+
+/**
+ * connman_device_get_index:
+ * @device: device structure
+ *
+ * Get index number of device
+ */
+int connman_device_get_index(struct connman_device *device)
+{
+       return device->index;
+}
+
+int __connman_device_get_phyindex(struct connman_device *device)
+{
+       return device->phyindex;
+}
+
+void __connman_device_set_phyindex(struct connman_device *device,
+                                                       int phyindex)
+{
+       device->phyindex = phyindex;
+}
+
+/**
+ * connman_device_set_interface:
+ * @device: device structure
+ * @interface: interface name
+ *
+ * Set interface name of device
+ */
+void connman_device_set_interface(struct connman_device *device,
+                                               const char *interface)
+{
+       g_free(device->devname);
+       device->devname = g_strdup(interface);
+
+       g_free(device->interface);
+       device->interface = g_strdup(interface);
+
+       if (device->name == NULL) {
+               const char *str = type2description(device->type);
+               if (str != NULL && device->interface != NULL)
+                       device->name = g_strdup_printf("%s (%s)", str,
+                                                       device->interface);
+       }
+}
+
+/**
+ * connman_device_set_ident:
+ * @device: device structure
+ * @ident: unique identifier
+ *
+ * Set unique identifier of device
+ */
+void connman_device_set_ident(struct connman_device *device,
+                                                       const char *ident)
+{
+       g_free(device->ident);
+       device->ident = g_strdup(ident);
+}
+
+const char *connman_device_get_ident(struct connman_device *device)
+{
+       return device->ident;
+}
+
+/**
+ * connman_device_set_powered:
+ * @device: device structure
+ * @powered: powered state
+ *
+ * Change power state of device
+ */
+int connman_device_set_powered(struct connman_device *device,
+                                               connman_bool_t powered)
+{
+       enum connman_service_type type;
+
+       DBG("driver %p powered %d", device, powered);
+
+       if (device->powered == powered)
+               return -EALREADY;
+
+       clear_pending_trigger(device);
+
+       device->powered_pending = PENDING_NONE;
+
+       device->powered = powered;
+
+       type = __connman_device_get_service_type(device);
+
+       if (device->powered == FALSE) {
+               __connman_technology_disabled(type);
+               return 0;
+       }
+
+       __connman_technology_enabled(type);
+
+       connman_device_set_disconnected(device, FALSE);
+       device->scanning = FALSE;
+
+       if (device->driver && device->driver->scan_fast)
+               device->driver->scan_fast(device);
+       else if (device->driver && device->driver->scan)
+               device->driver->scan(device);
+
+       return 0;
+}
+
+#if defined TIZEN_EXT
+static gboolean allow_wifi_scan(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_hash_table_iter_init(&iter, device->networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_network *network = value;
+
+               if (connman_network_get_connecting(network) == TRUE)
+                       return FALSE;
+
+               if (connman_network_get_associating(network) == TRUE)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+#endif
+
+static int device_scan(struct connman_device *device)
+{
+       if (!device->driver || !device->driver->scan)
+               return -EOPNOTSUPP;
+
+       if (device->powered == FALSE)
+               return -ENOLINK;
+
+#if defined TIZEN_EXT
+       if (device->type == CONNMAN_DEVICE_TYPE_WIFI &&
+                               allow_wifi_scan(device) == FALSE)
+               return -EBUSY;
+#endif
+       return device->driver->scan(device);
+}
+
+int __connman_device_disconnect(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       connman_device_set_disconnected(device, TRUE);
+
+       g_hash_table_iter_init(&iter, device->networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_network *network = value;
+
+               if (connman_network_get_connecting(network) == TRUE) {
+                       /*
+                        * Skip network in the process of connecting.
+                        * This is a workaround for WiFi networks serviced
+                        * by the supplicant plugin that hold a reference
+                        * to the network.  If we disconnect the network
+                        * here then the referenced object will not be
+                        * registered and usage (like launching DHCP client)
+                        * will fail.  There is nothing to be gained by
+                        * removing the network here anyway.
+                        */
+                       connman_warn("Skipping disconnect of %s, network is connecting.",
+                               connman_network_get_identifier(network));
+                       continue;
+               }
+
+               __connman_network_disconnect(network);
+       }
+
+       return 0;
+}
+
+static void mark_network_available(gpointer key, gpointer value,
+                                                       gpointer user_data)
+{
+       struct connman_network *network = value;
+
+       connman_network_set_available(network, TRUE);
+}
+
+static void mark_network_unavailable(gpointer key, gpointer value,
+                                                       gpointer user_data)
+{
+       struct connman_network *network = value;
+
+       if (connman_network_get_connected(network) == TRUE)
+               return;
+
+       connman_network_set_available(network, FALSE);
+}
+
+static gboolean remove_unavailable_network(gpointer key, gpointer value,
+                                                       gpointer user_data)
+{
+       struct connman_network *network = value;
+
+       if (connman_network_get_connected(network) == TRUE)
+               return FALSE;
+
+       if (connman_network_get_available(network) == TRUE)
+               return FALSE;
+
+       return TRUE;
+}
+
+void __connman_device_cleanup_networks(struct connman_device *device)
+{
+       g_hash_table_foreach_remove(device->networks,
+                                       remove_unavailable_network, NULL);
+}
+
+connman_bool_t connman_device_get_scanning(struct connman_device *device)
+{
+       return device->scanning;
+}
+
+void connman_device_reset_scanning(struct connman_device *device)
+{
+       g_hash_table_foreach(device->networks,
+                               mark_network_available, NULL);
+}
+
+/**
+ * connman_device_set_scanning:
+ * @device: device structure
+ * @scanning: scanning state
+ *
+ * Change scanning state of device
+ */
+int connman_device_set_scanning(struct connman_device *device,
+                                               connman_bool_t scanning)
+{
+       DBG("device %p scanning %d", device, scanning);
+
+       if (!device->driver || !device->driver->scan)
+               return -EINVAL;
+
+       if (device->scanning == scanning)
+               return -EALREADY;
+
+       device->scanning = scanning;
+
+       if (scanning == TRUE) {
+               __connman_technology_scan_started(device);
+
+               g_hash_table_foreach(device->networks,
+                                       mark_network_unavailable, NULL);
+
+               return 0;
+       }
+
+       __connman_device_cleanup_networks(device);
+
+       __connman_technology_scan_stopped(device);
+
+       __connman_service_auto_connect();
+
+       return 0;
+}
+
+/**
+ * connman_device_set_disconnected:
+ * @device: device structure
+ * @disconnected: disconnected state
+ *
+ * Change disconnected state of device (only for device with networks)
+ */
+int connman_device_set_disconnected(struct connman_device *device,
+                                               connman_bool_t disconnected)
+{
+       DBG("device %p disconnected %d", device, disconnected);
+
+       if (device->disconnected == disconnected)
+               return -EALREADY;
+
+       device->disconnected = disconnected;
+
+       return 0;
+}
+
+/**
+ * connman_device_get_disconnected:
+ * @device: device structure
+ *
+ * Get device disconnected state
+ */
+connman_bool_t connman_device_get_disconnected(struct connman_device *device)
+{
+       return device->disconnected;
+}
+
+/**
+ * connman_device_set_string:
+ * @device: device structure
+ * @key: unique identifier
+ * @value: string value
+ *
+ * Set string value for specific key
+ */
+int connman_device_set_string(struct connman_device *device,
+                                       const char *key, const char *value)
+{
+       DBG("device %p key %s value %s", device, key, value);
+
+       if (g_str_equal(key, "Address") == TRUE) {
+               g_free(device->address);
+               device->address = g_strdup(value);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               g_free(device->name);
+               device->name = g_strdup(value);
+       } else if (g_str_equal(key, "Node") == TRUE) {
+               g_free(device->node);
+               device->node = g_strdup(value);
+       } else if (g_str_equal(key, "Path") == TRUE) {
+               g_free(device->path);
+               device->path = g_strdup(value);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * connman_device_get_string:
+ * @device: device structure
+ * @key: unique identifier
+ *
+ * Get string value for specific key
+ */
+const char *connman_device_get_string(struct connman_device *device,
+                                                       const char *key)
+{
+       DBG("device %p key %s", device, key);
+
+       if (g_str_equal(key, "Address") == TRUE)
+               return device->address;
+       else if (g_str_equal(key, "Name") == TRUE)
+               return device->name;
+       else if (g_str_equal(key, "Node") == TRUE)
+               return device->node;
+       else if (g_str_equal(key, "Interface") == TRUE)
+               return device->interface;
+       else if (g_str_equal(key, "Path") == TRUE)
+               return device->path;
+
+       return NULL;
+}
+
+/**
+ * connman_device_add_network:
+ * @device: device structure
+ * @network: network structure
+ *
+ * Add new network to the device
+ */
+int connman_device_add_network(struct connman_device *device,
+                                       struct connman_network *network)
+{
+       const char *identifier = connman_network_get_identifier(network);
+
+       DBG("device %p network %p", device, network);
+
+       if (identifier == NULL)
+               return -EINVAL;
+
+       connman_network_ref(network);
+
+       __connman_network_set_device(network, device);
+
+       g_hash_table_replace(device->networks, g_strdup(identifier),
+                                                               network);
+
+       return 0;
+}
+
+/**
+ * connman_device_get_network:
+ * @device: device structure
+ * @identifier: network identifier
+ *
+ * Get network for given identifier
+ */
+struct connman_network *connman_device_get_network(struct connman_device *device,
+                                                       const char *identifier)
+{
+       DBG("device %p identifier %s", device, identifier);
+
+       return g_hash_table_lookup(device->networks, identifier);
+}
+
+/**
+ * connman_device_remove_network:
+ * @device: device structure
+ * @identifier: network identifier
+ *
+ * Remove network for given identifier
+ */
+int connman_device_remove_network(struct connman_device *device,
+                                               struct connman_network *network)
+{
+       const char *identifier;
+
+       DBG("device %p network %p", device, network);
+
+       if (network == NULL)
+               return 0;
+
+       identifier = connman_network_get_identifier(network);
+       g_hash_table_remove(device->networks, identifier);
+
+       return 0;
+}
+
+void connman_device_remove_all_networks(struct connman_device *device)
+{
+       g_hash_table_remove_all(device->networks);
+}
+
+void __connman_device_set_network(struct connman_device *device,
+                                       struct connman_network *network)
+{
+       const char *name;
+
+       if (device == NULL)
+               return;
+
+       if (device->network == network)
+               return;
+
+       if (network != NULL) {
+               name = connman_network_get_string(network, "Name");
+               g_free(device->last_network);
+               device->last_network = g_strdup(name);
+
+               device->network = network;
+       } else {
+               g_free(device->last_network);
+               device->last_network = NULL;
+
+               device->network = NULL;
+       }
+}
+
+void __connman_device_set_reconnect(struct connman_device *device,
+                                               connman_bool_t reconnect)
+{
+       device->reconnect = reconnect;
+}
+
+connman_bool_t  __connman_device_get_reconnect(
+                               struct connman_device *device)
+{
+       return device->reconnect;
+}
+
+static gboolean match_driver(struct connman_device *device,
+                                       struct connman_device_driver *driver)
+{
+       if (device->type == driver->type ||
+                       driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
+/**
+ * connman_device_register:
+ * @device: device structure
+ *
+ * Register device with the system
+ */
+int connman_device_register(struct connman_device *device)
+{
+       GSList *list;
+
+       DBG("device %p name %s", device, device->name);
+
+       if (device->driver != NULL)
+               return -EALREADY;
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_device_driver *driver = list->data;
+
+               if (match_driver(device, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(device) == 0) {
+                       device->driver = driver;
+                       break;
+               }
+       }
+
+       if (device->driver == NULL)
+               return 0;
+
+       return __connman_technology_add_device(device);
+}
+
+/**
+ * connman_device_unregister:
+ * @device: device structure
+ *
+ * Unregister device with the system
+ */
+void connman_device_unregister(struct connman_device *device)
+{
+       DBG("device %p name %s", device, device->name);
+
+       if (device->driver == NULL)
+               return;
+
+       remove_device(device);
+}
+
+/**
+ * connman_device_get_data:
+ * @device: device structure
+ *
+ * Get private device data pointer
+ */
+void *connman_device_get_data(struct connman_device *device)
+{
+       return device->driver_data;
+}
+
+/**
+ * connman_device_set_data:
+ * @device: device structure
+ * @data: data pointer
+ *
+ * Set private device data pointer
+ */
+void connman_device_set_data(struct connman_device *device, void *data)
+{
+       device->driver_data = data;
+}
+
+struct connman_device *__connman_device_find_device(
+                               enum connman_service_type type)
+{
+       GSList *list;
+
+       for (list = device_list; list != NULL; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != type)
+                       continue;
+
+               return device;
+       }
+
+       return NULL;
+}
+
+int __connman_device_request_scan(enum connman_service_type type)
+{
+       connman_bool_t success = FALSE;
+       int last_err = -ENOSYS;
+       GSList *list;
+       int err;
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EOPNOTSUPP;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               break;
+       }
+
+       for (list = device_list; list != NULL; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN &&
+                               service_type != type) {
+                       continue;
+               }
+
+               err = device_scan(device);
+               if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+                       success = TRUE;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
+               }
+       }
+
+       if (success == TRUE)
+               return 0;
+
+       return last_err;
+}
+
+int __connman_device_request_hidden_scan(struct connman_device *device,
+                               const char *ssid, unsigned int ssid_len,
+                               const char *identity, const char *passphrase,
+                               void *user_data)
+{
+       DBG("device %p", device);
+
+       if (device == NULL || device->driver == NULL ||
+                       device->driver->scan_hidden == NULL)
+               return -EINVAL;
+
+       if (device->scanning == TRUE)
+               return -EALREADY;
+
+       return device->driver->scan_hidden(device, ssid, ssid_len,
+                                       identity, passphrase, user_data);
+}
+
+connman_bool_t __connman_device_isfiltered(const char *devname)
+{
+       char **pattern;
+       char **blacklisted_interfaces;
+
+       if (device_filter == NULL)
+               goto nodevice;
+
+       for (pattern = device_filter; *pattern; pattern++) {
+               if (g_pattern_match_simple(*pattern, devname) == FALSE) {
+                       DBG("ignoring device %s (match)", devname);
+                       return TRUE;
+               }
+       }
+
+nodevice:
+       if (g_pattern_match_simple("dummy*", devname) == TRUE) {
+               DBG("ignoring dummy networking devices");
+               return TRUE;
+       }
+
+       if (nodevice_filter == NULL)
+               goto list;
+
+       for (pattern = nodevice_filter; *pattern; pattern++) {
+               if (g_pattern_match_simple(*pattern, devname) == TRUE) {
+                       DBG("ignoring device %s (no match)", devname);
+                       return TRUE;
+               }
+       }
+
+list:
+       blacklisted_interfaces =
+               connman_setting_get_string_list("NetworkInterfaceBlacklist");
+       if (blacklisted_interfaces == NULL)
+               return FALSE;
+
+       for (pattern = blacklisted_interfaces; *pattern; pattern++) {
+               if (g_str_has_prefix(devname, *pattern) == TRUE) {
+                       DBG("ignoring device %s (blacklist)", devname);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+int __connman_device_init(const char *device, const char *nodevice)
+{
+       DBG("");
+
+       if (device != NULL)
+               device_filter = g_strsplit(device, ",", -1);
+
+       if (nodevice != NULL)
+               nodevice_filter = g_strsplit(nodevice, ",", -1);
+
+       return 0;
+}
+
+void __connman_device_cleanup(void)
+{
+       DBG("");
+
+       g_strfreev(nodevice_filter);
+       g_strfreev(device_filter);
+}
diff --git a/src/dhcp.c b/src/dhcp.c
new file mode 100644 (file)
index 0000000..f26e62d
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <connman/ipconfig.h>
+
+#include <gdhcp/gdhcp.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+struct connman_dhcp {
+       struct connman_network *network;
+       dhcp_cb callback;
+
+       char **nameservers;
+       char **timeservers;
+       char *pac;
+
+       GDHCPClient *dhcp_client;
+};
+
+static GHashTable *network_table;
+
+static void dhcp_free(struct connman_dhcp *dhcp)
+{
+       g_strfreev(dhcp->nameservers);
+       g_strfreev(dhcp->timeservers);
+       g_free(dhcp->pac);
+
+       dhcp->nameservers = NULL;
+       dhcp->timeservers = NULL;
+       dhcp->pac = NULL;
+}
+
+/**
+ * dhcp_invalidate: Invalidate an existing DHCP lease
+ * @dhcp: pointer to the DHCP lease to invalidate.
+ * @callback: flag indicating whether or not to invoke the client callback
+ *            if present.
+ *
+ * Invalidates an existing DHCP lease, optionally invoking the client
+ * callback. The caller may wish to avoid the client callback invocation
+ * when the invocation of that callback might otherwise unnecessarily upset
+ * service state due to the IP configuration change implied by this
+ * invalidation.
+ */
+static void dhcp_invalidate(struct connman_dhcp *dhcp, connman_bool_t callback)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int i;
+
+       DBG("dhcp %p callback %u", dhcp, callback);
+
+       if (dhcp == NULL)
+               return;
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL)
+               goto out;
+
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL)
+               goto out;
+
+       __connman_6to4_remove(ipconfig);
+
+       __connman_service_set_domainname(service, NULL);
+       __connman_service_set_pac(service, NULL);
+
+       if (dhcp->timeservers != NULL) {
+               for (i = 0; dhcp->timeservers[i] != NULL; i++) {
+                       __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+               }
+       }
+
+       if (dhcp->nameservers != NULL) {
+               for (i = 0; dhcp->nameservers[i] != NULL; i++) {
+                       __connman_service_nameserver_remove(service,
+                                               dhcp->nameservers[i], FALSE);
+               }
+       }
+
+       __connman_ipconfig_set_dhcp_address(ipconfig,
+                               __connman_ipconfig_get_local(ipconfig));
+       DBG("last address %s", __connman_ipconfig_get_dhcp_address(ipconfig));
+
+       __connman_ipconfig_address_remove(ipconfig);
+
+       __connman_ipconfig_set_local(ipconfig, NULL);
+       __connman_ipconfig_set_broadcast(ipconfig, NULL);
+       __connman_ipconfig_set_gateway(ipconfig, NULL);
+       __connman_ipconfig_set_prefixlen(ipconfig, 0);
+
+       if (dhcp->callback != NULL && callback)
+               dhcp->callback(dhcp->network, FALSE);
+
+out:
+       dhcp_free(dhcp);
+}
+
+static void dhcp_valid(struct connman_dhcp *dhcp)
+{
+       if (dhcp->callback != NULL)
+               dhcp->callback(dhcp->network, TRUE);
+}
+
+static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+
+       DBG("No lease available");
+
+       dhcp_invalidate(dhcp, TRUE);
+}
+
+static void lease_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+
+       DBG("Lease lost");
+
+       dhcp_invalidate(dhcp, TRUE);
+}
+
+static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+
+       DBG("Lease lost");
+
+       dhcp_invalidate(dhcp, TRUE);
+}
+
+
+static gboolean compare_string_arrays(char **array_a, char **array_b)
+{
+       int i;
+
+       if (array_a == NULL || array_b == NULL)
+               return FALSE;
+
+       if (g_strv_length(array_a) != g_strv_length(array_b))
+               return FALSE;
+
+       for (i = 0; array_a[i] != NULL &&
+                            array_b[i] != NULL; i++) {
+               if (g_strcmp0(array_a[i], array_b[i]) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+       GList *list, *option = NULL;
+       char *address, *netmask = NULL, *gateway = NULL;
+       const char *c_address, *c_gateway;
+       char *domainname = NULL, *hostname = NULL;
+       char **nameservers, **timeservers, *pac = NULL;
+       int ns_entries;
+       struct connman_ipconfig *ipconfig;
+       struct connman_service *service;
+       unsigned char prefixlen, c_prefixlen;
+       gboolean ip_change;
+       int i;
+
+       DBG("Lease available");
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               connman_error("Can not lookup service");
+               return;
+       }
+
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL) {
+               connman_error("Could not lookup ipconfig");
+               return;
+       }
+
+       c_address = __connman_ipconfig_get_local(ipconfig);
+       c_gateway = __connman_ipconfig_get_gateway(ipconfig);
+       c_prefixlen = __connman_ipconfig_get_prefixlen(ipconfig);
+
+       address = g_dhcp_client_get_address(dhcp_client);
+
+       __connman_ipconfig_set_dhcp_address(ipconfig, address);
+       DBG("last address %s", address);
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
+       if (option != NULL)
+               netmask = g_strdup(option->data);
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
+       if (option != NULL)
+               gateway = g_strdup(option->data);
+
+       prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
+
+       DBG("c_address %s", c_address);
+
+       if (address != NULL && c_address != NULL &&
+                                       g_strcmp0(address, c_address) != 0)
+               ip_change = TRUE;
+       else if (gateway != NULL && c_gateway != NULL &&
+                                       g_strcmp0(gateway, c_gateway) != 0)
+               ip_change = TRUE;
+       else if (prefixlen != c_prefixlen)
+               ip_change = TRUE;
+       else if (c_address == NULL || c_gateway == NULL)
+               ip_change = TRUE;
+       else
+               ip_change = FALSE;
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
+       ns_entries = g_list_length(option);
+       nameservers = g_try_new0(char *, ns_entries + 1);
+       if (nameservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       nameservers[i] = g_strdup(list->data);
+               nameservers[ns_entries] = NULL;
+       }
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DOMAIN_NAME);
+       if (option != NULL)
+               domainname = g_strdup(option->data);
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_HOST_NAME);
+       if (option != NULL)
+               hostname = g_strdup(option->data);
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCP_NTP_SERVER);
+       ns_entries = g_list_length(option);
+       timeservers = g_try_new0(char *, ns_entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+               timeservers[ns_entries] = NULL;
+       }
+
+       option = g_dhcp_client_get_option(dhcp_client, 252);
+       if (option != NULL)
+               pac = g_strdup(option->data);
+
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+
+       if (ip_change == TRUE) {
+               __connman_ipconfig_set_local(ipconfig, address);
+               __connman_ipconfig_set_prefixlen(ipconfig, prefixlen);
+               __connman_ipconfig_set_gateway(ipconfig, gateway);
+       }
+
+       if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
+               if (dhcp->nameservers != NULL) {
+                       for (i = 0; dhcp->nameservers[i] != NULL; i++) {
+                               __connman_service_nameserver_remove(service,
+                                               dhcp->nameservers[i], FALSE);
+                       }
+                       g_strfreev(dhcp->nameservers);
+               }
+
+               dhcp->nameservers = nameservers;
+
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++) {
+                       __connman_service_nameserver_append(service,
+                                               dhcp->nameservers[i], FALSE);
+               }
+       } else {
+               g_strfreev(nameservers);
+       }
+
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++) {
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       }
+                       g_strfreev(dhcp->timeservers);
+               }
+
+               dhcp->timeservers = timeservers;
+
+               for (i = 0; dhcp->timeservers != NULL &&
+                                        dhcp->timeservers[i] != NULL; i++) {
+                       __connman_service_timeserver_append(service,
+                                                       dhcp->timeservers[i]);
+               }
+       } else {
+               g_strfreev(timeservers);
+       }
+
+       if (g_strcmp0(pac, dhcp->pac) != 0) {
+               g_free(dhcp->pac);
+               dhcp->pac = pac;
+
+               __connman_service_set_pac(service, dhcp->pac);
+       }
+
+       __connman_service_set_domainname(service, domainname);
+
+       if (domainname != NULL)
+               __connman_utsname_set_domainname(domainname);
+
+       if (hostname != NULL)
+               __connman_utsname_set_hostname(hostname);
+
+       if (ip_change == TRUE)
+               dhcp_valid(dhcp);
+
+       __connman_6to4_probe(service);
+
+       g_free(address);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(domainname);
+       g_free(hostname);
+}
+
+static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+       char *address, *netmask;
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       unsigned char prefixlen;
+
+       DBG("IPV4LL available");
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL)
+               return;
+
+#if defined DISABLE_LINK_LOCAL_ADDR
+       dhcp_invalidate(dhcp, TRUE);
+
+       __connman_service_ipconfig_indicate_state(service,
+                       CONNMAN_SERVICE_STATE_IDLE,
+                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_ipconfig_indicate_state(service,
+                       CONNMAN_SERVICE_STATE_IDLE,
+                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       return;
+#endif
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL)
+               return;
+
+       address = g_dhcp_client_get_address(dhcp_client);
+       netmask = g_dhcp_client_get_netmask(dhcp_client);
+
+       prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
+
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_ipconfig_set_local(ipconfig, address);
+       __connman_ipconfig_set_prefixlen(ipconfig, prefixlen);
+       __connman_ipconfig_set_gateway(ipconfig, NULL);
+
+       dhcp_valid(dhcp);
+
+       g_free(address);
+       g_free(netmask);
+}
+
+static void dhcp_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static int dhcp_request(struct connman_dhcp *dhcp)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       const char *hostname;
+       int index;
+#if defined TIZEN_EXT
+       const char *last_address;
+#endif
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE)
+               return -EINVAL;
+
+       if (getenv("CONNMAN_DHCP_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcp_debug, "DHCP");
+
+       g_dhcp_client_set_id(dhcp_client);
+
+       hostname = connman_utsname_get_hostname();
+       if (hostname != NULL)
+               g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, hostname);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
+       g_dhcp_client_set_request(dhcp_client, 252);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
+                                               lease_available_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE,
+                                               ipv4ll_available_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_LEASE_LOST, lease_lost_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_IPV4LL_LOST, ipv4ll_lost_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_NO_LEASE, no_lease_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       ipconfig = __connman_service_get_ip4config(service);
+
+#if defined TIZEN_EXT
+       last_address = __connman_ipconfig_get_dhcp_address(ipconfig);
+
+       if (last_address != NULL && strlen(last_address) > 0)
+               g_dhcp_client_set_address_known(dhcp_client, TRUE);
+
+       return g_dhcp_client_start(dhcp_client, last_address);
+#else
+       return g_dhcp_client_start(dhcp_client,
+                               __connman_ipconfig_get_dhcp_address(ipconfig));
+#endif
+}
+
+static int dhcp_release(struct connman_dhcp *dhcp)
+{
+       DBG("dhcp %p", dhcp);
+
+       if (dhcp->dhcp_client == NULL)
+               return 0;
+
+       g_dhcp_client_stop(dhcp->dhcp_client);
+       g_dhcp_client_unref(dhcp->dhcp_client);
+
+       dhcp->dhcp_client = NULL;
+
+       return 0;
+}
+
+static void remove_network(gpointer user_data)
+{
+       struct connman_dhcp *dhcp = user_data;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_invalidate(dhcp, FALSE);
+       dhcp_release(dhcp);
+
+       g_free(dhcp);
+}
+
+int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback)
+{
+       struct connman_dhcp *dhcp;
+
+       DBG("");
+
+       dhcp = g_try_new0(struct connman_dhcp, 1);
+       if (dhcp == NULL)
+               return -ENOMEM;
+
+       dhcp->network = network;
+       dhcp->callback = callback;
+
+       connman_network_ref(network);
+
+       g_hash_table_replace(network_table, network, dhcp);
+
+       return dhcp_request(dhcp);
+}
+
+void __connman_dhcp_stop(struct connman_network *network)
+{
+       DBG("");
+
+       if (network_table == NULL)
+               return;
+
+       if (g_hash_table_remove(network_table, network) == TRUE)
+               connman_network_unref(network);
+}
+
+int __connman_dhcp_init(void)
+{
+       DBG("");
+
+       network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, remove_network);
+
+       return 0;
+}
+
+void __connman_dhcp_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(network_table);
+       network_table = NULL;
+}
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
new file mode 100644 (file)
index 0000000..574b19e
--- /dev/null
@@ -0,0 +1,1237 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 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 <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <connman/ipconfig.h>
+#include <connman/storage.h>
+
+#include <gdhcp/gdhcp.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+/* Transmission params in msec, RFC 3315 chapter 5.5 */
+#define INF_MAX_DELAY  (1 * 1000)
+#define INF_TIMEOUT    (1 * 1000)
+#define INF_MAX_RT     (120 * 1000)
+#define SOL_MAX_DELAY   (1 * 1000)
+#define SOL_TIMEOUT     (1 * 1000)
+#define SOL_MAX_RT      (120 * 1000)
+#define REQ_TIMEOUT    (1 * 1000)
+#define REQ_MAX_RT     (30 * 1000)
+#define REQ_MAX_RC     10
+#define REN_TIMEOUT     (10 * 1000)
+#define REN_MAX_RT      (600 * 1000)
+#define REB_TIMEOUT     (10 * 1000)
+#define REB_MAX_RT      (600 * 1000)
+
+
+struct connman_dhcpv6 {
+       struct connman_network *network;
+       dhcp_cb callback;
+
+       char **nameservers;
+       char **timeservers;
+
+       GDHCPClient *dhcp_client;
+
+       guint timeout;          /* operation timeout in msec */
+       guint RT;               /* in msec */
+       gboolean use_ta;        /* set to TRUE if IPv6 privacy is enabled */
+       GSList *prefixes;       /* network prefixes from radvd */
+       int request_count;      /* how many times REQUEST have been sent */
+       gboolean stateless;     /* TRUE if stateless DHCPv6 is used */
+       gboolean started;       /* TRUE if we have DHCPv6 started */
+};
+
+static GHashTable *network_table;
+
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp, gboolean add_addresses);
+
+static void clear_timer(struct connman_dhcpv6 *dhcp)
+{
+       if (dhcp->timeout > 0) {
+               g_source_remove(dhcp->timeout);
+               dhcp->timeout = 0;
+       }
+}
+
+static inline float get_random()
+{
+       return (rand() % 200 - 100) / 1000.0;
+}
+
+/* Calculate a random delay, RFC 3315 chapter 14 */
+/* RT and MRT are milliseconds */
+static guint calc_delay(guint RT, guint MRT)
+{
+       float delay = get_random();
+       float rt = RT * (2 + delay);
+
+       if (rt > MRT)
+               rt = MRT * (1 + delay);
+
+       if (rt < 0)
+               rt = MRT;
+
+       return (guint)rt;
+}
+
+static void free_prefix(gpointer data, gpointer user_data)
+{
+       g_free(data);
+}
+
+static void dhcpv6_free(struct connman_dhcpv6 *dhcp)
+{
+       g_strfreev(dhcp->nameservers);
+       g_strfreev(dhcp->timeservers);
+
+       dhcp->nameservers = NULL;
+       dhcp->timeservers = NULL;
+       dhcp->started = FALSE;
+
+       g_slist_foreach(dhcp->prefixes, free_prefix, NULL);
+       g_slist_free(dhcp->prefixes);
+}
+
+static gboolean compare_string_arrays(char **array_a, char **array_b)
+{
+       int i;
+
+       if (array_a == NULL || array_b == NULL)
+               return FALSE;
+
+       if (g_strv_length(array_a) != g_strv_length(array_b))
+               return FALSE;
+
+       for (i = 0; array_a[i] != NULL && array_b[i] != NULL; i++)
+               if (g_strcmp0(array_a[i], array_b[i]) != 0)
+                       return FALSE;
+
+       return TRUE;
+}
+
+static void dhcpv6_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static gchar *convert_to_hex(unsigned char *buf, int len)
+{
+       gchar *ret = g_try_malloc(len * 2 + 1);
+       int i;
+
+       for (i = 0; ret != NULL && i < len; i++)
+               g_snprintf(ret + i * 2, 3, "%02x", buf[i]);
+
+       return ret;
+}
+
+/*
+ * DUID should not change over time so save it to file.
+ * See RFC 3315 chapter 9 for details.
+ */
+static int set_duid(struct connman_service *service,
+                       struct connman_network *network,
+                       GDHCPClient *dhcp_client, int index)
+{
+       GKeyFile *keyfile;
+       const char *ident;
+       char *hex_duid;
+       unsigned char *duid;
+       int duid_len;
+
+       ident = __connman_service_get_ident(service);
+
+       keyfile = connman_storage_load_service(ident);
+       if (keyfile == NULL)
+               return -EINVAL;
+
+       hex_duid = g_key_file_get_string(keyfile, ident, "IPv6.DHCP.DUID",
+                                       NULL);
+       if (hex_duid != NULL) {
+               unsigned int i, j = 0, hex;
+               size_t hex_duid_len = strlen(hex_duid);
+
+               duid = g_try_malloc0(hex_duid_len / 2);
+               if (duid == NULL) {
+                       g_key_file_free(keyfile);
+                       g_free(hex_duid);
+                       return -ENOMEM;
+               }
+
+               for (i = 0; i < hex_duid_len; i += 2) {
+                       sscanf(hex_duid + i, "%02x", &hex);
+                       duid[j++] = hex;
+               }
+
+               duid_len = hex_duid_len / 2;
+       } else {
+               int ret;
+               int type = __connman_ipconfig_get_type_from_index(index);
+
+               ret = g_dhcpv6_create_duid(G_DHCPV6_DUID_LLT, index, type,
+                                       &duid, &duid_len);
+               if (ret < 0) {
+                       g_key_file_free(keyfile);
+                       return ret;
+               }
+
+               hex_duid = convert_to_hex(duid, duid_len);
+               if (hex_duid == NULL) {
+                       g_key_file_free(keyfile);
+                       return -ENOMEM;
+               }
+
+               g_key_file_set_string(keyfile, ident, "IPv6.DHCP.DUID",
+                               hex_duid);
+
+               __connman_storage_save_service(keyfile, ident);
+       }
+       g_free(hex_duid);
+
+       g_key_file_free(keyfile);
+
+       g_dhcpv6_client_set_duid(dhcp_client, duid, duid_len);
+
+       return 0;
+}
+
+static void clear_callbacks(GDHCPClient *dhcp_client)
+{
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_SOLICITATION,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_ADVERTISE,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_REQUEST,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_RENEW,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_REBIND,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_RELEASE,
+                               NULL, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_INFORMATION_REQ,
+                               NULL, NULL);
+}
+
+static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       struct connman_service *service;
+       int entries, i;
+       GList *option, *list;
+       char **nameservers, **timeservers;
+
+       DBG("dhcpv6 information-request %p", dhcp);
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               connman_error("Can not lookup service");
+               return;
+       }
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       entries = g_list_length(option);
+
+       nameservers = g_try_new0(char *, entries + 1);
+       if (nameservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       nameservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
+               if (dhcp->nameservers != NULL) {
+                       for (i = 0; dhcp->nameservers[i] != NULL; i++)
+                               __connman_service_nameserver_remove(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+                       g_strfreev(dhcp->nameservers);
+               }
+
+               dhcp->nameservers = nameservers;
+
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++)
+                       __connman_service_nameserver_append(service,
+                                               dhcp->nameservers[i],
+                                               FALSE);
+       } else
+               g_strfreev(nameservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+       entries = g_list_length(option);
+
+       timeservers = g_try_new0(char *, entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++)
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       g_strfreev(dhcp->timeservers);
+               }
+
+               dhcp->timeservers = timeservers;
+
+               for (i = 0; dhcp->timeservers != NULL &&
+                                       dhcp->timeservers[i] != NULL; i++)
+                       __connman_service_timeserver_append(service,
+                                                       dhcp->timeservers[i]);
+       } else
+               g_strfreev(timeservers);
+
+
+       if (dhcp->callback != NULL) {
+               uint16_t status = g_dhcpv6_client_get_status(dhcp_client);
+               dhcp->callback(dhcp->network, status == 0 ? TRUE : FALSE);
+       }
+}
+
+static int dhcpv6_info_request(struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       int index, ret;
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE) {
+               clear_timer(dhcp);
+               return -EINVAL;
+       }
+
+       if (getenv("CONNMAN_DHCPV6_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return -EINVAL;
+       }
+
+       ret = set_duid(service, dhcp->network, dhcp_client, index);
+       if (ret < 0) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return ret;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_INFORMATION_REQ, info_req_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static int check_ipv6_addr_prefix(GSList *prefixes, char *address)
+{
+       struct in6_addr addr_prefix, addr;
+       GSList *list;
+       int ret = 128, len;
+
+       for (list = prefixes; list; list = list->next) {
+               char *prefix = list->data;
+               const char *slash = g_strrstr(prefix, "/");
+               const unsigned char bits[] = { 0x00, 0xFE, 0xFC, 0xF8,
+                                               0xF0, 0xE0, 0xC0, 0x80 };
+               int left, count, i, plen;
+
+               if (slash == NULL)
+                       continue;
+
+               prefix = g_strndup(prefix, slash - prefix);
+               len = strtol(slash + 1, NULL, 10);
+               plen = 128 - len;
+
+               count = plen / 8;
+               left = plen % 8;
+               i = 16 - count;
+
+               inet_pton(AF_INET6, prefix, &addr_prefix);
+               inet_pton(AF_INET6, address, &addr);
+
+               memset(&addr_prefix.s6_addr[i], 0, count);
+               memset(&addr.s6_addr[i], 0, count);
+
+               if (left) {
+                       addr_prefix.s6_addr[i - 1] &= bits[left];
+                       addr.s6_addr[i - 1] &= bits[left];
+               }
+
+               g_free(prefix);
+
+               if (memcmp(&addr_prefix, &addr, 16) == 0) {
+                       ret = len;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int set_addresses(GDHCPClient *dhcp_client,
+                                               struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int entries, i;
+       GList *option, *list;
+       char **nameservers, **timeservers;
+       const char *c_address;
+       char *address = NULL;
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               connman_error("Can not lookup service");
+               return -EINVAL;
+       }
+
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL) {
+               connman_error("Could not lookup ip6config");
+               return -EINVAL;
+       }
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       entries = g_list_length(option);
+
+       nameservers = g_try_new0(char *, entries + 1);
+       if (nameservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       nameservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(nameservers, dhcp->nameservers) == FALSE) {
+               if (dhcp->nameservers != NULL) {
+                       for (i = 0; dhcp->nameservers[i] != NULL; i++)
+                               __connman_service_nameserver_remove(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+                       g_strfreev(dhcp->nameservers);
+               }
+
+               dhcp->nameservers = nameservers;
+
+               for (i = 0; dhcp->nameservers != NULL &&
+                                       dhcp->nameservers[i] != NULL; i++)
+                       __connman_service_nameserver_append(service,
+                                                       dhcp->nameservers[i],
+                                                       FALSE);
+       } else
+               g_strfreev(nameservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+       entries = g_list_length(option);
+
+       timeservers = g_try_new0(char *, entries + 1);
+       if (timeservers != NULL) {
+               for (i = 0, list = option; list; list = list->next, i++)
+                       timeservers[i] = g_strdup(list->data);
+       }
+
+       if (compare_string_arrays(timeservers, dhcp->timeservers) == FALSE) {
+               if (dhcp->timeservers != NULL) {
+                       for (i = 0; dhcp->timeservers[i] != NULL; i++)
+                               __connman_service_timeserver_remove(service,
+                                                       dhcp->timeservers[i]);
+                       g_strfreev(dhcp->timeservers);
+               }
+
+               dhcp->timeservers = timeservers;
+
+               for (i = 0; dhcp->timeservers != NULL &&
+                                       dhcp->timeservers[i] != NULL; i++)
+                       __connman_service_timeserver_append(service,
+                                                       dhcp->timeservers[i]);
+       } else
+               g_strfreev(timeservers);
+
+
+       option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_NA);
+       if (option != NULL)
+               address = g_strdup(option->data);
+       else {
+               option = g_dhcp_client_get_option(dhcp_client, G_DHCPV6_IA_TA);
+               if (option != NULL)
+                       address = g_strdup(option->data);
+       }
+
+       c_address = __connman_ipconfig_get_local(ipconfig);
+
+       if (address != NULL &&
+                       ((c_address != NULL &&
+                               g_strcmp0(address, c_address) != 0) ||
+                       (c_address == NULL))) {
+               int prefix_len;
+
+               /* Is this prefix part of the subnet we are suppose to use? */
+               prefix_len = check_ipv6_addr_prefix(dhcp->prefixes, address);
+
+               __connman_ipconfig_set_local(ipconfig, address);
+               __connman_ipconfig_set_prefixlen(ipconfig, prefix_len);
+
+               DBG("new address %s/%d", address, prefix_len);
+       }
+
+       return 0;
+}
+
+static void re_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+       uint16_t status;
+       int ret;
+
+       ret = set_addresses(dhcp_client, dhcp);
+
+       status = g_dhcpv6_client_get_status(dhcp_client);
+
+       DBG("dhcpv6 cb msg %p ret %d status %d", dhcp, ret, status);
+
+       if (ret < 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       if (status  == G_DHCPV6_ERROR_BINDING) {
+               /* RFC 3315, 18.1.8 */
+               dhcpv6_request(dhcp, FALSE);
+       } else {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network,
+                                               status == 0 ? TRUE : FALSE);
+       }
+}
+
+static void rebind_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_reset_rebind(dhcp_client);
+       g_dhcpv6_client_reset_renew(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_rebind(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, FALSE);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REBIND,
+                                       rebind_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean dhcpv6_restart(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (dhcp->callback != NULL)
+               dhcp->callback(dhcp->network, FALSE);
+
+       return FALSE;
+}
+
+/*
+ * Check if we need to restart the solicitation procedure. This
+ * is done if all the addresses have expired. RFC 3315, 18.1.4
+ */
+static int check_restart(struct connman_dhcpv6 *dhcp)
+{
+       time_t current, expired;
+
+       g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, NULL, NULL,
+                               NULL, NULL, &expired);
+       current = time(NULL);
+
+       if (current > expired) {
+               DBG("expired by %d secs", (int)(current - expired));
+
+               g_timeout_add(0, dhcpv6_restart, dhcp);
+
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static gboolean timeout_rebind(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (check_restart(dhcp) < 0)
+               return FALSE;
+
+       dhcp->RT = calc_delay(dhcp->RT, REB_MAX_RT);
+
+       DBG("rebind RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_rebind(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = REB_TIMEOUT * (1 + get_random());
+
+       DBG("rebind initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_rebind, dhcp);
+
+       dhcpv6_rebind(dhcp);
+
+       return FALSE;
+}
+
+static void request_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_request(struct connman_dhcpv6 *dhcp,
+                       gboolean add_addresses)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p add %d", dhcp, add_addresses);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       &T1, &T2, add_addresses);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_REQUEST,
+                                       request_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_request(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (dhcp->request_count >= REQ_MAX_RC) {
+               DBG("max request retry attempts %d", dhcp->request_count);
+               dhcp->request_count = 0;
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return FALSE;
+       }
+
+       dhcp->request_count++;
+
+       dhcp->RT = calc_delay(dhcp->RT, REQ_MAX_RT);
+       DBG("request RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static void renew_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       DBG("");
+
+       g_dhcpv6_client_reset_renew(dhcp_client);
+
+       re_cb(dhcp_client, user_data);
+}
+
+static int dhcpv6_renew(struct connman_dhcpv6 *dhcp)
+{
+       GDHCPClient *dhcp_client;
+       uint32_t T1, T2;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+
+       g_dhcp_client_clear_requests(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_get_timeouts(dhcp_client, &T1, &T2, NULL, NULL, NULL);
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       &T1, &T2, TRUE);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client, G_DHCP_CLIENT_EVENT_RENEW,
+                                       renew_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean timeout_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       if (check_restart(dhcp) < 0)
+               return FALSE;
+
+       dhcp->RT = calc_delay(dhcp->RT, REN_MAX_RT);
+
+       DBG("renew RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_renew(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = REN_TIMEOUT * (1 + get_random());
+
+       DBG("renew initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_renew, dhcp);
+
+       dhcpv6_renew(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start_renew(struct connman_network *network,
+                                                       dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       uint32_t T1, T2;
+       time_t last_renew, last_rebind, current, expired;
+
+       dhcp = g_hash_table_lookup(network_table, network);
+       if (dhcp == NULL)
+               return -ENOENT;
+
+       DBG("network %p dhcp %p", network, dhcp);
+
+       clear_timer(dhcp);
+
+       g_dhcpv6_client_get_timeouts(dhcp->dhcp_client, &T1, &T2,
+                               &last_renew, &last_rebind, &expired);
+
+       current = time(NULL);
+
+       DBG("T1 %u T2 %u expires %lu current %lu", T1, T2,
+               (unsigned long)expired, current);
+
+       if (T1 == 0xffffffff)
+               /* RFC 3315, 22.4 */
+               return 0;
+
+       if (T1 == 0)
+               /* RFC 3315, 22.4
+                * Client can choose the timeout.
+                */
+               T1 = 1800;
+
+       /* RFC 3315, 18.1.4, start solicit if expired */
+       if (current > expired) {
+               DBG("expired by %d secs", (int)(current - expired));
+               return -ETIMEDOUT;
+       }
+
+       dhcp->callback = callback;
+
+       if (T2 != 0xffffffff && T2 > 0 &&
+                       (unsigned)current > (unsigned)last_rebind + T2) {
+               int timeout;
+
+               /* RFC 3315, chapter 18.1.3, start rebind */
+               if ((unsigned)current > (unsigned)last_renew + T1)
+                       timeout = 0;
+               else
+                       timeout = last_renew - current + T1;
+
+               /*
+                * If we just did a renew, do not restart the rebind
+                * immediately.
+                */
+               dhcp->timeout = g_timeout_add_seconds(timeout, start_rebind,
+                                               dhcp);
+       } else {
+               DBG("renew after %d secs", T1);
+
+               dhcp->timeout = g_timeout_add_seconds(T1, start_renew, dhcp);
+       }
+       return 0;
+}
+
+int __connman_dhcpv6_start_release(struct connman_network *network,
+                               dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       GDHCPClient *dhcp_client;
+
+       if (network_table == NULL)
+               return 0;   /* we are already released */
+
+       dhcp = g_hash_table_lookup(network_table, network);
+       if (dhcp == NULL)
+               return -ENOENT;
+
+       DBG("network %p dhcp %p client %p stateless %d", network, dhcp,
+                                       dhcp->dhcp_client, dhcp->stateless);
+
+       if (dhcp->stateless == TRUE)
+               return -EINVAL;
+
+       clear_timer(dhcp);
+
+       dhcp_client = dhcp->dhcp_client;
+       if (dhcp_client == NULL) {
+               /*
+                * We had started the DHCPv6 handshaking i.e., we have called
+                * __connman_dhcpv6_start() but it has not yet sent
+                * a solicitation message to server. This means that we do not
+                * have DHCPv6 configured yet so we can just quit here.
+                */
+               DBG("DHCPv6 was not started");
+               return 0;
+       }
+
+       g_dhcp_client_clear_requests(dhcp_client);
+       g_dhcp_client_clear_values(dhcp_client);
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SERVERID);
+
+       g_dhcpv6_client_set_ia(dhcp_client,
+                       connman_network_get_index(dhcp->network),
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, TRUE);
+
+       clear_callbacks(dhcp_client);
+
+       /*
+        * We do not register callback here because the answer might take too
+        * long time and network code might be in the middle of the disconnect.
+        * So we just inform the server that we are done with the addresses
+        * but ignore the reply from server. This is allowed by RFC 3315
+        * chapter 18.1.6.
+        */
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static int dhcpv6_release(struct connman_dhcpv6 *dhcp)
+{
+       DBG("dhcp %p", dhcp);
+
+       clear_timer(dhcp);
+
+       dhcpv6_free(dhcp);
+
+       if (dhcp->dhcp_client == NULL)
+               return 0;
+
+       g_dhcp_client_stop(dhcp->dhcp_client);
+       g_dhcp_client_unref(dhcp->dhcp_client);
+
+       dhcp->dhcp_client = NULL;
+
+       return 0;
+}
+
+static void remove_network(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcp %p", dhcp);
+
+       dhcpv6_release(dhcp);
+
+       g_free(dhcp);
+}
+
+static gboolean timeout_info_req(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, INF_MAX_RT);
+
+       DBG("info RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static gboolean start_info_req(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       /* Set the retransmission timeout, RFC 3315 chapter 14 */
+       dhcp->RT = INF_TIMEOUT * (1 + get_random());
+
+       DBG("info initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_info_req, dhcp);
+
+       dhcpv6_info_request(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start_info(struct connman_network *network,
+                               dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       int delay;
+
+       DBG("");
+
+       if (network_table != NULL) {
+               dhcp = g_hash_table_lookup(network_table, network);
+               if (dhcp != NULL && dhcp->started == TRUE)
+                       return -EBUSY;
+       }
+
+       dhcp = g_try_new0(struct connman_dhcpv6, 1);
+       if (dhcp == NULL)
+               return -ENOMEM;
+
+       dhcp->network = network;
+       dhcp->callback = callback;
+       dhcp->stateless = TRUE;
+       dhcp->started = TRUE;
+
+       connman_network_ref(network);
+
+       DBG("replace network %p dhcp %p", network, dhcp);
+
+       g_hash_table_replace(network_table, network, dhcp);
+
+       /* Initial timeout, RFC 3315, 18.1.5 */
+       delay = rand() % 1000;
+
+       dhcp->timeout = g_timeout_add(delay, start_info_req, dhcp);
+
+       return 0;
+}
+
+static void advertise_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcpv6 advertise msg %p", dhcp);
+
+       clear_timer(dhcp);
+
+       if (g_dhcpv6_client_get_status(dhcp_client) != 0) {
+               if (dhcp->callback != NULL)
+                       dhcp->callback(dhcp->network, FALSE);
+               return;
+       }
+
+       dhcp->RT = REQ_TIMEOUT * (1 + get_random());
+       DBG("request initial RT timeout %d msec", dhcp->RT);
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_request, dhcp);
+
+       dhcp->request_count = 1;
+
+       dhcpv6_request(dhcp, TRUE);
+}
+
+static void solicitation_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       /* We get here if server supports rapid commit */
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       DBG("dhcpv6 solicitation msg %p", dhcp);
+
+       clear_timer(dhcp);
+
+       set_addresses(dhcp_client, dhcp);
+}
+
+static gboolean timeout_solicitation(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       dhcp->RT = calc_delay(dhcp->RT, SOL_MAX_RT);
+
+       DBG("solicit RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
+
+       g_dhcp_client_start(dhcp->dhcp_client, NULL);
+
+       return FALSE;
+}
+
+static int dhcpv6_solicitation(struct connman_dhcpv6 *dhcp)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv6;
+       GDHCPClient *dhcp_client;
+       GDHCPClientError error;
+       int index, ret;
+
+       DBG("dhcp %p", dhcp);
+
+       index = connman_network_get_index(dhcp->network);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV6, index, &error);
+       if (error != G_DHCP_CLIENT_ERROR_NONE) {
+               clear_timer(dhcp);
+               return -EINVAL;
+       }
+
+       if (getenv("CONNMAN_DHCPV6_DEBUG"))
+               g_dhcp_client_set_debug(dhcp_client, dhcpv6_debug, "DHCPv6");
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return -EINVAL;
+       }
+
+       ret = set_duid(service, dhcp->network, dhcp_client, index);
+       if (ret < 0) {
+               clear_timer(dhcp);
+               g_dhcp_client_unref(dhcp_client);
+               return ret;
+       }
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_CLIENTID);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_RAPID_COMMIT);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_DNS_SERVERS);
+       g_dhcp_client_set_request(dhcp_client, G_DHCPV6_SNTP_SERVERS);
+
+       g_dhcpv6_client_set_oro(dhcp_client, 2, G_DHCPV6_DNS_SERVERS,
+                               G_DHCPV6_SNTP_SERVERS);
+
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       dhcp->use_ta = __connman_ipconfig_ipv6_privacy_enabled(ipconfig_ipv6);
+
+       g_dhcpv6_client_set_ia(dhcp_client, index,
+                       dhcp->use_ta == TRUE ? G_DHCPV6_IA_TA : G_DHCPV6_IA_NA,
+                       NULL, NULL, FALSE);
+
+       clear_callbacks(dhcp_client);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_SOLICITATION,
+                               solicitation_cb, dhcp);
+
+       g_dhcp_client_register_event(dhcp_client,
+                               G_DHCP_CLIENT_EVENT_ADVERTISE,
+                               advertise_cb, dhcp);
+
+       dhcp->dhcp_client = dhcp_client;
+
+       return g_dhcp_client_start(dhcp_client, NULL);
+}
+
+static gboolean start_solicitation(gpointer user_data)
+{
+       struct connman_dhcpv6 *dhcp = user_data;
+
+       /* Set the retransmission timeout, RFC 3315 chapter 14 */
+       dhcp->RT = SOL_TIMEOUT * (1 + get_random());
+
+       DBG("solicit initial RT timeout %d msec", dhcp->RT);
+
+       dhcp->timeout = g_timeout_add(dhcp->RT, timeout_solicitation, dhcp);
+
+       dhcpv6_solicitation(dhcp);
+
+       return FALSE;
+}
+
+int __connman_dhcpv6_start(struct connman_network *network,
+                               GSList *prefixes, dhcp_cb callback)
+{
+       struct connman_dhcpv6 *dhcp;
+       int delay;
+
+       DBG("");
+
+       if (network_table != NULL) {
+               dhcp = g_hash_table_lookup(network_table, network);
+               if (dhcp != NULL && dhcp->started == TRUE)
+                       return -EBUSY;
+       }
+
+       dhcp = g_try_new0(struct connman_dhcpv6, 1);
+       if (dhcp == NULL)
+               return -ENOMEM;
+
+       dhcp->network = network;
+       dhcp->callback = callback;
+       dhcp->prefixes = prefixes;
+       dhcp->started = TRUE;
+
+       connman_network_ref(network);
+
+       DBG("replace network %p dhcp %p", network, dhcp);
+
+       g_hash_table_replace(network_table, network, dhcp);
+
+       /* Initial timeout, RFC 3315, 17.1.2 */
+       delay = rand() % 1000;
+
+       dhcp->timeout = g_timeout_add(delay, start_solicitation, dhcp);
+
+       return 0;
+}
+
+void __connman_dhcpv6_stop(struct connman_network *network)
+{
+       DBG("");
+
+       if (network_table == NULL)
+               return;
+
+       if (g_hash_table_remove(network_table, network) == TRUE)
+               connman_network_unref(network);
+}
+
+int __connman_dhcpv6_init(void)
+{
+       DBG("");
+
+       srand(time(NULL));
+
+       network_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, remove_network);
+
+       return 0;
+}
+
+void __connman_dhcpv6_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(network_table);
+       network_table = NULL;
+}
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
new file mode 100644 (file)
index 0000000..d061378
--- /dev/null
@@ -0,0 +1,2936 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <gweb/gresolv.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#if defined TIZEN_EXT
+#include <sys/smack.h>
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+struct domain_hdr {
+       uint16_t id;
+       uint8_t rd:1;
+       uint8_t tc:1;
+       uint8_t aa:1;
+       uint8_t opcode:4;
+       uint8_t qr:1;
+       uint8_t rcode:4;
+       uint8_t z:3;
+       uint8_t ra:1;
+       uint16_t qdcount;
+       uint16_t ancount;
+       uint16_t nscount;
+       uint16_t arcount;
+} __attribute__ ((packed));
+#elif __BYTE_ORDER == __BIG_ENDIAN
+struct domain_hdr {
+       uint16_t id;
+       uint8_t qr:1;
+       uint8_t opcode:4;
+       uint8_t aa:1;
+       uint8_t tc:1;
+       uint8_t rd:1;
+       uint8_t ra:1;
+       uint8_t z:3;
+       uint8_t rcode:4;
+       uint16_t qdcount;
+       uint16_t ancount;
+       uint16_t nscount;
+       uint16_t arcount;
+} __attribute__ ((packed));
+#else
+#error "Unknown byte order"
+#endif
+
+struct partial_reply {
+       uint16_t len;
+       uint16_t received;
+       unsigned char buf[];
+};
+
+struct server_data {
+       char *interface;
+       GList *domains;
+       char *server;
+       int protocol;
+       GIOChannel *channel;
+       guint watch;
+       guint timeout;
+       gboolean enabled;
+       gboolean connected;
+       struct partial_reply *incoming_reply;
+};
+
+struct request_data {
+       union {
+               struct sockaddr_in6 __sin6; /* Only for the length */
+               struct sockaddr sa;
+       };
+       socklen_t sa_len;
+       int client_sk;
+       int protocol;
+       guint16 srcid;
+       guint16 dstid;
+       guint16 altid;
+       guint timeout;
+       guint watch;
+       guint numserv;
+       guint numresp;
+       gpointer request;
+       gsize request_len;
+       gpointer name;
+       gpointer resp;
+       gsize resplen;
+       struct listener_data *ifdata;
+       gboolean append_domain;
+};
+
+struct listener_data {
+       char *ifname;
+       GIOChannel *udp_listener_channel;
+       guint udp_listener_watch;
+       GIOChannel *tcp_listener_channel;
+       guint tcp_listener_watch;
+};
+
+struct cache_data {
+       time_t inserted;
+       time_t valid_until;
+       time_t cache_until;
+       int timeout;
+       uint16_t type;
+       uint16_t answers;
+       unsigned int data_len;
+       unsigned char *data; /* contains DNS header + body */
+};
+
+struct cache_entry {
+       char *key;
+       int want_refresh;
+       int hits;
+       struct cache_data *ipv4;
+       struct cache_data *ipv6;
+};
+
+struct domain_question {
+       uint16_t type;
+       uint16_t class;
+} __attribute__ ((packed));
+
+struct domain_rr {
+       uint16_t type;
+       uint16_t class;
+       uint32_t ttl;
+       uint16_t rdlen;
+} __attribute__ ((packed));
+
+/*
+ * We limit how long the cached DNS entry stays in the cache.
+ * By default the TTL (time-to-live) of the DNS response is used
+ * when setting the cache entry life time. The value is in seconds.
+ */
+#define MAX_CACHE_TTL (60 * 30)
+/*
+ * Also limit the other end, cache at least for 30 seconds.
+ */
+#define MIN_CACHE_TTL (30)
+
+/*
+ * We limit the cache size to some sane value so that cached data does
+ * not occupy too much memory. Each cached entry occupies on average
+ * about 100 bytes memory (depending on DNS name length).
+ * Example: caching www.connman.net uses 97 bytes memory.
+ * The value is the max amount of cached DNS responses (count).
+ */
+#define MAX_CACHE_SIZE 256
+
+static int cache_size;
+static GHashTable *cache;
+static int cache_refcount;
+static GSList *server_list = NULL;
+static GSList *request_list = NULL;
+static GHashTable *listener_table = NULL;
+static time_t next_refresh;
+
+static guint16 get_id()
+{
+       return random();
+}
+
+static int protocol_offset(int protocol)
+{
+       switch (protocol) {
+       case IPPROTO_UDP:
+               return 0;
+
+       case IPPROTO_TCP:
+               return 2;
+
+       default:
+               return -EINVAL;
+       }
+
+}
+
+/*
+ * There is a power and efficiency benefit to have entries
+ * in our cache expire at the same time. To this extend,
+ * we round down the cache valid time to common boundaries.
+ */
+static time_t round_down_ttl(time_t end_time, int ttl)
+{
+       if (ttl < 15)
+               return end_time;
+
+       /* Less than 5 minutes, round to 10 second boundary */
+       if (ttl < 300) {
+               end_time = end_time / 10;
+               end_time = end_time * 10;
+       } else { /* 5 or more minutes, round to 30 seconds */
+               end_time = end_time / 30;
+               end_time = end_time * 30;
+       }
+       return end_time;
+}
+
+static struct request_data *find_request(guint16 id)
+{
+       GSList *list;
+
+       for (list = request_list; list; list = list->next) {
+               struct request_data *req = list->data;
+
+               if (req->dstid == id || req->altid == id)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static struct server_data *find_server(const char *interface,
+                                       const char *server,
+                                               int protocol)
+{
+       GSList *list;
+
+       DBG("interface %s server %s", interface, server);
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (interface == NULL && data->interface == NULL &&
+                               g_str_equal(data->server, server) == TRUE &&
+                               data->protocol == protocol)
+                       return data;
+
+               if (interface == NULL ||
+                               data->interface == NULL || data->server == NULL)
+                       continue;
+
+               if (g_str_equal(data->interface, interface) == TRUE &&
+                               g_str_equal(data->server, server) == TRUE &&
+                               data->protocol == protocol)
+                       return data;
+       }
+
+       return NULL;
+}
+
+/* we can keep using the same resolve's */
+static GResolv *ipv4_resolve;
+static GResolv *ipv6_resolve;
+
+static void dummy_resolve_func(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+}
+
+/*
+ * Refresh a DNS entry, but also age the hit count a bit */
+static void refresh_dns_entry(struct cache_entry *entry, char *name)
+{
+       int age = 1;
+
+       if (ipv4_resolve == NULL) {
+               ipv4_resolve = g_resolv_new(0);
+               g_resolv_set_address_family(ipv4_resolve, AF_INET);
+               g_resolv_add_nameserver(ipv4_resolve, "127.0.0.1", 53, 0);
+       }
+
+       if (ipv6_resolve == NULL) {
+               ipv6_resolve = g_resolv_new(0);
+               g_resolv_set_address_family(ipv6_resolve, AF_INET6);
+               g_resolv_add_nameserver(ipv6_resolve, "127.0.0.1", 53, 0);
+       }
+
+       if (entry->ipv4 == NULL) {
+               DBG("Refresing A record for %s", name);
+               g_resolv_lookup_hostname(ipv4_resolve, name,
+                                       dummy_resolve_func, NULL);
+               age = 4;
+       }
+
+       if (entry->ipv6 == NULL) {
+               DBG("Refresing AAAA record for %s", name);
+               g_resolv_lookup_hostname(ipv6_resolve, name,
+                                       dummy_resolve_func, NULL);
+               age = 4;
+       }
+
+       entry->hits -= age;
+       if (entry->hits < 0)
+               entry->hits = 0;
+}
+
+static int dns_name_length(unsigned char *buf)
+{
+       if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */
+               return 2;
+       return strlen((char *)buf);
+}
+
+static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
+{
+       unsigned char *c;
+       uint32_t *i;
+       uint16_t *w;
+       int l;
+
+       /* skip the header */
+       c = buf + 12;
+       len -= 12;
+
+       /* skip the query, which is a name and 2 16 bit words */
+       l = dns_name_length(c);
+       c += l;
+       len -= l;
+       c += 4;
+       len -= 4;
+
+       /* now we get the answer records */
+
+       while (len > 0) {
+               /* first a name */
+               l = dns_name_length(c);
+               c += l;
+               len -= l;
+               if (len < 0)
+                       break;
+               /* then type + class, 2 bytes each */
+               c += 4;
+               len -= 4;
+               if (len < 0)
+                       break;
+
+               /* now the 4 byte TTL field */
+               i = (uint32_t *)c;
+               *i = htonl(new_ttl);
+               c += 4;
+               len -= 4;
+               if (len < 0)
+                       break;
+
+               /* now the 2 byte rdlen field */
+               w = (uint16_t *)c;
+               c += ntohs(*w) + 2;
+               len -= ntohs(*w) + 2;
+       }
+}
+
+static void send_cached_response(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen,
+                               int protocol, int id, uint16_t answers, int ttl)
+{
+       struct domain_hdr *hdr;
+       unsigned char *ptr = buf;
+       int err, offset, dns_len, adj_len = len - 2;
+
+       /*
+        * The cached packet contains always the TCP offset (two bytes)
+        * so skip them for UDP.
+        */
+       switch (protocol) {
+       case IPPROTO_UDP:
+               ptr += 2;
+               len -= 2;
+               dns_len = len;
+               offset = 0;
+               break;
+       case IPPROTO_TCP:
+               offset = 2;
+               dns_len = ptr[0] * 256 + ptr[1];
+               break;
+       default:
+               return;
+       }
+
+       if (len < 12)
+               return;
+
+       hdr = (void *) (ptr + offset);
+
+       hdr->id = id;
+       hdr->qr = 1;
+       hdr->rcode = 0;
+       hdr->ancount = htons(answers);
+       hdr->nscount = 0;
+       hdr->arcount = 0;
+
+       /* if this is a negative reply, we are authorative */
+       if (answers == 0)
+               hdr->aa = 1;
+       else
+               update_cached_ttl((unsigned char *)hdr, adj_len, ttl);
+
+       DBG("sk %d id 0x%04x answers %d ptr %p length %d dns %d",
+               sk, hdr->id, answers, ptr, len, dns_len);
+
+       err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen);
+       if (err < 0) {
+               connman_error("Cannot send cached DNS response: %s",
+                               strerror(errno));
+               return;
+       }
+
+       if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) ||
+                               (dns_len != len && protocol == IPPROTO_UDP))
+               DBG("Packet length mismatch, sent %d wanted %d dns %d",
+                       err, len, dns_len);
+}
+
+static void send_response(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen,
+                               int protocol)
+{
+       struct domain_hdr *hdr;
+       int err, offset = protocol_offset(protocol);
+
+       DBG("sk %d", sk);
+
+       if (offset < 0)
+               return;
+
+       if (len < 12)
+               return;
+
+       hdr = (void *) (buf + offset);
+
+       DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
+
+       hdr->qr = 1;
+       hdr->rcode = 2;
+
+       hdr->ancount = 0;
+       hdr->nscount = 0;
+       hdr->arcount = 0;
+
+       err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
+       if (err < 0) {
+               connman_error("Failed to send DNS response to %d: %s",
+                               sk, strerror(errno));
+               return;
+       }
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+       struct request_data *req = user_data;
+       struct listener_data *ifdata;
+
+       DBG("id 0x%04x", req->srcid);
+
+       if (req == NULL)
+               return FALSE;
+
+       ifdata = req->ifdata;
+
+       request_list = g_slist_remove(request_list, req);
+       req->numserv--;
+
+       if (req->resplen > 0 && req->resp != NULL) {
+               int sk, err;
+
+               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
+
+               err = sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
+                                               &req->sa, req->sa_len);
+               if (err < 0)
+                       return FALSE;
+       } else if (req->request && req->numserv == 0) {
+               struct domain_hdr *hdr;
+
+               if (req->protocol == IPPROTO_TCP) {
+                       hdr = (void *) (req->request + 2);
+                       hdr->id = req->srcid;
+                       send_response(req->client_sk, req->request,
+                               req->request_len, NULL, 0, IPPROTO_TCP);
+
+               } else if (req->protocol == IPPROTO_UDP) {
+                       int sk;
+
+                       hdr = (void *) (req->request);
+                       hdr->id = req->srcid;
+                       sk = g_io_channel_unix_get_fd(
+                                               ifdata->udp_listener_channel);
+                       send_response(sk, req->request, req->request_len,
+                                       &req->sa, req->sa_len, IPPROTO_UDP);
+               }
+       }
+
+       g_free(req->resp);
+       g_free(req);
+
+       return FALSE;
+}
+
+static int append_query(unsigned char *buf, unsigned int size,
+                               const char *query, const char *domain)
+{
+       unsigned char *ptr = buf;
+       int len;
+
+       DBG("query %s domain %s", query, domain);
+
+       while (query != NULL) {
+               const char *tmp;
+
+               tmp = strchr(query, '.');
+               if (tmp == NULL) {
+                       len = strlen(query);
+                       if (len == 0)
+                               break;
+                       *ptr = len;
+                       memcpy(ptr + 1, query, len);
+                       ptr += len + 1;
+                       break;
+               }
+
+               *ptr = tmp - query;
+               memcpy(ptr + 1, query, tmp - query);
+               ptr += tmp - query + 1;
+
+               query = tmp + 1;
+       }
+
+       while (domain != NULL) {
+               const char *tmp;
+
+               tmp = strchr(domain, '.');
+               if (tmp == NULL) {
+                       len = strlen(domain);
+                       if (len == 0)
+                               break;
+                       *ptr = len;
+                       memcpy(ptr + 1, domain, len);
+                       ptr += len + 1;
+                       break;
+               }
+
+               *ptr = tmp - domain;
+               memcpy(ptr + 1, domain, tmp - domain);
+               ptr += tmp - domain + 1;
+
+               domain = tmp + 1;
+       }
+
+       *ptr++ = 0x00;
+
+       return ptr - buf;
+}
+
+static gboolean cache_check_is_valid(struct cache_data *data,
+                               time_t current_time)
+{
+       if (data == NULL)
+               return FALSE;
+
+       if (data->cache_until < current_time)
+               return FALSE;
+
+       return TRUE;
+}
+
+/*
+ * remove stale cached entries so that they can be refreshed
+ */
+static void cache_enforce_validity(struct cache_entry *entry)
+{
+       time_t current_time = time(NULL);
+
+       if (cache_check_is_valid(entry->ipv4, current_time) == FALSE
+                                                       && entry->ipv4) {
+               DBG("cache timeout \"%s\" type A", entry->key);
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+               entry->ipv4 = NULL;
+
+       }
+
+       if (cache_check_is_valid(entry->ipv6, current_time) == FALSE
+                                                       && entry->ipv6) {
+               DBG("cache timeout \"%s\" type AAAA", entry->key);
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+               entry->ipv6 = NULL;
+       }
+}
+
+static uint16_t cache_check_validity(char *question, uint16_t type,
+                               struct cache_entry *entry)
+{
+       time_t current_time = time(NULL);
+       int want_refresh = 0;
+
+       /*
+        * if we have a popular entry, we want a refresh instead of
+        * total destruction of the entry.
+        */
+       if (entry->hits > 2)
+               want_refresh = 1;
+
+       cache_enforce_validity(entry);
+
+       switch (type) {
+       case 1:         /* IPv4 */
+               if (cache_check_is_valid(entry->ipv4, current_time) == FALSE) {
+                       DBG("cache %s \"%s\" type A", entry->ipv4 ?
+                                       "timeout" : "entry missing", question);
+
+                       if (want_refresh)
+                               entry->want_refresh = 1;
+
+                       /*
+                        * We do not remove cache entry if there is still
+                        * valid IPv6 entry found in the cache.
+                        */
+                       if (cache_check_is_valid(entry->ipv6, current_time)
+                                       == FALSE && want_refresh == FALSE) {
+                               g_hash_table_remove(cache, question);
+                               type = 0;
+                       }
+               }
+               break;
+
+       case 28:        /* IPv6 */
+               if (cache_check_is_valid(entry->ipv6, current_time) == FALSE) {
+                       DBG("cache %s \"%s\" type AAAA", entry->ipv6 ?
+                                       "timeout" : "entry missing", question);
+
+                       if (want_refresh)
+                               entry->want_refresh = 1;
+
+                       if (cache_check_is_valid(entry->ipv4, current_time)
+                                       == FALSE && want_refresh == FALSE) {
+                               g_hash_table_remove(cache, question);
+                               type = 0;
+                       }
+               }
+               break;
+       }
+
+       return type;
+}
+
+static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
+{
+       char *question;
+       struct cache_entry *entry;
+       struct domain_question *q;
+       uint16_t type;
+       int offset, proto_offset;
+
+       if (request == NULL)
+               return NULL;
+
+       proto_offset = protocol_offset(proto);
+       if (proto_offset < 0)
+               return NULL;
+
+       question = request + proto_offset + 12;
+
+       offset = strlen(question) + 1;
+       q = (void *) (question + offset);
+       type = ntohs(q->type);
+
+       /* We only cache either A (1) or AAAA (28) requests */
+       if (type != 1 && type != 28)
+               return NULL;
+
+       entry = g_hash_table_lookup(cache, question);
+       if (entry == NULL)
+               return NULL;
+
+       type = cache_check_validity(question, type, entry);
+       if (type == 0)
+               return NULL;
+
+       *qtype = type;
+       return entry;
+}
+
+/*
+ * Get a label/name from DNS resource record. The function decompresses the
+ * label if necessary. The function does not convert the name to presentation
+ * form. This means that the result string will contain label lengths instead
+ * of dots between labels. We intentionally do not want to convert to dotted
+ * format so that we can cache the wire format string directly.
+ */
+static int get_name(int counter,
+               unsigned char *pkt, unsigned char *start, unsigned char *max,
+               unsigned char *output, int output_max, int *output_len,
+               unsigned char **end, char *name, int *name_len)
+{
+       unsigned char *p;
+
+       /* Limit recursion to 10 (this means up to 10 labels in domain name) */
+       if (counter > 10)
+               return -EINVAL;
+
+       p = start;
+       while (*p) {
+               if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+                       uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
+
+                       if (offset >= max - pkt)
+                               return -ENOBUFS;
+
+                       if (*end == NULL)
+                               *end = p + 2;
+
+                       return get_name(counter + 1, pkt, pkt + offset, max,
+                                       output, output_max, output_len, end,
+                                       name, name_len);
+               } else {
+                       unsigned label_len = *p;
+
+                       if (pkt + label_len > max)
+                               return -ENOBUFS;
+
+                       if (*output_len > output_max)
+                               return -ENOBUFS;
+
+                       /*
+                        * We need the original name in order to check
+                        * if this answer is the correct one.
+                        */
+                       name[(*name_len)++] = label_len;
+                       memcpy(name + *name_len, p + 1, label_len + 1);
+                       *name_len += label_len;
+
+                       /* We compress the result */
+                       output[0] = NS_CMPRSFLGS;
+                       output[1] = 0x0C;
+                       *output_len = 2;
+
+                       p += label_len + 1;
+
+                       if (*end == NULL)
+                               *end = p;
+
+                       if (p >= max)
+                               return -ENOBUFS;
+               }
+       }
+
+       return 0;
+}
+
+static int parse_rr(unsigned char *buf, unsigned char *start,
+                       unsigned char *max,
+                       unsigned char *response, unsigned int *response_size,
+                       uint16_t *type, uint16_t *class, int *ttl, int *rdlen,
+                       unsigned char **end,
+                       char *name)
+{
+       struct domain_rr *rr;
+       int err, offset;
+       int name_len = 0, output_len = 0, max_rsp = *response_size;
+
+       err = get_name(0, buf, start, max, response, max_rsp,
+               &output_len, end, name, &name_len);
+       if (err < 0)
+               return err;
+
+       offset = output_len;
+
+       if ((unsigned int) offset > *response_size)
+               return -ENOBUFS;
+
+       rr = (void *) (*end);
+
+       if (rr == NULL)
+               return -EINVAL;
+
+       *type = ntohs(rr->type);
+       *class = ntohs(rr->class);
+       *ttl = ntohl(rr->ttl);
+       *rdlen = ntohs(rr->rdlen);
+
+       if (*ttl < 0)
+               return -EINVAL;
+
+       memcpy(response + offset, *end, sizeof(struct domain_rr));
+
+       offset += sizeof(struct domain_rr);
+       *end += sizeof(struct domain_rr);
+
+       if ((unsigned int) (offset + *rdlen) > *response_size)
+               return -ENOBUFS;
+
+       memcpy(response + offset, *end, *rdlen);
+
+       *end += *rdlen;
+
+       *response_size = offset + *rdlen;
+
+       return 0;
+}
+
+static gboolean check_alias(GSList *aliases, char *name)
+{
+       GSList *list;
+
+       if (aliases != NULL) {
+               for (list = aliases; list; list = list->next) {
+                       int len = strlen((char *)list->data);
+                       if (strncmp((char *)list->data, name, len) == 0)
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static int parse_response(unsigned char *buf, int buflen,
+                       char *question, int qlen,
+                       uint16_t *type, uint16_t *class, int *ttl,
+                       unsigned char *response, unsigned int *response_len,
+                       uint16_t *answers)
+{
+       struct domain_hdr *hdr = (void *) buf;
+       struct domain_question *q;
+       unsigned char *ptr;
+       uint16_t qdcount = ntohs(hdr->qdcount);
+       uint16_t ancount = ntohs(hdr->ancount);
+       int err, i;
+       uint16_t qtype, qclass;
+       unsigned char *next = NULL;
+       unsigned int maxlen = *response_len;
+       GSList *aliases = NULL, *list;
+       char name[NS_MAXDNAME + 1];
+
+       if (buflen < 12)
+               return -EINVAL;
+
+       DBG("qr %d qdcount %d", hdr->qr, qdcount);
+
+       /* We currently only cache responses where question count is 1 */
+       if (hdr->qr != 1 || qdcount != 1)
+               return -EINVAL;
+
+       ptr = buf + sizeof(struct domain_hdr);
+
+       strncpy(question, (char *) ptr, qlen);
+       qlen = strlen(question);
+       ptr += qlen + 1; /* skip \0 */
+
+       q = (void *) ptr;
+       qtype = ntohs(q->type);
+
+       /* We cache only A and AAAA records */
+       if (qtype != 1 && qtype != 28)
+               return -ENOMSG;
+
+       qclass = ntohs(q->class);
+
+       ptr += 2 + 2; /* ptr points now to answers */
+
+       err = -ENOMSG;
+       *response_len = 0;
+       *answers = 0;
+
+       /*
+        * We have a bunch of answers (like A, AAAA, CNAME etc) to
+        * A or AAAA question. We traverse the answers and parse the
+        * resource records. Only A and AAAA records are cached, all
+        * the other records in answers are skipped.
+        */
+       for (i = 0; i < ancount; i++) {
+               /*
+                * Get one address at a time to this buffer.
+                * The max size of the answer is
+                *   2 (pointer) + 2 (type) + 2 (class) +
+                *   4 (ttl) + 2 (rdlen) + addr (16 or 4) = 28
+                * for A or AAAA record.
+                * For CNAME the size can be bigger.
+                */
+               unsigned char rsp[NS_MAXCDNAME];
+               unsigned int rsp_len = sizeof(rsp) - 1;
+               int ret, rdlen;
+
+               memset(rsp, 0, sizeof(rsp));
+
+               ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
+                       type, class, ttl, &rdlen, &next, name);
+               if (ret != 0) {
+                       err = ret;
+                       goto out;
+               }
+
+               /*
+                * Now rsp contains compressed or uncompressed resource
+                * record. Next we check if this record answers the question.
+                * The name var contains the uncompressed label.
+                * One tricky bit is the CNAME records as they alias
+                * the name we might be interested in.
+                */
+
+               /*
+                * Go to next answer if the class is not the one we are
+                * looking for.
+                */
+               if (*class != qclass) {
+                       ptr = next;
+                       next = NULL;
+                       continue;
+               }
+
+               /*
+                * Try to resolve aliases also, type is CNAME(5).
+                * This is important as otherwise the aliased names would not
+                * be cached at all as the cache would not contain the aliased
+                * question.
+                *
+                * If any CNAME is found in DNS packet, then we cache the alias
+                * IP address instead of the question (as the server
+                * said that question has only an alias).
+                * This means in practice that if e.g., ipv6.google.com is
+                * queried, DNS server returns CNAME of that name which is
+                * ipv6.l.google.com. We then cache the address of the CNAME
+                * but return the question name to client. So the alias
+                * status of the name is not saved in cache and thus not
+                * returned to the client. We do not return DNS packets from
+                * cache to client saying that ipv6.google.com is an alias to
+                * ipv6.l.google.com but we return instead a DNS packet that
+                * says ipv6.google.com has address xxx which is in fact the
+                * address of ipv6.l.google.com. For caching purposes this
+                * should not cause any issues.
+                */
+               if (*type == 5 && strncmp(question, name, qlen) == 0) {
+                       /*
+                        * So now the alias answered the question. This is
+                        * not very useful from caching point of view as
+                        * the following A or AAAA records will not match the
+                        * question. We need to find the real A/AAAA record
+                        * of the alias and cache that.
+                        */
+                       unsigned char *end = NULL;
+                       int name_len = 0, output_len;
+
+                       memset(rsp, 0, sizeof(rsp));
+                       rsp_len = sizeof(rsp) - 1;
+
+                       /*
+                        * Alias is in rdata part of the message,
+                        * and next-rdlen points to it. So we need to get
+                        * the real name of the alias.
+                        */
+                       ret = get_name(0, buf, next - rdlen, buf + buflen,
+                                       rsp, rsp_len, &output_len, &end,
+                                       name, &name_len);
+                       if (ret != 0) {
+                               /* just ignore the error at this point */
+                               ptr = next;
+                               next = NULL;
+                               continue;
+                       }
+
+                       /*
+                        * We should now have the alias of the entry we might
+                        * want to cache. Just remember it for a while.
+                        * We check the alias list when we have parsed the
+                        * A or AAAA record.
+                        */
+                       aliases = g_slist_prepend(aliases, g_strdup(name));
+
+                       ptr = next;
+                       next = NULL;
+                       continue;
+               }
+
+               if (*type == qtype) {
+                       /*
+                        * We found correct type (A or AAAA)
+                        */
+                       if (check_alias(aliases, name) == TRUE ||
+                               (aliases == NULL && strncmp(question, name,
+                                                       qlen) == 0)) {
+                               /*
+                                * We found an alias or the name of the rr
+                                * matches the question. If so, we append
+                                * the compressed label to the cache.
+                                * The end result is a response buffer that
+                                * will contain one or more cached and
+                                * compressed resource records.
+                                */
+                               if (*response_len + rsp_len > maxlen) {
+                                       err = -ENOBUFS;
+                                       goto out;
+                               }
+                               memcpy(response + *response_len, rsp, rsp_len);
+                               *response_len += rsp_len;
+                               (*answers)++;
+                               err = 0;
+                       }
+               }
+
+               ptr = next;
+               next = NULL;
+       }
+
+out:
+       for (list = aliases; list; list = list->next)
+               g_free(list->data);
+       g_slist_free(aliases);
+
+       return err;
+}
+
+struct cache_timeout {
+       time_t current_time;
+       int max_timeout;
+       int try_harder;
+};
+
+static gboolean cache_check_entry(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_timeout *data = user_data;
+       struct cache_entry *entry = value;
+       int max_timeout;
+
+       /* Scale the number of hits by half as part of cache aging */
+
+       entry->hits /= 2;
+
+       /*
+        * If either IPv4 or IPv6 cached entry has expired, we
+        * remove both from the cache.
+        */
+
+       if (entry->ipv4 != NULL && entry->ipv4->timeout > 0) {
+               max_timeout = entry->ipv4->cache_until;
+               if (max_timeout > data->max_timeout)
+                       data->max_timeout = max_timeout;
+
+               if (entry->ipv4->cache_until < data->current_time)
+                       return TRUE;
+       }
+
+       if (entry->ipv6 != NULL && entry->ipv6->timeout > 0) {
+               max_timeout = entry->ipv6->cache_until;
+               if (max_timeout > data->max_timeout)
+                       data->max_timeout = max_timeout;
+
+               if (entry->ipv6->cache_until < data->current_time)
+                       return TRUE;
+       }
+
+       /*
+        * if we're asked to try harder, also remove entries that have
+        * few hits
+        */
+       if (data->try_harder && entry->hits < 4)
+               return TRUE;
+
+       return FALSE;
+}
+
+static void cache_cleanup(void)
+{
+       static int max_timeout;
+       struct cache_timeout data;
+       int count = 0;
+
+       data.current_time = time(NULL);
+       data.max_timeout = 0;
+       data.try_harder = 0;
+
+       /*
+        * In the first pass, we only remove entries that have timed out.
+        * We use a cache of the first time to expire to do this only
+        * when it makes sense.
+        */
+       if (max_timeout <= data.current_time) {
+               count = g_hash_table_foreach_remove(cache, cache_check_entry,
+                                               &data);
+       }
+       DBG("removed %d in the first pass", count);
+
+       /*
+        * In the second pass, if the first pass turned up blank,
+        * we also expire entries with a low hit count,
+        * while aging the hit count at the same time.
+        */
+       data.try_harder = 1;
+       if (count == 0)
+               count = g_hash_table_foreach_remove(cache, cache_check_entry,
+                                               &data);
+
+       if (count == 0)
+               /*
+                * If we could not remove anything, then remember
+                * what is the max timeout and do nothing if we
+                * have not yet reached it. This will prevent
+                * constant traversal of the cache if it is full.
+                */
+               max_timeout = data.max_timeout;
+       else
+               max_timeout = 0;
+}
+
+static gboolean cache_invalidate_entry(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_entry *entry = value;
+
+       /* first, delete any expired elements */
+       cache_enforce_validity(entry);
+
+       /* if anything is not expired, mark the entry for refresh */
+       if (entry->hits > 0 && (entry->ipv4 || entry->ipv6))
+               entry->want_refresh = 1;
+
+       /* delete the cached data */
+       if (entry->ipv4) {
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+               entry->ipv4 = NULL;
+       }
+
+       if (entry->ipv6) {
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+               entry->ipv6 = NULL;
+       }
+
+       /* keep the entry if we want it refreshed, delete it otherwise */
+       if (entry->want_refresh)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+/*
+ * cache_invalidate is called from places where the DNS landscape
+ * has changed, say because connections are added or we entered a VPN.
+ * The logic is to wipe all cache data, but mark all non-expired
+ * parts of the cache for refresh rather than deleting the whole cache.
+ */
+static void cache_invalidate(void)
+{
+       DBG("Invalidating the DNS cache %p", cache);
+
+       if (cache == NULL)
+               return;
+
+       g_hash_table_foreach_remove(cache, cache_invalidate_entry, NULL);
+}
+
+static void cache_refresh_entry(struct cache_entry *entry)
+{
+
+       cache_enforce_validity(entry);
+
+       if (entry->hits > 2 && entry->ipv4 == NULL)
+               entry->want_refresh = 1;
+       if (entry->hits > 2 && entry->ipv6 == NULL)
+               entry->want_refresh = 1;
+
+       if (entry->want_refresh) {
+               char *c;
+               char dns_name[NS_MAXDNAME + 1];
+               entry->want_refresh = 0;
+
+               /* turn a DNS name into a hostname with dots */
+               strncpy(dns_name, entry->key, NS_MAXDNAME);
+               c = dns_name;
+               while (c && *c) {
+                       int jump;
+                       jump = *c;
+                       *c = '.';
+                       c += jump + 1;
+               }
+               DBG("Refreshing %s\n", dns_name);
+               /* then refresh the hostname */
+               refresh_dns_entry(entry, &dns_name[1]);
+       }
+}
+
+static void cache_refresh_iterator(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct cache_entry *entry = value;
+
+       cache_refresh_entry(entry);
+}
+
+static void cache_refresh(void)
+{
+       if (cache == NULL)
+               return;
+
+       g_hash_table_foreach(cache, cache_refresh_iterator, NULL);
+}
+
+static int reply_query_type(unsigned char *msg, int len)
+{
+       unsigned char *c;
+       uint16_t *w;
+       int l;
+       int type;
+
+       /* skip the header */
+       c = msg + sizeof(struct domain_hdr);
+       len -= sizeof(struct domain_hdr);
+
+       if (len < 0)
+               return 0;
+
+       /* now the query, which is a name and 2 16 bit words */
+       l = dns_name_length(c) + 1;
+       c += l;
+       w = (uint16_t *) c;
+       type = ntohs(*w);
+
+       return type;
+}
+
+static int cache_update(struct server_data *srv, unsigned char *msg,
+                       unsigned int msg_len)
+{
+       int offset = protocol_offset(srv->protocol);
+       int err, qlen, ttl = 0;
+       uint16_t answers = 0, type = 0, class = 0;
+       struct domain_hdr *hdr = (void *)(msg + offset);
+       struct domain_question *q;
+       struct cache_entry *entry;
+       struct cache_data *data;
+       char question[NS_MAXDNAME + 1];
+       unsigned char response[NS_MAXDNAME + 1];
+       unsigned char *ptr;
+       unsigned int rsplen;
+       gboolean new_entry = TRUE;
+       time_t current_time;
+
+       if (cache_size >= MAX_CACHE_SIZE) {
+               cache_cleanup();
+               if (cache_size >= MAX_CACHE_SIZE)
+                       return 0;
+       }
+
+       current_time = time(NULL);
+
+       /* don't do a cache refresh more than twice a minute */
+       if (next_refresh < current_time) {
+               cache_refresh();
+               next_refresh = current_time + 30;
+       }
+
+       if (offset < 0)
+               return 0;
+
+       DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
+
+       /* Continue only if response code is 0 (=ok) */
+       if (hdr->rcode != 0)
+               return 0;
+
+       rsplen = sizeof(response) - 1;
+       question[sizeof(question) - 1] = '\0';
+
+       err = parse_response(msg + offset, msg_len - offset,
+                               question, sizeof(question) - 1,
+                               &type, &class, &ttl,
+                               response, &rsplen, &answers);
+
+       /*
+        * special case: if we do a ipv6 lookup and get no result
+        * for a record that's already in our ipv4 cache.. we want
+        * to cache the negative response.
+        */
+       if ((err == -ENOMSG || err == -ENOBUFS) &&
+                       reply_query_type(msg + offset,
+                                       msg_len - offset) == 28) {
+               entry = g_hash_table_lookup(cache, question);
+               if (entry && entry->ipv4 && entry->ipv6 == NULL) {
+                       int cache_offset = 0;
+
+                       data = g_try_new(struct cache_data, 1);
+                       if (data == NULL)
+                               return -ENOMEM;
+                       data->inserted = entry->ipv4->inserted;
+                       data->type = type;
+                       data->answers = hdr->ancount;
+                       data->timeout = entry->ipv4->timeout;
+                       if (srv->protocol == IPPROTO_UDP)
+                               cache_offset = 2;
+                       data->data_len = msg_len + cache_offset;
+                       data->data = ptr = g_malloc(data->data_len);
+                       ptr[0] = (data->data_len - 2) / 256;
+                       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+                       if (srv->protocol == IPPROTO_UDP)
+                               ptr += 2;
+                       data->valid_until = entry->ipv4->valid_until;
+                       data->cache_until = entry->ipv4->cache_until;
+                       memcpy(ptr, msg, msg_len);
+                       entry->ipv6 = data;
+                       /*
+                        * we will get a "hit" when we serve the response
+                        * out of the cache
+                        */
+                       entry->hits--;
+                       if (entry->hits < 0)
+                               entry->hits = 0;
+                       return 0;
+               }
+       }
+
+       if (err < 0 || ttl == 0)
+               return 0;
+
+       qlen = strlen(question);
+
+       /*
+        * If the cache contains already data, check if the
+        * type of the cached data is the same and do not add
+        * to cache if data is already there.
+        * This is needed so that we can cache both A and AAAA
+        * records for the same name.
+        */
+       entry = g_hash_table_lookup(cache, question);
+       if (entry == NULL) {
+               entry = g_try_new(struct cache_entry, 1);
+               if (entry == NULL)
+                       return -ENOMEM;
+
+               data = g_try_new(struct cache_data, 1);
+               if (data == NULL) {
+                       g_free(entry);
+                       return -ENOMEM;
+               }
+
+               entry->key = g_strdup(question);
+               entry->ipv4 = entry->ipv6 = NULL;
+               entry->want_refresh = 0;
+               entry->hits = 0;
+
+               if (type == 1)
+                       entry->ipv4 = data;
+               else
+                       entry->ipv6 = data;
+       } else {
+               if (type == 1 && entry->ipv4 != NULL)
+                       return 0;
+
+               if (type == 28 && entry->ipv6 != NULL)
+                       return 0;
+
+               data = g_try_new(struct cache_data, 1);
+               if (data == NULL)
+                       return -ENOMEM;
+
+               if (type == 1)
+                       entry->ipv4 = data;
+               else
+                       entry->ipv6 = data;
+
+               /*
+                * compensate for the hit we'll get for serving
+                * the response out of the cache
+                */
+               entry->hits--;
+               if (entry->hits < 0)
+                       entry->hits = 0;
+
+               new_entry = FALSE;
+       }
+
+       if (ttl < MIN_CACHE_TTL)
+               ttl = MIN_CACHE_TTL;
+
+       data->inserted = current_time;
+       data->type = type;
+       data->answers = answers;
+       data->timeout = ttl;
+       /*
+        * The "2" in start of the length is the TCP offset. We allocate it
+        * here even for UDP packet because it simplifies the sending
+        * of cached packet.
+        */
+       data->data_len = 2 + 12 + qlen + 1 + 2 + 2 + rsplen;
+       data->data = ptr = g_malloc(data->data_len);
+       data->valid_until = current_time + ttl;
+
+       /*
+        * Restrict the cached DNS record TTL to some sane value
+        * in order to prevent data staying in the cache too long.
+        */
+       if (ttl > MAX_CACHE_TTL)
+               ttl = MAX_CACHE_TTL;
+
+       data->cache_until = round_down_ttl(current_time + ttl, ttl);
+
+       if (data->data == NULL) {
+               g_free(entry->key);
+               g_free(data);
+               g_free(entry);
+               return -ENOMEM;
+       }
+
+       /*
+        * We cache the two extra bytes at the start of the message
+        * in a TCP packet. When sending UDP packet, we skip the first
+        * two bytes. This way we do not need to know the format
+        * (UDP/TCP) of the cached message.
+        */
+       ptr[0] = (data->data_len - 2) / 256;
+       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+       if (srv->protocol == IPPROTO_UDP)
+               ptr += 2;
+
+       memcpy(ptr, msg, offset + 12);
+       memcpy(ptr + offset + 12, question, qlen + 1); /* copy also the \0 */
+
+       q = (void *) (ptr + offset + 12 + qlen + 1);
+       q->type = htons(type);
+       q->class = htons(class);
+       memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question),
+               response, rsplen);
+
+       if (new_entry == TRUE) {
+               g_hash_table_replace(cache, entry->key, entry);
+               cache_size++;
+       }
+
+       DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u "
+                                                               "dns len %u",
+               cache_size, new_entry ? "new " : "old ",
+               question, type, ttl,
+               sizeof(*entry) + sizeof(*data) + data->data_len + qlen,
+               data->data_len,
+               srv->protocol == IPPROTO_TCP ?
+                       (unsigned int)(data->data[0] * 256 + data->data[1]) :
+                       data->data_len);
+
+       return 0;
+}
+
+static int ns_resolv(struct server_data *server, struct request_data *req,
+                               gpointer request, gpointer name)
+{
+       GList *list;
+       int sk, err, type = 0;
+       char *dot, *lookup = (char *) name;
+       struct cache_entry *entry;
+
+       entry = cache_check(request, &type, req->protocol);
+       if (entry != NULL) {
+               int ttl_left = 0;
+               struct cache_data *data;
+
+               DBG("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
+               if (type == 1)
+                       data = entry->ipv4;
+               else
+                       data = entry->ipv6;
+
+               if (data) {
+                       ttl_left = data->valid_until - time(NULL);
+                       entry->hits++;
+               }
+
+               if (data != NULL && req->protocol == IPPROTO_TCP) {
+                       send_cached_response(req->client_sk, data->data,
+                                       data->data_len, NULL, 0, IPPROTO_TCP,
+                                       req->srcid, data->answers, ttl_left);
+                       return 1;
+               }
+
+               if (data != NULL && req->protocol == IPPROTO_UDP) {
+                       int sk;
+                       sk = g_io_channel_unix_get_fd(
+                                       req->ifdata->udp_listener_channel);
+
+                       send_cached_response(sk, data->data,
+                               data->data_len, &req->sa, req->sa_len,
+                               IPPROTO_UDP, req->srcid, data->answers,
+                               ttl_left);
+                       return 1;
+               }
+       }
+
+       sk = g_io_channel_unix_get_fd(server->channel);
+
+       err = send(sk, request, req->request_len, MSG_NOSIGNAL);
+       if (err < 0)
+               return -EIO;
+
+       req->numserv++;
+
+       /* If we have more than one dot, we don't add domains */
+       dot = strchr(lookup, '.');
+       if (dot != NULL && dot != lookup + strlen(lookup) - 1)
+               return 0;
+
+       if (server->domains != NULL && server->domains->data != NULL)
+               req->append_domain = TRUE;
+
+       for (list = server->domains; list; list = list->next) {
+               char *domain;
+               unsigned char alt[1024];
+               struct domain_hdr *hdr = (void *) &alt;
+               int altlen, domlen, offset;
+
+               domain = list->data;
+
+               if (domain == NULL)
+                       continue;
+
+               offset = protocol_offset(server->protocol);
+               if (offset < 0)
+                       return offset;
+
+               domlen = strlen(domain) + 1;
+               if (domlen < 5)
+                       return -EINVAL;
+
+               alt[offset] = req->altid & 0xff;
+               alt[offset + 1] = req->altid >> 8;
+
+               memcpy(alt + offset + 2, request + offset + 2, 10);
+               hdr->qdcount = htons(1);
+
+               altlen = append_query(alt + offset + 12, sizeof(alt) - 12,
+                                       name, domain);
+               if (altlen < 0)
+                       return -EINVAL;
+
+               altlen += 12;
+
+               memcpy(alt + offset + altlen,
+                       request + offset + altlen - domlen,
+                               req->request_len - altlen - offset + domlen);
+
+               if (server->protocol == IPPROTO_TCP) {
+                       int req_len = req->request_len + domlen - 2;
+
+                       alt[0] = (req_len >> 8) & 0xff;
+                       alt[1] = req_len & 0xff;
+               }
+
+               err = send(sk, alt, req->request_len + domlen, MSG_NOSIGNAL);
+               if (err < 0)
+                       return -EIO;
+
+               req->numserv++;
+       }
+
+       return 0;
+}
+
+static void destroy_request_data(struct request_data *req)
+{
+       if (req->timeout > 0)
+               g_source_remove(req->timeout);
+
+       g_free(req->resp);
+       g_free(req->request);
+       g_free(req->name);
+       g_free(req);
+}
+
+static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
+                               struct server_data *data)
+{
+       struct domain_hdr *hdr;
+       struct request_data *req;
+       int dns_id, sk, err, offset = protocol_offset(protocol);
+       struct listener_data *ifdata;
+
+       if (offset < 0)
+               return offset;
+
+       hdr = (void *)(reply + offset);
+       dns_id = reply[offset] | reply[offset + 1] << 8;
+
+       DBG("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+
+       req = find_request(dns_id);
+       if (req == NULL)
+               return -EINVAL;
+
+       DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
+
+       ifdata = req->ifdata;
+
+       reply[offset] = req->srcid & 0xff;
+       reply[offset + 1] = req->srcid >> 8;
+
+       req->numresp++;
+
+       if (hdr->rcode == 0 || req->resp == NULL) {
+
+               /*
+                * If the domain name was append
+                * remove it before forwarding the reply.
+                */
+               if (req->append_domain == TRUE) {
+                       unsigned char *ptr;
+                       uint8_t host_len;
+                       unsigned int domain_len;
+
+                       /*
+                        * ptr points to the first char of the hostname.
+                        * ->hostname.domain.net
+                        */
+                       ptr = reply + offset + sizeof(struct domain_hdr);
+                       host_len = *ptr;
+                       domain_len = strlen((const char *)ptr + host_len + 1);
+
+                       /*
+                        * Remove the domain name and replace it by the end
+                        * of reply. Check if the domain is really there
+                        * before trying to copy the data. The domain_len can
+                        * be 0 because if the original query did not contain
+                        * a domain name, then we are sending two packets,
+                        * first without the domain name and the second packet
+                        * with domain name. The append_domain is set to true
+                        * even if we sent the first packet without domain
+                        * name. In this case we end up in this branch.
+                        */
+                       if (domain_len > 0) {
+                               /*
+                                * Note that we must use memmove() here,
+                                * because the memory areas can overlap.
+                                */
+                               memmove(ptr + host_len + 1,
+                                       ptr + host_len + domain_len + 1,
+                                       reply_len - (ptr - reply + domain_len));
+
+                               reply_len = reply_len - domain_len;
+                       }
+               }
+
+               g_free(req->resp);
+               req->resplen = 0;
+
+               req->resp = g_try_malloc(reply_len);
+               if (req->resp == NULL)
+                       return -ENOMEM;
+
+               memcpy(req->resp, reply, reply_len);
+               req->resplen = reply_len;
+
+               cache_update(data, reply, reply_len);
+       }
+
+       if (hdr->rcode > 0 && req->numresp < req->numserv)
+               return -EINVAL;
+
+       request_list = g_slist_remove(request_list, req);
+
+       if (protocol == IPPROTO_UDP) {
+               sk = g_io_channel_unix_get_fd(ifdata->udp_listener_channel);
+               err = sendto(sk, req->resp, req->resplen, 0,
+                            &req->sa, req->sa_len);
+       } else {
+               sk = req->client_sk;
+               err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
+               close(sk);
+       }
+
+       if (err < 0)
+               DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk,
+                       protocol, errno, strerror(errno));
+       else
+               DBG("proto %d sent %d bytes to %d", protocol, err, sk);
+
+       destroy_request_data(req);
+
+       return err;
+}
+
+static void cache_element_destroy(gpointer value)
+{
+       struct cache_entry *entry = value;
+
+       if (entry == NULL)
+               return;
+
+       if (entry->ipv4 != NULL) {
+               g_free(entry->ipv4->data);
+               g_free(entry->ipv4);
+       }
+
+       if (entry->ipv6 != NULL) {
+               g_free(entry->ipv6->data);
+               g_free(entry->ipv6);
+       }
+
+       g_free(entry->key);
+       g_free(entry);
+
+       if (--cache_size < 0)
+               cache_size = 0;
+}
+
+static gboolean try_remove_cache(gpointer user_data)
+{
+       if (__sync_fetch_and_sub(&cache_refcount, 1) == 1) {
+               DBG("No cache users, removing it.");
+
+               g_hash_table_destroy(cache);
+               cache = NULL;
+       }
+
+       return FALSE;
+}
+
+static void destroy_server(struct server_data *server)
+{
+       GList *list;
+
+       DBG("interface %s server %s sock %d", server->interface, server->server,
+               g_io_channel_unix_get_fd(server->channel));
+
+       server_list = g_slist_remove(server_list, server);
+
+       if (server->watch > 0)
+               g_source_remove(server->watch);
+
+       if (server->timeout > 0)
+               g_source_remove(server->timeout);
+
+       g_io_channel_unref(server->channel);
+
+       if (server->protocol == IPPROTO_UDP)
+               DBG("Removing DNS server %s", server->server);
+
+       g_free(server->incoming_reply);
+       g_free(server->server);
+       for (list = server->domains; list; list = list->next) {
+               char *domain = list->data;
+
+               server->domains = g_list_remove(server->domains, domain);
+               g_free(domain);
+       }
+       g_free(server->interface);
+
+       /*
+        * We do not remove cache right away but delay it few seconds.
+        * The idea is that when IPv6 DNS server is added via RDNSS, it has a
+        * lifetime. When the lifetime expires we decrease the refcount so it
+        * is possible that the cache is then removed. Because a new DNS server
+        * is usually created almost immediately we would then loose the cache
+        * without any good reason. The small delay allows the new RDNSS to
+        * create a new DNS server instance and the refcount does not go to 0.
+        */
+       g_timeout_add_seconds(3, try_remove_cache, NULL);
+
+       g_free(server);
+}
+
+static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       unsigned char buf[4096];
+       int sk, err, len;
+       struct server_data *data = user_data;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               connman_error("Error with UDP server %s", data->server);
+               data->watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       len = recv(sk, buf, sizeof(buf), 0);
+       if (len < 12)
+               return TRUE;
+
+       err = forward_dns_reply(buf, len, IPPROTO_UDP, data);
+       if (err < 0)
+               return TRUE;
+
+       return TRUE;
+}
+
+static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       int sk;
+       struct server_data *server = user_data;
+
+       sk = g_io_channel_unix_get_fd(channel);
+       if (sk == 0)
+               return FALSE;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               GSList *list;
+hangup:
+               DBG("TCP server channel closed, sk %d", sk);
+
+               /*
+                * Discard any partial response which is buffered; better
+                * to get a proper response from a working server.
+                */
+               g_free(server->incoming_reply);
+               server->incoming_reply = NULL;
+
+               for (list = request_list; list; list = list->next) {
+                       struct request_data *req = list->data;
+                       struct domain_hdr *hdr;
+
+                       if (req->protocol == IPPROTO_UDP)
+                               continue;
+
+                       if (req->request == NULL)
+                               continue;
+
+                       /*
+                        * If we're not waiting for any further response
+                        * from another name server, then we send an error
+                        * response to the client.
+                        */
+                       if (req->numserv && --(req->numserv))
+                               continue;
+
+                       hdr = (void *) (req->request + 2);
+                       hdr->id = req->srcid;
+                       send_response(req->client_sk, req->request,
+                               req->request_len, NULL, 0, IPPROTO_TCP);
+
+                       request_list = g_slist_remove(request_list, req);
+               }
+
+               destroy_server(server);
+
+               return FALSE;
+       }
+
+       if ((condition & G_IO_OUT) && !server->connected) {
+               GSList *list;
+               GList *domains;
+               int no_request_sent = TRUE;
+               struct server_data *udp_server;
+
+               udp_server = find_server(server->interface, server->server,
+                                                               IPPROTO_UDP);
+               if (udp_server != NULL) {
+                       for (domains = udp_server->domains; domains;
+                                               domains = domains->next) {
+                               char *dom = domains->data;
+
+                               DBG("Adding domain %s to %s",
+                                               dom, server->server);
+
+                               server->domains = g_list_append(server->domains,
+                                                               g_strdup(dom));
+                       }
+               }
+
+               server->connected = TRUE;
+               server_list = g_slist_append(server_list, server);
+
+               if (server->timeout > 0) {
+                       g_source_remove(server->timeout);
+                       server->timeout = 0;
+               }
+
+               for (list = request_list; list; ) {
+                       struct request_data *req = list->data;
+                       int status;
+
+                       if (req->protocol == IPPROTO_UDP) {
+                               list = list->next;
+                               continue;
+                       }
+
+                       DBG("Sending req %s over TCP", (char *)req->name);
+
+                       status = ns_resolv(server, req,
+                                               req->request, req->name);
+                       if (status > 0) {
+                               /*
+                                * A cached result was sent,
+                                * so the request can be released
+                                */
+                               list = list->next;
+                               request_list = g_slist_remove(request_list, req);
+                               destroy_request_data(req);
+                               continue;
+                       }
+
+                       if (status < 0) {
+                               list = list->next;
+                               continue;
+                       }
+
+                       no_request_sent = FALSE;
+
+                       if (req->timeout > 0)
+                               g_source_remove(req->timeout);
+
+                       req->timeout = g_timeout_add_seconds(30,
+                                               request_timeout, req);
+                       list = list->next;
+               }
+
+               if (no_request_sent == TRUE) {
+                       destroy_server(server);
+                       return FALSE;
+               }
+
+       } else if (condition & G_IO_IN) {
+               struct partial_reply *reply = server->incoming_reply;
+               int bytes_recv;
+
+               if (!reply) {
+                       unsigned char reply_len_buf[2];
+                       uint16_t reply_len;
+
+                       bytes_recv = recv(sk, reply_len_buf, 2, MSG_PEEK);
+                       if (!bytes_recv) {
+                               goto hangup;
+                       } else if (bytes_recv < 0) {
+                               if (errno == EAGAIN || errno == EWOULDBLOCK)
+                                       return TRUE;
+
+                               connman_error("DNS proxy error %s",
+                                               strerror(errno));
+                               goto hangup;
+                       } else if (bytes_recv < 2)
+                               return TRUE;
+
+                       reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
+                       reply_len += 2;
+
+                       DBG("TCP reply %d bytes from %d", reply_len, sk);
+
+                       reply = g_try_malloc(sizeof(*reply) + reply_len + 2);
+                       if (!reply)
+                               return TRUE;
+
+                       reply->len = reply_len;
+                       reply->received = 0;
+
+                       server->incoming_reply = reply;
+               }
+
+               while (reply->received < reply->len) {
+                       bytes_recv = recv(sk, reply->buf + reply->received,
+                                       reply->len - reply->received, 0);
+                       if (!bytes_recv) {
+                               connman_error("DNS proxy TCP disconnect");
+                               break;
+                       } else if (bytes_recv < 0) {
+                               if (errno == EAGAIN || errno == EWOULDBLOCK)
+                                       return TRUE;
+
+                               connman_error("DNS proxy error %s",
+                                               strerror(errno));
+                               break;
+                       }
+                       reply->received += bytes_recv;
+               }
+
+               forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP,
+                                       server);
+
+               g_free(reply);
+               server->incoming_reply = NULL;
+
+               destroy_server(server);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean tcp_idle_timeout(gpointer user_data)
+{
+       struct server_data *server = user_data;
+
+       DBG("");
+
+       if (server == NULL)
+               return FALSE;
+
+       destroy_server(server);
+
+       return FALSE;
+}
+
+static struct server_data *create_server(const char *interface,
+                                       const char *domain, const char *server,
+                                       int protocol)
+{
+       struct addrinfo hints, *rp;
+       struct server_data *data;
+       int sk, ret;
+
+       DBG("interface %s server %s", interface, server);
+
+       memset(&hints, 0, sizeof(hints));
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               hints.ai_socktype = SOCK_DGRAM;
+               break;
+
+       case IPPROTO_TCP:
+               hints.ai_socktype = SOCK_STREAM;
+               break;
+
+       default:
+               return NULL;
+       }
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_NUMERICHOST;
+
+       ret = getaddrinfo(server, "53", &hints, &rp);
+       if (ret) {
+               connman_error("Failed to parse server %s address: %s\n",
+                             server, gai_strerror(ret));
+               return NULL;
+       }
+       /* Do not blindly copy this code elsewhere; it doesn't loop over the
+          results using ->ai_next as it should. That's OK in *this* case
+          because it was a numeric lookup; we *know* there's only one. */
+
+       sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+       if (sk < 0) {
+               connman_error("Failed to create server %s socket", server);
+               freeaddrinfo(rp);
+               return NULL;
+       }
+
+       DBG("sk %d", sk);
+
+       if (interface != NULL) {
+               if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                               interface, strlen(interface) + 1) < 0) {
+                       connman_error("Failed to bind server %s "
+                                               "to interface %s",
+                                                       server, interface);
+                       freeaddrinfo(rp);
+                       close(sk);
+                       return NULL;
+               }
+       }
+
+       data = g_try_new0(struct server_data, 1);
+       if (data == NULL) {
+               connman_error("Failed to allocate server %s data", server);
+               freeaddrinfo(rp);
+               close(sk);
+               return NULL;
+       }
+
+       data->channel = g_io_channel_unix_new(sk);
+       if (data->channel == NULL) {
+               connman_error("Failed to create server %s channel", server);
+               freeaddrinfo(rp);
+               close(sk);
+               g_free(data);
+               return NULL;
+       }
+
+       g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+       if (protocol == IPPROTO_TCP) {
+               g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
+               data->watch = g_io_add_watch(data->channel,
+                       G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                                               tcp_server_event, data);
+               data->timeout = g_timeout_add_seconds(30, tcp_idle_timeout,
+                                                               data);
+       } else
+               data->watch = g_io_add_watch(data->channel,
+                       G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               udp_server_event, data);
+
+       data->interface = g_strdup(interface);
+       if (domain)
+               data->domains = g_list_append(data->domains, g_strdup(domain));
+       data->server = g_strdup(server);
+       data->protocol = protocol;
+
+       ret = connect(sk, rp->ai_addr, rp->ai_addrlen);
+       freeaddrinfo(rp);
+       if (ret < 0) {
+               if ((protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
+                               protocol == IPPROTO_UDP) {
+                       GList *list;
+
+                       connman_error("Failed to connect to server %s", server);
+                       if (data->watch > 0)
+                               g_source_remove(data->watch);
+                       if (data->timeout > 0)
+                               g_source_remove(data->timeout);
+
+                       g_io_channel_unref(data->channel);
+                       close(sk);
+
+                       g_free(data->server);
+                       g_free(data->interface);
+                       for (list = data->domains; list; list = list->next) {
+                               char *domain = list->data;
+
+                               data->domains = g_list_remove(data->domains,
+                                                                       domain);
+                               g_free(domain);
+                       }
+                       g_free(data);
+                       return NULL;
+               }
+       }
+
+       if (__sync_fetch_and_add(&cache_refcount, 1) == 0)
+               cache = g_hash_table_new_full(g_str_hash,
+                                       g_str_equal,
+                                       NULL,
+                                       cache_element_destroy);
+
+       if (protocol == IPPROTO_UDP) {
+               /* Enable new servers by default */
+               data->enabled = TRUE;
+               DBG("Adding DNS server %s", data->server);
+
+               server_list = g_slist_append(server_list, data);
+       }
+
+       return data;
+}
+
+static gboolean resolv(struct request_data *req,
+                               gpointer request, gpointer name)
+{
+       GSList *list;
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               DBG("server %s enabled %d", data->server, data->enabled);
+
+               if (data->enabled == FALSE)
+                       continue;
+
+               if (data->watch == 0 && data->protocol == IPPROTO_UDP)
+                       data->watch = g_io_add_watch(data->channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
+                                               udp_server_event, data);
+
+               if (ns_resolv(data, req, request, name) > 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void append_domain(const char *interface, const char *domain)
+{
+       GSList *list;
+
+       DBG("interface %s domain %s", interface, domain);
+
+       if (domain == NULL)
+               return;
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+               GList *dom_list;
+               char *dom;
+               gboolean dom_found = FALSE;
+
+               if (data->interface == NULL)
+                       continue;
+
+               if (g_str_equal(data->interface, interface) == FALSE)
+                       continue;
+
+               for (dom_list = data->domains; dom_list;
+                               dom_list = dom_list->next) {
+                       dom = dom_list->data;
+
+                       if (g_str_equal(dom, domain)) {
+                               dom_found = TRUE;
+                               break;
+                       }
+               }
+
+               if (dom_found == FALSE) {
+                       data->domains =
+                               g_list_append(data->domains, g_strdup(domain));
+               }
+       }
+}
+
+int __connman_dnsproxy_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       struct server_data *data;
+
+       DBG("interface %s server %s", interface, server);
+
+       if (server == NULL && domain == NULL)
+               return -EINVAL;
+
+       if (server == NULL) {
+               append_domain(interface, domain);
+
+               return 0;
+       }
+
+       if (g_str_equal(server, "127.0.0.1") == TRUE)
+               return -ENODEV;
+
+       data = find_server(interface, server, IPPROTO_UDP);
+       if (data != NULL) {
+               append_domain(interface, domain);
+               return 0;
+       }
+
+       data = create_server(interface, domain, server, IPPROTO_UDP);
+       if (data == NULL)
+               return -EIO;
+
+       return 0;
+}
+
+static void remove_server(const char *interface, const char *domain,
+                       const char *server, int protocol)
+{
+       struct server_data *data;
+
+       data = find_server(interface, server, protocol);
+       if (data == NULL)
+               return;
+
+       destroy_server(data);
+}
+
+int __connman_dnsproxy_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       DBG("interface %s server %s", interface, server);
+
+       if (server == NULL)
+               return -EINVAL;
+
+       if (g_str_equal(server, "127.0.0.1") == TRUE)
+               return -ENODEV;
+
+       remove_server(interface, domain, server, IPPROTO_UDP);
+       remove_server(interface, domain, server, IPPROTO_TCP);
+
+       return 0;
+}
+
+void __connman_dnsproxy_flush(void)
+{
+       GSList *list;
+
+       list = request_list;
+       while (list) {
+               struct request_data *req = list->data;
+
+               list = list->next;
+
+               if (resolv(req, req->request, req->name) == TRUE) {
+                       /*
+                        * A cached result was sent,
+                        * so the request can be released
+                        */
+                       request_list =
+                               g_slist_remove(request_list, req);
+                       destroy_request_data(req);
+                       continue;
+               }
+
+               if (req->timeout > 0)
+                       g_source_remove(req->timeout);
+               req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       }
+}
+
+static void dnsproxy_offline_mode(connman_bool_t enabled)
+{
+       GSList *list;
+
+       DBG("enabled %d", enabled);
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (enabled == FALSE) {
+                       DBG("Enabling DNS server %s", data->server);
+                       data->enabled = TRUE;
+                       cache_invalidate();
+                       cache_refresh();
+               } else {
+                       DBG("Disabling DNS server %s", data->server);
+                       data->enabled = FALSE;
+                       cache_invalidate();
+               }
+       }
+}
+
+static void dnsproxy_default_changed(struct connman_service *service)
+{
+       GSList *list;
+       char *interface;
+
+       DBG("service %p", service);
+
+       /* DNS has changed, invalidate the cache */
+       cache_invalidate();
+
+       if (service == NULL) {
+               /* When no services are active, then disable DNS proxying */
+               dnsproxy_offline_mode(TRUE);
+               return;
+       }
+
+       interface = connman_service_get_interface(service);
+       if (interface == NULL)
+               return;
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (g_strcmp0(data->interface, interface) == 0) {
+                       DBG("Enabling DNS server %s", data->server);
+                       data->enabled = TRUE;
+               } else {
+                       DBG("Disabling DNS server %s", data->server);
+                       data->enabled = FALSE;
+               }
+       }
+
+       g_free(interface);
+       cache_refresh();
+}
+
+static struct connman_notifier dnsproxy_notifier = {
+       .name                   = "dnsproxy",
+       .default_changed        = dnsproxy_default_changed,
+       .offline_mode           = dnsproxy_offline_mode,
+};
+
+static unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
+
+static int parse_request(unsigned char *buf, int len,
+                                       char *name, unsigned int size)
+{
+       struct domain_hdr *hdr = (void *) buf;
+       uint16_t qdcount = ntohs(hdr->qdcount);
+       uint16_t arcount = ntohs(hdr->arcount);
+       unsigned char *ptr;
+       char *last_label = NULL;
+       unsigned int remain, used = 0;
+
+       if (len < 12)
+               return -EINVAL;
+
+       DBG("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
+                                       hdr->id, hdr->qr, hdr->opcode,
+                                                       qdcount, arcount);
+
+       if (hdr->qr != 0 || qdcount != 1)
+               return -EINVAL;
+
+       name[0] = '\0';
+
+       ptr = buf + sizeof(struct domain_hdr);
+       remain = len - sizeof(struct domain_hdr);
+
+       while (remain > 0) {
+               uint8_t len = *ptr;
+
+               if (len == 0x00) {
+                       last_label = (char *) (ptr + 1);
+                       break;
+               }
+
+               if (used + len + 1 > size)
+                       return -ENOBUFS;
+
+               strncat(name, (char *) (ptr + 1), len);
+               strcat(name, ".");
+
+               used += len + 1;
+
+               ptr += len + 1;
+               remain -= len + 1;
+       }
+
+       if (last_label && arcount && remain >= 9 && last_label[4] == 0 &&
+                               !memcmp(last_label + 5, opt_edns0_type, 2)) {
+               uint16_t edns0_bufsize;
+
+               edns0_bufsize = last_label[7] << 8 | last_label[8];
+
+               DBG("EDNS0 buffer size %u", edns0_bufsize);
+
+               /* This is an evil hack until full TCP support has been
+                * implemented.
+                *
+                * Somtimes the EDNS0 request gets send with a too-small
+                * buffer size. Since glibc doesn't seem to crash when it
+                * gets a response biffer then it requested, just bump
+                * the buffer size up to 4KiB.
+                */
+               if (edns0_bufsize < 0x1000) {
+                       last_label[7] = 0x10;
+                       last_label[8] = 0x00;
+               }
+       }
+
+       DBG("query %s", name);
+
+       return 0;
+}
+
+static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       unsigned char buf[768];
+       char query[512];
+       struct request_data *req;
+       int sk, client_sk, len, err;
+       struct sockaddr_in6 client_addr;
+       socklen_t client_addr_len = sizeof(client_addr);
+       GSList *list;
+       struct listener_data *ifdata = user_data;
+       int waiting_for_connect = FALSE, qtype = 0;
+       struct cache_entry *entry;
+
+       DBG("condition 0x%x", condition);
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               if (ifdata->tcp_listener_watch > 0)
+                       g_source_remove(ifdata->tcp_listener_watch);
+               ifdata->tcp_listener_watch = 0;
+
+               connman_error("Error with TCP listener channel");
+
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       client_sk = accept(sk, (void *)&client_addr, &client_addr_len);
+       if (client_sk < 0) {
+               connman_error("Accept failure on TCP listener");
+               ifdata->tcp_listener_watch = 0;
+               return FALSE;
+       }
+
+       len = recv(client_sk, buf, sizeof(buf), 0);
+       if (len < 2)
+               return TRUE;
+
+       DBG("Received %d bytes (id 0x%04x) from %d", len,
+               buf[2] | buf[3] << 8, client_sk);
+
+       err = parse_request(buf + 2, len - 2, query, sizeof(query));
+       if (err < 0 || (g_slist_length(server_list) == 0)) {
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               return TRUE;
+       }
+
+       req = g_try_new0(struct request_data, 1);
+       if (req == NULL)
+               return TRUE;
+
+       memcpy(&req->sa, &client_addr, client_addr_len);
+       req->sa_len = client_addr_len;
+       req->client_sk = client_sk;
+       req->protocol = IPPROTO_TCP;
+
+       req->srcid = buf[2] | (buf[3] << 8);
+       req->dstid = get_id();
+       req->altid = get_id();
+       req->request_len = len;
+
+       buf[2] = req->dstid & 0xff;
+       buf[3] = req->dstid >> 8;
+
+       req->numserv = 0;
+       req->ifdata = (struct listener_data *) ifdata;
+       req->append_domain = FALSE;
+
+       /*
+        * Check if the answer is found in the cache before
+        * creating sockets to the server.
+        */
+       entry = cache_check(buf, &qtype, IPPROTO_TCP);
+       if (entry != NULL) {
+               int ttl_left = 0;
+               struct cache_data *data;
+
+               DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
+               if (qtype == 1)
+                       data = entry->ipv4;
+               else
+                       data = entry->ipv6;
+
+               if (data != NULL) {
+                       ttl_left = data->valid_until - time(NULL);
+                       entry->hits++;
+
+                       send_cached_response(client_sk, data->data,
+                                       data->data_len, NULL, 0, IPPROTO_TCP,
+                                       req->srcid, data->answers, ttl_left);
+
+                       g_free(req);
+                       return TRUE;
+               } else
+                       DBG("data missing, ignoring cache for this query");
+       }
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (data->protocol != IPPROTO_UDP || data->enabled == FALSE)
+                       continue;
+
+               if(create_server(data->interface, NULL,
+                                       data->server, IPPROTO_TCP) == NULL)
+                       continue;
+
+               waiting_for_connect = TRUE;
+       }
+
+       if (waiting_for_connect == FALSE) {
+               /* No server is waiting for connect */
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               return TRUE;
+       }
+
+       /*
+        * The server is not connected yet.
+        * Copy the relevant buffers.
+        * The request will actually be sent once we're
+        * properly connected over TCP to the nameserver.
+        */
+       req->request = g_try_malloc0(req->request_len);
+       if (req->request == NULL) {
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               return TRUE;
+       }
+       memcpy(req->request, buf, req->request_len);
+
+       req->name = g_try_malloc0(sizeof(query));
+       if (req->name == NULL) {
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req->request);
+               g_free(req);
+               return TRUE;
+       }
+       memcpy(req->name, query, sizeof(query));
+
+       req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+
+       request_list = g_slist_append(request_list, req);
+
+       return TRUE;
+}
+
+static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       unsigned char buf[768];
+       char query[512];
+       struct request_data *req;
+       struct sockaddr_in6 client_addr;
+       socklen_t client_addr_len = sizeof(client_addr);
+       int sk, err, len;
+       struct listener_data *ifdata = user_data;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               connman_error("Error with UDP listener channel");
+               ifdata->udp_listener_watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       memset(&client_addr, 0, client_addr_len);
+       len = recvfrom(sk, buf, sizeof(buf), 0, (void *)&client_addr,
+                      &client_addr_len);
+       if (len < 2)
+               return TRUE;
+
+       DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+
+       err = parse_request(buf, len, query, sizeof(query));
+       if (err < 0 || (g_slist_length(server_list) == 0)) {
+               send_response(sk, buf, len, (void *)&client_addr,
+                               client_addr_len, IPPROTO_UDP);
+               return TRUE;
+       }
+
+       req = g_try_new0(struct request_data, 1);
+       if (req == NULL)
+               return TRUE;
+
+       memcpy(&req->sa, &client_addr, client_addr_len);
+       req->sa_len = client_addr_len;
+       req->client_sk = 0;
+       req->protocol = IPPROTO_UDP;
+
+       req->srcid = buf[0] | (buf[1] << 8);
+       req->dstid = get_id();
+       req->altid = get_id();
+       req->request_len = len;
+
+       buf[0] = req->dstid & 0xff;
+       buf[1] = req->dstid >> 8;
+
+       req->numserv = 0;
+       req->ifdata = (struct listener_data *) ifdata;
+       req->append_domain = FALSE;
+
+       if (resolv(req, buf, query) == TRUE) {
+               /* a cached result was sent, so the request can be released */
+               g_free(req);
+               return TRUE;
+       }
+
+       req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       request_list = g_slist_append(request_list, req);
+
+       return TRUE;
+}
+
+static int create_dns_listener(int protocol, struct listener_data *ifdata)
+{
+       GIOChannel *channel;
+       const char *proto;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in6 sin6;
+               struct sockaddr_in sin;
+       } s;
+       socklen_t slen;
+       int sk, type, v6only = 0;
+       int family = AF_INET6;
+
+
+       DBG("interface %s", ifdata->ifname);
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               proto = "UDP";
+               type = SOCK_DGRAM | SOCK_CLOEXEC;
+               break;
+
+       case IPPROTO_TCP:
+               proto = "TCP";
+               type = SOCK_STREAM | SOCK_CLOEXEC;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       sk = socket(family, type, protocol);
+       if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
+               connman_error("No IPv6 support; DNS proxy listening only on Legacy IP");
+               family = AF_INET;
+               sk = socket(family, type, protocol);
+       }
+       if (sk < 0) {
+               connman_error("Failed to create %s listener socket", proto);
+               return -EIO;
+       }
+
+#if !defined TIZEN_EXT
+       /* ConnMan listens DNS from multiple interfaces
+        * E.g. various technology based and tethered interfaces
+        */
+       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       ifdata->ifname,
+                                       strlen(ifdata->ifname) + 1) < 0) {
+               connman_error("Failed to bind %s listener interface", proto);
+               close(sk);
+               return -EIO;
+       }
+#endif
+       /* Ensure it accepts Legacy IP connections too */
+       if (family == AF_INET6 &&
+                       setsockopt(sk, SOL_IPV6, IPV6_V6ONLY,
+                                       &v6only, sizeof(v6only)) < 0) {
+               connman_error("Failed to clear V6ONLY on %s listener socket",
+                             proto);
+               close(sk);
+               return -EIO;
+       }
+
+       if (family == AF_INET) {
+               memset(&s.sin, 0, sizeof(s.sin));
+               s.sin.sin_family = AF_INET;
+               s.sin.sin_port = htons(53);
+               s.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+               slen = sizeof(s.sin);
+       } else {
+               memset(&s.sin6, 0, sizeof(s.sin6));
+               s.sin6.sin6_family = AF_INET6;
+               s.sin6.sin6_port = htons(53);
+               s.sin6.sin6_addr = in6addr_any;
+               slen = sizeof(s.sin6);
+       }
+
+#if defined TIZEN_EXT
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPOUT) != 0)
+               connman_error("Failed to label system::user_internet");
+
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPIN) != 0)
+               connman_error("Failed to label system::user_internet");
+#endif
+       if (bind(sk, &s.sa, slen) < 0) {
+               connman_error("Failed to bind %s listener socket", proto);
+               close(sk);
+               return -EIO;
+       }
+
+       if (protocol == IPPROTO_TCP && listen(sk, 10) < 0) {
+               connman_error("Failed to listen on TCP socket");
+               close(sk);
+               return -EIO;
+       }
+
+       channel = g_io_channel_unix_new(sk);
+       if (channel == NULL) {
+               connman_error("Failed to create %s listener channel", proto);
+               close(sk);
+               return -EIO;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       if (protocol == IPPROTO_TCP) {
+               ifdata->tcp_listener_channel = channel;
+               ifdata->tcp_listener_watch = g_io_add_watch(channel,
+                               G_IO_IN, tcp_listener_event, (gpointer) ifdata);
+       } else {
+               ifdata->udp_listener_channel = channel;
+               ifdata->udp_listener_watch = g_io_add_watch(channel,
+                               G_IO_IN, udp_listener_event, (gpointer) ifdata);
+       }
+
+       return 0;
+}
+
+static void destroy_udp_listener(struct listener_data *ifdata)
+{
+       DBG("interface %s", ifdata->ifname);
+
+       if (ifdata->udp_listener_watch > 0)
+               g_source_remove(ifdata->udp_listener_watch);
+
+       g_io_channel_unref(ifdata->udp_listener_channel);
+}
+
+static void destroy_tcp_listener(struct listener_data *ifdata)
+{
+       DBG("interface %s", ifdata->ifname);
+
+       if (ifdata->tcp_listener_watch > 0)
+               g_source_remove(ifdata->tcp_listener_watch);
+
+       g_io_channel_unref(ifdata->tcp_listener_channel);
+}
+
+static int create_listener(struct listener_data *ifdata)
+{
+       int err;
+
+       err = create_dns_listener(IPPROTO_UDP, ifdata);
+       if (err < 0)
+               return err;
+
+       err = create_dns_listener(IPPROTO_TCP, ifdata);
+       if (err < 0) {
+               destroy_udp_listener(ifdata);
+               return err;
+       }
+
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
+               __connman_resolvfile_append("lo", NULL, "127.0.0.1");
+
+       return 0;
+}
+
+static void destroy_listener(struct listener_data *ifdata)
+{
+       GSList *list;
+
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
+               __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
+
+       for (list = request_list; list; list = list->next) {
+               struct request_data *req = list->data;
+
+               DBG("Dropping request (id 0x%04x -> 0x%04x)",
+                                               req->srcid, req->dstid);
+               destroy_request_data(req);
+               list->data = NULL;
+       }
+
+       g_slist_free(request_list);
+       request_list = NULL;
+
+       destroy_tcp_listener(ifdata);
+       destroy_udp_listener(ifdata);
+}
+
+int __connman_dnsproxy_add_listener(const char *interface)
+{
+       struct listener_data *ifdata;
+       int err;
+
+       DBG("interface %s", interface);
+
+       if (g_hash_table_lookup(listener_table, interface) != NULL)
+               return 0;
+
+       ifdata = g_try_new0(struct listener_data, 1);
+       if (ifdata == NULL)
+               return -ENOMEM;
+
+       ifdata->ifname = g_strdup(interface);
+       ifdata->udp_listener_channel = NULL;
+       ifdata->udp_listener_watch = 0;
+       ifdata->tcp_listener_channel = NULL;
+       ifdata->tcp_listener_watch = 0;
+
+       err = create_listener(ifdata);
+       if (err < 0) {
+               connman_error("Couldn't create listener for %s err %d",
+                               interface, err);
+               g_free(ifdata->ifname);
+               g_free(ifdata);
+               return err;
+       }
+       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
+       return 0;
+}
+
+void __connman_dnsproxy_remove_listener(const char *interface)
+{
+       struct listener_data *ifdata;
+
+       DBG("interface %s", interface);
+
+       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (ifdata == NULL)
+               return;
+
+       destroy_listener(ifdata);
+
+       g_hash_table_remove(listener_table, interface);
+}
+
+static void remove_listener(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *interface = key;
+       struct listener_data *ifdata = value;
+
+       DBG("interface %s", interface);
+
+       destroy_listener(ifdata);
+}
+
+int __connman_dnsproxy_init(void)
+{
+       int err;
+
+       DBG("");
+
+       srandom(time(NULL));
+
+       listener_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+       err = __connman_dnsproxy_add_listener("lo");
+       if (err < 0)
+               return err;
+
+       err = connman_notifier_register(&dnsproxy_notifier);
+       if (err < 0)
+               goto destroy;
+
+       return 0;
+
+destroy:
+       __connman_dnsproxy_remove_listener("lo");
+       g_hash_table_destroy(listener_table);
+
+       return err;
+}
+
+void __connman_dnsproxy_cleanup(void)
+{
+       DBG("");
+
+       connman_notifier_unregister(&dnsproxy_notifier);
+
+       g_hash_table_foreach(listener_table, remove_listener, NULL);
+
+       g_hash_table_destroy(listener_table);
+}
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..c414469
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum)
+{
+       const char *str = strerror(errnum);
+
+       switch (errnum) {
+       case ESRCH:
+               return __connman_error_not_registered(msg);
+       case ENXIO:
+               return __connman_error_not_found(msg);
+       case EACCES:
+               return __connman_error_permission_denied(msg);
+       case EEXIST:
+               return __connman_error_already_exists(msg);
+       case EINVAL:
+               return __connman_error_invalid_arguments(msg);
+       case ENOSYS:
+               return __connman_error_not_implemented(msg);
+       case ENOLINK:
+               return __connman_error_no_carrier(msg);
+       case ENOTUNIQ:
+               return __connman_error_not_unique(msg);
+       case EOPNOTSUPP:
+               return __connman_error_not_supported(msg);
+       case ECONNABORTED:
+               return __connman_error_operation_aborted(msg);
+       case EISCONN:
+               return __connman_error_already_connected(msg);
+       case ENOTCONN:
+               return __connman_error_not_connected(msg);
+       case ETIMEDOUT:
+               return __connman_error_operation_timeout(msg);
+       case EALREADY:
+               return __connman_error_in_progress(msg);
+       case ENOKEY:
+               return __connman_error_passphrase_required(msg);
+       }
+
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".Failed", "%s", str);
+}
+
+DBusMessage *__connman_error_invalid_arguments(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".InvalidArguments", "Invalid arguments");
+}
+
+DBusMessage *__connman_error_permission_denied(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".PermissionDenied", "Permission denied");
+}
+
+DBusMessage *__connman_error_passphrase_required(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".PassphraseRequired", "Passphrase required");
+}
+
+DBusMessage *__connman_error_not_registered(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotRegistered", "Not registered");
+}
+
+DBusMessage *__connman_error_not_unique(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotUnique", "Not unique");
+}
+
+DBusMessage *__connman_error_not_supported(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotSupported", "Not supported");
+}
+
+DBusMessage *__connman_error_not_implemented(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotImplemented", "Not implemented");
+}
+
+DBusMessage *__connman_error_not_found(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                               ".NotFound", "Not found");
+}
+
+DBusMessage *__connman_error_no_carrier(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                               ".NoCarrier", "No carrier");
+}
+
+DBusMessage *__connman_error_in_progress(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                               ".InProgress", "In progress");
+}
+
+DBusMessage *__connman_error_already_exists(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".AlreadyExists", "Already exists");
+}
+
+DBusMessage *__connman_error_already_enabled(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".AlreadyEnabled", "Already enabled");
+}
+
+DBusMessage *__connman_error_already_disabled(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".AlreadyDisabled", "Already disabled");
+}
+
+DBusMessage *__connman_error_already_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".AlreadyConnected", "Already connected");
+}
+
+DBusMessage *__connman_error_not_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotConnected", "Not connected");
+}
+DBusMessage *__connman_error_operation_aborted(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".OperationAborted", "Operation aborted");
+}
+
+DBusMessage *__connman_error_operation_timeout(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".OperationTimeout", "Operation timeout");
+}
+
+DBusMessage *__connman_error_invalid_service(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".InvalidService", "Invalid service");
+}
+
+DBusMessage *__connman_error_invalid_property(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".InvalidProperty", "Invalid property");
+}
diff --git a/src/genbuiltin b/src/genbuiltin
new file mode 100755 (executable)
index 0000000..3e1bb0c
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+       echo "extern struct connman_plugin_desc __connman_builtin_$i;"
+done
+
+echo
+echo "static struct connman_plugin_desc *__connman_builtin[] = {"
+
+for i in $*
+do
+       echo "  &__connman_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/src/inet.c b/src/inet.c
new file mode 100644 (file)
index 0000000..7208d4e
--- /dev/null
@@ -0,0 +1,2240 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2003-2005  Go-Core Project
+ *  Copyright (C) 2003-2006  Helsinki University of Technology
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/sockios.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+#include <ctype.h>
+
+#include "connman.h"
+
+#define NLMSG_TAIL(nmsg)                               \
+       ((struct rtattr *) (((uint8_t*) (nmsg)) +       \
+       NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+int __connman_inet_rtnl_addattr_l(struct nlmsghdr *n, size_t max_length,
+                               int type, const void *data, size_t data_length)
+{
+       size_t length;
+       struct rtattr *rta;
+
+       length = RTA_LENGTH(data_length);
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length)
+               return -E2BIG;
+
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = length;
+       memcpy(RTA_DATA(rta), data, data_length);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length);
+
+       return 0;
+}
+
+int __connman_inet_modify_address(int cmd, int flags,
+                               int index, int family,
+                               const char *address,
+                               const char *peer,
+                               unsigned char prefixlen,
+                               const char *broadcast)
+{
+       uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+                       NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
+                       RTA_LENGTH(sizeof(struct in6_addr)) +
+                       RTA_LENGTH(sizeof(struct in6_addr))];
+
+       struct nlmsghdr *header;
+       struct sockaddr_nl nl_addr;
+       struct ifaddrmsg *ifaddrmsg;
+       struct in6_addr ipv6_addr;
+       struct in_addr ipv4_addr, ipv4_dest, ipv4_bcast;
+       int sk, err;
+
+       DBG("cmd %#x flags %#x index %d family %d address %s peer %s "
+               "prefixlen %hhu broadcast %s", cmd, flags, index, family,
+               address, peer, prefixlen, broadcast);
+
+       if (address == NULL)
+               return -EINVAL;
+
+       if (family != AF_INET && family != AF_INET6)
+               return -EINVAL;
+
+       memset(&request, 0, sizeof(request));
+
+       header = (struct nlmsghdr *)request;
+       header->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+       header->nlmsg_type = cmd;
+       header->nlmsg_flags = NLM_F_REQUEST | flags;
+       header->nlmsg_seq = 1;
+
+       ifaddrmsg = NLMSG_DATA(header);
+       ifaddrmsg->ifa_family = family;
+       ifaddrmsg->ifa_prefixlen = prefixlen;
+       ifaddrmsg->ifa_flags = IFA_F_PERMANENT;
+       ifaddrmsg->ifa_scope = RT_SCOPE_UNIVERSE;
+       ifaddrmsg->ifa_index = index;
+
+       if (family == AF_INET) {
+               if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+                       return -1;
+
+               if (broadcast != NULL)
+                       inet_pton(AF_INET, broadcast, &ipv4_bcast);
+               else
+                       ipv4_bcast.s_addr = ipv4_addr.s_addr |
+                               htonl(0xfffffffflu >> prefixlen);
+
+               if (peer != NULL) {
+                       if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
+                               return -1;
+
+                       err = __connman_inet_rtnl_addattr_l(header,
+                                                       sizeof(request),
+                                                       IFA_ADDRESS,
+                                                       &ipv4_dest,
+                                                       sizeof(ipv4_dest));
+                       if (err < 0)
+                               return err;
+               }
+
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_LOCAL,
+                                               &ipv4_addr,
+                                               sizeof(ipv4_addr));
+               if (err < 0)
+                       return err;
+
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_BROADCAST,
+                                               &ipv4_bcast,
+                                               sizeof(ipv4_bcast));
+               if (err < 0)
+                       return err;
+
+       } else if (family == AF_INET6) {
+               if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
+                       return -1;
+
+               err = __connman_inet_rtnl_addattr_l(header,
+                                               sizeof(request),
+                                               IFA_LOCAL,
+                                               &ipv6_addr,
+                                               sizeof(ipv6_addr));
+               if (err < 0)
+                       return err;
+       }
+
+       sk = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (sk < 0)
+               return -errno;
+
+       memset(&nl_addr, 0, sizeof(nl_addr));
+       nl_addr.nl_family = AF_NETLINK;
+
+       if ((err = sendto(sk, request, header->nlmsg_len, 0,
+                       (struct sockaddr *) &nl_addr, sizeof(nl_addr))) < 0)
+               goto done;
+
+       err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_ifindex(const char *name)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (name == NULL)
+               return -1;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+       err = ioctl(sk, SIOCGIFINDEX, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return -1;
+
+       return ifr.ifr_ifindex;
+}
+
+char *connman_inet_ifname(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       return strdup(ifr.ifr_name);
+}
+
+short int connman_inet_ifflags(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       err = ifr.ifr_flags;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_ifup(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       ifr.ifr_flags |= IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_ifdown(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (!(ifr.ifr_flags & IFF_UP)) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       ifr.ifr_flags &= ~IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
+               err = -errno;
+       else
+               err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+static char *index2addr(int index)
+{
+       struct ifreq ifr;
+       struct ether_addr eth;
+       char *str;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       if (err == 0)
+               err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       str = malloc(18);
+       if (!str)
+               return NULL;
+
+       memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+       snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+                                               eth.ether_addr_octet[0],
+                                               eth.ether_addr_octet[1],
+                                               eth.ether_addr_octet[2],
+                                               eth.ether_addr_octet[3],
+                                               eth.ether_addr_octet[4],
+                                               eth.ether_addr_octet[5]);
+
+       return str;
+}
+
+static char *index2ident(int index, const char *prefix)
+{
+       struct ifreq ifr;
+       struct ether_addr eth;
+       char *str;
+       int sk, err, len;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       if (err == 0)
+               err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       len = prefix ? strlen(prefix) + 18 : 18;
+
+       str = malloc(len);
+       if (!str)
+               return NULL;
+
+       memcpy(&eth, &ifr.ifr_hwaddr.sa_data, sizeof(eth));
+       snprintf(str, len, "%s%02x%02x%02x%02x%02x%02x",
+                                               prefix ? prefix : "",
+                                               eth.ether_addr_octet[0],
+                                               eth.ether_addr_octet[1],
+                                               eth.ether_addr_octet[2],
+                                               eth.ether_addr_octet[3],
+                                               eth.ether_addr_octet[4],
+                                               eth.ether_addr_octet[5]);
+
+       return str;
+}
+
+connman_bool_t connman_inet_is_cfg80211(int index)
+{
+       connman_bool_t result = FALSE;
+       char phy80211_path[PATH_MAX];
+       struct stat st;
+       struct ifreq ifr;
+       int sk;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return FALSE;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0)
+               goto done;
+
+       snprintf(phy80211_path, PATH_MAX,
+                               "/sys/class/net/%s/phy80211", ifr.ifr_name);
+
+       if (stat(phy80211_path, &st) == 0 && (st.st_mode & S_IFDIR))
+               result = TRUE;
+
+done:
+       close(sk);
+
+       return result;
+}
+
+struct connman_device *connman_inet_create_device(int index)
+{
+       enum connman_device_type type;
+       struct connman_device *device;
+       char *devname, *ident = NULL;
+       char *addr = NULL, *name = NULL;
+
+       if (index < 0)
+               return NULL;
+
+       devname = connman_inet_ifname(index);
+       if (devname == NULL)
+               return NULL;
+
+       if (__connman_device_isfiltered(devname) == TRUE) {
+               connman_info("Ignoring interface %s (filtered)", devname);
+               free(devname);
+               return NULL;
+       }
+
+       type = __connman_rtnl_get_device_type(index);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+               connman_info("Ignoring interface %s (type unknown)", devname);
+               free(devname);
+               return NULL;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_GADGET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               name = index2ident(index, "");
+               addr = index2addr(index);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               name = strdup(devname);
+               break;
+       }
+
+       device = connman_device_create(name, type);
+       if (device == NULL)
+               goto done;
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_GADGET:
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               break;
+       case CONNMAN_DEVICE_TYPE_CELLULAR:
+               ident = index2ident(index, NULL);
+               break;
+       }
+
+       connman_device_set_index(device, index);
+       connman_device_set_interface(device, devname);
+
+       if (ident != NULL) {
+               connman_device_set_ident(device, ident);
+               free(ident);
+       }
+
+       connman_device_set_string(device, "Address", addr);
+
+done:
+       free(devname);
+       free(name);
+       free(addr);
+
+       return device;
+}
+
+struct in6_ifreq {
+       struct in6_addr ifr6_addr;
+       __u32 ifr6_prefixlen;
+       unsigned int ifr6_ifindex;
+};
+
+int connman_inet_set_ipv6_address(int index,
+               struct connman_ipaddress *ipaddress)
+{
+       int err;
+       unsigned char prefix_len;
+       const char *address;
+
+       if (ipaddress->local == NULL)
+               return 0;
+
+       prefix_len = ipaddress->prefixlen;
+       address = ipaddress->local;
+
+       DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+
+       err = __connman_inet_modify_address(RTM_NEWADDR,
+                               NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
+                               address, NULL, prefix_len, NULL);
+       if (err < 0) {
+               connman_error("%s: %s", __func__, strerror(-err));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
+{
+       int err;
+       unsigned char prefix_len;
+       const char *address, *broadcast, *peer;
+
+       if (ipaddress->local == NULL)
+               return -1;
+
+       prefix_len = ipaddress->prefixlen;
+       address = ipaddress->local;
+       broadcast = ipaddress->broadcast;
+       peer = ipaddress->peer;
+
+       DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+
+       err = __connman_inet_modify_address(RTM_NEWADDR,
+                               NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
+                               address, peer, prefix_len, broadcast);
+       if (err < 0) {
+               connman_error("%s: %s", __func__, strerror(-err));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_clear_ipv6_address(int index, const char *address,
+                                                       int prefix_len)
+{
+       int err;
+
+       DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+
+       err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
+                               address, NULL, prefix_len, NULL);
+       if (err < 0) {
+               connman_error("%s: %s", __func__, strerror(-err));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
+{
+       int err;
+       unsigned char prefix_len;
+       const char *address, *broadcast, *peer;
+
+       prefix_len = ipaddress->prefixlen;
+       address = ipaddress->local;
+       broadcast = ipaddress->broadcast;
+       peer = ipaddress->peer;
+
+       DBG("index %d address %s prefix_len %d", index, address, prefix_len);
+
+       err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
+                               address, peer, prefix_len, broadcast);
+       if (err < 0) {
+               connman_error("%s: %s", __func__, strerror(-err));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_add_host_route(int index, const char *host,
+                               const char *gateway)
+{
+       return connman_inet_add_network_route(index, host, gateway, NULL);
+}
+
+int connman_inet_del_host_route(int index, const char *host)
+{
+       return connman_inet_del_network_route(index, host);
+}
+
+int connman_inet_add_network_route(int index, const char *host,
+                                       const char *gateway,
+                                       const char *netmask)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d host %s gateway %s netmask %s", index,
+               host, gateway, netmask);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP;
+       if (gateway != NULL)
+               rt.rt_flags |= RTF_GATEWAY;
+       if (netmask == NULL)
+               rt.rt_flags |= RTF_HOST;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(host);
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       if (gateway != NULL)
+               addr.sin_addr.s_addr = inet_addr(gateway);
+       else
+               addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       if (netmask != NULL)
+               addr.sin_addr.s_addr = inet_addr(netmask);
+       else
+               addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Adding host route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_del_network_route(int index, const char *host)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d host %s", index, host);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_HOST;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(host);
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Deleting host route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_del_ipv6_network_route(int index, const char *host,
+                                               unsigned char prefix_len)
+{
+       struct in6_rtmsg rt;
+       int sk, err;
+
+       DBG("index %d host %s", index, host);
+
+       if (host == NULL)
+               return -EINVAL;
+
+       memset(&rt, 0, sizeof(rt));
+
+       rt.rtmsg_dst_len = prefix_len;
+
+       err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
+       if (err < 0)
+               goto out;
+
+       rt.rtmsg_flags = RTF_UP | RTF_HOST;
+
+       rt.rtmsg_metric = 1;
+       rt.rtmsg_ifindex = index;
+
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               err = -1;
+               goto out;
+       }
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       close(sk);
+out:
+       if (err < 0)
+               connman_error("Del IPv6 host route error (%s)",
+                                               strerror(errno));
+
+       return err;
+}
+
+int connman_inet_del_ipv6_host_route(int index, const char *host)
+{
+       return connman_inet_del_ipv6_network_route(index, host, 128);
+}
+
+int connman_inet_add_ipv6_network_route(int index, const char *host,
+                                       const char *gateway,
+                                       unsigned char prefix_len)
+{
+       struct in6_rtmsg rt;
+       int sk, err;
+
+       DBG("index %d host %s gateway %s", index, host, gateway);
+
+       if (host == NULL)
+               return -EINVAL;
+
+       memset(&rt, 0, sizeof(rt));
+
+       rt.rtmsg_dst_len = prefix_len;
+
+       err = inet_pton(AF_INET6, host, &rt.rtmsg_dst);
+       if (err < 0)
+               goto out;
+
+       rt.rtmsg_flags = RTF_UP | RTF_HOST;
+
+       if (gateway != NULL) {
+               rt.rtmsg_flags |= RTF_GATEWAY;
+               inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
+       }
+
+       rt.rtmsg_metric = 1;
+       rt.rtmsg_ifindex = index;
+
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               err = -1;
+               goto out;
+       }
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       close(sk);
+out:
+       if (err < 0)
+               connman_error("Set IPv6 host route error (%s)",
+                                               strerror(errno));
+
+       return err;
+}
+
+int connman_inet_add_ipv6_host_route(int index, const char *host,
+                                       const char *gateway)
+{
+       return connman_inet_add_ipv6_network_route(index, host, gateway, 128);
+}
+
+int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
+{
+       struct in6_rtmsg rt;
+       int sk, err;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       if (gateway == NULL)
+               return -EINVAL;
+
+       memset(&rt, 0, sizeof(rt));
+
+       err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
+       if (err < 0)
+               goto out;
+
+       rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
+       rt.rtmsg_metric = 1;
+       rt.rtmsg_dst_len = 0;
+       rt.rtmsg_ifindex = index;
+
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               err = -1;
+               goto out;
+       }
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       close(sk);
+out:
+       if (err < 0)
+               connman_error("Set default IPv6 gateway error (%s)",
+                                               strerror(errno));
+
+       return err;
+}
+
+int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
+{
+       struct in6_rtmsg rt;
+       int sk, err;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       if (gateway == NULL)
+               return -EINVAL;
+
+       memset(&rt, 0, sizeof(rt));
+
+       err = inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway);
+       if (err < 0)
+               goto out;
+
+       rt.rtmsg_flags = RTF_UP | RTF_GATEWAY;
+       rt.rtmsg_metric = 1;
+       rt.rtmsg_dst_len = 0;
+       rt.rtmsg_ifindex = index;
+
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0) {
+               err = -1;
+               goto out;
+       }
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       close(sk);
+out:
+       if (err < 0)
+               connman_error("Clear default IPv6 gateway error (%s)",
+                                               strerror(errno));
+
+       return err;
+}
+
+int connman_inet_set_gateway_address(int index, const char *gateway)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(gateway);
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting default gateway route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_set_gateway_interface(int index)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_set_ipv6_gateway_interface(int index)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in6 addr;
+       const struct in6_addr any = IN6ADDR_ANY_INIT;
+       int sk, err;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin6_family = AF_INET6;
+       addr.sin6_addr = any;
+
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_clear_gateway_address(int index, const char *gateway)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(gateway);
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Removing default gateway route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_clear_gateway_interface(int index)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in addr;
+       int sk, err;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Removing default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_clear_ipv6_gateway_interface(int index)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in6 addr;
+       const struct in6_addr any = IN6ADDR_ANY_INIT;
+       int sk, err;
+
+       DBG("index %d", index);
+
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin6_family = AF_INET6;
+       addr.sin6_addr = any;
+
+       memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask));
+       memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst));
+       memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway));
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Removing default interface route failed (%s)",
+                                                       strerror(errno));
+       close(sk);
+
+       return err;
+}
+
+connman_bool_t connman_inet_compare_subnet(int index, const char *host)
+{
+       struct ifreq ifr;
+       struct in_addr _host_addr;
+       in_addr_t host_addr, netmask_addr, if_addr;
+       struct sockaddr_in *netmask, *addr;
+       int sk;
+
+       DBG("host %s", host);
+
+       if (host == NULL)
+               return FALSE;
+
+       if (inet_aton(host, &_host_addr) == 0)
+               return -1;
+       host_addr = _host_addr.s_addr;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return FALSE;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return FALSE;
+       }
+
+       if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
+               close(sk);
+               return FALSE;
+       }
+
+       netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
+       netmask_addr = netmask->sin_addr.s_addr;
+
+       if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
+               close(sk);
+               return FALSE;
+       }
+
+       close(sk);
+
+       addr = (struct sockaddr_in *)&ifr.ifr_addr;
+       if_addr = addr->sin_addr.s_addr;
+
+       return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
+}
+
+int connman_inet_remove_from_bridge(int index, const char *bridge)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (bridge == NULL)
+               return -EINVAL;
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return sk;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCBRDELIF, &ifr);
+
+       close(sk);
+
+       if (err < 0) {
+               connman_error("Remove interface from bridge error %s",
+                                                       strerror(errno));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_add_to_bridge(int index, const char *bridge)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (bridge == NULL)
+               return -EINVAL;
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return sk;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCBRADDIF, &ifr);
+
+       close(sk);
+
+       if (err < 0) {
+               connman_error("Add interface to bridge error %s",
+                                                       strerror(errno));
+               return err;
+       }
+
+       return 0;
+}
+
+int connman_inet_set_mtu(int index, int mtu)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return sk;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+       if (err == 0) {
+               ifr.ifr_mtu = mtu;
+               err = ioctl(sk, SIOCSIFMTU, &ifr);
+       }
+
+       close(sk);
+       return err;
+}
+
+int connman_inet_setup_tunnel(char *tunnel, int mtu)
+{
+       struct ifreq ifr;
+       int sk, err, index;
+       __u32 mask;
+       __u32 flags;
+
+       if (tunnel == NULL)
+               return -EINVAL;
+
+       sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return sk;
+
+       index = if_nametoindex(tunnel);
+
+       err = connman_inet_set_mtu(index, mtu);
+       if (err < 0)
+               return err;
+       else if (err)
+               goto done;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, tunnel, IFNAMSIZ);
+       err = ioctl(sk, SIOCGIFFLAGS, &ifr);
+       if (err)
+               goto done;
+
+       mask = IFF_UP;
+       flags = IFF_UP;
+
+       if ((ifr.ifr_flags ^ flags) & mask) {
+               ifr.ifr_flags &= ~mask;
+               ifr.ifr_flags |= mask & flags;
+               err = ioctl(sk, SIOCSIFFLAGS, &ifr);
+               if (err)
+                       connman_error("SIOCSIFFLAGS failed: %s",
+                                                       strerror(errno));
+       }
+
+done:
+       close(sk);
+       return err;
+}
+
+int connman_inet_create_tunnel(char **iface)
+{
+       struct ifreq ifr;
+       int i, fd;
+
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               i = -errno;
+               connman_error("Failed to open /dev/net/tun: %s",
+                               strerror(errno));
+               return i;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+
+       for (i = 0; i < 256; i++) {
+               sprintf(ifr.ifr_name, "tun%d", i);
+
+               if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
+                       break;
+       }
+
+       if (i == 256) {
+               connman_error("Failed to find available tun device");
+               close(fd);
+               return -ENODEV;
+       }
+
+       *iface = g_strdup(ifr.ifr_name);
+
+       return fd;
+}
+
+struct rs_cb_data {
+       GIOChannel *channel;
+       __connman_inet_rs_cb_t callback;
+       struct sockaddr_in6 addr;
+       guint rs_timeout;
+       guint watch_id;
+       void *user_data;
+};
+
+#define CMSG_BUF_LEN 512
+#define IN6ADDR_ALL_NODES_MC_INIT \
+       { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x1 } } } /* ff02::1 */
+#define IN6ADDR_ALL_ROUTERS_MC_INIT \
+       { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x2 } } } /* ff02::2 */
+
+static const struct in6_addr in6addr_all_nodes_mc = IN6ADDR_ALL_NODES_MC_INIT;
+static const struct in6_addr in6addr_all_routers_mc =
+                                               IN6ADDR_ALL_ROUTERS_MC_INIT;
+
+static void rs_cleanup(struct rs_cb_data *data)
+{
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
+
+       if (data->rs_timeout > 0)
+               g_source_remove(data->rs_timeout);
+
+       if (data->watch_id > 0)
+               g_source_remove(data->watch_id);
+
+       g_free(data);
+}
+
+static gboolean rs_timeout_cb(gpointer user_data)
+{
+       struct rs_cb_data *data = user_data;
+
+       DBG("user data %p", user_data);
+
+       if (data == NULL)
+               return FALSE;
+
+       if (data->callback != NULL)
+               data->callback(NULL, 0, data->user_data);
+
+       data->rs_timeout = 0;
+       rs_cleanup(data);
+       return FALSE;
+}
+
+static int icmpv6_recv(int fd, gpointer user_data)
+{
+       struct msghdr mhdr;
+       struct iovec iov;
+       unsigned char chdr[CMSG_BUF_LEN];
+       unsigned char buf[1540];
+       struct rs_cb_data *data = user_data;
+       struct nd_router_advert *hdr;
+       struct sockaddr_in6 saddr;
+       ssize_t len;
+
+       DBG("");
+
+       iov.iov_len = sizeof(buf);
+       iov.iov_base = buf;
+
+       mhdr.msg_name = (void *)&saddr;
+       mhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       mhdr.msg_iov = &iov;
+       mhdr.msg_iovlen = 1;
+       mhdr.msg_control = (void *)chdr;
+       mhdr.msg_controllen = CMSG_BUF_LEN;
+
+       len = recvmsg(fd, &mhdr, 0);
+       if (len < 0) {
+               data->callback(NULL, 0, data->user_data);
+               rs_cleanup(data);
+               return -errno;
+       }
+
+       hdr = (struct nd_router_advert *)buf;
+       DBG("code %d len %zd hdr %zd", hdr->nd_ra_code, len,
+                               sizeof(struct nd_router_advert));
+       if (hdr->nd_ra_code != 0)
+               return 0;
+
+       data->callback(hdr, len, data->user_data);
+       rs_cleanup(data);
+
+       return len;
+}
+
+static gboolean icmpv6_event(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       int fd, ret;
+
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(chan);
+       ret = icmpv6_recv(fd, data);
+       if (ret == 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+/* Adapted from RFC 1071 "C" Implementation Example */
+static uint16_t csum(const void *phdr, const void *data, socklen_t datalen)
+{
+       register unsigned long sum = 0;
+       socklen_t count;
+       uint16_t *addr;
+       int i;
+
+       /* caller must make sure datalen is even */
+
+       addr = (uint16_t *)phdr;
+       for (i = 0; i < 20; i++)
+               sum += *addr++;
+
+       count = datalen;
+       addr = (uint16_t *)data;
+
+       while (count > 1) {
+               sum += *(addr++);
+               count -= 2;
+       }
+
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return (uint16_t)~sum;
+}
+
+static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest)
+{
+       struct _phdr {
+               struct in6_addr src;
+               struct in6_addr dst;
+               uint32_t plen;
+               uint8_t reserved[3];
+               uint8_t nxt;
+       } phdr;
+
+       struct {
+               struct ip6_hdr ip;
+               union {
+                       struct icmp6_hdr icmp;
+                       struct nd_neighbor_solicit ns;
+                       struct nd_router_solicit rs;
+               } i;
+       } frame;
+
+       struct msghdr msgh;
+       struct cmsghdr *cmsg;
+       struct in6_pktinfo *pinfo;
+       struct sockaddr_in6 dst;
+       char cbuf[CMSG_SPACE(sizeof(*pinfo))];
+       struct iovec iov;
+       int fd, datalen, ret;
+
+       DBG("");
+
+       fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+       if (fd < 0)
+               return -errno;
+
+       memset(&frame, 0, sizeof(frame));
+       memset(&dst, 0, sizeof(dst));
+
+       datalen = sizeof(frame.i.rs); /* 8, csum() safe */
+       dst.sin6_addr = *dest;
+
+       /* Fill in the IPv6 header */
+       frame.ip.ip6_vfc = 0x60;
+       frame.ip.ip6_plen = htons(datalen);
+       frame.ip.ip6_nxt = IPPROTO_ICMPV6;
+       frame.ip.ip6_hlim = 255;
+       frame.ip.ip6_dst = dst.sin6_addr;
+       /* all other fields are already set to zero */
+
+       /* Prepare pseudo header for csum */
+       memset(&phdr, 0, sizeof(phdr));
+       phdr.dst = dst.sin6_addr;
+       phdr.plen = htonl(datalen);
+       phdr.nxt = IPPROTO_ICMPV6;
+
+       /* Fill in remaining ICMP header fields */
+       frame.i.icmp.icmp6_type = type;
+       frame.i.icmp.icmp6_cksum = csum(&phdr, &frame.i, datalen);
+
+       iov.iov_base = &frame;
+       iov.iov_len = sizeof(frame.ip) + datalen;
+
+       dst.sin6_family = AF_INET6;
+       msgh.msg_name = &dst;
+       msgh.msg_namelen = sizeof(dst);
+       msgh.msg_iov = &iov;
+       msgh.msg_iovlen = 1;
+       msgh.msg_flags = 0;
+
+       memset(cbuf, 0, CMSG_SPACE(sizeof(*pinfo)));
+       cmsg = (struct cmsghdr *)cbuf;
+       pinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+       pinfo->ipi6_ifindex = oif;
+
+       cmsg->cmsg_len = CMSG_LEN(sizeof(*pinfo));
+       cmsg->cmsg_level = IPPROTO_IPV6;
+       cmsg->cmsg_type = IPV6_PKTINFO;
+       msgh.msg_control = cmsg;
+       msgh.msg_controllen = cmsg->cmsg_len;
+
+       ret = sendmsg(fd, &msgh, 0);
+
+       close(fd);
+       return ret;
+}
+
+static inline void ipv6_addr_set(struct in6_addr *addr,
+                               uint32_t w1, uint32_t w2,
+                               uint32_t w3, uint32_t w4)
+{
+       addr->s6_addr32[0] = w1;
+       addr->s6_addr32[1] = w2;
+       addr->s6_addr32[2] = w3;
+       addr->s6_addr32[3] = w4;
+}
+
+static inline void ipv6_addr_solict_mult(const struct in6_addr *addr,
+                                       struct in6_addr *solicited)
+{
+       ipv6_addr_set(solicited, htonl(0xFF020000), 0, htonl(0x1),
+                       htonl(0xFF000000) | addr->s6_addr32[3]);
+}
+
+static int if_mc_group(int sock, int ifindex, const struct in6_addr *mc_addr,
+                                                               int cmd)
+{
+       unsigned int val = 0;
+       struct ipv6_mreq mreq;
+       int ret;
+
+       memset(&mreq, 0, sizeof(mreq));
+       mreq.ipv6mr_interface = ifindex;
+       mreq.ipv6mr_multiaddr = *mc_addr;
+
+       ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                       &val, sizeof(int));
+
+       if (ret < 0)
+               return ret;
+
+       return setsockopt(sock, IPPROTO_IPV6, cmd, &mreq, sizeof(mreq));
+}
+
+int __connman_inet_ipv6_send_rs(int index, int timeout,
+                       __connman_inet_rs_cb_t callback, void *user_data)
+{
+       struct rs_cb_data *data;
+       struct icmp6_filter filter;
+       struct in6_addr solicit;
+       struct in6_addr dst = in6addr_all_routers_mc;
+       int sk;
+
+       DBG("");
+
+       if (timeout <= 0)
+               return -EINVAL;
+
+       data = g_try_malloc0(sizeof(struct rs_cb_data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->callback = callback;
+       data->user_data = user_data;
+       data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data);
+
+       sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+       if (sk < 0)
+               return -errno;
+
+       ICMP6_FILTER_SETBLOCKALL(&filter);
+       ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+
+       setsockopt(sk, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
+                                               sizeof(struct icmp6_filter));
+
+       ipv6_addr_solict_mult(&dst, &solicit);
+       if_mc_group(sk, index, &in6addr_all_nodes_mc, IPV6_JOIN_GROUP);
+       if_mc_group(sk, index, &solicit, IPV6_JOIN_GROUP);
+
+       data->channel = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+       g_io_channel_set_encoding(data->channel, NULL, NULL);
+       g_io_channel_set_buffered(data->channel, FALSE);
+
+       data->watch_id = g_io_add_watch(data->channel,
+                       G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                       icmpv6_event, data);
+
+       ndisc_send_unspec(ND_ROUTER_SOLICIT, index, &dst);
+
+       return 0;
+}
+
+GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
+                                       unsigned int length)
+{
+       GSList *prefixes = NULL;
+       uint8_t *pos;
+       int len;
+
+       if (length <= sizeof(struct nd_router_advert))
+               return NULL;
+
+       len = length - sizeof(struct nd_router_advert);
+       pos = (uint8_t *)hdr + sizeof(struct nd_router_advert);
+
+       while (len > 0) {
+               struct nd_opt_prefix_info *pinfo;
+               char prefix_str[INET6_ADDRSTRLEN+1], *str;
+               const char *prefix;
+               int optlen;
+
+               if (len < 2)
+                       break;
+
+               optlen = pos[1] << 3;
+               if (optlen == 0 || optlen > len)
+                       break;
+
+               switch (pos[0]) {
+               case ND_OPT_PREFIX_INFORMATION:
+                       pinfo = (struct nd_opt_prefix_info *)pos;
+                       prefix = inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+                                       prefix_str, INET6_ADDRSTRLEN);
+                       if (prefix == NULL)
+                               break;
+
+                       str = g_strdup_printf("%s/%d", prefix,
+                                               pinfo->nd_opt_pi_prefix_len);
+                       prefixes = g_slist_append(prefixes, str);
+
+                       DBG("prefix %s", str);
+
+                       break;
+               }
+
+               len -= optlen;
+               pos += optlen;
+       }
+
+       return prefixes;
+}
+
+static int get_dest_addr(int family, int index, char *buf, int len)
+{
+       struct ifreq ifr;
+       void *addr;
+       int sk;
+
+       sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+               close(sk);
+               errno = EINVAL;
+               return -errno;
+       }
+
+       DBG("index %d %s", index, ifr.ifr_name);
+
+       if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
+               connman_error("Get destination address failed (%s)",
+                                                       strerror(errno));
+               close(sk);
+               return -errno;
+       }
+
+       close(sk);
+
+       switch (family) {
+       case AF_INET:
+               addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
+               break;
+       case AF_INET6:
+               addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
+               break;
+       default:
+               errno = EINVAL;
+               return -errno;
+       }
+
+       if (inet_ntop(family, addr, buf, len) == NULL) {
+               DBG("error %d/%s", errno, strerror(errno));
+               return -errno;
+       }
+
+       return 0;
+}
+
+int connman_inet_get_dest_addr(int index, char **dest)
+{
+       char addr[INET_ADDRSTRLEN];
+       int ret;
+
+       ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
+       if (ret < 0)
+               return ret;
+
+       *dest = g_strdup(addr);
+
+       DBG("destination %s", *dest);
+
+       return 0;
+}
+
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+       char addr[INET6_ADDRSTRLEN];
+       int ret;
+
+       ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
+       if (ret < 0)
+               return ret;
+
+       *dest = g_strdup(addr);
+
+       DBG("destination %s", *dest);
+
+       return 0;
+}
+
+int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
+{
+       int sndbuf = 1024;
+       int rcvbuf = 1024 * 4;
+
+       rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (rth->fd < 0) {
+               connman_error("Can not open netlink socket: %s",
+                                               strerror(errno));
+               return -errno;
+       }
+
+       if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
+                       sizeof(sndbuf)) < 0) {
+               connman_error("SO_SNDBUF: %s", strerror(errno));
+               return -errno;
+       }
+
+       if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+                       sizeof(rcvbuf)) < 0) {
+               connman_error("SO_RCVBUF: %s", strerror(errno));
+               return -errno;
+       }
+
+       memset(&rth->local, 0, sizeof(rth->local));
+       rth->local.nl_family = AF_NETLINK;
+       rth->local.nl_groups = 0;
+
+       if (bind(rth->fd, (struct sockaddr *)&rth->local,
+                                               sizeof(rth->local)) < 0) {
+               connman_error("Can not bind netlink socket: %s",
+                                                       strerror(errno));
+               return -errno;
+       }
+
+       rth->seq = time(NULL);
+
+       DBG("fd %d", rth->fd);
+
+       return 0;
+}
+
+struct inet_rtnl_cb_data {
+       GIOChannel *channel;
+       __connman_inet_rtnl_cb_t callback;
+       guint rtnl_timeout;
+       guint watch_id;
+       struct __connman_inet_rtnl_handle *rtnl;
+       void *user_data;
+};
+
+static void inet_rtnl_cleanup(struct inet_rtnl_cb_data *data)
+{
+       struct __connman_inet_rtnl_handle *rth = data->rtnl;
+
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
+
+       DBG("data %p", data);
+
+       if (data->rtnl_timeout > 0)
+               g_source_remove(data->rtnl_timeout);
+
+       if (data->watch_id > 0)
+               g_source_remove(data->watch_id);
+
+       if (rth != NULL) {
+               __connman_inet_rtnl_close(rth);
+               g_free(rth);
+       }
+
+       g_free(data);
+}
+
+static gboolean inet_rtnl_timeout_cb(gpointer user_data)
+{
+       struct inet_rtnl_cb_data *data = user_data;
+
+       DBG("user data %p", user_data);
+
+       if (data == NULL)
+               return FALSE;
+
+       if (data->callback != NULL)
+               data->callback(NULL, data->user_data);
+
+       data->rtnl_timeout = 0;
+       inet_rtnl_cleanup(data);
+       return FALSE;
+}
+
+static int inet_rtnl_recv(GIOChannel *chan, gpointer user_data)
+{
+       struct inet_rtnl_cb_data *rtnl_data = user_data;
+       struct __connman_inet_rtnl_handle *rth = rtnl_data->rtnl;
+       struct nlmsghdr *h = NULL;
+       struct sockaddr_nl nladdr;
+       socklen_t addr_len = sizeof(nladdr);
+       unsigned char buf[4096];
+       void *ptr = buf;
+       gsize len;
+       int status, fd;
+
+       memset(buf, 0, sizeof(buf));
+       memset(&nladdr, 0, sizeof(nladdr));
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+       if (status < 0) {
+               if (errno == EINTR || errno == EAGAIN)
+                       return 0;
+
+               return -1;
+       }
+
+       if (status == 0)
+               return -1;
+
+       if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+               DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+               return 0;
+       }
+
+       len = status;
+
+       while (len > 0) {
+               struct nlmsgerr *err;
+
+               h = ptr;
+
+               if (!NLMSG_OK(h, len)) {
+                       return -1;
+                       break;
+               }
+
+               if (h->nlmsg_seq != rth->seq) {
+                       /* Skip this msg */
+                       DBG("skip %d/%d len %d", rth->seq,
+                               h->nlmsg_seq, h->nlmsg_len);
+
+                       len -= h->nlmsg_len;
+                       ptr += h->nlmsg_len;
+                       continue;
+               }
+
+               switch (h->nlmsg_type) {
+               case NLMSG_NOOP:
+               case NLMSG_OVERRUN:
+                       return -1;
+
+               case NLMSG_ERROR:
+                       err = (struct nlmsgerr *)NLMSG_DATA(h);
+                       connman_error("RTNETLINK answers %s (%d)",
+                               strerror(-err->error), -err->error);
+                       return err->error;
+               }
+
+               break;
+       }
+
+       if (h->nlmsg_seq == rth->seq) {
+               DBG("received %d seq %d", h->nlmsg_len, h->nlmsg_seq);
+
+               rtnl_data->callback(h, rtnl_data->user_data);
+
+               if (rtnl_data->rtnl_timeout > 0) {
+                       g_source_remove(rtnl_data->rtnl_timeout);
+                       rtnl_data->rtnl_timeout = 0;
+               }
+
+               __connman_inet_rtnl_close(rth);
+               g_free(rth);
+       }
+
+       return 0;
+}
+
+static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       int ret;
+
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       ret = inet_rtnl_recv(chan, user_data);
+       if (ret != 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+int __connman_inet_rtnl_talk(struct __connman_inet_rtnl_handle *rtnl,
+                       struct nlmsghdr *n, int timeout,
+                       __connman_inet_rtnl_cb_t callback, void *user_data)
+{
+       struct sockaddr_nl nladdr;
+       struct inet_rtnl_cb_data *data;
+       unsigned seq;
+       int err;
+
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       n->nlmsg_seq = seq = ++rtnl->seq;
+
+       if (callback != NULL) {
+               data = g_try_malloc0(sizeof(struct inet_rtnl_cb_data));
+               if (data == NULL)
+                       return -ENOMEM;
+
+               data->callback = callback;
+               data->user_data = user_data;
+               data->rtnl = rtnl;
+               data->rtnl_timeout = g_timeout_add_seconds(timeout,
+                                               inet_rtnl_timeout_cb, data);
+
+               data->channel = g_io_channel_unix_new(rtnl->fd);
+               g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+               g_io_channel_set_encoding(data->channel, NULL, NULL);
+               g_io_channel_set_buffered(data->channel, FALSE);
+
+               data->watch_id = g_io_add_watch(data->channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                               inet_rtnl_event, data);
+       } else
+               n->nlmsg_flags |= NLM_F_ACK;
+
+       err = sendto(rtnl->fd, &rtnl->req.n, rtnl->req.n.nlmsg_len, 0,
+               (struct sockaddr *) &nladdr, sizeof(nladdr));
+       DBG("handle %p len %d err %d", rtnl, rtnl->req.n.nlmsg_len, err);
+       if (err < 0) {
+               connman_error("Can not talk to rtnetlink");
+               return -errno;
+       }
+
+       if ((unsigned int)err != rtnl->req.n.nlmsg_len) {
+               connman_error("Sent %d bytes, msg truncated", err);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void __connman_inet_rtnl_close(struct __connman_inet_rtnl_handle *rth)
+{
+       DBG("handle %p", rth);
+
+       if (rth->fd >= 0) {
+               close(rth->fd);
+               rth->fd = -1;
+       }
+}
+
+int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, int type,
+                               __u32 data)
+{
+       int len = RTA_LENGTH(4);
+       struct rtattr *rta;
+
+       if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+               DBG("Error! max allowed bound %zd exceeded", maxlen);
+               return -1;
+       }
+       rta = NLMSG_TAIL(n);
+       rta->rta_type = type;
+       rta->rta_len = len;
+       memcpy(RTA_DATA(rta), &data, 4);
+       n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+       return 0;
+}
+
+int connman_inet_check_ipaddress(const char *host)
+{
+       struct addrinfo hints;
+       struct addrinfo *addr;
+       int result;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_flags = AI_NUMERICHOST;
+       addr = NULL;
+
+       result = getaddrinfo(host, NULL, &hints, &addr);
+       if (result == 0)
+               result = addr->ai_family;
+       freeaddrinfo(addr);
+
+       return result;
+}
+
+/* Check routine modified from ics-dhcp 4.2.3-P2 */
+connman_bool_t connman_inet_check_hostname(const char *ptr, size_t len)
+{
+       const char *p;
+
+       /*
+        * Not empty or complete length not over 255 characters.
+        */
+       if ((len == 0) || (len > 256))
+               return FALSE;
+
+       /*
+        * Consists of [[:alnum:]-]+ labels separated by [.]
+        * a [_] is against RFC but seems to be "widely used"
+        */
+       for (p = ptr; (*p != 0) && (len-- > 0); p++) {
+
+               if ((*p == '-') || (*p == '_')) {
+                       /*
+                        * Not allowed at begin or end of a label.
+                        */
+                       if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
+                               return FALSE;
+
+               } else if (*p == '.') {
+                       /*
+                        * Each label has to be 1-63 characters;
+                        * we allow [.] at the end ('foo.bar.')
+                        */
+                       size_t d = p - ptr;
+
+                       if ((d <= 0) || (d >= 64))
+                               return FALSE;
+
+                       ptr = p + 1; /* Jump to the next label */
+
+               } else if (isalnum((unsigned char)*p) == 0) {
+                       /*
+                        * Also numbers at the begin are fine
+                        */
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
diff --git a/src/ipconfig.c b/src/ipconfig.c
new file mode 100644 (file)
index 0000000..5e34ac0
--- /dev/null
@@ -0,0 +1,2395 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/if_link.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000
+#endif
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+struct connman_ipconfig {
+       int refcount;
+       int index;
+       enum connman_ipconfig_type type;
+
+       struct connman_ipconfig *origin;
+
+       const struct connman_ipconfig_ops *ops;
+       void *ops_data;
+
+       connman_bool_t enabled;
+       enum connman_ipconfig_method method;
+       struct connman_ipaddress *address;
+       struct connman_ipaddress *system;
+
+       int ipv6_privacy_config;
+       char *last_dhcp_address;
+};
+
+struct connman_ipdevice {
+       int index;
+       char *ifname;
+       unsigned short type;
+       unsigned int flags;
+       char *address;
+       uint16_t mtu;
+       uint32_t rx_packets;
+       uint32_t tx_packets;
+       uint32_t rx_bytes;
+       uint32_t tx_bytes;
+       uint32_t rx_errors;
+       uint32_t tx_errors;
+       uint32_t rx_dropped;
+       uint32_t tx_dropped;
+
+       GSList *address_list;
+       char *ipv4_gateway;
+       char *ipv6_gateway;
+
+       char *pac;
+
+       struct connman_ipconfig *config_ipv4;
+       struct connman_ipconfig *config_ipv6;
+
+       gboolean ipv6_enabled;
+       int ipv6_privacy;
+};
+
+static GHashTable *ipdevice_hash = NULL;
+static GList *ipconfig_list = NULL;
+
+struct connman_ipaddress *connman_ipaddress_alloc(int family)
+{
+       struct connman_ipaddress *ipaddress;
+
+       ipaddress = g_try_new0(struct connman_ipaddress, 1);
+       if (ipaddress == NULL)
+               return NULL;
+
+       ipaddress->family = family;
+       ipaddress->prefixlen = 0;
+       ipaddress->local = NULL;
+       ipaddress->peer = NULL;
+       ipaddress->broadcast = NULL;
+       ipaddress->gateway = NULL;
+
+       return ipaddress;
+}
+
+void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
+{
+       if (ipaddress == NULL)
+               return;
+
+       g_free(ipaddress->broadcast);
+       g_free(ipaddress->peer);
+       g_free(ipaddress->local);
+       g_free(ipaddress->gateway);
+       g_free(ipaddress);
+}
+
+unsigned char __connman_ipconfig_netmask_prefix_len(const char *netmask)
+{
+       unsigned char bits;
+       in_addr_t mask;
+       in_addr_t host;
+
+       if (netmask == NULL)
+               return 32;
+
+       mask = inet_network(netmask);
+       host = ~mask;
+
+       /* a valid netmask must be 2^n - 1 */
+       if ((host & (host + 1)) != 0)
+               return -1;
+
+       bits = 0;
+       for (; mask; mask <<= 1)
+               ++bits;
+
+       return bits;
+}
+
+static gboolean check_ipv6_address(const char *address)
+{
+       unsigned char buf[sizeof(struct in6_addr)];
+       int err;
+
+       if (address == NULL)
+               return FALSE;
+
+       err = inet_pton(AF_INET6, address, buf);
+       if (err > 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+int connman_ipaddress_set_ipv6(struct connman_ipaddress *ipaddress,
+                               const char *address,
+                               unsigned char prefix_length,
+                               const char *gateway)
+{
+       if (ipaddress == NULL)
+               return -EINVAL;
+
+       if (check_ipv6_address(address) == FALSE)
+               return -EINVAL;
+
+       if (check_ipv6_address(gateway) == FALSE)
+               return -EINVAL;
+
+       DBG("prefix_len %d address %s gateway %s",
+                       prefix_length, address, gateway);
+
+       ipaddress->family = AF_INET6;
+
+       ipaddress->prefixlen = prefix_length;
+
+       g_free(ipaddress->local);
+       ipaddress->local = g_strdup(address);
+
+       g_free(ipaddress->gateway);
+       ipaddress->gateway = g_strdup(gateway);
+
+       return 0;
+}
+
+int connman_ipaddress_set_ipv4(struct connman_ipaddress *ipaddress,
+               const char *address, const char *netmask, const char *gateway)
+{
+       if (ipaddress == NULL)
+               return -EINVAL;
+
+       ipaddress->family = AF_INET;
+
+       ipaddress->prefixlen = __connman_ipconfig_netmask_prefix_len(netmask);
+
+       g_free(ipaddress->local);
+       ipaddress->local = g_strdup(address);
+
+       g_free(ipaddress->gateway);
+       ipaddress->gateway = g_strdup(gateway);
+
+       return 0;
+}
+
+void connman_ipaddress_set_peer(struct connman_ipaddress *ipaddress,
+                               const char *peer)
+{
+       if (ipaddress == NULL)
+               return;
+
+       g_free(ipaddress->peer);
+       ipaddress->peer = g_strdup(peer);
+}
+
+void connman_ipaddress_clear(struct connman_ipaddress *ipaddress)
+{
+       if (ipaddress == NULL)
+               return;
+
+       ipaddress->prefixlen = 0;
+
+       g_free(ipaddress->local);
+       ipaddress->local = NULL;
+
+       g_free(ipaddress->peer);
+       ipaddress->peer = NULL;
+
+       g_free(ipaddress->broadcast);
+       ipaddress->broadcast = NULL;
+
+       g_free(ipaddress->gateway);
+       ipaddress->gateway = NULL;
+}
+
+void connman_ipaddress_copy(struct connman_ipaddress *ipaddress,
+                                       struct connman_ipaddress *source)
+{
+       if (ipaddress == NULL || source == NULL)
+               return;
+
+       ipaddress->family = source->family;
+       ipaddress->prefixlen = source->prefixlen;
+
+       g_free(ipaddress->local);
+       ipaddress->local = g_strdup(source->local);
+
+       g_free(ipaddress->peer);
+       ipaddress->peer = g_strdup(source->peer);
+
+       g_free(ipaddress->broadcast);
+       ipaddress->broadcast = g_strdup(source->broadcast);
+
+       g_free(ipaddress->gateway);
+       ipaddress->gateway = g_strdup(source->gateway);
+}
+
+static void free_address_list(struct connman_ipdevice *ipdevice)
+{
+       GSList *list;
+
+       for (list = ipdevice->address_list; list; list = list->next) {
+               struct connman_ipaddress *ipaddress = list->data;
+
+               connman_ipaddress_free(ipaddress);
+               list->data = NULL;
+       }
+
+       g_slist_free(ipdevice->address_list);
+       ipdevice->address_list = NULL;
+}
+
+static struct connman_ipaddress *find_ipaddress(struct connman_ipdevice *ipdevice,
+                               unsigned char prefixlen, const char *local)
+{
+       GSList *list;
+
+       for (list = ipdevice->address_list; list; list = list->next) {
+               struct connman_ipaddress *ipaddress = list->data;
+
+               if (g_strcmp0(ipaddress->local, local) == 0 &&
+                                       ipaddress->prefixlen == prefixlen)
+                       return ipaddress;
+       }
+
+       return NULL;
+}
+
+const char *__connman_ipconfig_type2string(enum connman_ipconfig_type type)
+{
+       switch (type) {
+       case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+               return "unknown";
+       case CONNMAN_IPCONFIG_TYPE_IPV4:
+               return "IPv4";
+       case CONNMAN_IPCONFIG_TYPE_IPV6:
+               return "IPv6";
+       }
+
+       return NULL;
+}
+
+static const char *type2str(unsigned short type)
+{
+       switch (type) {
+       case ARPHRD_ETHER:
+               return "ETHER";
+       case ARPHRD_LOOPBACK:
+               return "LOOPBACK";
+       case ARPHRD_PPP:
+               return "PPP";
+       case ARPHRD_NONE:
+               return "NONE";
+       case ARPHRD_VOID:
+               return "VOID";
+       }
+
+       return "";
+}
+
+static const char *scope2str(unsigned char scope)
+{
+       switch (scope) {
+       case 0:
+               return "UNIVERSE";
+       case 253:
+               return "LINK";
+       }
+
+       return "";
+}
+
+static gboolean get_ipv6_state(gchar *ifname)
+{
+       int disabled;
+       gchar *path;
+       FILE *f;
+       gboolean enabled = FALSE;
+
+       if (ifname == NULL)
+               path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
+       else
+               path = g_strdup_printf(
+                       "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+
+       if (path == NULL)
+               return enabled;
+
+       f = fopen(path, "r");
+
+       g_free(path);
+
+       if (f != NULL) {
+               if (fscanf(f, "%d", &disabled) > 0)
+                       enabled = !disabled;
+               fclose(f);
+       }
+
+       return enabled;
+}
+
+static void set_ipv6_state(gchar *ifname, gboolean enable)
+{
+       gchar *path;
+       FILE *f;
+
+       if (ifname == NULL)
+               path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
+       else
+               path = g_strdup_printf(
+                       "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+
+       if (path == NULL)
+               return;
+
+       f = fopen(path, "r+");
+
+       g_free(path);
+
+       if (f == NULL)
+               return;
+
+       if (enable == FALSE)
+               fprintf(f, "1");
+       else
+               fprintf(f, "0");
+
+       fclose(f);
+}
+
+static int get_ipv6_privacy(gchar *ifname)
+{
+       gchar *path;
+       FILE *f;
+       int value;
+
+       if (ifname == NULL)
+               return 0;
+
+       path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
+                                                               ifname);
+
+       if (path == NULL)
+               return 0;
+
+       f = fopen(path, "r");
+
+       g_free(path);
+
+       if (f == NULL)
+               return 0;
+
+       if (fscanf(f, "%d", &value) <= 0)
+               value = 0;
+
+       fclose(f);
+
+       return value;
+}
+
+/* Enable the IPv6 privacy extension for stateless address autoconfiguration.
+ * The privacy extension is described in RFC 3041 and RFC 4941
+ */
+static void set_ipv6_privacy(gchar *ifname, int value)
+{
+       gchar *path;
+       FILE *f;
+
+       if (ifname == NULL)
+               return;
+
+       path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
+                                                               ifname);
+
+       if (path == NULL)
+               return;
+
+       if (value < 0)
+               value = 0;
+
+       f = fopen(path, "r+");
+
+       g_free(path);
+
+       if (f == NULL)
+               return;
+
+       fprintf(f, "%d", value);
+       fclose(f);
+}
+
+static int get_rp_filter()
+{
+       FILE *f;
+       int value = -EINVAL, tmp;
+
+       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
+
+       if (f != NULL) {
+               if (fscanf(f, "%d", &tmp) == 1)
+                       value = tmp;
+               fclose(f);
+       }
+
+       return value;
+}
+
+static void set_rp_filter(int value)
+{
+       FILE *f;
+
+       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
+
+       if (f == NULL)
+               return;
+
+       fprintf(f, "%d", value);
+
+       fclose(f);
+}
+
+int __connman_ipconfig_set_rp_filter()
+{
+       int value;
+
+       value = get_rp_filter();
+
+       if (value < 0)
+               return value;
+
+       set_rp_filter(2);
+
+       connman_info("rp_filter set to 2 (loose mode routing), "
+                       "old value was %d", value);
+
+       return value;
+}
+
+void __connman_ipconfig_unset_rp_filter(int old_value)
+{
+       set_rp_filter(old_value);
+
+       connman_info("rp_filter restored to %d", old_value);
+}
+
+gboolean __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return FALSE;
+
+       return ipconfig->ipv6_privacy_config == 0 ? FALSE : TRUE;
+}
+
+static void free_ipdevice(gpointer data)
+{
+       struct connman_ipdevice *ipdevice = data;
+
+       connman_info("%s {remove} index %d", ipdevice->ifname,
+                                                       ipdevice->index);
+
+       if (ipdevice->config_ipv4 != NULL) {
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
+               ipdevice->config_ipv4 = NULL;
+       }
+
+       if (ipdevice->config_ipv6 != NULL) {
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
+               ipdevice->config_ipv6 = NULL;
+       }
+
+       free_address_list(ipdevice);
+       g_free(ipdevice->ipv4_gateway);
+       g_free(ipdevice->ipv6_gateway);
+       g_free(ipdevice->pac);
+
+       g_free(ipdevice->address);
+
+       set_ipv6_state(ipdevice->ifname, ipdevice->ipv6_enabled);
+       set_ipv6_privacy(ipdevice->ifname, ipdevice->ipv6_privacy);
+
+       g_free(ipdevice->ifname);
+       g_free(ipdevice);
+}
+
+static void __connman_ipconfig_lower_up(struct connman_ipdevice *ipdevice)
+{
+       DBG("ipconfig ipv4 %p ipv6 %p", ipdevice->config_ipv4,
+                                       ipdevice->config_ipv6);
+
+       if (ipdevice->config_ipv6 != NULL &&
+                       ipdevice->config_ipv6->enabled == TRUE)
+               return;
+
+       if (__connman_device_isfiltered(ipdevice->ifname) == FALSE) {
+               ipdevice->ipv6_enabled = get_ipv6_state(ipdevice->ifname);
+               set_ipv6_state(ipdevice->ifname, FALSE);
+       }
+}
+
+static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
+{
+       DBG("ipconfig ipv4 %p ipv6 %p", ipdevice->config_ipv4,
+                                       ipdevice->config_ipv6);
+
+       if (ipdevice->config_ipv4)
+               connman_inet_clear_address(ipdevice->index,
+                                       ipdevice->config_ipv4->address);
+
+       if (ipdevice->config_ipv6)
+               connman_inet_clear_ipv6_address(ipdevice->index,
+                               ipdevice->config_ipv6->address->local,
+                               ipdevice->config_ipv6->address->prefixlen);
+}
+
+static void update_stats(struct connman_ipdevice *ipdevice,
+                                               struct rtnl_link_stats *stats)
+{
+       struct connman_service *service;
+
+       if (stats->rx_packets == 0 && stats->tx_packets == 0)
+               return;
+
+       connman_info("%s {RX} %u packets %u bytes", ipdevice->ifname,
+                                       stats->rx_packets, stats->rx_bytes);
+       connman_info("%s {TX} %u packets %u bytes", ipdevice->ifname,
+                                       stats->tx_packets, stats->tx_bytes);
+
+       if (ipdevice->config_ipv4 == NULL && ipdevice->config_ipv6 == NULL)
+               return;
+
+       if (ipdevice->config_ipv4)
+               service = __connman_ipconfig_get_data(ipdevice->config_ipv4);
+       else if (ipdevice->config_ipv6)
+               service = __connman_ipconfig_get_data(ipdevice->config_ipv6);
+       else
+               return;
+
+       if (service == NULL)
+               return;
+
+       ipdevice->rx_packets = stats->rx_packets;
+       ipdevice->tx_packets = stats->tx_packets;
+       ipdevice->rx_bytes = stats->rx_bytes;
+       ipdevice->tx_bytes = stats->tx_bytes;
+       ipdevice->rx_errors = stats->rx_errors;
+       ipdevice->tx_errors = stats->tx_errors;
+       ipdevice->rx_dropped = stats->rx_dropped;
+       ipdevice->tx_dropped = stats->tx_dropped;
+
+       __connman_service_notify(service,
+                               ipdevice->rx_packets, ipdevice->tx_packets,
+                               ipdevice->rx_bytes, ipdevice->tx_bytes,
+                               ipdevice->rx_errors, ipdevice->tx_errors,
+                               ipdevice->rx_dropped, ipdevice->tx_dropped);
+}
+
+void __connman_ipconfig_newlink(int index, unsigned short type,
+                               unsigned int flags, const char *address,
+                                                       unsigned short mtu,
+                                               struct rtnl_link_stats *stats)
+{
+       struct connman_ipdevice *ipdevice;
+       GList *list;
+       GString *str;
+       gboolean up = FALSE, down = FALSE;
+       gboolean lower_up = FALSE, lower_down = FALSE;
+
+       DBG("index %d", index);
+
+       if (type == ARPHRD_LOOPBACK)
+               return;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice != NULL)
+               goto update;
+
+       ipdevice = g_try_new0(struct connman_ipdevice, 1);
+       if (ipdevice == NULL)
+               return;
+
+       ipdevice->index = index;
+       ipdevice->ifname = connman_inet_ifname(index);
+       ipdevice->type = type;
+
+       ipdevice->ipv6_enabled = get_ipv6_state(ipdevice->ifname);
+       ipdevice->ipv6_privacy = get_ipv6_privacy(ipdevice->ifname);
+
+       ipdevice->address = g_strdup(address);
+
+       g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice);
+
+       connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname,
+                                               index, type, type2str(type));
+
+update:
+       ipdevice->mtu = mtu;
+
+       update_stats(ipdevice, stats);
+
+       if (flags == ipdevice->flags)
+               return;
+
+       if ((ipdevice->flags & IFF_UP) != (flags & IFF_UP)) {
+               if (flags & IFF_UP)
+                       up = TRUE;
+               else
+                       down = TRUE;
+       }
+
+       if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) !=
+                               (flags & (IFF_RUNNING | IFF_LOWER_UP))) {
+               if ((flags & (IFF_RUNNING | IFF_LOWER_UP)) ==
+                                       (IFF_RUNNING | IFF_LOWER_UP))
+                       lower_up = TRUE;
+               else if ((flags & (IFF_RUNNING | IFF_LOWER_UP)) == 0)
+                       lower_down = TRUE;
+       }
+
+       ipdevice->flags = flags;
+
+       str = g_string_new(NULL);
+       if (str == NULL)
+               return;
+
+       if (flags & IFF_UP)
+               g_string_append(str, "UP");
+       else
+               g_string_append(str, "DOWN");
+
+       if (flags & IFF_RUNNING)
+               g_string_append(str, ",RUNNING");
+
+       if (flags & IFF_LOWER_UP)
+               g_string_append(str, ",LOWER_UP");
+
+       connman_info("%s {update} flags %u <%s>", ipdevice->ifname,
+                                                       flags, str->str);
+
+       g_string_free(str, TRUE);
+
+       for (list = g_list_first(ipconfig_list); list;
+                                               list = g_list_next(list)) {
+               struct connman_ipconfig *ipconfig = list->data;
+
+               if (index != ipconfig->index)
+                       continue;
+
+               if (ipconfig->ops == NULL)
+                       continue;
+
+               if (up == TRUE && ipconfig->ops->up)
+                       ipconfig->ops->up(ipconfig);
+               if (lower_up == TRUE && ipconfig->ops->lower_up)
+                       ipconfig->ops->lower_up(ipconfig);
+
+               if (lower_down == TRUE && ipconfig->ops->lower_down)
+                       ipconfig->ops->lower_down(ipconfig);
+               if (down == TRUE && ipconfig->ops->down)
+                       ipconfig->ops->down(ipconfig);
+       }
+
+       if (lower_up)
+               __connman_ipconfig_lower_up(ipdevice);
+       if (lower_down)
+               __connman_ipconfig_lower_down(ipdevice);
+}
+
+void __connman_ipconfig_dellink(int index, struct rtnl_link_stats *stats)
+{
+       struct connman_ipdevice *ipdevice;
+       GList *list;
+
+       DBG("index %d", index);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return;
+
+       update_stats(ipdevice, stats);
+
+       for (list = g_list_first(ipconfig_list); list;
+                                               list = g_list_next(list)) {
+               struct connman_ipconfig *ipconfig = list->data;
+
+               if (index != ipconfig->index)
+                       continue;
+
+               ipconfig->index = -1;
+
+               if (ipconfig->ops == NULL)
+                       continue;
+
+               if (ipconfig->ops->lower_down)
+                       ipconfig->ops->lower_down(ipconfig);
+               if (ipconfig->ops->down)
+                       ipconfig->ops->down(ipconfig);
+       }
+
+       __connman_ipconfig_lower_down(ipdevice);
+
+       g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index));
+}
+
+static inline gint check_duplicate_address(gconstpointer a, gconstpointer b)
+{
+       const struct connman_ipaddress *addr1 = a;
+       const struct connman_ipaddress *addr2 = b;
+
+       if (addr1->prefixlen != addr2->prefixlen)
+               return addr2->prefixlen - addr1->prefixlen;
+
+       return g_strcmp0(addr1->local, addr2->local);
+}
+
+void __connman_ipconfig_newaddr(int index, int family, const char *label,
+                               unsigned char prefixlen, const char *address)
+{
+       struct connman_ipdevice *ipdevice;
+       struct connman_ipaddress *ipaddress;
+       enum connman_ipconfig_type type;
+       GList *list;
+
+       DBG("index %d", index);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return;
+
+       ipaddress = connman_ipaddress_alloc(family);
+       if (ipaddress == NULL)
+               return;
+
+       ipaddress->prefixlen = prefixlen;
+       ipaddress->local = g_strdup(address);
+
+       if (g_slist_find_custom(ipdevice->address_list, ipaddress,
+                                       check_duplicate_address)) {
+               connman_ipaddress_free(ipaddress);
+               return;
+       }
+
+       if (family == AF_INET)
+               type = CONNMAN_IPCONFIG_TYPE_IPV4;
+       else if (family == AF_INET6)
+               type = CONNMAN_IPCONFIG_TYPE_IPV6;
+       else
+               return;
+
+       ipdevice->address_list = g_slist_append(ipdevice->address_list,
+                                                               ipaddress);
+
+       connman_info("%s {add} address %s/%u label %s family %d",
+               ipdevice->ifname, address, prefixlen, label, family);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_ippool_newaddr(index, address, prefixlen);
+
+       if (ipdevice->config_ipv4 != NULL && family == AF_INET)
+               connman_ipaddress_copy(ipdevice->config_ipv4->system,
+                                       ipaddress);
+
+       else if (ipdevice->config_ipv6 != NULL && family == AF_INET6)
+               connman_ipaddress_copy(ipdevice->config_ipv6->system,
+                                       ipaddress);
+       else
+               return;
+
+       if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) != (IFF_RUNNING | IFF_LOWER_UP))
+               return;
+
+       for (list = g_list_first(ipconfig_list); list;
+                                               list = g_list_next(list)) {
+               struct connman_ipconfig *ipconfig = list->data;
+
+               if (index != ipconfig->index)
+                       continue;
+
+               if (type != ipconfig->type)
+                       continue;
+
+               if (ipconfig->ops == NULL)
+                       continue;
+
+               if (ipconfig->ops->ip_bound)
+                       ipconfig->ops->ip_bound(ipconfig);
+       }
+}
+
+void __connman_ipconfig_deladdr(int index, int family, const char *label,
+                               unsigned char prefixlen, const char *address)
+{
+       struct connman_ipdevice *ipdevice;
+       struct connman_ipaddress *ipaddress;
+       enum connman_ipconfig_type type;
+       GList *list;
+
+       DBG("index %d", index);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return;
+
+       ipaddress = find_ipaddress(ipdevice, prefixlen, address);
+       if (ipaddress == NULL)
+               return;
+
+       if (family == AF_INET)
+               type = CONNMAN_IPCONFIG_TYPE_IPV4;
+       else if (family == AF_INET6)
+               type = CONNMAN_IPCONFIG_TYPE_IPV6;
+       else
+               return;
+
+       ipdevice->address_list = g_slist_remove(ipdevice->address_list,
+                                                               ipaddress);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_ippool_deladdr(index, address, prefixlen);
+
+       connman_ipaddress_clear(ipaddress);
+       g_free(ipaddress);
+
+       connman_info("%s {del} address %s/%u label %s", ipdevice->ifname,
+                                               address, prefixlen, label);
+
+       if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) != (IFF_RUNNING | IFF_LOWER_UP))
+               return;
+
+       if (g_slist_length(ipdevice->address_list) > 0)
+               return;
+
+       for (list = g_list_first(ipconfig_list); list;
+                                               list = g_list_next(list)) {
+               struct connman_ipconfig *ipconfig = list->data;
+
+               if (index != ipconfig->index)
+                       continue;
+
+               if (type != ipconfig->type)
+                       continue;
+
+               if (ipconfig->ops == NULL)
+                       continue;
+
+               if (ipconfig->ops->ip_release)
+                       ipconfig->ops->ip_release(ipconfig);
+       }
+}
+
+void __connman_ipconfig_newroute(int index, int family, unsigned char scope,
+                                       const char *dst, const char *gateway)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("index %d", index);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return;
+
+       if (scope == 0 && (g_strcmp0(dst, "0.0.0.0") == 0 ||
+                                               g_strcmp0(dst, "::") == 0)) {
+               GList *config_list;
+               enum connman_ipconfig_type type;
+
+               if (family == AF_INET6) {
+                       type = CONNMAN_IPCONFIG_TYPE_IPV6;
+                       g_free(ipdevice->ipv6_gateway);
+                       ipdevice->ipv6_gateway = g_strdup(gateway);
+
+                       if (ipdevice->config_ipv6 != NULL &&
+                               ipdevice->config_ipv6->system != NULL) {
+                               g_free(ipdevice->config_ipv6->system->gateway);
+                               ipdevice->config_ipv6->system->gateway =
+                                       g_strdup(gateway);
+                       }
+               } else if (family == AF_INET) {
+                       type = CONNMAN_IPCONFIG_TYPE_IPV4;
+                       g_free(ipdevice->ipv4_gateway);
+                       ipdevice->ipv4_gateway = g_strdup(gateway);
+
+                       if (ipdevice->config_ipv4 != NULL &&
+                               ipdevice->config_ipv4->system != NULL) {
+                               g_free(ipdevice->config_ipv4->system->gateway);
+                               ipdevice->config_ipv4->system->gateway =
+                                       g_strdup(gateway);
+                       }
+               } else
+                       return;
+
+               for (config_list = g_list_first(ipconfig_list); config_list;
+                                       config_list = g_list_next(config_list)) {
+                       struct connman_ipconfig *ipconfig = config_list->data;
+
+                       if (index != ipconfig->index)
+                               continue;
+
+                       if (type != ipconfig->type)
+                               continue;
+
+                       if (ipconfig->ops == NULL)
+                               continue;
+
+                       if (ipconfig->ops->route_set)
+                               ipconfig->ops->route_set(ipconfig);
+               }
+       }
+
+       connman_info("%s {add} route %s gw %s scope %u <%s>",
+                                       ipdevice->ifname, dst, gateway,
+                                               scope, scope2str(scope));
+}
+
+void __connman_ipconfig_delroute(int index, int family, unsigned char scope,
+                                       const char *dst, const char *gateway)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("index %d", index);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return;
+
+       if (scope == 0 && (g_strcmp0(dst, "0.0.0.0") == 0 ||
+                                               g_strcmp0(dst, "::") == 0)) {
+               GList *config_list;
+               enum connman_ipconfig_type type;
+
+               if (family == AF_INET6) {
+                       type = CONNMAN_IPCONFIG_TYPE_IPV6;
+                       g_free(ipdevice->ipv6_gateway);
+                       ipdevice->ipv6_gateway = NULL;
+
+                       if (ipdevice->config_ipv6 != NULL &&
+                               ipdevice->config_ipv6->system != NULL) {
+                               g_free(ipdevice->config_ipv6->system->gateway);
+                               ipdevice->config_ipv6->system->gateway = NULL;
+                       }
+               } else if (family == AF_INET) {
+                       type = CONNMAN_IPCONFIG_TYPE_IPV4;
+                       g_free(ipdevice->ipv4_gateway);
+                       ipdevice->ipv4_gateway = NULL;
+
+                       if (ipdevice->config_ipv4 != NULL &&
+                               ipdevice->config_ipv4->system != NULL) {
+                               g_free(ipdevice->config_ipv4->system->gateway);
+                               ipdevice->config_ipv4->system->gateway = NULL;
+                       }
+               } else
+                       return;
+
+               for (config_list = g_list_first(ipconfig_list); config_list;
+                                       config_list = g_list_next(config_list)) {
+                       struct connman_ipconfig *ipconfig = config_list->data;
+
+                       if (index != ipconfig->index)
+                               continue;
+
+                       if (type != ipconfig->type)
+                               continue;
+
+                       if (ipconfig->ops == NULL)
+                               continue;
+
+                       if (ipconfig->ops->route_unset)
+                               ipconfig->ops->route_unset(ipconfig);
+               }
+       }
+
+       connman_info("%s {del} route %s gw %s scope %u <%s>",
+                                       ipdevice->ifname, dst, gateway,
+                                               scope, scope2str(scope));
+}
+
+void __connman_ipconfig_foreach(void (*function) (int index, void *user_data),
+                                                       void *user_data)
+{
+       GList *list, *keys;
+
+       keys = g_hash_table_get_keys(ipdevice_hash);
+       if (keys == NULL)
+               return;
+
+       for (list = g_list_first(keys); list; list = g_list_next(list)) {
+               int index = GPOINTER_TO_INT(list->data);
+
+               function(index, user_data);
+       }
+
+       g_list_free(keys);
+}
+
+enum connman_ipconfig_type __connman_ipconfig_get_config_type(
+                                       struct connman_ipconfig *ipconfig)
+{
+       return ipconfig ? ipconfig->type : CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+}
+
+unsigned short __connman_ipconfig_get_type_from_index(int index)
+{
+       struct connman_ipdevice *ipdevice;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return ARPHRD_VOID;
+
+       return ipdevice->type;
+}
+
+unsigned int __connman_ipconfig_get_flags_from_index(int index)
+{
+       struct connman_ipdevice *ipdevice;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return 0;
+
+       return ipdevice->flags;
+}
+
+const char *__connman_ipconfig_get_gateway_from_index(int index,
+       enum connman_ipconfig_type type)
+{
+       struct connman_ipdevice *ipdevice;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice == NULL)
+               return NULL;
+
+       if (type != CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (ipdevice->ipv4_gateway != NULL)
+                       return ipdevice->ipv4_gateway;
+
+               if (ipdevice->config_ipv4 != NULL &&
+                               ipdevice->config_ipv4->address != NULL)
+                       return ipdevice->config_ipv4->address->gateway;
+       }
+
+       if (type != CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (ipdevice->ipv6_gateway != NULL)
+                       return ipdevice->ipv6_gateway;
+
+               if (ipdevice->config_ipv6 != NULL &&
+                               ipdevice->config_ipv6->address != NULL)
+                       return ipdevice->config_ipv6->address->gateway;
+       }
+
+       return NULL;
+}
+
+void __connman_ipconfig_set_index(struct connman_ipconfig *ipconfig, int index)
+{
+       ipconfig->index = index;
+}
+
+const char *__connman_ipconfig_get_local(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig->address == NULL)
+               return NULL;
+
+       return ipconfig->address->local;
+}
+
+void __connman_ipconfig_set_local(struct connman_ipconfig *ipconfig, const char *address)
+{
+       if (ipconfig->address == NULL)
+               return;
+
+       g_free(ipconfig->address->local);
+       ipconfig->address->local = g_strdup(address);
+}
+
+const char *__connman_ipconfig_get_peer(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig->address == NULL)
+               return NULL;
+
+       return ipconfig->address->peer;
+}
+
+void __connman_ipconfig_set_peer(struct connman_ipconfig *ipconfig, const char *address)
+{
+       if (ipconfig->address == NULL)
+               return;
+
+       g_free(ipconfig->address->peer);
+       ipconfig->address->peer = g_strdup(address);
+}
+
+const char *__connman_ipconfig_get_broadcast(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig->address == NULL)
+               return NULL;
+
+       return ipconfig->address->broadcast;
+}
+
+void __connman_ipconfig_set_broadcast(struct connman_ipconfig *ipconfig, const char *broadcast)
+{
+       if (ipconfig->address == NULL)
+               return;
+
+       g_free(ipconfig->address->broadcast);
+       ipconfig->address->broadcast = g_strdup(broadcast);
+}
+
+const char *__connman_ipconfig_get_gateway(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig->address == NULL)
+               return NULL;
+
+       return ipconfig->address->gateway;
+}
+
+void __connman_ipconfig_set_gateway(struct connman_ipconfig *ipconfig, const char *gateway)
+{
+       DBG("");
+
+       if (ipconfig->address == NULL)
+               return;
+       g_free(ipconfig->address->gateway);
+       ipconfig->address->gateway = g_strdup(gateway);
+}
+
+int __connman_ipconfig_gateway_add(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service;
+
+       DBG("");
+
+       if (ipconfig->address == NULL)
+               return -EINVAL;
+
+       service = __connman_service_lookup_from_index(ipconfig->index);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_connection_gateway_remove(service, ipconfig->type);
+
+       DBG("type %d gw %s peer %s", ipconfig->type,
+               ipconfig->address->gateway, ipconfig->address->peer);
+
+       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6 ||
+                               ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               return __connman_connection_gateway_add(service,
+                                               ipconfig->address->gateway,
+                                               ipconfig->type,
+                                               ipconfig->address->peer);
+
+       return 0;
+}
+
+void __connman_ipconfig_gateway_remove(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service;
+
+       DBG("");
+
+       service = __connman_service_lookup_from_index(ipconfig->index);
+       if (service != NULL)
+               __connman_connection_gateway_remove(service, ipconfig->type);
+}
+
+unsigned char __connman_ipconfig_get_prefixlen(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig->address == NULL)
+               return 0;
+
+       return ipconfig->address->prefixlen;
+}
+
+void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig, unsigned char prefixlen)
+{
+       if (ipconfig->address == NULL)
+               return;
+
+       ipconfig->address->prefixlen = prefixlen;
+}
+
+static struct connman_ipconfig *create_ipv6config(int index)
+{
+       struct connman_ipconfig *ipv6config;
+       struct connman_ipdevice *ipdevice;
+
+       DBG("index %d", index);
+
+       ipv6config = g_try_new0(struct connman_ipconfig, 1);
+       if (ipv6config == NULL)
+               return NULL;
+
+       ipv6config->refcount = 1;
+
+       ipv6config->index = index;
+       ipv6config->enabled = FALSE;
+       ipv6config->type = CONNMAN_IPCONFIG_TYPE_IPV6;
+       ipv6config->method = CONNMAN_IPCONFIG_METHOD_AUTO;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+       if (ipdevice != NULL)
+               ipv6config->ipv6_privacy_config = ipdevice->ipv6_privacy;
+
+       ipv6config->address = connman_ipaddress_alloc(AF_INET6);
+       if (ipv6config->address == NULL) {
+               g_free(ipv6config);
+               return NULL;
+       }
+
+       ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+
+       DBG("ipconfig %p", ipv6config);
+
+       return ipv6config;
+}
+
+/**
+ * connman_ipconfig_create:
+ *
+ * Allocate a new ipconfig structure.
+ *
+ * Returns: a newly-allocated #connman_ipconfig structure
+ */
+struct connman_ipconfig *__connman_ipconfig_create(int index,
+                                       enum connman_ipconfig_type type)
+{
+       struct connman_ipconfig *ipconfig;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               return create_ipv6config(index);
+
+       DBG("index %d", index);
+
+       ipconfig = g_try_new0(struct connman_ipconfig, 1);
+       if (ipconfig == NULL)
+               return NULL;
+
+       ipconfig->refcount = 1;
+
+       ipconfig->index = index;
+       ipconfig->enabled = FALSE;
+       ipconfig->type = CONNMAN_IPCONFIG_TYPE_IPV4;
+
+       ipconfig->address = connman_ipaddress_alloc(AF_INET);
+       if (ipconfig->address == NULL) {
+               g_free(ipconfig);
+               return NULL;
+       }
+
+       ipconfig->system = connman_ipaddress_alloc(AF_INET);
+
+       DBG("ipconfig %p", ipconfig);
+
+       return ipconfig;
+}
+
+
+/**
+ * connman_ipconfig_ref:
+ * @ipconfig: ipconfig structure
+ *
+ * Increase reference counter of ipconfig
+ */
+struct connman_ipconfig *
+__connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&ipconfig->refcount, 1);
+
+       return ipconfig;
+}
+
+/**
+ * connman_ipconfig_unref:
+ * @ipconfig: ipconfig structure
+ *
+ * Decrease reference counter of ipconfig
+ */
+void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig,
+                               const char *file, int line, const char *caller)
+{
+       if (ipconfig == NULL)
+               return;
+
+       DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&ipconfig->refcount, 1) != 1)
+               return;
+
+       if (__connman_ipconfig_disable(ipconfig) < 0)
+               ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
+
+       __connman_ipconfig_set_ops(ipconfig, NULL);
+
+       if (ipconfig->origin != NULL && ipconfig->origin != ipconfig) {
+               __connman_ipconfig_unref(ipconfig->origin);
+               ipconfig->origin = NULL;
+       }
+
+       connman_ipaddress_free(ipconfig->system);
+       connman_ipaddress_free(ipconfig->address);
+       g_free(ipconfig->last_dhcp_address);
+       g_free(ipconfig);
+}
+
+/**
+ * connman_ipconfig_get_data:
+ * @ipconfig: ipconfig structure
+ *
+ * Get private data pointer
+ */
+void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return NULL;
+
+       return ipconfig->ops_data;
+}
+
+/**
+ * connman_ipconfig_set_data:
+ * @ipconfig: ipconfig structure
+ * @data: data pointer
+ *
+ * Set private data pointer
+ */
+void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
+{
+       ipconfig->ops_data = data;
+}
+
+/**
+ * connman_ipconfig_get_index:
+ * @ipconfig: ipconfig structure
+ *
+ * Get interface index
+ */
+int __connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return -1;
+
+       if (ipconfig->origin != NULL)
+               return ipconfig->origin->index;
+
+       return ipconfig->index;
+}
+
+/**
+ * connman_ipconfig_get_ifname:
+ * @ipconfig: ipconfig structure
+ *
+ * Get interface name
+ */
+const char *__connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       if (ipconfig == NULL)
+               return NULL;
+
+       if (ipconfig->index < 0)
+               return NULL;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return NULL;
+
+       return ipdevice->ifname;
+}
+
+/**
+ * connman_ipconfig_set_ops:
+ * @ipconfig: ipconfig structure
+ * @ops: operation callbacks
+ *
+ * Set the operation callbacks
+ */
+void __connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
+                               const struct connman_ipconfig_ops *ops)
+{
+       ipconfig->ops = ops;
+}
+
+/**
+ * connman_ipconfig_set_method:
+ * @ipconfig: ipconfig structure
+ * @method: configuration method
+ *
+ * Set the configuration method
+ */
+int __connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
+                                       enum connman_ipconfig_method method)
+{
+       ipconfig->method = method;
+
+       return 0;
+}
+
+enum connman_ipconfig_method __connman_ipconfig_get_method(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+
+       return ipconfig->method;
+}
+
+int __connman_ipconfig_address_add(struct connman_ipconfig *ipconfig)
+{
+       DBG("");
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       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:
+               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                       return connman_inet_set_address(ipconfig->index,
+                                                       ipconfig->address);
+               else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+                       return connman_inet_set_ipv6_address(
+                                       ipconfig->index, ipconfig->address);
+       }
+
+       return 0;
+}
+
+int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig)
+{
+       int err;
+
+       DBG("");
+
+       if (ipconfig == NULL)
+               return 0;
+
+       DBG("method %d", ipconfig->method);
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       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;
+       }
+
+       return 0;
+}
+
+int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig)
+{
+       int err;
+
+       DBG("");
+
+       if (ipconfig == NULL)
+               return 0;
+
+       DBG("method %d", ipconfig->method);
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       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:
+               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                       err = connman_inet_clear_address(ipconfig->index,
+                                                       ipconfig->address);
+               else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+                       err = connman_inet_clear_ipv6_address(
+                                               ipconfig->index,
+                                               ipconfig->address->local,
+                                               ipconfig->address->prefixlen);
+               else
+                       err = -EINVAL;
+
+               return err;
+       }
+
+       return 0;
+}
+
+int __connman_ipconfig_set_proxy_autoconfig(struct connman_ipconfig *ipconfig,
+                                                        const char *url)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return -ENODEV;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return -ENXIO;
+
+       g_free(ipdevice->pac);
+       ipdevice->pac = g_strdup(url);
+
+       return 0;
+}
+
+const char *__connman_ipconfig_get_proxy_autoconfig(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return NULL;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return NULL;
+
+       return ipdevice->pac;
+}
+
+void __connman_ipconfig_set_dhcp_address(struct connman_ipconfig *ipconfig,
+                                       const char *address)
+{
+       if (ipconfig == NULL)
+               return;
+
+       g_free(ipconfig->last_dhcp_address);
+       ipconfig->last_dhcp_address = g_strdup(address);
+}
+
+char *__connman_ipconfig_get_dhcp_address(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL)
+               return NULL;
+
+       return ipconfig->last_dhcp_address;
+}
+
+static void disable_ipv6(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("");
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return;
+
+       set_ipv6_state(ipdevice->ifname, FALSE);
+}
+
+static void enable_ipv6(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("");
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return;
+
+       if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO)
+               set_ipv6_privacy(ipdevice->ifname,
+                               ipconfig->ipv6_privacy_config);
+
+       set_ipv6_state(ipdevice->ifname, TRUE);
+}
+
+void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       enable_ipv6(ipconfig);
+}
+
+void __connman_ipconfig_disable_ipv6(struct connman_ipconfig *ipconfig)
+{
+       if (ipconfig == NULL || ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       disable_ipv6(ipconfig);
+}
+
+int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+       gboolean up = FALSE, down = FALSE;
+       gboolean lower_up = FALSE, lower_down = FALSE;
+       enum connman_ipconfig_type type;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return -ENODEV;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return -ENXIO;
+
+       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (ipdevice->config_ipv4 == ipconfig)
+                       return -EALREADY;
+               type = CONNMAN_IPCONFIG_TYPE_IPV4;
+       } else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (ipdevice->config_ipv6 == ipconfig)
+                       return -EALREADY;
+               type = CONNMAN_IPCONFIG_TYPE_IPV6;
+               enable_ipv6(ipconfig);
+       } else
+               return -EINVAL;
+
+       ipconfig->enabled = TRUE;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                                       ipdevice->config_ipv4 != NULL) {
+               ipconfig_list = g_list_remove(ipconfig_list,
+                                                       ipdevice->config_ipv4);
+
+               connman_ipaddress_clear(ipdevice->config_ipv4->system);
+
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                                       ipdevice->config_ipv6 != NULL) {
+               ipconfig_list = g_list_remove(ipconfig_list,
+                                                       ipdevice->config_ipv6);
+
+               connman_ipaddress_clear(ipdevice->config_ipv6->system);
+
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               ipdevice->config_ipv4 = __connman_ipconfig_ref(ipconfig);
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               ipdevice->config_ipv6 = __connman_ipconfig_ref(ipconfig);
+
+       ipconfig_list = g_list_append(ipconfig_list, ipconfig);
+
+       if (ipdevice->flags & IFF_UP)
+               up = TRUE;
+       else
+               down = TRUE;
+
+       if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) ==
+                       (IFF_RUNNING | IFF_LOWER_UP))
+               lower_up = TRUE;
+       else if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) == 0)
+               lower_down = TRUE;
+
+       if (up == TRUE && ipconfig->ops->up)
+               ipconfig->ops->up(ipconfig);
+       if (lower_up == TRUE && ipconfig->ops->lower_up)
+               ipconfig->ops->lower_up(ipconfig);
+
+       if (lower_down == TRUE && ipconfig->ops->lower_down)
+               ipconfig->ops->lower_down(ipconfig);
+       if (down == TRUE && ipconfig->ops->down)
+               ipconfig->ops->down(ipconfig);
+
+       return 0;
+}
+
+int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
+{
+       struct connman_ipdevice *ipdevice;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (ipconfig == NULL || ipconfig->index < 0)
+               return -ENODEV;
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return -ENXIO;
+
+       if (ipdevice->config_ipv4 == NULL && ipdevice->config_ipv6 == NULL)
+               return -EINVAL;
+
+       ipconfig->enabled = FALSE;
+
+       if (ipdevice->config_ipv4 == ipconfig) {
+               ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
+
+               connman_ipaddress_clear(ipdevice->config_ipv4->system);
+               __connman_ipconfig_unref(ipdevice->config_ipv4);
+               ipdevice->config_ipv4 = NULL;
+               return 0;
+       }
+
+       if (ipdevice->config_ipv6 == ipconfig) {
+               ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
+
+               if (ipdevice->config_ipv6->method ==
+                                               CONNMAN_IPCONFIG_METHOD_AUTO)
+                       disable_ipv6(ipdevice->config_ipv6);
+
+               connman_ipaddress_clear(ipdevice->config_ipv6->system);
+               __connman_ipconfig_unref(ipdevice->config_ipv6);
+               ipdevice->config_ipv6 = NULL;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+const char *__connman_ipconfig_method2string(enum connman_ipconfig_method method)
+{
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               return "off";
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               return "fixed";
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               return "manual";
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               return "dhcp";
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return "auto";
+       }
+
+       return NULL;
+}
+
+enum connman_ipconfig_method __connman_ipconfig_string2method(const char *method)
+{
+       if (g_strcmp0(method, "off") == 0)
+               return CONNMAN_IPCONFIG_METHOD_OFF;
+       else if (g_strcmp0(method, "fixed") == 0)
+               return CONNMAN_IPCONFIG_METHOD_FIXED;
+       else if (g_strcmp0(method, "manual") == 0)
+               return CONNMAN_IPCONFIG_METHOD_MANUAL;
+       else if (g_strcmp0(method, "dhcp") == 0)
+               return CONNMAN_IPCONFIG_METHOD_DHCP;
+       else if (g_strcmp0(method, "auto") == 0)
+               return CONNMAN_IPCONFIG_METHOD_AUTO;
+       else
+               return CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+}
+
+static const char *privacy2string(int privacy)
+{
+       if (privacy <= 0)
+               return "disabled";
+       else if (privacy == 1)
+               return "enabled";
+       else if (privacy > 1)
+               return "prefered";
+
+       return "disabled";
+}
+
+static int string2privacy(const char *privacy)
+{
+       if (g_strcmp0(privacy, "disabled") == 0)
+               return 0;
+       else if (g_strcmp0(privacy, "enabled") == 0)
+               return 1;
+       else if (g_strcmp0(privacy, "prefered") == 0)
+               return 2;
+       else
+               return 0;
+}
+
+void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter)
+{
+       struct connman_ipaddress *append_addr = NULL;
+       const char *str;
+
+       DBG("");
+
+       if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4)
+               return;
+
+       str = __connman_ipconfig_method2string(ipconfig->method);
+       if (str == NULL)
+               return;
+
+       connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+       append_addr = ipconfig->system;
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               if (append_addr == NULL)
+                       append_addr = ipconfig->address;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+       }
+
+       if (append_addr == NULL)
+               return;
+
+       if (append_addr->local != NULL) {
+               in_addr_t addr;
+               struct in_addr netmask;
+               char *mask;
+
+               connman_dbus_dict_append_basic(iter, "Address",
+                               DBUS_TYPE_STRING, &append_addr->local);
+
+               addr = 0xffffffff << (32 - append_addr->prefixlen);
+               netmask.s_addr = htonl(addr);
+               mask = inet_ntoa(netmask);
+               connman_dbus_dict_append_basic(iter, "Netmask",
+                                               DBUS_TYPE_STRING, &mask);
+       }
+
+       if (append_addr->gateway != NULL)
+               connman_dbus_dict_append_basic(iter, "Gateway",
+                               DBUS_TYPE_STRING, &append_addr->gateway);
+}
+
+void __connman_ipconfig_append_ipv6(struct connman_ipconfig *ipconfig,
+                                       DBusMessageIter *iter,
+                                       struct connman_ipconfig *ipconfig_ipv4)
+{
+       struct connman_ipaddress *append_addr = NULL;
+       const char *str, *privacy;
+
+       DBG("");
+
+       if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+               return;
+
+       str = __connman_ipconfig_method2string(ipconfig->method);
+       if (str == NULL)
+               return;
+
+       if (ipconfig_ipv4 != NULL &&
+                       ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+               if (__connman_6to4_check(ipconfig_ipv4) == 1)
+                       str = "6to4";
+       }
+
+       connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+       append_addr = ipconfig->system;
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               return;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               if (append_addr == NULL)
+                       append_addr = ipconfig->address;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+       }
+
+       if (append_addr == NULL)
+               return;
+
+       if (append_addr->local != NULL) {
+               connman_dbus_dict_append_basic(iter, "Address",
+                               DBUS_TYPE_STRING, &append_addr->local);
+               connman_dbus_dict_append_basic(iter, "PrefixLength",
+                                               DBUS_TYPE_BYTE,
+                                               &append_addr->prefixlen);
+       }
+
+       if (append_addr->gateway != NULL)
+               connman_dbus_dict_append_basic(iter, "Gateway",
+                               DBUS_TYPE_STRING, &append_addr->gateway);
+
+       privacy = privacy2string(ipconfig->ipv6_privacy_config);
+       connman_dbus_dict_append_basic(iter, "Privacy",
+                               DBUS_TYPE_STRING, &privacy);
+}
+
+void __connman_ipconfig_append_ipv6config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter)
+{
+       const char *str, *privacy;
+
+       DBG("");
+
+       str = __connman_ipconfig_method2string(ipconfig->method);
+       if (str == NULL)
+               return;
+
+       connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+       }
+
+       if (ipconfig->address == NULL)
+               return;
+
+       if (ipconfig->address->local != NULL) {
+               connman_dbus_dict_append_basic(iter, "Address",
+                               DBUS_TYPE_STRING, &ipconfig->address->local);
+               connman_dbus_dict_append_basic(iter, "PrefixLength",
+                                               DBUS_TYPE_BYTE,
+                                               &ipconfig->address->prefixlen);
+       }
+
+       if (ipconfig->address->gateway != NULL)
+               connman_dbus_dict_append_basic(iter, "Gateway",
+                               DBUS_TYPE_STRING, &ipconfig->address->gateway);
+
+       privacy = privacy2string(ipconfig->ipv6_privacy_config);
+       connman_dbus_dict_append_basic(iter, "Privacy",
+                               DBUS_TYPE_STRING, &privacy);
+}
+
+void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter)
+{
+       const char *str;
+
+       DBG("");
+
+       str = __connman_ipconfig_method2string(ipconfig->method);
+       if (str == NULL)
+               return;
+
+       connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
+
+       switch (ipconfig->method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               break;
+       }
+
+       if (ipconfig->address == NULL)
+               return;
+
+       if (ipconfig->address->local != NULL) {
+               in_addr_t addr;
+               struct in_addr netmask;
+               char *mask;
+
+               connman_dbus_dict_append_basic(iter, "Address",
+                               DBUS_TYPE_STRING, &ipconfig->address->local);
+
+               addr = 0xffffffff << (32 - ipconfig->address->prefixlen);
+               netmask.s_addr = htonl(addr);
+               mask = inet_ntoa(netmask);
+               connman_dbus_dict_append_basic(iter, "Netmask",
+                                               DBUS_TYPE_STRING, &mask);
+       }
+
+       if (ipconfig->address->gateway != NULL)
+               connman_dbus_dict_append_basic(iter, "Gateway",
+                               DBUS_TYPE_STRING, &ipconfig->address->gateway);
+}
+
+int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *array)
+{
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       const char *address = NULL, *netmask = NULL, *gateway = NULL,
+               *prefix_length_string = NULL, *privacy_string = NULL;
+       int prefix_length = 0, privacy = 0;
+       DBusMessageIter dict;
+
+       DBG("ipconfig %p", ipconfig);
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+               int type;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               type = dbus_message_iter_get_arg_type(&value);
+
+               if (g_str_equal(key, "Method") == TRUE) {
+                       const char *str;
+
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value, &str);
+                       method = __connman_ipconfig_string2method(str);
+               } else if (g_str_equal(key, "Address") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value, &address);
+               } else if (g_str_equal(key, "PrefixLength") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value,
+                                                       &prefix_length_string);
+
+                       prefix_length = atoi(prefix_length_string);
+                       if (prefix_length < 0 || prefix_length > 128)
+                               return -EINVAL;
+               } else if (g_str_equal(key, "Netmask") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value, &netmask);
+               } else if (g_str_equal(key, "Gateway") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value, &gateway);
+               } else if (g_str_equal(key, "Privacy") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               return -EINVAL;
+
+                       dbus_message_iter_get_basic(&value, &privacy_string);
+                       privacy = string2privacy(privacy_string);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       DBG("method %d address %s netmask %s gateway %s prefix_length %d "
+               "privacy %s",
+               method, address, netmask, gateway, prefix_length,
+               privacy_string);
+
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               return -EINVAL;
+
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               ipconfig->method = method;
+               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+                       disable_ipv6(ipconfig);
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV6)
+                       return -EINVAL;
+
+               ipconfig->method = method;
+               if (privacy_string != NULL)
+                       ipconfig->ipv6_privacy_config = privacy;
+               enable_ipv6(ipconfig);
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               if (address == NULL)
+                       return -EINVAL;
+
+               ipconfig->method = method;
+
+               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                       connman_ipaddress_set_ipv4(ipconfig->address,
+                                               address, netmask, gateway);
+               else
+                       return connman_ipaddress_set_ipv6(
+                                       ipconfig->address, address,
+                                               prefix_length, gateway);
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
+                       return -EOPNOTSUPP;
+
+               ipconfig->method = method;
+               break;
+       }
+
+       return 0;
+}
+
+void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig,
+                                                       DBusMessageIter *iter)
+{
+       struct connman_ipdevice *ipdevice;
+       const char *method = "auto";
+
+       connman_dbus_dict_append_basic(iter, "Method",
+                                               DBUS_TYPE_STRING, &method);
+
+       ipdevice = g_hash_table_lookup(ipdevice_hash,
+                                       GINT_TO_POINTER(ipconfig->index));
+       if (ipdevice == NULL)
+               return;
+
+       if (ipdevice->ifname != NULL)
+               connman_dbus_dict_append_basic(iter, "Interface",
+                                       DBUS_TYPE_STRING, &ipdevice->ifname);
+
+       if (ipdevice->address != NULL)
+               connman_dbus_dict_append_basic(iter, "Address",
+                                       DBUS_TYPE_STRING, &ipdevice->address);
+
+       if (ipdevice->mtu > 0)
+               connman_dbus_dict_append_basic(iter, "MTU",
+                                       DBUS_TYPE_UINT16, &ipdevice->mtu);
+}
+
+int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
+               GKeyFile *keyfile, const char *identifier, const char *prefix)
+{
+       char *method;
+       char *key;
+       char *str;
+
+       DBG("ipconfig %p identifier %s", ipconfig, identifier);
+
+       key = g_strdup_printf("%smethod", prefix);
+       method = g_key_file_get_string(keyfile, identifier, key, NULL);
+       if (method == NULL) {
+               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:
+                       ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF;
+                       break;
+               }
+       } else
+               ipconfig->method = __connman_ipconfig_string2method(method);
+
+       if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_UNKNOWN)
+               ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF;
+
+       if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO ||
+                       ipconfig->method == CONNMAN_IPCONFIG_METHOD_MANUAL) {
+                       char *privacy;
+                       char *pprefix = g_strdup_printf("%sprivacy", prefix);
+                       privacy = g_key_file_get_string(keyfile, identifier,
+                                                       pprefix, NULL);
+                       ipconfig->ipv6_privacy_config = string2privacy(privacy);
+                       g_free(pprefix);
+                       g_free(privacy);
+               }
+       }
+
+       g_free(method);
+       g_free(key);
+
+       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);
+       ipconfig->address->local = g_key_file_get_string(
+                       keyfile, identifier, key, NULL);
+       g_free(key);
+
+       key = g_strdup_printf("%speer_address", prefix);
+       ipconfig->address->peer = g_key_file_get_string(
+                               keyfile, identifier, key, NULL);
+       g_free(key);
+
+       key = g_strdup_printf("%sbroadcast_address", prefix);
+       ipconfig->address->broadcast = g_key_file_get_string(
+                               keyfile, identifier, key, NULL);
+       g_free(key);
+
+       key = g_strdup_printf("%sgateway", prefix);
+       ipconfig->address->gateway = g_key_file_get_string(
+                               keyfile, identifier, key, NULL);
+       g_free(key);
+
+       key = g_strdup_printf("%sDHCP.LastAddress", prefix);
+       str = g_key_file_get_string(keyfile, identifier, key, NULL);
+       if (str != NULL) {
+               g_free(ipconfig->last_dhcp_address);
+               ipconfig->last_dhcp_address = str;
+       }
+       g_free(key);
+
+       return 0;
+}
+
+int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
+               GKeyFile *keyfile, const char *identifier, const char *prefix)
+{
+       const char *method;
+       char *key;
+
+       DBG("ipconfig %p identifier %s", ipconfig, identifier);
+
+       method = __connman_ipconfig_method2string(ipconfig->method);
+
+       key = g_strdup_printf("%smethod", prefix);
+       g_key_file_set_string(keyfile, identifier, key, method);
+       g_free(key);
+
+       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);
+       }
+
+       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 != NULL &&
+                               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);
+               /* fall through */
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return 0;
+       }
+
+       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 != NULL)
+               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 != NULL)
+               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 != NULL)
+               g_key_file_set_string(keyfile, identifier,
+                       key, ipconfig->address->broadcast);
+       g_free(key);
+
+       key = g_strdup_printf("%sgateway", prefix);
+       if (ipconfig->address->gateway != NULL)
+               g_key_file_set_string(keyfile, identifier,
+                       key, ipconfig->address->gateway);
+       g_free(key);
+
+       return 0;
+}
+
+int __connman_ipconfig_init(void)
+{
+       DBG("");
+
+       ipdevice_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_ipdevice);
+
+       return 0;
+}
+
+void __connman_ipconfig_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(ipdevice_hash);
+       ipdevice_hash = NULL;
+}
diff --git a/src/ippool.c b/src/ippool.c
new file mode 100644 (file)
index 0000000..52446ce
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. 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 <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+
+#include "connman.h"
+
+struct address_info {
+       int index;
+       uint32_t start;
+       uint32_t end;
+
+       unsigned int use_count;
+       struct connman_ippool *pool;
+};
+
+struct connman_ippool {
+       unsigned int refcount;
+
+       struct address_info *info;
+
+       char *gateway;
+       char *broadcast;
+       char *start_ip;
+       char *end_ip;
+       char *subnet_mask;
+
+       ippool_collision_cb_t collision_cb;
+       void *user_data;
+};
+
+GSList *allocated_blocks;
+GHashTable *pool_hash;
+
+static uint32_t last_block;
+static uint32_t block_16_bits;
+static uint32_t block_20_bits;
+static uint32_t block_24_bits;
+static uint32_t subnet_mask_24;
+
+struct connman_ippool *
+__connman_ippool_ref_debug(struct connman_ippool *pool,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&pool->refcount, 1);
+
+       return pool;
+}
+
+void __connman_ippool_unref_debug(struct connman_ippool *pool,
+                               const char *file, int line, const char *caller)
+{
+       if (pool == NULL)
+               return;
+
+       DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&pool->refcount, 1) != 1)
+               return;
+
+       g_hash_table_remove(pool_hash, pool);
+}
+
+static char *get_ip(uint32_t ip)
+{
+       struct in_addr addr;
+
+       addr.s_addr = htonl(ip);
+
+       return g_strdup(inet_ntoa(addr));
+}
+
+static uint32_t next_block(uint32_t block)
+{
+       uint32_t next;
+
+       /*
+        * Return the next IP block within the private IP range
+        *
+        * 16-bit block 192.168.0.0 â€“ 192.168.255.255
+        * 20-bit block  172.16.0.0 â€“  172.31.255.255
+        * 24-bit block    10.0.0.0 â€“  10.255.255.255
+        */
+
+       next = (block & 0x0000ff00) >> 8;
+       next += 1;
+
+       if (next == 255) {
+               if ((block & 0xffff0000) == block_16_bits) {
+                       /*
+                        * Reached the end of the 16 bit block, switch
+                        * to the 20-bit block.
+                        */
+                       return block_20_bits;
+               }
+
+               if ((block & 0xffff0000) >= block_20_bits) {
+                       next = (block & 0x00ff0000) >> 16;
+                       if (next >= 16 && next < 32)
+                               next += 1;
+
+                       if (next == 32) {
+                               /*
+                                * Reached the end of the 20 bit
+                                * block, switch to the 24-bit block.
+                                */
+                               return block_24_bits;
+                       }
+
+                       return (block & 0xff000000) |
+                               ((next << 16) & 0x00ff0000);
+               }
+
+               if ((block & 0xff000000) == block_24_bits) {
+                       next = (block & 0x00ff0000) >> 16;
+                       if (next < 255)
+                               next += 1;
+
+                       if (next == 255) {
+                               /*
+                                * Reached the end of the 24 bit
+                                * block, switch to the 16-bit block.
+                                */
+                               return block_16_bits;
+                       }
+
+                       return (block & 0xff000000) |
+                               ((next << 16) & 0x00ff0000);
+               }
+       }
+
+       return (block & 0xffff0000) | ((next << 8) & 0x0000ff00);
+}
+
+static uint32_t get_free_block(unsigned int size)
+{
+       struct address_info *info;
+       uint32_t block;
+       GSList *list;
+       connman_bool_t collision;
+
+       /*
+        * Instead starting always from the 16 bit block, we start
+        * from the last assigned block. This is a simple optimimazion
+        * for the case where a lot of blocks have been assigned, e.g.
+        * the first half of the private IP pool is in use and a new
+        * we need to find a new block.
+        *
+        * To only thing we have to make sure is that we terminated if
+        * there is no block left.
+        */
+       if (last_block == 0)
+               block = block_16_bits;
+       else
+               block = next_block(last_block);
+
+       do {
+               collision = FALSE;
+               for (list = allocated_blocks; list != NULL; list = list->next) {
+                       info = list->data;
+
+                       if (info->start <= block && block <= info->end) {
+                               collision = TRUE;
+                               break;
+                       }
+               }
+
+               if (collision == FALSE)
+                       return block;
+
+               block = next_block(block);
+       } while (block != last_block);
+
+       return 0;
+}
+
+static struct address_info *lookup_info(int index, uint32_t start)
+{
+       GSList *list;
+
+       for (list = allocated_blocks; list != NULL; list = list->next) {
+               struct address_info *info = list->data;
+
+               if (info->index == index && info->start == start)
+                       return info;
+       }
+
+       return NULL;
+}
+
+static connman_bool_t is_private_address(uint32_t address)
+{
+       uint32_t val;
+
+       if ((address & 0xff000000) == block_24_bits)
+               return TRUE;
+
+       if ((address & 0xffff0000) == block_20_bits) {
+               val = (address & 0x00ff0000) >> 16;
+
+               if (val < 16 || val > 31)
+                       return FALSE;
+
+               return TRUE;
+       }
+
+       if ((address & 0xffffff00) == block_16_bits)
+               return TRUE;
+
+       return FALSE;
+}
+
+void __connman_ippool_newaddr(int index, const char *address,
+                               unsigned char prefixlen)
+{
+       struct address_info *info, *it;
+       struct in_addr inp;
+       uint32_t start, end, mask;
+       GSList *list;
+
+       if (inet_aton(address, &inp) == 0)
+               return;
+
+       start = ntohl(inp.s_addr);
+       if (is_private_address(start) == FALSE)
+               return;
+
+       if (prefixlen >= 32)
+               mask = 0xffffffff;
+       else
+               mask = ~(0xffffffff >> prefixlen);
+
+       start = start & mask;
+       end = start | ~mask;
+
+       info = lookup_info(index, start);
+       if (info != NULL)
+               goto update;
+
+       info = g_try_new0(struct address_info, 1);
+       if (info == NULL)
+               return;
+
+       info->index = index;
+       info->start = start;
+       info->end = end;
+
+       allocated_blocks = g_slist_prepend(allocated_blocks, info);
+
+update:
+       info->use_count = info->use_count + 1;
+
+       if (info->use_count > 1 || info->pool != NULL) {
+               /*
+                * We need only to check for the first IP in a block for
+                * collisions.
+                */
+               return;
+       }
+
+       for (list = allocated_blocks; list != NULL; list = list->next) {
+               it = list->data;
+
+               if (it == info)
+                       continue;
+
+               if (!(it->start <= info->start || info->start <= it->end))
+                       continue;
+
+               if (it->pool != NULL && it->pool->collision_cb != NULL)
+                       it->pool->collision_cb(it->pool, it->pool->user_data);
+
+               return;
+       }
+}
+
+void __connman_ippool_deladdr(int index, const char *address,
+                               unsigned char prefixlen)
+{
+       struct address_info *info;
+       struct in_addr inp;
+       uint32_t start, mask;
+
+       if (inet_aton(address, &inp) == 0)
+               return;
+
+       start = ntohl(inp.s_addr);
+       if (is_private_address(start) == FALSE)
+               return;
+
+       mask = ~(0xffffffff >> prefixlen);
+       start = start & mask;
+
+       info = lookup_info(index, start);
+       if (info == NULL) {
+               /* In theory this should never happen */
+               connman_error("Inconsistent IP pool management (start not found)");
+               return;
+       }
+
+       info->use_count = info->use_count - 1;
+       if (info->pool != NULL)
+               return;
+
+       if (info->use_count > 0)
+               return;
+
+       allocated_blocks = g_slist_remove(allocated_blocks, info);
+}
+
+struct connman_ippool *__connman_ippool_create(int index,
+                                       unsigned int start,
+                                       unsigned int range,
+                                       ippool_collision_cb_t collision_cb,
+                                       void *user_data)
+{
+       struct connman_ippool *pool;
+       struct address_info *info;
+       uint32_t block;
+
+       DBG("");
+
+       /*
+        * The range is at max 255 and we don't support overlapping
+        * blocks.
+        */
+       if (start + range > 254) {
+               connman_error("IP pool does not support pool size larger than 254");
+               return NULL;
+       }
+
+       block = get_free_block(start + range);
+       if (block == 0) {
+               connman_warn("Could not find a free IP block");
+               return NULL;
+       }
+
+       pool = g_try_new0(struct connman_ippool, 1);
+       if (pool == NULL)
+               return NULL;
+
+       info = g_try_new0(struct address_info, 1);
+       if (info == NULL) {
+               g_free(pool);
+               return NULL;
+       }
+
+       last_block = block;
+
+       info->index = index;
+       info->start = block;
+       info->end = block + range;
+
+       pool->refcount = 1;
+       pool->info = info;
+       pool->collision_cb = collision_cb;
+       pool->user_data = user_data;
+
+       info->pool = pool;
+
+       if (range == 0)
+               range = 1;
+
+       pool->gateway = get_ip(info->start + 1);
+       pool->broadcast = get_ip(info->start + 255);
+       pool->subnet_mask = get_ip(subnet_mask_24);
+       pool->start_ip = get_ip(block + start);
+       pool->end_ip = get_ip(block + start + range);
+
+       allocated_blocks = g_slist_prepend(allocated_blocks, info);
+       g_hash_table_insert(pool_hash, pool, pool);
+
+       return pool;
+}
+
+const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
+{
+       return pool->gateway;
+}
+
+const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
+{
+       return pool->broadcast;
+}
+
+const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
+{
+       return pool->start_ip;
+}
+
+const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
+{
+       return pool->end_ip;
+}
+
+const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
+{
+       return pool->subnet_mask;
+}
+
+static void pool_free(gpointer data)
+{
+       struct connman_ippool *pool = data;
+
+       if (pool->info != NULL) {
+               allocated_blocks = g_slist_remove(allocated_blocks, pool->info);
+               g_free(pool->info);
+       }
+
+       g_free(pool->gateway);
+       g_free(pool->broadcast);
+       g_free(pool->start_ip);
+       g_free(pool->end_ip);
+       g_free(pool->subnet_mask);
+
+       g_free(pool);
+}
+
+int __connman_ippool_init(void)
+{
+       DBG("");
+
+       block_16_bits = ntohl(inet_addr("192.168.0.0"));
+       block_20_bits = ntohl(inet_addr("172.16.0.0"));
+       block_24_bits = ntohl(inet_addr("10.0.0.0"));
+       subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
+
+       pool_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                       pool_free);
+
+       return 0;
+}
+
+void __connman_ippool_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(pool_hash);
+       pool_hash = NULL;
+
+       g_slist_free(allocated_blocks);
+       last_block = 0;
+}
diff --git a/src/iptables.c b/src/iptables.c
new file mode 100644 (file)
index 0000000..531e933
--- /dev/null
@@ -0,0 +1,1970 @@
+/*
+ *
+ *  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 <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <xtables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#include "connman.h"
+
+
+static const char *hooknames[] = {
+       [NF_IP_PRE_ROUTING]     = "PREROUTING",
+       [NF_IP_LOCAL_IN]        = "INPUT",
+       [NF_IP_FORWARD]         = "FORWARD",
+       [NF_IP_LOCAL_OUT]       = "OUTPUT",
+       [NF_IP_POST_ROUTING]    = "POSTROUTING",
+};
+
+#define LABEL_ACCEPT  "ACCEPT"
+#define LABEL_DROP    "DROP"
+#define LABEL_QUEUE   "QUEUE"
+#define LABEL_RETURN  "RETURN"
+
+#define XT_OPTION_OFFSET_SCALE 256
+
+/* fn returns 0 to continue iteration */
+#define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
+({                                                             \
+       unsigned int __i;                                       \
+       int __n;                                                \
+       int __ret = 0;                                          \
+       type *__entry;                                          \
+                                                               \
+       for (__i = 0, __n = 0; __i < (size);                    \
+            __i += __entry->next_offset, __n++) {              \
+               __entry = (void *)(entries) + __i;              \
+               if (__n < n)                                    \
+                       continue;                               \
+                                                               \
+               __ret = fn(__entry,  ## args);                  \
+               if (__ret != 0)                                 \
+                       break;                                  \
+       }                                                       \
+       __ret;                                                  \
+})
+
+/* fn returns 0 to continue iteration */
+#define _XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
+       _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
+
+#define ENTRY_ITERATE(entries, size, fn, args...) \
+       _XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
+
+#define MIN_ALIGN (__alignof__(struct ipt_entry))
+
+#define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
+
+struct error_target {
+       struct xt_entry_target t;
+       char error[IPT_TABLE_MAXNAMELEN];
+};
+
+struct connman_iptables_entry {
+       int offset;
+       int builtin;
+
+       struct ipt_entry *entry;
+};
+
+struct connman_iptables {
+       int ipt_sock;
+
+       struct ipt_getinfo *info;
+       struct ipt_get_entries *blob_entries;
+
+       unsigned int num_entries;
+       unsigned int old_entries;
+       unsigned int size;
+
+       unsigned int underflow[NF_INET_NUMHOOKS];
+       unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+       GList *entries;
+};
+
+static GHashTable *table_hash = NULL;
+
+static struct ipt_entry *get_entry(struct connman_iptables *table,
+                                       unsigned int offset)
+{
+       return (struct ipt_entry *)((char *)table->blob_entries->entrytable +
+                                                                       offset);
+}
+
+static int is_hook_entry(struct connman_iptables *table,
+                               struct ipt_entry *entry)
+{
+       unsigned int i;
+
+       for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+               if ((table->info->valid_hooks & (1 << i))
+               && get_entry(table, table->info->hook_entry[i]) == entry)
+                       return i;
+       }
+
+       return -1;
+}
+
+static unsigned long entry_to_offset(struct connman_iptables *table,
+                                       struct ipt_entry *entry)
+{
+       return (void *)entry - (void *)table->blob_entries->entrytable;
+}
+
+static int target_to_verdict(char *target_name)
+{
+       if (!strcmp(target_name, LABEL_ACCEPT))
+               return -NF_ACCEPT - 1;
+
+       if (!strcmp(target_name, LABEL_DROP))
+               return -NF_DROP - 1;
+
+       if (!strcmp(target_name, LABEL_QUEUE))
+               return -NF_QUEUE - 1;
+
+       if (!strcmp(target_name, LABEL_RETURN))
+               return XT_RETURN;
+
+       return 0;
+}
+
+static gboolean is_builtin_target(char *target_name)
+{
+       if (!strcmp(target_name, LABEL_ACCEPT) ||
+               !strcmp(target_name, LABEL_DROP) ||
+               !strcmp(target_name, LABEL_QUEUE) ||
+               !strcmp(target_name, LABEL_RETURN))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean is_jump(struct connman_iptables_entry *e)
+{
+       struct xt_entry_target *target;
+
+       target = ipt_get_target(e->entry);
+
+       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+               struct xt_standard_target *t;
+
+               t = (struct xt_standard_target *)target;
+
+               switch (t->verdict) {
+               case XT_RETURN:
+               case -NF_ACCEPT - 1:
+               case -NF_DROP - 1:
+               case -NF_QUEUE - 1:
+               case -NF_STOP - 1:
+                       return false;
+
+               default:
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static gboolean is_chain(struct connman_iptables *table,
+                               struct connman_iptables_entry *e)
+{
+       struct ipt_entry *entry;
+       struct xt_entry_target *target;
+
+       entry = e->entry;
+       if (e->builtin >= 0)
+               return TRUE;
+
+       target = ipt_get_target(entry);
+       if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
+               return TRUE;
+
+       return FALSE;
+}
+
+static GList *find_chain_head(struct connman_iptables *table,
+                               char *chain_name)
+{
+       GList *list;
+       struct connman_iptables_entry *head;
+       struct ipt_entry *entry;
+       struct xt_entry_target *target;
+       int builtin;
+
+       for (list = table->entries; list; list = list->next) {
+               head = list->data;
+               entry = head->entry;
+
+               /* Buit-in chain */
+               builtin = head->builtin;
+               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
+                       break;
+
+               /* User defined chain */
+               target = ipt_get_target(entry);
+               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
+                   !strcmp((char *)target->data, chain_name))
+                       break;
+       }
+
+       return list;
+}
+
+static GList *find_chain_tail(struct connman_iptables *table,
+                               char *chain_name)
+{
+       struct connman_iptables_entry *tail;
+       GList *chain_head, *list;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       /* Then we look for the next chain */
+       for (list = chain_head->next; list; list = list->next) {
+               tail = list->data;
+
+               if (is_chain(table, tail))
+                       return list;
+       }
+
+       /* Nothing found, we return the table end */
+       return g_list_last(table->entries);
+}
+
+
+static void update_offsets(struct connman_iptables *table)
+{
+       GList *list, *prev;
+       struct connman_iptables_entry *entry, *prev_entry;
+
+       for (list = table->entries; list; list = list->next) {
+               entry = list->data;
+
+               if (list == table->entries) {
+                       entry->offset = 0;
+
+                       continue;
+               }
+
+               prev = list->prev;
+               prev_entry = prev->data;
+
+               entry->offset = prev_entry->offset +
+                                       prev_entry->entry->next_offset;
+       }
+}
+
+static void update_targets_reference(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry_before,
+                               struct connman_iptables_entry *modified_entry,
+                               gboolean is_removing)
+{
+       struct connman_iptables_entry *tmp;
+       struct xt_standard_target *t;
+       GList *list;
+       int offset;
+
+       offset = modified_entry->entry->next_offset;
+
+       for (list = table->entries; list; list = list->next) {
+               tmp = list->data;
+
+               if (!is_jump(tmp))
+                       continue;
+
+               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+
+               if (is_removing == TRUE) {
+                       if (t->verdict >= entry_before->offset)
+                               t->verdict -= offset;
+               } else {
+                       if (t->verdict > entry_before->offset)
+                               t->verdict += offset;
+               }
+       }
+}
+
+static int iptables_add_entry(struct connman_iptables *table,
+                               struct ipt_entry *entry, GList *before,
+                                       int builtin)
+{
+       struct connman_iptables_entry *e, *entry_before;
+
+       if (table == NULL)
+               return -1;
+
+       e = g_try_malloc0(sizeof(struct connman_iptables_entry));
+       if (e == NULL)
+               return -1;
+
+       e->entry = entry;
+       e->builtin = builtin;
+
+       table->entries = g_list_insert_before(table->entries, before, e);
+       table->num_entries++;
+       table->size += entry->next_offset;
+
+       if (before == NULL) {
+               e->offset = table->size - entry->next_offset;
+
+               return 0;
+       }
+
+       entry_before = before->data;
+
+       /*
+        * We've just appended/insterted a new entry. All references
+        * should be bumped accordingly.
+        */
+       update_targets_reference(table, entry_before, e, FALSE);
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int remove_table_entry(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry)
+{
+       int removed = 0;
+
+       table->num_entries--;
+       table->size -= entry->entry->next_offset;
+       removed = entry->entry->next_offset;
+
+       g_free(entry->entry);
+
+       table->entries = g_list_remove(table->entries, entry);
+
+       return removed;
+}
+
+static int iptables_flush_chain(struct connman_iptables *table,
+                                               char *name)
+{
+       GList *chain_head, *chain_tail, *list, *next;
+       struct connman_iptables_entry *entry;
+       int builtin, removed = 0;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       if (list == chain_tail->prev)
+               return 0;
+
+       while (list != chain_tail->prev) {
+               entry = list->data;
+               next = g_list_next(list);
+
+               removed += remove_table_entry(table, entry);
+
+               list = next;
+       }
+
+       if (builtin >= 0) {
+               struct connman_iptables_entry *e;
+
+               entry = list->data;
+
+               entry->builtin = builtin;
+
+               table->underflow[builtin] -= removed;
+
+               for (list = chain_tail; list; list = list->next) {
+                       e = list->data;
+
+                       builtin = e->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int iptables_add_chain(struct connman_iptables *table,
+                                       char *name)
+{
+       GList *last;
+       struct ipt_entry *entry_head;
+       struct ipt_entry *entry_return;
+       struct error_target *error;
+       struct ipt_standard_target *standard;
+       u_int16_t entry_head_size, entry_return_size;
+
+       last = g_list_last(table->entries);
+
+       /*
+        * An empty chain is composed of:
+        * - A head entry, with no match and an error target.
+        *   The error target data is the chain name.
+        * - A tail entry, with no match and a standard target.
+        *   The standard target verdict is XT_RETURN (return to the
+        *   caller).
+        */
+
+       /* head entry */
+       entry_head_size = sizeof(struct ipt_entry) +
+                               sizeof(struct error_target);
+       entry_head = g_try_malloc0(entry_head_size);
+       if (entry_head == NULL)
+               goto err_head;
+
+       memset(entry_head, 0, entry_head_size);
+
+       entry_head->target_offset = sizeof(struct ipt_entry);
+       entry_head->next_offset = entry_head_size;
+
+       error = (struct error_target *) entry_head->elems;
+       strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
+       error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
+       strcpy(error->error, name);
+
+       if (iptables_add_entry(table, entry_head, last, -1) < 0)
+               goto err_head;
+
+       /* tail entry */
+       entry_return_size = sizeof(struct ipt_entry) +
+                               sizeof(struct ipt_standard_target);
+       entry_return = g_try_malloc0(entry_return_size);
+       if (entry_return == NULL)
+               goto err;
+
+       memset(entry_return, 0, entry_return_size);
+
+       entry_return->target_offset = sizeof(struct ipt_entry);
+       entry_return->next_offset = entry_return_size;
+
+       standard = (struct ipt_standard_target *) entry_return->elems;
+       standard->target.u.user.target_size =
+                               ALIGN(sizeof(struct ipt_standard_target));
+       standard->verdict = XT_RETURN;
+
+       if (iptables_add_entry(table, entry_return, last, -1) < 0)
+               goto err;
+
+       return 0;
+
+err:
+       g_free(entry_return);
+err_head:
+       g_free(entry_head);
+
+       return -ENOMEM;
+}
+
+static int iptables_delete_chain(struct connman_iptables *table, char *name)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_head, *chain_tail;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+
+       /* We cannot remove builtin chain */
+       if (entry->builtin >= 0)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       /* Chain must be flushed */
+       if (chain_head->next != chain_tail->prev)
+               return -EINVAL;
+
+       remove_table_entry(table, entry);
+
+       entry = chain_tail->prev->data;
+       remove_table_entry(table, entry);
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static struct ipt_entry *new_rule(struct ipt_ip *ip,
+               char *target_name, struct xtables_target *xt_t,
+               struct xtables_rule_match *xt_rm)
+{
+       struct xtables_rule_match *tmp_xt_rm;
+       struct ipt_entry *new_entry;
+       size_t match_size, target_size;
+
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
+               match_size += tmp_xt_rm->match->m->u.match_size;
+
+       if (xt_t)
+               target_size = ALIGN(xt_t->t->u.target_size);
+       else
+               target_size = ALIGN(sizeof(struct xt_standard_target));
+
+       new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
+                                                               match_size);
+       if (new_entry == NULL)
+               return NULL;
+
+       memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
+
+       new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
+       new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
+                                                               match_size;
+
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next) {
+               memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+                                       tmp_xt_rm->match->m->u.match_size);
+               match_size += tmp_xt_rm->match->m->u.match_size;
+       }
+
+       if (xt_t) {
+               struct xt_entry_target *entry_target;
+
+               entry_target = ipt_get_target(new_entry);
+               memcpy(entry_target, xt_t->t, target_size);
+       }
+
+       return new_entry;
+}
+
+static void update_hooks(struct connman_iptables *table, GList *chain_head,
+                               struct ipt_entry *entry)
+{
+       GList *list;
+       struct connman_iptables_entry *head, *e;
+       int builtin;
+
+       if (chain_head == NULL)
+               return;
+
+       head = chain_head->data;
+
+       builtin = head->builtin;
+       if (builtin < 0)
+               return;
+
+       table->underflow[builtin] += entry->next_offset;
+
+       for (list = chain_head->next; list; list = list->next) {
+               e = list->data;
+
+               builtin = e->builtin;
+               if (builtin < 0)
+                       continue;
+
+               table->hook_entry[builtin] += entry->next_offset;
+               table->underflow[builtin] += entry->next_offset;
+       }
+}
+
+static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               int *builtin, struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head;
+       struct ipt_entry *new_entry;
+       struct connman_iptables_entry *head;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return NULL;
+
+       new_entry = new_rule(ip, target_name, xt_t, xt_rm);
+       if (new_entry == NULL)
+               return NULL;
+
+       update_hooks(table, chain_head, new_entry);
+
+       /*
+        * If the chain is builtin, and does not have any rule,
+        * then the one that we're inserting is becoming the head
+        * and thus needs the builtin flag.
+        */
+       head = chain_head->data;
+       if (head->builtin < 0)
+               *builtin = -1;
+       else if (chain_head == chain_tail->prev) {
+               *builtin = head->builtin;
+               head->builtin = -1;
+       }
+
+       return new_entry;
+}
+
+static int iptables_append_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       DBG("");
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static int iptables_insert_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+       GList *chain_head;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       if (builtin == -1)
+               chain_head = chain_head->next;
+
+       ret = iptables_add_entry(table, new_entry, chain_head, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
+                                       struct ipt_entry *i_e2)
+{
+       if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
+               return FALSE;
+
+       if (i_e1->target_offset != i_e2->target_offset)
+               return FALSE;
+
+       if (i_e1->next_offset != i_e2->next_offset)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
+                                       struct xt_entry_target *xt_e_t2)
+{
+       if (xt_e_t1 == NULL || xt_e_t2 == NULL)
+               return FALSE;
+
+       if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+               struct xt_standard_target *xt_s_t1;
+               struct xt_standard_target *xt_s_t2;
+
+               xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
+               xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
+
+               if (xt_s_t1->verdict != xt_s_t2->verdict)
+                       return FALSE;
+       } else {
+               if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
+                       return FALSE;
+
+               if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
+                               struct xt_entry_match *xt_e_m2)
+{
+       if (xt_e_m1 == NULL || xt_e_m2 == NULL)
+               return FALSE;
+
+       if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
+               return FALSE;
+
+       if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
+               return FALSE;
+
+       if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static GList *find_existing_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head, *list;
+       struct xt_entry_target *xt_e_t = NULL;
+       struct xt_entry_match *xt_e_m = NULL;
+       struct connman_iptables_entry *entry;
+       struct ipt_entry *entry_test;
+       int builtin;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return NULL;
+
+       if (!xt_t && !xt_m)
+               return NULL;
+
+       entry_test = new_rule(ip, target_name, xt_t, xt_rm);
+       if (entry_test == NULL)
+               return NULL;
+
+       if (xt_t != NULL)
+               xt_e_t = ipt_get_target(entry_test);
+       if (xt_m != NULL)
+               xt_e_m = (struct xt_entry_match *)entry_test->elems;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       for (; list != chain_tail->prev; list = list->next) {
+               struct connman_iptables_entry *tmp;
+               struct ipt_entry *tmp_e;
+
+               tmp = list->data;
+               tmp_e = tmp->entry;
+
+               if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
+                       continue;
+
+               if (xt_t != NULL) {
+                       struct xt_entry_target *tmp_xt_e_t;
+
+                       tmp_xt_e_t = ipt_get_target(tmp_e);
+
+                       if (!is_same_target(tmp_xt_e_t, xt_e_t))
+                               continue;
+               }
+
+               if (xt_m != NULL) {
+                       struct xt_entry_match *tmp_xt_e_m;
+
+                       tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+
+                       if (!is_same_match(tmp_xt_e_m, xt_e_m))
+                               continue;
+               }
+
+               break;
+       }
+
+       g_free(entry_test);
+
+       if (list != chain_tail->prev)
+               return list;
+
+       return NULL;
+}
+
+static int iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_tail, *list;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       list = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (list == NULL)
+               return -EINVAL;
+
+       entry = list->data;
+       if (entry == NULL)
+               return -EINVAL;
+
+       builtin = entry->builtin;
+
+       /* We have deleted a rule,
+        * all references should be bumped accordingly */
+       if (list->next != NULL)
+               update_targets_reference(table, list->next->data,
+                                               list->data, TRUE);
+
+       removed += remove_table_entry(table, entry);
+
+       if (builtin >= 0) {
+               list = list->next;
+               if (list) {
+                       entry = list->data;
+                       entry->builtin = builtin;
+               }
+
+               table->underflow[builtin] -= removed;
+               for (list = chain_tail; list; list = list->next) {
+                       entry = list->data;
+
+                       builtin = entry->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int iptables_compare_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *found;
+
+       found = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (found == NULL)
+               return -EINVAL;
+
+       entry = found->data;
+       if (entry == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int iptables_change_policy(struct connman_iptables *table,
+                                       char *chain_name, char *policy)
+{
+       GList *chain_head;
+       struct connman_iptables_entry *entry;
+       struct xt_entry_target *target;
+       struct xt_standard_target *t;
+       int verdict;
+
+       verdict = target_to_verdict(policy);
+       if (verdict == 0)
+               return -EINVAL;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       if (entry->builtin < 0)
+               return -EINVAL;
+
+       target = ipt_get_target(entry->entry);
+
+       t = (struct xt_standard_target *)target;
+       t->verdict = verdict;
+
+       return 0;
+}
+
+static struct ipt_replace *iptables_blob(struct connman_iptables *table)
+{
+       struct ipt_replace *r;
+       GList *list;
+       struct connman_iptables_entry *e;
+       unsigned char *entry_index;
+
+       r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
+       if (r == NULL)
+               return NULL;
+
+       memset(r, 0, sizeof(*r) + table->size);
+
+       r->counters = g_try_malloc0(sizeof(struct xt_counters)
+                               * table->old_entries);
+       if (r->counters == NULL) {
+               g_free(r);
+               return NULL;
+       }
+
+       strcpy(r->name, table->info->name);
+       r->num_entries = table->num_entries;
+       r->size = table->size;
+
+       r->num_counters = table->old_entries;
+       r->valid_hooks  = table->info->valid_hooks;
+
+       memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
+       memcpy(r->underflow, table->underflow, sizeof(table->underflow));
+
+       entry_index = (unsigned char *)r->entries;
+       for (list = table->entries; list; list = list->next) {
+               e = list->data;
+
+               memcpy(entry_index, e->entry, e->entry->next_offset);
+               entry_index += e->entry->next_offset;
+       }
+
+       return r;
+}
+
+static void dump_ip(struct ipt_entry *entry)
+{
+       struct ipt_ip *ip = &entry->ip;
+       char ip_string[INET6_ADDRSTRLEN];
+       char ip_mask[INET6_ADDRSTRLEN];
+
+       if (strlen(ip->iniface))
+               connman_info("\tin %s", ip->iniface);
+
+       if (strlen(ip->outiface))
+               connman_info("\tout %s", ip->outiface);
+
+       if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) != NULL &&
+                       inet_ntop(AF_INET, &ip->smsk,
+                                       ip_mask, INET6_ADDRSTRLEN) != NULL)
+               connman_info("\tsrc %s/%s", ip_string, ip_mask);
+
+       if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) != NULL &&
+                       inet_ntop(AF_INET, &ip->dmsk,
+                                       ip_mask, INET6_ADDRSTRLEN) != NULL)
+               connman_info("\tdst %s/%s", ip_string, ip_mask);
+}
+
+static void dump_target(struct connman_iptables *table,
+                               struct ipt_entry *entry)
+
+{
+       struct xtables_target *xt_t;
+       struct xt_entry_target *target;
+
+       target = ipt_get_target(entry);
+
+       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+               struct xt_standard_target *t;
+
+               t = (struct xt_standard_target *)target;
+
+               switch (t->verdict) {
+               case XT_RETURN:
+                       connman_info("\ttarget RETURN");
+                       break;
+
+               case -NF_ACCEPT - 1:
+                       connman_info("\ttarget ACCEPT");
+                       break;
+
+               case -NF_DROP - 1:
+                       connman_info("\ttarget DROP");
+                       break;
+
+               case -NF_QUEUE - 1:
+                       connman_info("\ttarget QUEUE");
+                       break;
+
+               case -NF_STOP - 1:
+                       connman_info("\ttarget STOP");
+                       break;
+
+               default:
+                       connman_info("\tJUMP @%p (0x%x)",
+                               (char*)table->blob_entries->entrytable +
+                               t->verdict, t->verdict);
+                       break;
+               }
+
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+
+               if(xt_t->print != NULL)
+                       xt_t->print(NULL, target, 1);
+       } else {
+               xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
+               if (xt_t == NULL) {
+                       connman_info("\ttarget %s", target->u.user.name);
+                       return;
+               }
+
+               if(xt_t->print != NULL) {
+                       connman_info("\ttarget ");
+                       xt_t->print(NULL, target, 1);
+               }
+       }
+}
+
+static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
+{
+       struct xtables_match *xt_m;
+       struct xt_entry_match *match;
+
+       if (entry->elems == (unsigned char *)entry + entry->target_offset)
+               return;
+
+       match = (struct xt_entry_match *) entry->elems;
+
+       if (!strlen(match->u.user.name))
+               return;
+
+       xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
+       if (xt_m == NULL)
+               goto out;
+
+       if(xt_m->print != NULL) {
+               connman_info("\tmatch ");
+               xt_m->print(NULL, match, 1);
+
+               return;
+       }
+
+out:
+       connman_info("\tmatch %s", match->u.user.name);
+
+}
+
+static int dump_entry(struct ipt_entry *entry,
+                               struct connman_iptables *table)
+{
+       struct xt_entry_target *target;
+       unsigned int offset;
+       int builtin;
+
+       offset = (char *)entry - (char *)table->blob_entries->entrytable;
+       target = ipt_get_target(entry);
+       builtin = is_hook_entry(table, entry);
+
+       if (entry_to_offset(table, entry) + entry->next_offset ==
+                                       table->blob_entries->size) {
+               connman_info("End of CHAIN 0x%x", offset);
+               return 0;
+       }
+
+       if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
+               connman_info("USER CHAIN (%s) %p  match %p  target %p  size %d",
+                       target->data, entry, entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+
+               return 0;
+       } else if (builtin >= 0) {
+               connman_info("CHAIN (%s) %p  match %p  target %p  size %d",
+                       hooknames[builtin], entry, entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+       } else {
+               connman_info("RULE %p  match %p  target %p  size %d", entry,
+                       entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+       }
+
+       dump_match(table, entry);
+       dump_target(table, entry);
+       dump_ip(entry);
+
+       return 0;
+}
+
+static void iptables_dump(struct connman_iptables *table)
+{
+       connman_info("%s valid_hooks=0x%08x, num_entries=%u, size=%u",
+                       table->info->name,
+                       table->info->valid_hooks, table->info->num_entries,
+                               table->info->size);
+
+       ENTRY_ITERATE(table->blob_entries->entrytable,
+                       table->blob_entries->size,
+                       dump_entry, table);
+
+}
+
+static int iptables_get_entries(struct connman_iptables *table)
+{
+       socklen_t entry_size;
+
+       entry_size = sizeof(struct ipt_get_entries) + table->info->size;
+
+       return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
+                               table->blob_entries, &entry_size);
+}
+
+static int iptables_replace(struct connman_iptables *table,
+                                       struct ipt_replace *r)
+{
+       return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
+                        sizeof(*r) + r->size);
+}
+
+static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
+{
+       struct ipt_entry *new_entry;
+       int builtin;
+
+       new_entry = g_try_malloc0(entry->next_offset);
+       if (new_entry == NULL)
+               return -ENOMEM;
+
+       memcpy(new_entry, entry, entry->next_offset);
+
+       builtin = is_hook_entry(table, entry);
+
+       return iptables_add_entry(table, new_entry, NULL, builtin);
+}
+
+static void table_cleanup(struct connman_iptables *table)
+{
+       GList *list;
+       struct connman_iptables_entry *entry;
+
+       if (table == NULL)
+               return;
+
+       close(table->ipt_sock);
+
+       for (list = table->entries; list; list = list->next) {
+               entry = list->data;
+
+               g_free(entry->entry);
+               g_free(entry);
+       }
+
+       g_list_free(table->entries);
+       g_free(table->info);
+       g_free(table->blob_entries);
+       g_free(table);
+}
+
+static struct connman_iptables *iptables_init(char *table_name)
+{
+       struct connman_iptables *table = NULL;
+       char *module = NULL;
+       socklen_t s;
+
+       DBG("%s", table_name);
+
+       if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
+               return NULL;
+
+       module = g_strconcat("iptable_", table_name, NULL);
+       if (module == NULL)
+               return NULL;
+
+       if (xtables_insmod(module, NULL, TRUE) != 0) {
+               g_free(module);
+               return NULL;
+       }
+
+       g_free(module);
+
+       table = g_hash_table_lookup(table_hash, table_name);
+       if (table != NULL)
+               return table;
+
+       table = g_try_new0(struct connman_iptables, 1);
+       if (table == NULL)
+               return NULL;
+
+       table->info = g_try_new0(struct ipt_getinfo, 1);
+       if (table->info == NULL)
+               goto err;
+
+       table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+       if (table->ipt_sock < 0)
+               goto err;
+
+       s = sizeof(*table->info);
+       strcpy(table->info->name, table_name);
+       if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
+                                               table->info, &s) < 0)
+               goto err;
+
+       table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
+                                               table->info->size);
+       if (table->blob_entries == NULL)
+               goto err;
+
+       strcpy(table->blob_entries->name, table_name);
+       table->blob_entries->size = table->info->size;
+
+       if (iptables_get_entries(table) < 0)
+               goto err;
+
+       table->num_entries = 0;
+       table->old_entries = table->info->num_entries;
+       table->size = 0;
+
+       memcpy(table->underflow, table->info->underflow,
+                               sizeof(table->info->underflow));
+       memcpy(table->hook_entry, table->info->hook_entry,
+                               sizeof(table->info->hook_entry));
+
+       ENTRY_ITERATE(table->blob_entries->entrytable,
+                       table->blob_entries->size,
+                               add_entry, table);
+
+       g_hash_table_insert(table_hash, g_strdup(table_name), table);
+
+       return table;
+
+err:
+       table_cleanup(table);
+
+       return NULL;
+}
+
+static struct option iptables_opts[] = {
+       {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "compare",       .has_arg = 1, .val = 'C'},
+       {.name = "delete",        .has_arg = 1, .val = 'D'},
+       {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
+       {.name = "insert",        .has_arg = 1, .val = 'I'},
+       {.name = "list",          .has_arg = 2, .val = 'L'},
+       {.name = "new-chain",     .has_arg = 1, .val = 'N'},
+       {.name = "policy",        .has_arg = 1, .val = 'P'},
+       {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
+       {.name = "destination",   .has_arg = 1, .val = 'd'},
+       {.name = "in-interface",  .has_arg = 1, .val = 'i'},
+       {.name = "jump",          .has_arg = 1, .val = 'j'},
+       {.name = "match",         .has_arg = 1, .val = 'm'},
+       {.name = "out-interface", .has_arg = 1, .val = 'o'},
+       {.name = "source",        .has_arg = 1, .val = 's'},
+       {.name = "table",         .has_arg = 1, .val = 't'},
+       {NULL},
+};
+
+struct xtables_globals iptables_globals = {
+       .option_offset = 0,
+       .opts = iptables_opts,
+       .orig_opts = iptables_opts,
+};
+
+static struct xtables_target *prepare_target(struct connman_iptables *table,
+                                                       char *target_name)
+{
+       struct xtables_target *xt_t = NULL;
+       gboolean is_builtin, is_user_defined;
+       GList *chain_head = NULL;
+       size_t target_size;
+
+       is_builtin = FALSE;
+       is_user_defined = FALSE;
+
+       if (is_builtin_target(target_name))
+               is_builtin = TRUE;
+       else {
+               chain_head = find_chain_head(table, target_name);
+               if (chain_head != NULL && chain_head->next != NULL)
+                       is_user_defined = TRUE;
+       }
+
+       if (is_builtin || is_user_defined)
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+       else
+               xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+       if (xt_t == NULL)
+               return NULL;
+
+       target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+       xt_t->t = g_try_malloc0(target_size);
+       if (xt_t->t == NULL)
+               return NULL;
+
+       xt_t->t->u.target_size = target_size;
+
+       if (is_builtin || is_user_defined) {
+               struct xt_standard_target *target;
+
+               target = (struct xt_standard_target *)(xt_t->t);
+               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+
+               if (is_builtin == TRUE)
+                       target->verdict = target_to_verdict(target_name);
+               else if (is_user_defined == TRUE) {
+                       struct connman_iptables_entry *target_rule;
+
+                       if (chain_head == NULL) {
+                               g_free(xt_t->t);
+                               return NULL;
+                       }
+
+                       target_rule = chain_head->next->data;
+                       target->verdict = target_rule->offset;
+               }
+       } else {
+               strcpy(xt_t->t->u.user.name, target_name);
+               xt_t->t->u.user.revision = xt_t->revision;
+               if (xt_t->init != NULL)
+                       xt_t->init(xt_t->t);
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_t->x6_options != NULL)
+               iptables_globals.opts =
+                       xtables_options_xfrm(
+                               iptables_globals.orig_opts,
+                               iptables_globals.opts,
+                               xt_t->x6_options,
+                               &xt_t->option_offset);
+       else
+#endif
+               iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               iptables_globals.orig_opts,
+#endif
+                               iptables_globals.opts,
+                               xt_t->extra_opts,
+                               &xt_t->option_offset);
+
+       if (iptables_globals.opts == NULL) {
+               g_free(xt_t->t);
+               xt_t = NULL;
+       }
+
+       return xt_t;
+}
+
+static struct xtables_match *prepare_matches(struct connman_iptables *table,
+                       struct xtables_rule_match **xt_rm, char *match_name)
+{
+       struct xtables_match *xt_m;
+       size_t match_size;
+
+       if (match_name == NULL)
+               return NULL;
+
+       xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
+       match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+       xt_m->m = g_try_malloc0(match_size);
+       if (xt_m->m == NULL)
+               return NULL;
+
+       xt_m->m->u.match_size = match_size;
+       strcpy(xt_m->m->u.user.name, xt_m->name);
+       xt_m->m->u.user.revision = xt_m->revision;
+
+       if (xt_m->init != NULL)
+               xt_m->init(xt_m->m);
+
+       if (xt_m == xt_m->next)
+               goto done;
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_m->x6_options != NULL)
+               iptables_globals.opts =
+                       xtables_options_xfrm(
+                               iptables_globals.orig_opts,
+                               iptables_globals.opts,
+                               xt_m->x6_options,
+                               &xt_m->option_offset);
+       else
+#endif
+                       iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               iptables_globals.orig_opts,
+#endif
+                               iptables_globals.opts,
+                               xt_m->extra_opts,
+                               &xt_m->option_offset);
+
+       if (iptables_globals.opts == NULL) {
+               g_free(xt_m->m);
+               xt_m = NULL;
+       }
+
+done:
+       return xt_m;
+}
+
+static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
+{
+       char **tokens;
+       uint32_t prefixlength;
+       uint32_t tmp;
+       int err;
+
+       tokens = g_strsplit(str, "/", 2);
+       if (tokens == NULL)
+               return -1;
+
+       if (!inet_pton(AF_INET, tokens[0], ip)) {
+               err = -1;
+               goto out;
+       }
+
+       if (tokens[1] != NULL) {
+               prefixlength = strtol(tokens[1], NULL, 10);
+               if (prefixlength > 31) {
+                       err = -1;
+                       goto out;
+               }
+
+               tmp = ~(0xffffffff >> prefixlength);
+       } else {
+               tmp = 0xffffffff;
+       }
+
+       mask->s_addr = htonl(tmp);
+       ip->s_addr = ip->s_addr & mask->s_addr;
+       err = 0;
+out:
+       g_strfreev(tokens);
+
+       return err;
+}
+
+static int iptables_command(int argc, char *argv[])
+{
+       struct connman_iptables *table;
+       struct xtables_rule_match *xt_rm, *tmp_xt_rm;
+       struct xtables_match *xt_m, *xt_m_t;
+       struct xtables_target *xt_t;
+       struct ipt_ip ip;
+       char *table_name, *chain, *new_chain, *match_name, *target_name;
+       char *flush_chain, *delete_chain, *policy;
+       int c, ret, in_len, out_len;
+       gboolean dump, invert, insert, delete, compare;
+
+       if (argc == 0)
+               return -EINVAL;
+
+       dump = FALSE;
+       invert = FALSE;
+       insert = FALSE;
+       delete = FALSE;
+       compare = FALSE;
+       chain = new_chain = match_name = target_name = NULL;
+       flush_chain = delete_chain = policy = NULL;
+       memset(&ip, 0, sizeof(struct ipt_ip));
+       table = NULL;
+       xt_rm = NULL;
+       xt_m = NULL;
+       xt_t = NULL;
+       ret = 0;
+
+       /* extension's options will generate false-positives errors */
+       opterr = 0;
+
+       optind = 0;
+
+       while ((c = getopt_long(argc, argv,
+                                       "-A:C:D:F:I:L::N:P:X:d:j:i:m:o:s:t:",
+                                       iptables_globals.opts, NULL)) != -1) {
+               switch (c) {
+               case 'A':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       break;
+
+               case 'C':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       compare = TRUE;
+                       break;
+
+               case 'D':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       delete = TRUE;
+                       break;
+
+               case 'F':
+                       flush_chain = optarg;
+                       break;
+
+               case 'I':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       insert = TRUE;
+                       break;
+
+               case 'L':
+                       dump = TRUE;
+                       break;
+
+               case 'N':
+                       new_chain = optarg;
+                       break;
+
+               case 'P':
+                       chain = optarg;
+                       if (optind < argc)
+                               policy = argv[optind++];
+                       else
+                               goto out;
+
+                       break;
+
+               case 'X':
+                       delete_chain = optarg;
+                       break;
+
+               case 'd':
+                       if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
+                               break;
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_DSTIP;
+
+                       break;
+
+               case 'i':
+                       in_len = strlen(optarg);
+
+                       if (in_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.iniface, optarg);
+                       memset(ip.iniface_mask, 0xff, in_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_IN;
+
+                       break;
+
+               case 'j':
+                       target_name = optarg;
+                       xt_t = prepare_target(table, target_name);
+                       if (xt_t == NULL)
+                               goto out;
+
+                       break;
+
+               case 'm':
+                       match_name = optarg;
+                       xt_m = prepare_matches(table, &xt_rm, match_name);
+                       if (xt_m == NULL)
+                               goto out;
+
+                       break;
+
+               case 'o':
+                       out_len = strlen(optarg);
+
+                       if (out_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.outiface, optarg);
+                       memset(ip.outiface_mask, 0xff, out_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_OUT;
+
+                       break;
+
+               case 's':
+                       if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
+                               break;
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_SRCIP;
+
+                       break;
+
+               case 't':
+                       table_name = optarg;
+
+                       table = iptables_init(table_name);
+                       if (table == NULL) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       break;
+
+               case 1:
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+
+                       connman_error("Invalid option");
+
+                       ret = -EINVAL;
+                       goto out;
+
+               default:
+#if XTABLES_VERSION_CODE > 5
+                       if (xt_t != NULL && (xt_t->x6_parse != NULL ||
+                                               xt_t->parse != NULL) &&
+                                       (c >= (int) xt_t->option_offset &&
+                                       c < (int) xt_t->option_offset +
+                                       XT_OPTION_OFFSET_SCALE)) {
+                               xtables_option_tpcall(c, argv,
+                                                       invert, xt_t, NULL);
+
+                               break;
+                       }
+
+                       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                               xt_m_t = tmp_xt_rm->match;
+
+                               if (tmp_xt_rm->completed ||
+                                               (xt_m_t->x6_parse == NULL &&
+                                                xt_m_t->parse == NULL))
+                                       continue;
+
+                               if (c < (int) xt_m_t->option_offset ||
+                                       c >= (int) xt_m_t->option_offset
+                                       + XT_OPTION_OFFSET_SCALE)
+                                       continue;
+
+                               xtables_option_mpcall(c, argv,
+                                                       invert, xt_m_t, NULL);
+
+                               break;
+                       }
+#else
+                       if (xt_t == NULL || xt_t->parse == NULL ||
+                               !xt_t->parse(c - xt_t->option_offset,
+                               argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
+
+                               for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                                       xt_m_t = tmp_xt_rm->match;
+
+                                       if (tmp_xt_rm->completed ||
+                                                       xt_m_t->parse == NULL)
+                                               continue;
+
+                                       if (xt_m->parse(c - xt_m->option_offset,
+                                               argv, invert, &xt_m->mflags,
+                                               NULL, &xt_m->m))
+                                               break;
+                               }
+                       }
+#endif
+                       break;
+               }
+
+               invert = FALSE;
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               xtables_option_mfcall(tmp_xt_rm->match);
+
+       if (xt_t != NULL)
+               xtables_option_tfcall(xt_t);
+#else
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               if (tmp_xt_rm->match->final_check != NULL)
+                       tmp_xt_rm->match->final_check(
+                                       tmp_xt_rm->match->mflags);
+
+       if (xt_t != NULL && xt_t->final_check != NULL)
+               xt_t->final_check(xt_t->tflags);
+#endif
+
+       if (table == NULL) {
+               table_name = "filter";
+
+               table = iptables_init(table_name);
+               if (table == NULL) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (delete_chain != NULL) {
+               printf("Delete chain %s\n", delete_chain);
+
+               iptables_delete_chain(table, delete_chain);
+
+               goto out;
+       }
+
+       if (dump) {
+               iptables_dump(table);
+
+               ret = 0;
+               goto out;
+       }
+
+       if (flush_chain) {
+               DBG("Flush chain %s", flush_chain);
+
+               iptables_flush_chain(table, flush_chain);
+
+               goto out;
+       }
+
+       if (chain && new_chain) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (new_chain) {
+               DBG("New chain %s", new_chain);
+
+               ret = iptables_add_chain(table, new_chain);
+               goto out;
+       }
+
+       if (chain) {
+               if (policy != NULL) {
+                       printf("Changing policy of %s to %s\n", chain, policy);
+
+                       iptables_change_policy(table, chain, policy);
+
+                       goto out;
+               }
+
+               if (xt_t == NULL)
+                       goto out;
+
+               if (compare == TRUE) {
+                       ret = iptables_compare_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+                       goto out;
+               }
+
+               if (delete == TRUE) {
+                       DBG("Deleting %s to %s (match %s)\n",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_delete_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+
+                       goto out;
+               }
+
+               if (insert == TRUE) {
+                       DBG("Inserting %s to %s (match %s)",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_insert_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+
+                       goto out;
+               } else {
+                       DBG("Adding %s to %s (match %s)",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_append_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+
+                       goto out;
+               }
+       }
+
+out:
+       if (xt_t)
+               g_free(xt_t->t);
+
+       if (xt_m)
+               g_free(xt_m->m);
+
+       return ret;
+}
+
+int __connman_iptables_command(const char *format, ...)
+{
+       char **argv, **arguments, *command;
+       int argc, i, ret;
+       va_list args;
+
+       if (format == NULL)
+               return -EINVAL;
+
+       va_start(args, format);
+
+       command = g_strdup_vprintf(format, args);
+
+       va_end(args);
+
+       if (command == NULL)
+               return -ENOMEM;
+
+       arguments = g_strsplit_set(command, " ", -1);
+
+       for (argc = 0; arguments[argc]; argc++);
+       ++argc;
+
+       DBG("command %s argc %d", command, argc);
+
+       argv = g_try_malloc0(argc * sizeof(char *));
+       if (argv == NULL) {
+               g_free(command);
+               g_strfreev(arguments);
+               return -ENOMEM;
+       }
+
+       argv[0] = "iptables";
+       for (i = 1; i < argc; i++)
+               argv[i] = arguments[i - 1];
+
+       ret = iptables_command(argc, argv);
+
+       g_free(command);
+       g_strfreev(arguments);
+       g_free(argv);
+
+       return ret;
+}
+
+
+int __connman_iptables_commit(const char *table_name)
+{
+       struct connman_iptables *table;
+       struct ipt_replace *repl;
+       int err;
+
+       DBG("%s", table_name);
+
+       table = g_hash_table_lookup(table_hash, table_name);
+       if (table == NULL)
+               return -EINVAL;
+
+       repl = iptables_blob(table);
+
+       err = iptables_replace(table, repl);
+
+       g_free(repl->counters);
+       g_free(repl);
+
+       if (err < 0)
+           return err;
+
+       g_hash_table_remove(table_hash, table_name);
+
+       return 0;
+}
+
+static void remove_table(gpointer user_data)
+{
+       struct connman_iptables *table = user_data;
+
+       table_cleanup(table);
+}
+
+int __connman_iptables_init(void)
+{
+       DBG("");
+
+       table_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_table);
+
+       xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+
+       return 0;
+
+}
+
+void __connman_iptables_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(table_hash);
+
+       xtables_free_opts(1);
+}
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..be11fa5
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,335 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <execinfo.h>
+#include <dlfcn.h>
+
+#include "connman.h"
+
+static const char *program_exec;
+static const char *program_path;
+
+/**
+ * connman_info:
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Output general information
+ */
+void connman_info(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_INFO, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * connman_warn:
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Output warning messages
+ */
+void connman_warn(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_WARNING, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * connman_error:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output error messages
+ */
+void connman_error(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_ERR, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * connman_debug:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output debug message
+ */
+void connman_debug(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+
+#if !defined TIZEN_EXT
+static void print_backtrace(unsigned int offset)
+{
+       void *frames[99];
+       size_t n_ptrs;
+       unsigned int i;
+       int outfd[2], infd[2];
+       int pathlen;
+       pid_t pid;
+
+       if (program_exec == NULL)
+               return;
+
+       pathlen = strlen(program_path);
+
+       n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
+       if (n_ptrs < offset)
+               return;
+
+       if (pipe(outfd) < 0)
+               return;
+
+       if (pipe(infd) < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               return;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               close(infd[0]);
+               close(infd[1]);
+               return;
+       }
+
+       if (pid == 0) {
+               close(outfd[1]);
+               close(infd[0]);
+
+               dup2(outfd[0], STDIN_FILENO);
+               dup2(infd[1], STDOUT_FILENO);
+
+               execlp("addr2line", "-C", "-f", "-e", program_exec, NULL);
+
+               exit(EXIT_FAILURE);
+       }
+
+       close(outfd[0]);
+       close(infd[1]);
+
+       connman_error("++++++++ backtrace ++++++++");
+
+       for (i = offset; i < n_ptrs - 1; i++) {
+               Dl_info info;
+               char addr[20], buf[PATH_MAX * 2];
+               int len, written;
+               char *ptr, *pos;
+
+               dladdr(frames[i], &info);
+
+               len = snprintf(addr, sizeof(addr), "%p\n", frames[i]);
+               if (len < 0)
+                       break;
+
+               written = write(outfd[1], addr, len);
+               if (written < 0)
+                       break;
+
+               len = read(infd[0], buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               buf[len] = '\0';
+
+               pos = strchr(buf, '\n');
+               *pos++ = '\0';
+
+               if (strcmp(buf, "??") == 0) {
+                       connman_error("#%-2u %p in %s", i - offset,
+                                               frames[i], info.dli_fname);
+                       continue;
+               }
+
+               ptr = strchr(pos, '\n');
+               *ptr++ = '\0';
+
+               if (strncmp(pos, program_path, pathlen) == 0)
+                       pos += pathlen + 1;
+
+               connman_error("#%-2u %p in %s() at %s", i - offset,
+                                               frames[i], buf, pos);
+       }
+
+       connman_error("+++++++++++++++++++++++++++");
+
+       kill(pid, SIGTERM);
+
+       close(outfd[1]);
+       close(infd[0]);
+}
+
+static void signal_handler(int signo)
+{
+       connman_error("Aborting (signal %d) [%s]", signo, program_exec);
+
+       print_backtrace(2);
+
+       exit(EXIT_FAILURE);
+}
+
+static void signal_setup(sighandler_t handler)
+{
+       struct sigaction sa;
+       sigset_t mask;
+
+       sigemptyset(&mask);
+       sa.sa_handler = handler;
+       sa.sa_mask = mask;
+       sa.sa_flags = 0;
+       sigaction(SIGBUS, &sa, NULL);
+       sigaction(SIGILL, &sa, NULL);
+       sigaction(SIGFPE, &sa, NULL);
+       sigaction(SIGSEGV, &sa, NULL);
+       sigaction(SIGABRT, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+}
+#endif
+
+extern struct connman_debug_desc __start___debug[];
+extern struct connman_debug_desc __stop___debug[];
+
+static gchar **enabled = NULL;
+
+static connman_bool_t is_enabled(struct connman_debug_desc *desc)
+{
+       int i;
+
+       if (enabled == NULL)
+               return FALSE;
+
+       for (i = 0; enabled[i] != NULL; i++) {
+               if (desc->name != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->name) == TRUE)
+                       return TRUE;
+               if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->file) == TRUE)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+void __connman_log_enable(struct connman_debug_desc *start,
+                                       struct connman_debug_desc *stop)
+{
+       struct connman_debug_desc *desc;
+       const char *name = NULL, *file = NULL;
+
+       if (start == NULL || stop == NULL)
+               return;
+
+       for (desc = start; desc < stop; desc++) {
+               if (desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) {
+                       file = desc->file;
+                       name = desc->name;
+                       continue;
+               }
+
+               if (file != NULL || name != NULL) {
+                       if (g_strcmp0(desc->file, file) == 0) {
+                               if (desc->name == NULL)
+                                       desc->name = name;
+                       } else
+                               file = NULL;
+               }
+
+               if (is_enabled(desc) == TRUE)
+                       desc->flags |= CONNMAN_DEBUG_FLAG_PRINT;
+       }
+}
+
+int __connman_log_init(const char *program, const char *debug,
+                                               connman_bool_t detach)
+{
+       static char path[PATH_MAX];
+       int option = LOG_NDELAY | LOG_PID;
+
+       program_exec = program;
+       program_path = getcwd(path, sizeof(path));
+
+       if (debug != NULL)
+               enabled = g_strsplit_set(debug, ":, ", 0);
+
+       __connman_log_enable(__start___debug, __stop___debug);
+
+       if (detach == FALSE)
+               option |= LOG_PERROR;
+
+#if !defined TIZEN_EXT
+       signal_setup(signal_handler);
+#endif
+
+       openlog(basename(program), option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "Connection Manager version %s", VERSION);
+
+       return 0;
+}
+
+void __connman_log_cleanup(void)
+{
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+#if !defined TIZEN_EXT
+       signal_setup(SIG_DFL);
+#endif
+
+       g_strfreev(enabled);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..abbef58
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#if !defined TIZEN_EXT
+#include <signal.h>
+#include <sys/signalfd.h>
+#endif
+#include <getopt.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netdb.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+#define DEFAULT_INPUT_REQUEST_TIMEOUT 120 * 1000
+#define DEFAULT_BROWSER_LAUNCH_TIMEOUT 300 * 1000
+
+static struct {
+       connman_bool_t bg_scan;
+       char **pref_timeservers;
+       unsigned int *auto_connect;
+       unsigned int *preferred_techs;
+       char **fallback_nameservers;
+       unsigned int timeout_inputreq;
+       unsigned int timeout_browserlaunch;
+       char **blacklisted_interfaces;
+       connman_bool_t single_tech;
+} connman_settings  = {
+       .bg_scan = TRUE,
+       .pref_timeservers = NULL,
+       .auto_connect = NULL,
+       .preferred_techs = NULL,
+       .fallback_nameservers = NULL,
+       .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
+       .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT,
+       .blacklisted_interfaces = NULL,
+       .single_tech = FALSE,
+};
+
+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 uint *parse_service_types(char **str_list, gsize len)
+{
+       unsigned int *type_list;
+       int i, j;
+       enum connman_service_type type;
+
+       type_list = g_try_new0(unsigned int, len + 1);
+       if (type_list == NULL)
+               return NULL;
+
+       i = 0;
+       j = 0;
+       while (str_list[i] != NULL)
+       {
+               type = __connman_service_string2type(str_list[i]);
+
+               if (type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       type_list[j] = type;
+                       j += 1;
+               }
+               i += 1;
+       }
+
+       return type_list;
+}
+
+static char **parse_fallback_nameservers(char **nameservers, gsize len)
+{
+       char **servers;
+       int i, j;
+
+       servers = g_try_new0(char *, len + 1);
+       if (servers == NULL)
+               return NULL;
+
+       i = 0;
+       j = 0;
+       while (nameservers[i] != NULL) {
+               if (connman_inet_check_ipaddress(nameservers[i]) > 0) {
+                       servers[j] = g_strdup(nameservers[i]);
+                       j += 1;
+               }
+               i += 1;
+       }
+
+       return servers;
+}
+
+static void parse_config(GKeyFile *config)
+{
+       GError *error = NULL;
+       gboolean boolean;
+       char **timeservers;
+       char **interfaces;
+       char **str_list;
+       gsize len;
+       static char *default_auto_connect[] = {
+               "wifi",
+               "ethernet",
+               "cellular",
+               NULL
+       };
+       static char *default_blacklist[] = {
+               "vmnet",
+               "vboxnet",
+               "virbr",
+               NULL
+       };
+       int timeout;
+
+       if (config == NULL) {
+               connman_settings.auto_connect =
+                       parse_service_types(default_auto_connect, 3);
+               connman_settings.blacklisted_interfaces = default_blacklist;
+               return;
+       }
+
+       DBG("parsing main.conf");
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "BackgroundScanning", &error);
+       if (error == NULL)
+               connman_settings.bg_scan = boolean;
+
+       g_clear_error(&error);
+
+       timeservers = g_key_file_get_string_list(config, "General",
+                                               "FallbackTimeservers", NULL, &error);
+       if (error == NULL)
+               connman_settings.pref_timeservers = timeservers;
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "DefaultAutoConnectTechnologies", &len, &error);
+
+       if (error == NULL)
+               connman_settings.auto_connect =
+                       parse_service_types(str_list, len);
+       else
+               connman_settings.auto_connect =
+                       parse_service_types(default_auto_connect, 3);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "PreferredTechnologies", &len, &error);
+
+       if (error == NULL)
+               connman_settings.preferred_techs =
+                       parse_service_types(str_list, len);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       str_list = g_key_file_get_string_list(config, "General",
+                       "FallbackNameservers", &len, &error);
+
+       if (error == NULL)
+               connman_settings.fallback_nameservers =
+                       parse_fallback_nameservers(str_list, len);
+
+       g_strfreev(str_list);
+
+       g_clear_error(&error);
+
+       timeout = g_key_file_get_integer(config, "General",
+                       "InputRequestTimeout", &error);
+       if (error == NULL && timeout >= 0)
+               connman_settings.timeout_inputreq = timeout * 1000;
+
+       g_clear_error(&error);
+
+       timeout = g_key_file_get_integer(config, "General",
+                       "BrowserLaunchTimeout", &error);
+       if (error == NULL && timeout >= 0)
+               connman_settings.timeout_browserlaunch = timeout * 1000;
+
+       g_clear_error(&error);
+
+       interfaces = g_key_file_get_string_list(config, "General",
+                       "NetworkInterfaceBlacklist", &len, &error);
+
+       if (error == NULL)
+               connman_settings.blacklisted_interfaces = interfaces;
+       else
+               connman_settings.blacklisted_interfaces = default_blacklist;
+
+       g_clear_error(&error);
+
+       boolean = g_key_file_get_boolean(config, "General",
+                       "SingleConnectedTechnology", &error);
+       if (error == NULL)
+               connman_settings.single_tech = boolean;
+
+       g_clear_error(&error);
+}
+
+static GMainLoop *main_loop = NULL;
+
+#if !defined TIZEN_EXT
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct signalfd_siginfo si;
+       ssize_t result;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+       case SIGTERM:
+               if (__terminated == 0) {
+                       connman_info("Terminating");
+                       g_main_loop_quit(main_loop);
+               }
+
+               __terminated = 1;
+               break;
+       }
+
+       return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+       GIOChannel *channel;
+       guint source;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Failed to set signal mask");
+               return 0;
+       }
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0) {
+               perror("Failed to create signal descriptor");
+               return 0;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               signal_handler, NULL);
+
+       g_io_channel_unref(channel);
+
+       return source;
+}
+#endif
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       connman_error("D-Bus disconnect");
+
+       g_main_loop_quit(main_loop);
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_device = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_nodevice = NULL;
+static gchar *option_noplugin = NULL;
+static gchar *option_wifi = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_dnsproxy = TRUE;
+static gboolean option_compat = FALSE;
+static gboolean option_version = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+                                       gpointer user_data, GError **error)
+{
+       if (value)
+               option_debug = g_strdup(value);
+       else
+               option_debug = g_strdup("*");
+
+       return TRUE;
+}
+
+static GOptionEntry options[] = {
+       { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+                               G_OPTION_ARG_CALLBACK, parse_debug,
+                               "Specify debug options to enable", "DEBUG" },
+       { "device", 'i', 0, G_OPTION_ARG_STRING, &option_device,
+                       "Specify networking device or interface", "DEV" },
+       { "nodevice", 'I', 0, G_OPTION_ARG_STRING, &option_nodevice,
+                       "Specify networking interface to ignore", "DEV" },
+       { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+                               "Specify plugins to load", "NAME,..." },
+       { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+                               "Specify plugins not to load", "NAME,..." },
+       { "wifi", 'W', 0, G_OPTION_ARG_STRING, &option_wifi,
+                               "Specify driver for WiFi/Supplicant", "NAME" },
+       { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_detach,
+                               "Don't fork daemon to background" },
+       { "nodnsproxy", 'r', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_dnsproxy,
+                               "Don't enable DNS Proxy" },
+       { "compat", 'c', 0, G_OPTION_ARG_NONE, &option_compat,
+                               "(obsolete)" },
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+const char *connman_option_get_string(const char *key)
+{
+       if (g_strcmp0(key, "wifi") == 0) {
+               if (option_wifi == NULL)
+                       return "nl80211,wext";
+               else
+                       return option_wifi;
+       }
+
+       return NULL;
+}
+
+connman_bool_t connman_setting_get_bool(const char *key)
+{
+       if (g_str_equal(key, "BackgroundScanning") == TRUE)
+               return connman_settings.bg_scan;
+
+       if (g_str_equal(key, "SingleConnectedTechnology") == TRUE)
+               return connman_settings.single_tech;
+
+       return FALSE;
+}
+
+char **connman_setting_get_string_list(const char *key)
+{
+       if (g_str_equal(key, "FallbackTimeservers") == TRUE)
+               return connman_settings.pref_timeservers;
+
+       if (g_str_equal(key, "FallbackNameservers") == TRUE)
+               return connman_settings.fallback_nameservers;
+
+       if (g_str_equal(key, "NetworkInterfaceBlacklist") == TRUE)
+               return connman_settings.blacklisted_interfaces;
+
+       return NULL;
+}
+
+unsigned int *connman_setting_get_uint_list(const char *key)
+{
+       if (g_str_equal(key, "DefaultAutoConnectTechnologies") == TRUE)
+               return connman_settings.auto_connect;
+
+       if (g_str_equal(key, "PreferredTechnologies") == TRUE)
+               return connman_settings.preferred_techs;
+
+       return NULL;
+}
+
+unsigned int connman_timeout_input_request(void) {
+       return connman_settings.timeout_inputreq;
+}
+
+unsigned int connman_timeout_browser_launch(void) {
+       return connman_settings.timeout_browserlaunch;
+}
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       DBusConnection *conn;
+       DBusError err;
+       GKeyFile *config;
+#if !defined TIZEN_EXT
+       guint signal;
+#endif
+
+#ifdef NEED_THREADS
+       if (g_thread_supported() == FALSE)
+               g_thread_init(NULL);
+#endif
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (option_detach == TRUE) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       if (mkdir(STATEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+                               S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+               if (errno != EEXIST)
+                       perror("Failed to create state directory");
+       }
+
+       if (mkdir(STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+                               S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+               if (errno != EEXIST)
+                       perror("Failed to create storage directory");
+       }
+
+       umask(0077);
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+#ifdef NEED_THREADS
+       if (dbus_threads_init_default() == FALSE) {
+               fprintf(stderr, "Can't init usage of threads\n");
+               exit(1);
+       }
+#endif
+
+#if !defined TIZEN_EXT
+       signal = setup_signalfd();
+#endif
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, CONNMAN_SERVICE, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       __connman_log_init(argv[0], option_debug, option_detach);
+
+       __connman_dbus_init(conn);
+
+       config = load_config(CONFIGDIR "/main.conf");
+       parse_config(config);
+       if (config != NULL)
+               g_key_file_free(config);
+
+       __connman_storage_migrate();
+       __connman_technology_init();
+       __connman_notifier_init();
+       __connman_service_init();
+       __connman_provider_init();
+       __connman_network_init();
+       __connman_device_init(option_device, option_nodevice);
+
+       __connman_agent_init();
+       __connman_ippool_init();
+       __connman_iptables_init();
+       __connman_nat_init();
+       __connman_tethering_init();
+       __connman_counter_init();
+       __connman_manager_init();
+       __connman_config_init();
+       __connman_stats_init();
+       __connman_clock_init();
+
+       __connman_resolver_init(option_dnsproxy);
+       __connman_ipconfig_init();
+       __connman_rtnl_init();
+       __connman_task_init();
+       __connman_proxy_init();
+       __connman_detect_init();
+       __connman_session_init();
+       __connman_timeserver_init();
+       __connman_connection_init();
+
+       __connman_plugin_init(option_plugin, option_noplugin);
+
+       __connman_rtnl_start();
+       __connman_dhcp_init();
+       __connman_dhcpv6_init();
+       __connman_wpad_init();
+       __connman_wispr_init();
+       __connman_rfkill_init();
+
+       g_free(option_device);
+       g_free(option_plugin);
+       g_free(option_nodevice);
+       g_free(option_noplugin);
+
+       g_main_loop_run(main_loop);
+
+#if !defined TIZEN_EXT
+       g_source_remove(signal);
+#endif
+
+       __connman_rfkill_cleanup();
+       __connman_wispr_cleanup();
+       __connman_wpad_cleanup();
+       __connman_dhcpv6_cleanup();
+       __connman_dhcp_cleanup();
+       __connman_provider_cleanup();
+       __connman_plugin_cleanup();
+       __connman_connection_cleanup();
+       __connman_timeserver_cleanup();
+       __connman_session_cleanup();
+       __connman_detect_cleanup();
+       __connman_proxy_cleanup();
+       __connman_task_cleanup();
+       __connman_rtnl_cleanup();
+       __connman_resolver_cleanup();
+
+       __connman_clock_cleanup();
+       __connman_stats_cleanup();
+       __connman_config_cleanup();
+       __connman_manager_cleanup();
+       __connman_counter_cleanup();
+       __connman_agent_cleanup();
+       __connman_tethering_cleanup();
+       __connman_nat_cleanup();
+       __connman_iptables_cleanup();
+       __connman_ippool_cleanup();
+       __connman_device_cleanup();
+       __connman_network_cleanup();
+       __connman_service_cleanup();
+       __connman_ipconfig_cleanup();
+       __connman_notifier_cleanup();
+       __connman_technology_cleanup();
+
+       __connman_dbus_cleanup();
+
+       __connman_log_cleanup();
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       if (connman_settings.pref_timeservers != NULL)
+               g_strfreev(connman_settings.pref_timeservers);
+
+       g_free(connman_settings.auto_connect);
+       g_free(connman_settings.preferred_techs);
+       g_strfreev(connman_settings.fallback_nameservers);
+
+       g_free(option_debug);
+
+       return 0;
+}
diff --git a/src/main.conf b/src/main.conf
new file mode 100644 (file)
index 0000000..a31634a
--- /dev/null
@@ -0,0 +1,73 @@
+[General]
+
+# Set input request timeout. Default is 120 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.
+# InputRequestTimeout = 120
+
+# Set browser launch timeout. Default is 300 seconds
+# The request for launching a browser for portal pages
+# will timeout after certain amount of time. Use this
+# setting to increase the value in case of different
+# user interface designs.
+# BrowserLaunchTimeout = 300
+
+# Enable background scanning. Default is true.
+# Background scanning will start every 5 minutes unless
+# the scan list is empty. In that case, a simple backoff
+# mechanism starting from 10s up to 5 minutes will run.
+BackgroundScanning = false
+
+# List of Fallback timeservers separated by ",".
+# These timeservers are used for NTP sync when there are
+# no timeserver set by the user or by the service.
+# These can contain mixed combination of fully qualified
+# domain names, IPv4 and IPv6 addresses.
+# FallbackTimeservers =
+
+# List of fallback nameservers separated by "," appended
+# to the list of nameservers given by the service. The
+# nameserver entries must be in numeric format, host
+# names are ignored.
+# FallbackNameservers =
+
+# List of technologies that are marked autoconnectable
+# by default, separated by commas ",". The default value
+# for this entry when empty is ethernet,wifi,cellular.
+# Services that are automatically connected must have been
+# set up and saved to storage beforehand.
+# DefaultAutoConnectTechnologies =
+
+# List of preferred technologies from the most preferred
+# one to the least preferred one separated by commas ",".
+# Services of the listed technology type will be tried one
+# by one in the order given, until one of them gets connected
+# or they are all tried. A service of a preferred technology
+# type in state 'ready' will get the default route when
+# compared to another preferred type further down the list
+# with state 'ready' or with a non-preferred type; a service
+# of a preferred technology type in state 'online' will get
+# the default route when compared to either a non-preferred
+# type or a preferred type further down in the list.
+PreferredTechnologies = wifi
+
+# List of blacklisted network interfaces separated by ",".
+# Found interfaces will be compared to the list and will
+# not be handled by connman, if their first characters
+# match any of the list entries. Default value is
+# vmnet,vboxnet,virbr.
+NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,usb,rndis
+
+# Keep only a single connected technology at any time. When a new
+# service is connected by the user or a better one is found according
+# to PreferredTechnologies, the new service is kept connected and all
+# the other previously connected services are disconnected. With this
+# setting it does not matter whether the previously connected services
+# are in 'online' or 'ready' states, the newly connected service is
+# the only one that will be kept connected. A service connected by the
+# user will be used until going out of network coverage. With this
+# setting enabled applications will notice more network breaks than
+# normal. Default value is false.
+SingleConnectedTechnology = true
diff --git a/src/manager.c b/src/manager.c
new file mode 100644 (file)
index 0000000..1bf9189
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ *
+ *  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 <errno.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static connman_bool_t connman_state_idle;
+static DBusMessage *session_mode_pending = NULL;
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       connman_bool_t offlinemode, sessionmode;
+       const char *str;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       connman_dbus_dict_open(&array, &dict);
+
+       str = __connman_notifier_get_state();
+       connman_dbus_dict_append_basic(&dict, "State",
+                                               DBUS_TYPE_STRING, &str);
+
+       offlinemode = __connman_technology_get_offlinemode();
+       connman_dbus_dict_append_basic(&dict, "OfflineMode",
+                                       DBUS_TYPE_BOOLEAN, &offlinemode);
+
+       sessionmode = __connman_session_mode();
+       connman_dbus_dict_append_basic(&dict, "SessionMode",
+                                       DBUS_TYPE_BOOLEAN,
+                                       &sessionmode);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "OfflineMode") == TRUE) {
+               connman_bool_t offlinemode;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &offlinemode);
+
+               __connman_technology_set_offlinemode(offlinemode);
+       } else if (g_str_equal(name, "SessionMode") == TRUE) {
+               connman_bool_t sessionmode;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &sessionmode);
+
+               if (session_mode_pending != NULL)
+                       return __connman_error_in_progress(msg);
+
+               __connman_session_set_mode(sessionmode);
+
+               if (sessionmode == TRUE && connman_state_idle == FALSE) {
+                       session_mode_pending = msg;
+                       return NULL;
+               }
+
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void append_technology_structs(DBusMessageIter *iter, void *user_data)
+{
+       __connman_technology_list_struct(iter);
+}
+
+static DBusMessage *get_technologies(DBusConnection *conn,
+               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       __connman_dbus_append_objpath_dict_array(reply,
+                       append_technology_structs, NULL);
+
+       return reply;
+}
+
+static DBusMessage *remove_provider(DBusConnection *conn,
+                                   DBusMessage *msg, void *data)
+{
+       const char *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __connman_provider_remove(path);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusConnection *connection = NULL;
+
+static void session_mode_notify(void)
+{
+       DBusMessage *reply;
+
+       reply = g_dbus_create_reply(session_mode_pending, DBUS_TYPE_INVALID);
+       g_dbus_send_message(connection, reply);
+
+       dbus_message_unref(session_mode_pending);
+       session_mode_pending = NULL;
+}
+
+static void idle_state(connman_bool_t idle)
+{
+
+       DBG("idle %d", idle);
+
+       connman_state_idle = idle;
+
+       if (connman_state_idle == FALSE || session_mode_pending == NULL)
+               return;
+
+       session_mode_notify();
+}
+
+static struct connman_notifier technology_notifier = {
+       .name           = "manager",
+       .priority       = CONNMAN_NOTIFIER_PRIORITY_HIGH,
+       .idle_state     = idle_state,
+};
+
+static void append_service_structs(DBusMessageIter *iter, void *user_data)
+{
+       __connman_service_list_struct(iter);
+}
+
+static DBusMessage *get_services(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       __connman_dbus_append_objpath_dict_array(reply,
+                       append_service_structs, NULL);
+
+       return reply;
+}
+
+static DBusMessage *connect_provider(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (__connman_session_mode() == TRUE) {
+               connman_info("Session mode enabled: "
+                               "direct provider connect disabled");
+
+               return __connman_error_failed(msg, -EINVAL);
+       }
+
+       err = __connman_provider_create_and_connect(msg);
+       if (err < 0) {
+               if (err == -EINPROGRESS) {
+                       connman_error("Invalid return code from connect");
+                       err = -EINVAL;
+               }
+
+               return __connman_error_failed(msg, -err);
+       }
+
+       return NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __connman_agent_register(sender, path);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __connman_agent_unregister(sender, path);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *register_counter(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       unsigned int accuracy, period;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_UINT32, &accuracy,
+                                               DBUS_TYPE_UINT32, &period,
+                                                       DBUS_TYPE_INVALID);
+
+       /* FIXME: add handling of accuracy parameter */
+
+       err = __connman_counter_register(sender, path, period);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_counter(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender, *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __connman_counter_unregister(sender, path);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *create_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       int err;
+
+       DBG("conn %p", conn);
+
+       err = __connman_session_create(msg);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *destroy_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       int err;
+
+       DBG("conn %p", conn);
+
+       err = __connman_session_destroy(msg);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *request_private_network(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *sender;
+       int  err;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       err = __connman_private_network_request(msg, sender);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return NULL;
+}
+
+static DBusMessage *release_private_network(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *path;
+       int err;
+
+       DBG("conn %p", conn);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       err = __connman_private_network_release(path);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_METHOD("GetTechnologies",
+                       NULL, GDBUS_ARGS({ "technologies", "a(oa{sv})" }),
+                       get_technologies) },
+       { GDBUS_METHOD("RemoveProvider",
+                       GDBUS_ARGS({ "provider", "o" }), NULL,
+                       remove_provider) },
+       { GDBUS_METHOD("GetServices",
+                       NULL, GDBUS_ARGS({ "services", "a(oa{sv})" }),
+                       get_services) },
+       { GDBUS_ASYNC_METHOD("ConnectProvider",
+                             GDBUS_ARGS({ "provider", "a{sv}" }),
+                             GDBUS_ARGS({ "path", "o" }),
+                             connect_provider) },
+       { GDBUS_METHOD("RegisterAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       register_agent) },
+       { GDBUS_METHOD("UnregisterAgent",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       unregister_agent) },
+       { GDBUS_METHOD("RegisterCounter",
+                       GDBUS_ARGS({ "path", "o" }, { "accuracy", "u" },
+                                       { "period", "u" }),
+                       NULL, register_counter) },
+       { GDBUS_METHOD("UnregisterCounter",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       unregister_counter) },
+       { GDBUS_METHOD("CreateSession",
+                       GDBUS_ARGS({ "settings", "a{sv}" },
+                                               { "notifier", "o" }),
+                       GDBUS_ARGS({ "session", "o" }),
+                       create_session) },
+       { GDBUS_METHOD("DestroySession",
+                       GDBUS_ARGS({ "session", "o" }), NULL,
+                       destroy_session) },
+       { GDBUS_ASYNC_METHOD("RequestPrivateNetwork",
+                             NULL, GDBUS_ARGS({ "path", "o" },
+                                              { "settings", "a{sv}" },
+                                              { "socket", "h" }),
+                             request_private_network) },
+       { GDBUS_METHOD("ReleasePrivateNetwork",
+                       GDBUS_ARGS({ "path", "o" }), NULL,
+                       release_private_network) },
+       { },
+};
+
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { GDBUS_SIGNAL("TechnologyAdded",
+                       GDBUS_ARGS({ "path", "o" },
+                                  { "properties", "a{sv}" })) },
+       { GDBUS_SIGNAL("TechnologyRemoved",
+                       GDBUS_ARGS({ "path", "o" })) },
+       { GDBUS_SIGNAL("ServicesChanged",
+                       GDBUS_ARGS({ "changed", "a(oa{sv})" },
+                                       { "removed", "ao" })) },
+       { },
+};
+
+int __connman_manager_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       if (connman_notifier_register(&technology_notifier) < 0)
+               connman_error("Failed to register technology notifier");
+
+       g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
+                                       CONNMAN_MANAGER_INTERFACE,
+                                       manager_methods,
+                                       manager_signals, NULL, NULL, NULL);
+
+       connman_state_idle = TRUE;
+
+       return 0;
+}
+
+void __connman_manager_cleanup(void)
+{
+       DBG("");
+
+       if (connection == NULL)
+               return;
+
+       connman_notifier_unregister(&technology_notifier);
+
+       g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/nat.c b/src/nat.c
new file mode 100644 (file)
index 0000000..e17a3f8
--- /dev/null
+++ b/src/nat.c
@@ -0,0 +1,235 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. 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 <errno.h>
+#include <stdio.h>
+
+#include "connman.h"
+
+static char *default_interface;
+static GHashTable *nat_hash;
+
+struct connman_nat {
+       char *address;
+       unsigned char prefixlen;
+
+       char *interface;
+};
+
+static int enable_ip_forward(connman_bool_t enable)
+{
+       FILE *f;
+
+       f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
+       if (f == NULL)
+               return -errno;
+
+       if (enable == TRUE)
+               fprintf(f, "1");
+       else
+               fprintf(f, "0");
+
+       fclose(f);
+
+       return 0;
+}
+
+static void flush_nat(void)
+{
+       int err;
+
+       err = __connman_iptables_command("-t nat -F POSTROUTING");
+       if (err < 0) {
+               DBG("Flushing the nat table failed");
+
+               return;
+       }
+
+       __connman_iptables_commit("nat");
+}
+
+static int enable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       g_free(nat->interface);
+       nat->interface = g_strdup(default_interface);
+
+       if (nat->interface == NULL)
+               return 0;
+
+       /* Enable masquerading */
+       err = __connman_iptables_command("-t nat -A POSTROUTING "
+                                       "-s %s/%d -o %s -j MASQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return err;
+
+       return __connman_iptables_commit("nat");
+}
+
+static void disable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       if (nat->interface == NULL)
+               return;
+
+       /* Disable masquerading */
+       err = __connman_iptables_command("-t nat -D POSTROUTING "
+                                       "-s %s/%d -o %s -j MASQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return;
+
+       __connman_iptables_commit("nat");
+}
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen)
+{
+       struct connman_nat *nat;
+       int err;
+
+       if (g_hash_table_size(nat_hash) == 0) {
+               err = enable_ip_forward(TRUE);
+               if (err < 0)
+                       return err;
+       }
+
+       nat = g_try_new0(struct connman_nat, 1);
+       if (nat == NULL) {
+               if (g_hash_table_size(nat_hash) == 0)
+                       enable_ip_forward(FALSE);
+
+               return -ENOMEM;
+       }
+
+       nat->address = g_strdup(address);
+       nat->prefixlen = prefixlen;
+
+       g_hash_table_replace(nat_hash, g_strdup(name), nat);
+
+       return enable_nat(nat);
+}
+
+void __connman_nat_disable(const char *name)
+{
+       struct connman_nat *nat;
+
+       nat = g_hash_table_lookup(nat_hash, name);
+       if (nat == NULL)
+               return;
+
+       disable_nat(nat);
+
+       g_hash_table_remove(nat_hash, name);
+
+       if (g_hash_table_size(nat_hash) == 0)
+               enable_ip_forward(FALSE);
+}
+
+static void update_default_interface(struct connman_service *service)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       char *interface;
+       int err;
+
+       interface = connman_service_get_interface(service);
+
+       DBG("interface %s", interface);
+
+       g_free(default_interface);
+       default_interface = interface;
+
+       g_hash_table_iter_init(&iter, nat_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               const char *name = key;
+               struct connman_nat *nat = value;
+
+               disable_nat(nat);
+               err = enable_nat(nat);
+               if (err < 0)
+                       DBG("Failed to enable nat for %s", name);
+       }
+}
+
+static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *name = key;
+
+       __connman_nat_disable(name);
+}
+
+static void cleanup_nat(gpointer data)
+{
+       struct connman_nat *nat = data;
+
+       g_free(nat->address);
+       g_free(nat->interface);
+}
+
+static struct connman_notifier nat_notifier = {
+       .name                   = "nat",
+       .default_changed        = update_default_interface,
+};
+
+int __connman_nat_init(void)
+{
+       int err;
+
+       DBG("");
+
+       err = connman_notifier_register(&nat_notifier);
+       if (err < 0)
+               return err;
+
+       nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, cleanup_nat);
+
+       flush_nat();
+
+       return 0;
+}
+
+void __connman_nat_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
+       g_hash_table_destroy(nat_hash);
+       nat_hash = NULL;
+
+       flush_nat();
+
+       connman_notifier_unregister(&nat_notifier);
+}
diff --git a/src/network.c b/src/network.c
new file mode 100644 (file)
index 0000000..e7dc149
--- /dev/null
@@ -0,0 +1,2262 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include "connman.h"
+
+/*
+ * How many times to send RS with the purpose of
+ * refreshing RDNSS entries before they actually expire.
+ * With a value of 1, one RS will be sent, with no retries.
+ */
+#define RS_REFRESH_COUNT       1
+
+/*
+ * Value in seconds to wait for RA after RS was sent.
+ * After this time elapsed, we can send another RS.
+ */
+#define RS_REFRESH_TIMEOUT     3
+
+#if defined TIZEN_EXT
+#define WIFI_ENCYPTION_MODE_LEN_MAX 6
+#define WIFI_BSSID_LEN_MAX 6
+#endif
+
+static GSList *network_list = NULL;
+static GSList *driver_list = NULL;
+
+struct connman_network {
+       int refcount;
+       enum connman_network_type type;
+       connman_bool_t available;
+       connman_bool_t connected;
+       connman_bool_t roaming;
+       connman_uint8_t strength;
+       connman_uint16_t frequency;
+       char *identifier;
+       char *name;
+       char *node;
+       char *group;
+       char *path;
+       int index;
+       int router_solicit_count;
+       int router_solicit_refresh_count;
+
+       struct connman_network_driver *driver;
+       void *driver_data;
+
+       connman_bool_t connecting;
+       connman_bool_t associating;
+
+       struct connman_device *device;
+
+       struct {
+               void *ssid;
+               int ssid_len;
+               char *mode;
+               unsigned short channel;
+               char *security;
+               char *passphrase;
+               char *agent_passphrase;
+               char *eap;
+               char *identity;
+               char *agent_identity;
+               char *ca_cert_path;
+               char *client_cert_path;
+               char *private_key_path;
+               char *private_key_passphrase;
+               char *phase2_auth;
+               connman_bool_t wps;
+               connman_bool_t use_wps;
+               char *pin_wps;
+#if defined TIZEN_EXT
+               char encryption_mode[WIFI_ENCYPTION_MODE_LEN_MAX];
+               unsigned char bssid[WIFI_BSSID_LEN_MAX];
+               unsigned int maxrate;
+#endif
+       } wifi;
+
+       struct {
+               char *nsp_name;
+               int nsp_name_len;
+       } wimax;
+};
+
+static const char *type2string(enum connman_network_type type)
+{
+       switch (type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               break;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+               return "bluetooth";
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+               return "cellular";
+       }
+
+       return NULL;
+}
+
+static gboolean match_driver(struct connman_network *network,
+                                       struct connman_network_driver *driver)
+{
+       if (network->type == driver->type ||
+                       driver->type == CONNMAN_NETWORK_TYPE_UNKNOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
+static int network_probe(struct connman_network *network)
+{
+       GSList *list;
+       struct connman_network_driver *driver = NULL;
+
+       DBG("network %p name %s", network, network->name);
+
+       if (network->driver != NULL)
+               return -EALREADY;
+
+       for (list = driver_list; list; list = list->next) {
+               driver = list->data;
+
+               if (match_driver(network, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(network) == 0)
+                       break;
+
+               driver = NULL;
+       }
+
+       if (driver == NULL)
+               return -ENODEV;
+
+       if (network->group == NULL)
+               return -EINVAL;
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return 0;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               network->driver = driver;
+               if (__connman_service_create_from_network(network) == NULL) {
+                       network->driver = NULL;
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       if (network->driver == NULL)
+               return;
+
+       connman_network_set_connected(network, FALSE);
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               break;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               if (network->group != NULL) {
+                       __connman_service_remove_from_network(network);
+
+                       g_free(network->group);
+                       network->group = NULL;
+               }
+               break;
+       }
+
+       if (network->driver->remove)
+               network->driver->remove(network);
+
+       network->driver = NULL;
+}
+
+static void network_change(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       if (network->connected == FALSE)
+               return;
+
+       connman_device_set_disconnected(network->device, TRUE);
+
+       if (network->driver && network->driver->disconnect) {
+               network->driver->disconnect(network);
+               return;
+       }
+
+       network->connected = FALSE;
+}
+
+static void probe_driver(struct connman_network_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = network_list; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               if (network->driver != NULL)
+                       continue;
+
+               if (driver->type != network->type)
+                       continue;
+
+               if (driver->probe(network) < 0)
+                       continue;
+
+               network->driver = driver;
+       }
+}
+
+static void remove_driver(struct connman_network_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = network_list; list != NULL; list = list->next) {
+               struct connman_network *network = list->data;
+
+               if (network->driver == driver)
+                       network_remove(network);
+       }
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_network_driver *driver1 = a;
+       const struct connman_network_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_network_driver_register:
+ * @driver: network driver definition
+ *
+ * Register a new network driver
+ *
+ * Returns: %0 on success
+ */
+int connman_network_driver_register(struct connman_network_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_network_driver *tmp = list->data;
+
+               if (tmp->type == driver->type)
+                       return -EALREADY;
+
+       }
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       probe_driver(driver);
+
+       return 0;
+}
+
+/**
+ * connman_network_driver_unregister:
+ * @driver: network driver definition
+ *
+ * Remove a previously registered network driver
+ */
+void connman_network_driver_unregister(struct connman_network_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       remove_driver(driver);
+}
+
+static void network_destruct(struct connman_network *network)
+{
+       DBG("network %p name %s", network, network->name);
+
+       g_free(network->wifi.ssid);
+       g_free(network->wifi.mode);
+       g_free(network->wifi.security);
+       g_free(network->wifi.passphrase);
+       g_free(network->wifi.agent_passphrase);
+       g_free(network->wifi.eap);
+       g_free(network->wifi.identity);
+       g_free(network->wifi.agent_identity);
+       g_free(network->wifi.ca_cert_path);
+       g_free(network->wifi.client_cert_path);
+       g_free(network->wifi.private_key_path);
+       g_free(network->wifi.private_key_passphrase);
+       g_free(network->wifi.phase2_auth);
+       g_free(network->wifi.pin_wps);
+
+       g_free(network->path);
+       g_free(network->group);
+       g_free(network->node);
+       g_free(network->name);
+       g_free(network->identifier);
+
+       network->device = NULL;
+
+       g_free(network);
+}
+
+/**
+ * connman_network_create:
+ * @identifier: network identifier (for example an unqiue name)
+ *
+ * Allocate a new network and assign the #identifier to it.
+ *
+ * Returns: a newly-allocated #connman_network structure
+ */
+struct connman_network *connman_network_create(const char *identifier,
+                                               enum connman_network_type type)
+{
+       struct connman_network *network;
+       char *ident;
+
+       DBG("identifier %s type %d", identifier, type);
+
+       network = g_try_new0(struct connman_network, 1);
+       if (network == NULL)
+               return NULL;
+
+       DBG("network %p", network);
+
+       network->refcount = 1;
+
+       ident = g_strdup(identifier);
+
+       if (ident == NULL) {
+               g_free(network);
+               return NULL;
+       }
+
+       network->type       = type;
+       network->identifier = ident;
+
+       network_list = g_slist_append(network_list, network);
+
+       return network;
+}
+
+/**
+ * connman_network_ref:
+ * @network: network structure
+ *
+ * Increase reference counter of  network
+ */
+struct connman_network *
+connman_network_ref_debug(struct connman_network *network,
+                       const char *file, int line, const char *caller)
+{
+       DBG("%p name %s ref %d by %s:%d:%s()", network, network->name,
+               network->refcount + 1, file, line, caller);
+
+       __sync_fetch_and_add(&network->refcount, 1);
+
+       return network;
+}
+
+/**
+ * connman_network_unref:
+ * @network: network structure
+ *
+ * Decrease reference counter of network
+ */
+void connman_network_unref_debug(struct connman_network *network,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p name %s ref %d by %s:%d:%s()", network, network->name,
+               network->refcount - 1, file, line, caller);
+
+       if (__sync_fetch_and_sub(&network->refcount, 1) != 1)
+               return;
+
+       network_list = g_slist_remove(network_list, network);
+
+       network_destruct(network);
+}
+
+const char *__connman_network_get_type(struct connman_network *network)
+{
+       return type2string(network->type);
+}
+
+/**
+ * connman_network_get_type:
+ * @network: network structure
+ *
+ * Get type of network
+ */
+enum connman_network_type connman_network_get_type(struct connman_network *network)
+{
+       return network->type;
+}
+
+/**
+ * connman_network_get_identifier:
+ * @network: network structure
+ *
+ * Get identifier of network
+ */
+const char *connman_network_get_identifier(struct connman_network *network)
+{
+       return network->identifier;
+}
+
+/**
+ * connman_network_set_index:
+ * @network: network structure
+ * @index: index number
+ *
+ * Set index number of network
+ */
+void connman_network_set_index(struct connman_network *network, int index)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               goto done;
+
+       ipconfig = __connman_service_get_ip4config(service);
+
+       DBG("index %d service %p ip4config %p", network->index,
+               service, ipconfig);
+
+       if (network->index < 0 && ipconfig == NULL) {
+
+               ipconfig = __connman_service_get_ip4config(service);
+               if (ipconfig == NULL)
+                       /*
+                        * This is needed for plugins that havent set their
+                        * ipconfig layer yet, due to not being able to get
+                        * a network index prior to creating a service.
+                        */
+                       __connman_service_create_ip4config(service, index);
+               else
+                       __connman_ipconfig_set_index(ipconfig, index);
+
+       } else {
+               /* If index changed, the index of ipconfig must be reset. */
+               if (ipconfig == NULL)
+                       goto done;
+
+               __connman_ipconfig_set_index(ipconfig, index);
+       }
+
+done:
+       network->index = index;
+}
+
+/**
+ * connman_network_get_index:
+ * @network: network structure
+ *
+ * Get index number of network
+ */
+int connman_network_get_index(struct connman_network *network)
+{
+       return network->index;
+}
+
+/**
+ * connman_network_set_group:
+ * @network: network structure
+ * @group: group name
+ *
+ * Set group name for automatic clustering
+ */
+void connman_network_set_group(struct connman_network *network,
+                                                       const char *group)
+{
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               break;
+       }
+
+       if (g_strcmp0(network->group, group) == 0) {
+               if (group != NULL)
+                       __connman_service_update_from_network(network);
+               return;
+       }
+
+       if (network->group != NULL) {
+               __connman_service_remove_from_network(network);
+
+               g_free(network->group);
+       }
+
+       network->group = g_strdup(group);
+
+       if (network->group != NULL)
+               network_probe(network);
+}
+
+/**
+ * connman_network_get_group:
+ * @network: network structure
+ *
+ * Get group name for automatic clustering
+ */
+const char *connman_network_get_group(struct connman_network *network)
+{
+       return network->group;
+}
+
+const char *__connman_network_get_ident(struct connman_network *network)
+{
+       if (network->device == NULL)
+               return NULL;
+
+       return connman_device_get_ident(network->device);
+}
+
+connman_bool_t __connman_network_get_weakness(struct connman_network *network)
+{
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               if (g_strcmp0(network->wifi.mode, "adhoc") == 0)
+                       return TRUE;
+               if (network->strength > 0 && network->strength < 20)
+                       return TRUE;
+               break;
+       }
+
+       return FALSE;
+}
+
+connman_bool_t connman_network_get_connecting(struct connman_network *network)
+{
+       return network->connecting;
+}
+
+/**
+ * connman_network_set_available:
+ * @network: network structure
+ * @available: availability state
+ *
+ * Change availability state of network (in range)
+ */
+int connman_network_set_available(struct connman_network *network,
+                                               connman_bool_t available)
+{
+       DBG("network %p available %d", network, available);
+
+       if (network->available == available)
+               return -EALREADY;
+
+       network->available = available;
+
+       return 0;
+}
+
+/**
+ * connman_network_get_available:
+ * @network: network structure
+ *
+ * Get network available setting
+ */
+connman_bool_t connman_network_get_available(struct connman_network *network)
+{
+       return network->available;
+}
+
+/**
+ * connman_network_set_associating:
+ * @network: network structure
+ * @associating: associating state
+ *
+ * Change associating state of network
+ */
+int connman_network_set_associating(struct connman_network *network,
+                                               connman_bool_t associating)
+{
+       DBG("network %p associating %d", network, associating);
+
+       if (network->associating == associating)
+               return -EALREADY;
+
+       network->associating = associating;
+
+       if (associating == TRUE) {
+               struct connman_service *service;
+
+               service = __connman_service_lookup_from_network(network);
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       }
+
+       return 0;
+}
+
+static void set_associate_error(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+}
+
+static void set_configure_error(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+}
+
+static void set_invalid_key_error(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
+}
+
+static void set_connect_error(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
+}
+
+void connman_network_set_ipv4_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       ipconfig = __connman_service_get_ip4config(service);
+       if (ipconfig == NULL)
+               return;
+
+       __connman_ipconfig_set_method(ipconfig, method);
+}
+
+void connman_network_set_ipv6_method(struct connman_network *network,
+                                       enum connman_ipconfig_method method)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL)
+               return;
+
+       __connman_ipconfig_set_method(ipconfig, method);
+}
+
+void connman_network_set_error(struct connman_network *network,
+                                       enum connman_network_error error)
+{
+       DBG("nework %p, error %d", network, error);
+
+       network->connecting = FALSE;
+       network->associating = FALSE;
+
+       switch (error) {
+       case CONNMAN_NETWORK_ERROR_UNKNOWN:
+               return;
+       case CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL:
+               set_associate_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL:
+               set_configure_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_INVALID_KEY:
+               set_invalid_key_error(network);
+               break;
+       case CONNMAN_NETWORK_ERROR_CONNECT_FAIL:
+               set_connect_error(network);
+               break;
+       }
+
+       network_change(network);
+}
+
+void connman_network_clear_error(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       DBG("network %p", network);
+
+       if (network == NULL)
+               return;
+
+       if (network->connecting == TRUE || network->associating == TRUE)
+               return;
+
+       service = __connman_service_lookup_from_network(network);
+       __connman_service_clear_error(service);
+}
+
+static void set_configuration(struct connman_network *network,
+                       enum connman_ipconfig_type type)
+{
+       struct connman_service *service;
+
+       DBG("network %p", network);
+
+       if (network->device == NULL)
+               return;
+
+       __connman_device_set_network(network->device, network);
+
+       connman_device_set_disconnected(network->device, FALSE);
+
+       service = __connman_service_lookup_from_network(network);
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION,
+                                       type);
+}
+
+static void dhcp_success(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv4;
+       int err;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               goto err;
+
+       connman_network_set_associating(network, FALSE);
+
+       network->connecting = FALSE;
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       err = __connman_ipconfig_address_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
+
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
+
+       return;
+
+err:
+       connman_network_set_error(network,
+                               CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+}
+
+static void dhcp_failure(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_IDLE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+}
+
+static void dhcp_callback(struct connman_network *network,
+                       connman_bool_t success)
+{
+       DBG("success %d", success);
+
+       if (success == TRUE)
+               dhcp_success(network);
+       else
+               dhcp_failure(network);
+}
+
+static int set_connected_fixed(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv4;
+       int err;
+
+       DBG("");
+
+       service = __connman_service_lookup_from_network(network);
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       network->connecting = FALSE;
+
+       connman_network_set_associating(network, FALSE);
+
+       err = __connman_ipconfig_address_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
+
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+       if (err < 0)
+               goto err;
+
+       return 0;
+
+err:
+       connman_network_set_error(network,
+                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+
+       return err;
+}
+
+static void set_connected_manual(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int err;
+
+       DBG("network %p", network);
+
+       service = __connman_service_lookup_from_network(network);
+
+       ipconfig = __connman_service_get_ip4config(service);
+
+       if (__connman_ipconfig_get_local(ipconfig) == NULL)
+               __connman_service_read_ip4config(service);
+
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       err = __connman_ipconfig_address_add(ipconfig);
+       if (err < 0)
+               goto err;
+
+       err = __connman_ipconfig_gateway_add(ipconfig);
+       if (err < 0)
+               goto err;
+
+       network->connecting = FALSE;
+
+       connman_network_set_associating(network, FALSE);
+
+       return;
+
+err:
+       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       return;
+}
+
+static int set_connected_dhcp(struct connman_network *network)
+{
+       int err;
+
+       DBG("network %p", network);
+
+       set_configuration(network, CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       err = __connman_dhcp_start(network, dhcp_callback);
+       if (err < 0) {
+               connman_error("Can not request DHCP lease");
+               return err;
+       }
+
+       return 0;
+}
+
+static int manual_ipv6_set(struct connman_network *network,
+                               struct connman_ipconfig *ipconfig_ipv6)
+{
+       struct connman_service *service;
+       int err;
+
+       DBG("network %p ipv6 %p", network, ipconfig_ipv6);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       if (__connman_ipconfig_get_local(ipconfig_ipv6) == NULL)
+               __connman_service_read_ip6config(service);
+
+       err = __connman_ipconfig_address_add(ipconfig_ipv6);
+       if (err < 0) {
+               connman_network_set_error(network,
+                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+               return err;
+       }
+
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+       if (err < 0)
+               return err;
+
+       __connman_connection_gateway_activate(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       __connman_device_set_network(network->device, network);
+
+       connman_device_set_disconnected(network->device, FALSE);
+
+       network->connecting = FALSE;
+
+       return 0;
+}
+
+static void stop_dhcpv6(struct connman_network *network)
+{
+       __connman_dhcpv6_stop(network);
+}
+
+static void dhcpv6_release_callback(struct connman_network *network,
+                               connman_bool_t success)
+{
+       DBG("success %d", success);
+
+       stop_dhcpv6(network);
+}
+
+static void release_dhcpv6(struct connman_network *network)
+{
+       __connman_dhcpv6_start_release(network, dhcpv6_release_callback);
+       stop_dhcpv6(network);
+}
+
+static void dhcpv6_info_callback(struct connman_network *network,
+                               connman_bool_t success)
+{
+       DBG("success %d", success);
+
+       stop_dhcpv6(network);
+}
+
+static gboolean dhcpv6_set_addresses(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig_ipv6;
+       int err = -EINVAL;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               goto err;
+
+       connman_network_set_associating(network, FALSE);
+
+       network->connecting = FALSE;
+
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       err = __connman_ipconfig_address_add(ipconfig_ipv6);
+       if (err < 0)
+               goto err;
+
+       err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+       if (err < 0)
+               goto err;
+
+       return 0;
+
+err:
+       connman_network_set_error(network,
+                               CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+       return err;
+}
+
+static void autoconf_ipv6_set(struct connman_network *network);
+static void dhcpv6_callback(struct connman_network *network,
+                       connman_bool_t success);
+
+/*
+ * Have a separate callback for renew so that we do not do autoconf
+ * in wrong phase as the dhcpv6_callback() is also called when doing
+ * DHCPv6 solicitation.
+ */
+static void dhcpv6_renew_callback(struct connman_network *network,
+                                       connman_bool_t success)
+{
+       if (success == TRUE)
+               dhcpv6_callback(network, success);
+       else {
+               stop_dhcpv6(network);
+
+               /* restart and do solicit again. */
+               autoconf_ipv6_set(network);
+       }
+}
+
+static void dhcpv6_callback(struct connman_network *network,
+                                       connman_bool_t success)
+{
+       DBG("success %d", success);
+
+       /* Start the renew process if necessary */
+       if (success == TRUE) {
+
+               if (dhcpv6_set_addresses(network) < 0) {
+                       stop_dhcpv6(network);
+                       return;
+               }
+
+               if (__connman_dhcpv6_start_renew(network,
+                                       dhcpv6_renew_callback) == -ETIMEDOUT)
+                       dhcpv6_renew_callback(network, FALSE);
+       } else
+               stop_dhcpv6(network);
+}
+
+static void check_dhcpv6(struct nd_router_advert *reply,
+                       unsigned int length, void *user_data)
+{
+       struct connman_network *network = user_data;
+       GSList *prefixes;
+
+       DBG("reply %p", reply);
+
+       if (reply == NULL) {
+               /*
+                * Router solicitation message seem to get lost easily so
+                * try to send it again.
+                */
+               if (network->router_solicit_count > 0) {
+                       DBG("re-send router solicitation %d",
+                                               network->router_solicit_count);
+                       network->router_solicit_count--;
+                       __connman_inet_ipv6_send_rs(network->index, 1,
+                                               check_dhcpv6, network);
+                       return;
+               }
+               connman_network_unref(network);
+               return;
+       }
+
+       network->router_solicit_count = 0;
+
+       /*
+        * If we were disconnected while waiting router advertisement,
+        * we just quit and do not start DHCPv6
+        */
+       if (network->connected == FALSE) {
+               connman_network_unref(network);
+               return;
+       }
+
+       prefixes = __connman_inet_ipv6_get_prefixes(reply, length);
+
+       /*
+        * We do stateful/stateless DHCPv6 if router advertisement says so.
+        */
+       if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
+               __connman_dhcpv6_start(network, prefixes, dhcpv6_callback);
+       else if (reply->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
+               __connman_dhcpv6_start_info(network, dhcpv6_info_callback);
+
+       connman_network_unref(network);
+}
+
+static void receive_refresh_rs_reply(struct nd_router_advert *reply,
+               unsigned int length, void *user_data)
+{
+       struct connman_network *network = user_data;
+
+       DBG("reply %p", reply);
+
+       if (reply == NULL) {
+               /*
+                * Router solicitation message seem to get lost easily so
+                * try to send it again.
+                */
+               if (network->router_solicit_refresh_count > 1) {
+                       network->router_solicit_refresh_count--;
+                       DBG("re-send router solicitation %d",
+                                       network->router_solicit_refresh_count);
+                       __connman_inet_ipv6_send_rs(network->index,
+                                       RS_REFRESH_TIMEOUT,
+                                       receive_refresh_rs_reply,
+                                       network);
+                       return;
+               }
+       }
+
+       /* RS refresh not in progress anymore */
+       network->router_solicit_refresh_count = 0;
+
+       connman_network_unref(network);
+       return;
+}
+
+int __connman_refresh_rs_ipv6(struct connman_network *network, int index)
+{
+       int ret = 0;
+
+       DBG("network %p index %d", network, index);
+
+       /* Send only one RS for all RDNSS entries which are about to expire */
+       if (network->router_solicit_refresh_count > 0) {
+               DBG("RS refresh already started");
+               return 0;
+       }
+
+       network->router_solicit_refresh_count = RS_REFRESH_COUNT;
+
+       connman_network_ref(network);
+
+       ret = __connman_inet_ipv6_send_rs(index, RS_REFRESH_TIMEOUT,
+                       receive_refresh_rs_reply, network);
+       return ret;
+}
+
+static void autoconf_ipv6_set(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       int index;
+
+       DBG("network %p", network);
+
+       if (network->router_solicit_count > 0) {
+               /*
+                * The autoconfiguration is already pending and we have sent
+                * router solicitation messages and are now waiting answers.
+                * There is no need to continue any further.
+                */
+               DBG("autoconfiguration already started");
+               return;
+       }
+
+       __connman_device_set_network(network->device, network);
+
+       connman_device_set_disconnected(network->device, FALSE);
+
+       network->connecting = FALSE;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       ipconfig = __connman_service_get_ip6config(service);
+       if (ipconfig == NULL)
+               return;
+
+       index = __connman_ipconfig_get_index(ipconfig);
+
+       connman_network_ref(network);
+
+       /* Try to get stateless DHCPv6 information, RFC 3736 */
+       network->router_solicit_count = 3;
+       __connman_inet_ipv6_send_rs(index, 1, check_dhcpv6, network);
+}
+
+static void set_connected(struct connman_network *network)
+{
+       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+       enum connman_ipconfig_method ipv4_method, ipv6_method;
+       struct connman_service *service;
+       int ret;
+
+       if (network->connected == TRUE)
+               return;
+
+       network->connected = TRUE;
+
+       service = __connman_service_lookup_from_network(network);
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+
+       DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+               ipconfig_ipv6);
+
+       ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
+       ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
+
+       DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
+
+       switch (ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               autoconf_ipv6_set(network);
+               break;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               ret = manual_ipv6_set(network, ipconfig_ipv6);
+               if (ret != 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+               break;
+       }
+
+       switch (ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               if (set_connected_fixed(network) < 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+               return;
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               set_connected_manual(network);
+               return;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               if (set_connected_dhcp(network) < 0) {
+                       connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                       return;
+               }
+       }
+
+       network->connecting = FALSE;
+
+       connman_network_set_associating(network, FALSE);
+}
+
+static void set_disconnected(struct connman_network *network)
+{
+       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+       enum connman_ipconfig_method ipv4_method, ipv6_method;
+       enum connman_service_state state;
+       struct connman_service *service;
+
+       if (network->connected == FALSE)
+               return;
+
+       network->connected = FALSE;
+
+       service = __connman_service_lookup_from_network(network);
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+
+       DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+               ipconfig_ipv6);
+
+       ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
+       ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
+
+       DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
+
+       /*
+        * Resetting solicit count here will prevent the RS resend loop
+        * from sending packets in check_dhcpv6()
+        */
+       network->router_solicit_count = 0;
+
+       __connman_device_set_network(network->device, NULL);
+
+       switch (ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               release_dhcpv6(network);
+               break;
+       }
+
+       switch (ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               break;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               __connman_dhcp_stop(network);
+               break;
+       }
+
+       /*
+        * We only set the disconnect state if we were not in idle
+        * or in failure. It does not make sense to go to disconnect
+        * state if we were not connected.
+        */
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                       state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                               state != CONNMAN_SERVICE_STATE_FAILURE)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       __connman_connection_gateway_remove(service,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+#if defined TIZEN_EXT
+       /**
+         * Skip the functions if there is any connected profiles
+         * that use same interface
+         */
+       if (connman_service_get_type(service) != CONNMAN_SERVICE_TYPE_CELLULAR
+               || __connman_service_get_connected_count_of_iface(service)
+                                                                <= 0) {
+#endif
+       __connman_ipconfig_address_unset(ipconfig_ipv4);
+       __connman_ipconfig_address_unset(ipconfig_ipv6);
+#if defined TIZEN_EXT
+       }
+#endif
+
+       /*
+        * Special handling for IPv6 autoconfigured address.
+        * The simplest way to remove autoconfigured routes is to
+        * disable IPv6 temporarily so that kernel will do the cleanup
+        * automagically.
+        */
+       if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+               __connman_ipconfig_disable_ipv6(ipconfig_ipv6);
+               __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
+       }
+
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       network->connecting = FALSE;
+
+       connman_network_set_associating(network, FALSE);
+}
+
+/**
+ * connman_network_set_connected:
+ * @network: network structure
+ * @connected: connected state
+ *
+ * Change connected state of network
+ */
+int connman_network_set_connected(struct connman_network *network,
+                                               connman_bool_t connected)
+{
+       DBG("network %p connected %d/%d connecting %d associating %d",
+               network, network->connected, connected, network->connecting,
+               network->associating);
+
+       if ((network->connecting == TRUE || network->associating == TRUE) &&
+                                                       connected == FALSE) {
+               connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
+               if (__connman_network_disconnect(network) == 0)
+                       return 0;
+       }
+
+       if (network->connected == connected)
+               return -EALREADY;
+
+       if (connected == FALSE)
+               set_disconnected(network);
+       else
+               set_connected(network);
+
+       return 0;
+}
+
+/**
+ * connman_network_get_connected:
+ * @network: network structure
+ *
+ * Get network connection status
+ */
+connman_bool_t connman_network_get_connected(struct connman_network *network)
+{
+       return network->connected;
+}
+
+/**
+ * connman_network_get_associating:
+ * @network: network structure
+ *
+ * Get network associating status
+ */
+connman_bool_t connman_network_get_associating(struct connman_network *network)
+{
+       return network->associating;
+}
+
+void connman_network_clear_hidden(void *user_data)
+{
+       if (user_data == NULL)
+               return;
+
+       DBG("user_data %p", user_data);
+
+       /*
+        * Hidden service does not have a connect timeout so
+        * we do not need to remove it. We can just return
+        * error to the caller telling that we could not find
+        * any network that we could connect to.
+        */
+       __connman_service_reply_dbus_pending(user_data, EIO);
+}
+
+int connman_network_connect_hidden(struct connman_network *network,
+                       char *identity, char* passphrase, void *user_data)
+{
+       int err = 0;
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       DBG("network %p service %p user_data %p", network, service, user_data);
+
+       if (service == NULL) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (identity != NULL)
+               __connman_service_set_agent_identity(service, identity);
+
+       if (passphrase != NULL)
+               err = __connman_service_add_passphrase(service, passphrase);
+
+       if (err == -ENOKEY) {
+               __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
+               goto out;
+       } else {
+               __connman_service_set_hidden(service);
+               __connman_service_set_userconnect(service, TRUE);
+               __connman_service_set_hidden_data(service, user_data);
+               return __connman_service_connect(service);
+       }
+
+out:
+       __connman_service_return_error(service, -err, user_data);
+       return err;
+}
+
+/**
+ * __connman_network_connect:
+ * @network: network structure
+ *
+ * Connect network
+ */
+int __connman_network_connect(struct connman_network *network)
+{
+       int err;
+
+       DBG("network %p", network);
+
+       if (network->connected == TRUE)
+               return -EISCONN;
+
+       if (network->connecting == TRUE || network->associating == TRUE)
+               return -EALREADY;
+
+       if (network->driver == NULL)
+               return -EUNATCH;
+
+       if (network->driver->connect == NULL)
+               return -ENOSYS;
+
+       if (network->device == NULL)
+               return -ENODEV;
+
+       network->connecting = TRUE;
+
+#if defined TIZEN_EXT
+       if (network->type != CONNMAN_NETWORK_TYPE_CELLULAR)
+#endif
+       __connman_device_disconnect(network->device);
+
+       err = network->driver->connect(network);
+       if (err < 0) {
+               if (err == -EINPROGRESS)
+                       connman_network_set_associating(network, TRUE);
+               else {
+                       network->connecting = FALSE;
+               }
+
+               return err;
+       }
+
+       set_connected(network);
+
+       return err;
+}
+
+/**
+ * __connman_network_disconnect:
+ * @network: network structure
+ *
+ * Disconnect network
+ */
+int __connman_network_disconnect(struct connman_network *network)
+{
+       int err;
+
+       DBG("network %p", network);
+
+       if (network->connected == FALSE && network->connecting == FALSE &&
+                                               network->associating == FALSE)
+               return -ENOTCONN;
+
+       if (network->driver == NULL)
+               return -EUNATCH;
+
+       if (network->driver->disconnect == NULL)
+               return -ENOSYS;
+
+       network->connecting = FALSE;
+
+       err = network->driver->disconnect(network);
+       if (err == 0)
+               set_disconnected(network);
+
+       return err;
+}
+
+static int manual_ipv4_set(struct connman_network *network,
+                               struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service;
+       int err;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       err = __connman_ipconfig_address_add(ipconfig);
+       if (err < 0) {
+               connman_network_set_error(network,
+                       CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+               return err;
+       }
+
+       return __connman_ipconfig_gateway_add(ipconfig);
+}
+
+int __connman_network_clear_ipconfig(struct connman_network *network,
+                                       struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service;
+       enum connman_ipconfig_method method;
+       enum connman_ipconfig_type type;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       method = __connman_ipconfig_get_method(ipconfig);
+       type = __connman_ipconfig_get_config_type(ipconfig);
+
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               return -EINVAL;
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               release_dhcpv6(network);
+               break;
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+               __connman_ipconfig_address_remove(ipconfig);
+               break;
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               __connman_dhcp_stop(network);
+               break;
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       return 0;
+}
+
+int __connman_network_set_ipconfig(struct connman_network *network,
+                                       struct connman_ipconfig *ipconfig_ipv4,
+                                       struct connman_ipconfig *ipconfig_ipv6)
+{
+       enum connman_ipconfig_method method;
+       int ret;
+
+       if (network == NULL)
+               return -EINVAL;
+
+       if (ipconfig_ipv6) {
+               method = __connman_ipconfig_get_method(ipconfig_ipv6);
+
+               switch (method) {
+               case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+               case CONNMAN_IPCONFIG_METHOD_OFF:
+                       break;
+               case CONNMAN_IPCONFIG_METHOD_AUTO:
+                       autoconf_ipv6_set(network);
+                       break;
+               case CONNMAN_IPCONFIG_METHOD_FIXED:
+               case CONNMAN_IPCONFIG_METHOD_MANUAL:
+                       ret = manual_ipv6_set(network, ipconfig_ipv6);
+                       if (ret != 0) {
+                               connman_network_set_error(network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+                               return ret;
+                       }
+                       break;
+               case CONNMAN_IPCONFIG_METHOD_DHCP:
+                       break;
+               }
+       }
+
+       if (ipconfig_ipv4) {
+               method = __connman_ipconfig_get_method(ipconfig_ipv4);
+
+               switch (method) {
+               case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+               case CONNMAN_IPCONFIG_METHOD_OFF:
+               case CONNMAN_IPCONFIG_METHOD_FIXED:
+               case CONNMAN_IPCONFIG_METHOD_AUTO:
+                       return -EINVAL;
+               case CONNMAN_IPCONFIG_METHOD_MANUAL:
+                       return manual_ipv4_set(network, ipconfig_ipv4);
+               case CONNMAN_IPCONFIG_METHOD_DHCP:
+                       return __connman_dhcp_start(network, dhcp_callback);
+               }
+       }
+
+       return 0;
+}
+
+int connman_network_set_ipaddress(struct connman_network *network,
+                                       struct connman_ipaddress *ipaddress)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig = NULL;
+
+       DBG("network %p", network);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       ipconfig = __connman_service_get_ipconfig(service, ipaddress->family);
+       if (ipconfig == NULL)
+               return -EINVAL;
+
+       __connman_ipconfig_set_local(ipconfig, ipaddress->local);
+       __connman_ipconfig_set_peer(ipconfig, ipaddress->peer);
+       __connman_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
+       __connman_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
+       __connman_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
+
+       return 0;
+}
+
+#if defined TIZEN_EXT
+/*
+ * Description: Network client requires additional wifi specific info
+ */
+int connman_network_set_bssid(struct connman_network *network,
+                               const unsigned char *bssid)
+{
+       int i = 0;
+
+       if (bssid == NULL)
+               return -EINVAL;
+
+       DBG("network %p bssid %02x:%02x:%02x:%02x:%02x:%02x", network,
+                       bssid[0], bssid[1], bssid[2],
+                       bssid[3], bssid[4], bssid[5]);
+
+       for (;i < WIFI_BSSID_LEN_MAX;i++)
+               network->wifi.bssid[i] = bssid[i];
+
+       return 0;
+}
+
+unsigned char *connman_network_get_bssid(struct connman_network *network)
+{
+       return (unsigned char *)network->wifi.bssid;
+}
+
+int connman_network_set_maxrate(struct connman_network *network,
+                               unsigned int maxrate)
+{
+       DBG("network %p maxrate %d", network, maxrate);
+
+       network->wifi.maxrate = maxrate;
+
+       return 0;
+}
+
+unsigned int connman_network_get_maxrate(struct connman_network *network)
+{
+       return network->wifi.maxrate;
+}
+
+int connman_network_set_enc_mode(struct connman_network *network,
+                               const char *encryption_mode)
+{
+       if (encryption_mode == NULL)
+               return -EINVAL;
+
+       DBG("network %p encryption mode %s", network, encryption_mode);
+
+       g_strlcpy(network->wifi.encryption_mode, encryption_mode,
+                                       WIFI_ENCYPTION_MODE_LEN_MAX);
+
+       return 0;
+}
+
+const char *connman_network_get_enc_mode(struct connman_network *network)
+{
+       return (const char *)network->wifi.encryption_mode;
+}
+
+int connman_network_set_proxy(struct connman_network *network,
+                               const char *proxies)
+{
+       struct connman_service *service;
+
+       DBG("network %p proxies %s", network, proxies);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_service_set_proxy(service, proxies);
+
+       connman_service_set_proxy_method(service,
+                               CONNMAN_SERVICE_PROXY_METHOD_MANUAL);
+
+       return 0;
+}
+
+const char *connman_network_get_ifname(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_ipconfig *ipconfig;
+       const char *ifname = NULL;
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return NULL;
+
+       ipconfig = __connman_service_get_ip4config(service);
+
+       if (ipconfig != NULL)
+               ifname = __connman_ipconfig_get_ifname(ipconfig);
+
+       DBG("index %d, service %p ip4config %p ifname %s",
+                       network->index, service, ipconfig, ifname);
+       return ifname;
+}
+#endif
+
+int connman_network_set_nameservers(struct connman_network *network,
+                               const char *nameservers)
+{
+       struct connman_service *service;
+       char **nameservers_array;
+       int i;
+
+       DBG("network %p nameservers %s", network, nameservers);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_service_nameserver_clear(service);
+
+       if (nameservers == NULL)
+               return 0;
+
+       nameservers_array = g_strsplit(nameservers, " ", 0);
+
+       for (i = 0; nameservers_array[i] != NULL; i++) {
+               __connman_service_nameserver_append(service,
+                                               nameservers_array[i], FALSE);
+       }
+
+       g_strfreev(nameservers_array);
+
+       return 0;
+}
+
+int connman_network_set_domain(struct connman_network *network,
+                               const char *domain)
+{
+       struct connman_service *service;
+
+       DBG("network %p domain %s", network, domain);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_service_set_domainname(service, domain);
+
+       return 0;
+}
+
+/**
+ * connman_network_set_name:
+ * @network: network structure
+ * @name: name value
+ *
+ * Set display name value for network
+ */
+int connman_network_set_name(struct connman_network *network,
+                                                       const char *name)
+{
+       DBG("network %p name %s", network, name);
+
+       g_free(network->name);
+       network->name = g_strdup(name);
+
+       return 0;
+}
+
+/**
+ * connman_network_set_strength:
+ * @network: network structure
+ * @strength: strength value
+ *
+ * Set signal strength value for network
+ */
+
+int connman_network_set_strength(struct connman_network *network,
+                                               connman_uint8_t strength)
+{
+       DBG("network %p strengh %d", network, strength);
+
+       network->strength = strength;
+
+       return 0;
+}
+
+connman_uint8_t connman_network_get_strength(struct connman_network *network)
+{
+       return network->strength;
+}
+
+int connman_network_set_frequency(struct connman_network *network,
+                                               connman_uint16_t frequency)
+{
+       DBG("network %p frequency %d", network, frequency);
+
+       network->frequency = frequency;
+
+       return 0;
+}
+
+connman_uint16_t connman_network_get_frequency(struct connman_network *network)
+{
+       return network->frequency;
+}
+
+int connman_network_set_wifi_channel(struct connman_network *network,
+                                               connman_uint16_t channel)
+{
+       DBG("network %p wifi channel %d", network, channel);
+
+       network->wifi.channel = channel;
+
+       return 0;
+}
+
+connman_uint16_t connman_network_get_wifi_channel(struct connman_network *network)
+{
+       return network->wifi.channel;
+}
+
+/**
+ * connman_network_set_string:
+ * @network: network structure
+ * @key: unique identifier
+ * @value: string value
+ *
+ * Set string value for specific key
+ */
+int connman_network_set_string(struct connman_network *network,
+                                       const char *key, const char *value)
+{
+       DBG("network %p key %s value %s", network, key, value);
+
+       if (g_strcmp0(key, "Name") == 0)
+               return connman_network_set_name(network, value);
+
+       if (g_str_equal(key, "Path") == TRUE) {
+               g_free(network->path);
+               network->path = g_strdup(value);
+       } else if (g_str_equal(key, "Node") == TRUE) {
+               g_free(network->node);
+               network->node = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Mode") == TRUE) {
+               g_free(network->wifi.mode);
+               network->wifi.mode = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Security") == TRUE) {
+               g_free(network->wifi.security);
+               network->wifi.security = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) {
+               g_free(network->wifi.passphrase);
+               network->wifi.passphrase = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.AgentPassphrase") == TRUE) {
+               g_free(network->wifi.agent_passphrase);
+               network->wifi.agent_passphrase = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.EAP") == TRUE) {
+               g_free(network->wifi.eap);
+               network->wifi.eap = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Identity") == TRUE) {
+               g_free(network->wifi.identity);
+               network->wifi.identity = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.AgentIdentity") == TRUE) {
+               g_free(network->wifi.agent_identity);
+               network->wifi.agent_identity = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.CACertFile") == TRUE) {
+               g_free(network->wifi.ca_cert_path);
+               network->wifi.ca_cert_path = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.ClientCertFile") == TRUE) {
+               g_free(network->wifi.client_cert_path);
+               network->wifi.client_cert_path = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.PrivateKeyFile") == TRUE) {
+               g_free(network->wifi.private_key_path);
+               network->wifi.private_key_path = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase") == TRUE) {
+               g_free(network->wifi.private_key_passphrase);
+               network->wifi.private_key_passphrase = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Phase2") == TRUE) {
+               g_free(network->wifi.phase2_auth);
+               network->wifi.phase2_auth = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.PinWPS") == TRUE) {
+               g_free(network->wifi.pin_wps);
+               network->wifi.pin_wps = g_strdup(value);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * connman_network_get_string:
+ * @network: network structure
+ * @key: unique identifier
+ *
+ * Get string value for specific key
+ */
+const char *connman_network_get_string(struct connman_network *network,
+                                                       const char *key)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "Path") == TRUE)
+               return network->path;
+       else if (g_str_equal(key, "Name") == TRUE)
+               return network->name;
+       else if (g_str_equal(key, "Node") == TRUE)
+               return network->node;
+       else if (g_str_equal(key, "WiFi.Mode") == TRUE)
+               return network->wifi.mode;
+       else if (g_str_equal(key, "WiFi.Security") == TRUE)
+               return network->wifi.security;
+       else if (g_str_equal(key, "WiFi.Passphrase") == TRUE)
+               return network->wifi.passphrase;
+       else if (g_str_equal(key, "WiFi.AgentPassphrase") == TRUE)
+               return network->wifi.agent_passphrase;
+       else if (g_str_equal(key, "WiFi.EAP") == TRUE)
+               return network->wifi.eap;
+       else if (g_str_equal(key, "WiFi.Identity") == TRUE)
+               return network->wifi.identity;
+       else if (g_str_equal(key, "WiFi.AgentIdentity") == TRUE)
+               return network->wifi.agent_identity;
+       else if (g_str_equal(key, "WiFi.CACertFile") == TRUE)
+               return network->wifi.ca_cert_path;
+       else if (g_str_equal(key, "WiFi.ClientCertFile") == TRUE)
+               return network->wifi.client_cert_path;
+       else if (g_str_equal(key, "WiFi.PrivateKeyFile") == TRUE)
+               return network->wifi.private_key_path;
+       else if (g_str_equal(key, "WiFi.PrivateKeyPassphrase") == TRUE)
+               return network->wifi.private_key_passphrase;
+       else if (g_str_equal(key, "WiFi.Phase2") == TRUE)
+               return network->wifi.phase2_auth;
+       else if (g_str_equal(key, "WiFi.PinWPS") == TRUE)
+               return network->wifi.pin_wps;
+
+       return NULL;
+}
+
+/**
+ * connman_network_set_bool:
+ * @network: network structure
+ * @key: unique identifier
+ * @value: boolean value
+ *
+ * Set boolean value for specific key
+ */
+int connman_network_set_bool(struct connman_network *network,
+                                       const char *key, connman_bool_t value)
+{
+       DBG("network %p key %s value %d", network, key, value);
+
+       if (g_strcmp0(key, "Roaming") == 0)
+               network->roaming = value;
+       else if (g_strcmp0(key, "WiFi.WPS") == 0)
+               network->wifi.wps = value;
+       else if (g_strcmp0(key, "WiFi.UseWPS") == 0)
+               network->wifi.use_wps = value;
+
+       return -EINVAL;
+}
+
+/**
+ * connman_network_get_bool:
+ * @network: network structure
+ * @key: unique identifier
+ *
+ * Get boolean value for specific key
+ */
+connman_bool_t connman_network_get_bool(struct connman_network *network,
+                                                       const char *key)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "Roaming") == TRUE)
+               return network->roaming;
+       else if (g_str_equal(key, "WiFi.WPS") == TRUE)
+               return network->wifi.wps;
+       else if (g_str_equal(key, "WiFi.UseWPS") == TRUE)
+               return network->wifi.use_wps;
+
+       return FALSE;
+}
+
+/**
+ * connman_network_set_blob:
+ * @network: network structure
+ * @key: unique identifier
+ * @data: blob data
+ * @size: blob size
+ *
+ * Set binary blob value for specific key
+ */
+int connman_network_set_blob(struct connman_network *network,
+                       const char *key, const void *data, unsigned int size)
+{
+       DBG("network %p key %s size %d", network, key, size);
+
+       if (g_str_equal(key, "WiFi.SSID") == TRUE) {
+               g_free(network->wifi.ssid);
+               network->wifi.ssid = g_try_malloc(size);
+               if (network->wifi.ssid != NULL) {
+                       memcpy(network->wifi.ssid, data, size);
+                       network->wifi.ssid_len = size;
+               } else
+                       network->wifi.ssid_len = 0;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * connman_network_get_blob:
+ * @network: network structure
+ * @key: unique identifier
+ * @size: pointer to blob size
+ *
+ * Get binary blob value for specific key
+ */
+const void *connman_network_get_blob(struct connman_network *network,
+                                       const char *key, unsigned int *size)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "WiFi.SSID") == TRUE) {
+               if (size != NULL)
+                       *size = network->wifi.ssid_len;
+               return network->wifi.ssid;
+       }
+
+       return NULL;
+}
+
+void __connman_network_set_device(struct connman_network *network,
+                                       struct connman_device *device)
+{
+       if (network->device == device)
+               return;
+
+       if (network->device != NULL)
+               network_remove(network);
+
+       network->device = device;
+
+       if (network->device != NULL)
+               network_probe(network);
+}
+
+/**
+ * connman_network_get_device:
+ * @network: network structure
+ *
+ * Get parent device of network
+ */
+struct connman_device *connman_network_get_device(struct connman_network *network)
+{
+       return network->device;
+}
+
+/**
+ * connman_network_get_data:
+ * @network: network structure
+ *
+ * Get private network data pointer
+ */
+void *connman_network_get_data(struct connman_network *network)
+{
+       return network->driver_data;
+}
+
+/**
+ * connman_network_set_data:
+ * @network: network structure
+ * @data: data pointer
+ *
+ * Set private network data pointer
+ */
+void connman_network_set_data(struct connman_network *network, void *data)
+{
+       network->driver_data = data;
+}
+
+void connman_network_update(struct connman_network *network)
+{
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               break;
+       }
+
+       if (network->group != NULL)
+               __connman_service_update_from_network(network);
+}
+
+int __connman_network_init(void)
+{
+       DBG("");
+
+       return 0;
+}
+
+void __connman_network_cleanup(void)
+{
+       DBG("");
+}
diff --git a/src/notifier.c b/src/notifier.c
new file mode 100644 (file)
index 0000000..b328a2a
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ *
+ *  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 <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection = NULL;
+
+static GSList *notifier_list = NULL;
+static GHashTable *service_hash = NULL;
+
+static const char *notifier_state;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_notifier *notifier1 = a;
+       const struct connman_notifier *notifier2 = b;
+
+       return notifier2->priority - notifier1->priority;
+}
+
+/**
+ * connman_notifier_register:
+ * @notifier: notifier module
+ *
+ * Register a new notifier module
+ *
+ * Returns: %0 on success
+ */
+int connman_notifier_register(struct connman_notifier *notifier)
+{
+       DBG("notifier %p name %s", notifier, notifier->name);
+
+       notifier_list = g_slist_insert_sorted(notifier_list, notifier,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_notifier_unregister:
+ * @notifier: notifier module
+ *
+ * Remove a previously registered notifier module
+ */
+void connman_notifier_unregister(struct connman_notifier *notifier)
+{
+       DBG("notifier %p name %s", notifier, notifier->name);
+
+       notifier_list = g_slist_remove(notifier_list, notifier);
+}
+
+#define MAX_TECHNOLOGIES 10
+
+static int connected[MAX_TECHNOLOGIES];
+static int online[MAX_TECHNOLOGIES];
+
+static connman_bool_t notifier_is_online(void)
+{
+       unsigned int i;
+
+       __sync_synchronize();
+       for (i = 0; i < MAX_TECHNOLOGIES; i++) {
+               if (online[i] > 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+connman_bool_t __connman_notifier_is_connected(void)
+{
+       unsigned int i;
+
+       __sync_synchronize();
+       for (i = 0; i < MAX_TECHNOLOGIES; i++) {
+               if (connected[i] > 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const char *evaluate_notifier_state(void)
+{
+       if (notifier_is_online() == TRUE)
+               return "online";
+
+       if (__connman_notifier_is_connected() == TRUE)
+               return "ready";
+
+       if ( __connman_technology_get_offlinemode() == TRUE)
+               return "offline";
+
+       return "idle";
+}
+
+const char *__connman_notifier_get_state(void)
+{
+       return notifier_state;
+}
+
+static void state_changed(void)
+{
+       const char *state;
+
+       state = evaluate_notifier_state();
+
+       if (g_strcmp0(state, notifier_state) == 0)
+               return;
+
+       notifier_state = state;
+
+       connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &notifier_state);
+}
+
+static void technology_connected(enum connman_service_type type,
+                                               connman_bool_t connected)
+{
+       DBG("type %d connected %d", type, connected);
+
+       __connman_technology_set_connected(type, connected);
+       state_changed();
+}
+
+void __connman_notifier_connect(enum connman_service_type type)
+{
+       DBG("type %d", type);
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               break;
+       }
+
+       if (__sync_fetch_and_add(&connected[type], 1) == 0)
+               technology_connected(type, TRUE);
+}
+
+void __connman_notifier_enter_online(enum connman_service_type type)
+{
+       DBG("type %d", type);
+
+       if (__sync_fetch_and_add(&online[type], 1) == 0)
+               state_changed();
+}
+
+void __connman_notifier_leave_online(enum connman_service_type type)
+{
+       DBG("type %d", type);
+
+       if (__sync_fetch_and_sub(&online[type], 1) == 1)
+               state_changed();
+}
+
+void __connman_notifier_disconnect(enum connman_service_type type)
+{
+       DBG("type %d", type);
+
+       __sync_synchronize();
+       if (connected[type] == 0) {
+               connman_error("notifier disconnect underflow");
+               return;
+       }
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               break;
+       }
+
+       if (__sync_fetch_and_sub(&connected[type], 1) != 1)
+               return;
+
+       technology_connected(type, FALSE);
+}
+
+void __connman_notifier_default_changed(struct connman_service *service)
+{
+       GSList *list;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->default_changed)
+                       notifier->default_changed(service);
+       }
+}
+
+void __connman_notifier_service_add(struct connman_service *service,
+                                       const char *name)
+{
+       GSList *list;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->service_add)
+                       notifier->service_add(service, name);
+       }
+}
+
+void __connman_notifier_service_remove(struct connman_service *service)
+{
+       GSList *list;
+
+       if (g_hash_table_lookup(service_hash, service) != NULL) {
+               /*
+                * This is a tempory check for consistency. It can be
+                * removed when there are no reports for the following
+                * error message.
+                */
+               connman_error("Service state machine inconsistency detected.");
+
+               g_hash_table_remove(service_hash, service);
+       }
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->service_remove)
+                       notifier->service_remove(service);
+       }
+}
+
+void __connman_notifier_proxy_changed(struct connman_service *service)
+{
+       GSList *list;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->proxy_changed)
+                       notifier->proxy_changed(service);
+       }
+}
+
+static void offlinemode_changed(dbus_bool_t enabled)
+{
+       DBG("enabled %d", enabled);
+
+       connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "OfflineMode",
+                                               DBUS_TYPE_BOOLEAN, &enabled);
+}
+
+void __connman_notifier_offlinemode(connman_bool_t enabled)
+{
+       GSList *list;
+
+       DBG("enabled %d", enabled);
+
+       offlinemode_changed(enabled);
+       state_changed();
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->offline_mode)
+                       notifier->offline_mode(enabled);
+       }
+}
+
+static void notify_idle_state(connman_bool_t idle)
+{
+       GSList *list;
+
+       DBG("idle %d", idle);
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->idle_state)
+                       notifier->idle_state(idle);
+       }
+}
+
+void __connman_notifier_service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       GSList *list;
+       unsigned int old_size;
+       connman_bool_t found;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->service_state_changed)
+                       notifier->service_state_changed(service, state);
+       }
+
+       old_size = g_hash_table_size(service_hash);
+       found = g_hash_table_lookup(service_hash, service) != NULL;
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_IDLE:
+               if (found == FALSE)
+                       break;
+
+               g_hash_table_remove(service_hash, service);
+               if (old_size == 1)
+                       notify_idle_state(TRUE);
+
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               if (found == TRUE)
+                       break;
+
+               g_hash_table_insert(service_hash, service, service);
+               if (old_size == 0)
+                       notify_idle_state(FALSE);
+
+               break;
+       }
+}
+
+void __connman_notifier_ipconfig_changed(struct connman_service *service,
+                                       struct connman_ipconfig *ipconfig)
+{
+       GSList *list;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->ipconfig_changed)
+                       notifier->ipconfig_changed(service, ipconfig);
+       }
+}
+
+int __connman_notifier_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                               NULL, NULL);
+
+       notifier_state = evaluate_notifier_state();
+
+       return 0;
+}
+
+void __connman_notifier_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(service_hash);
+       service_hash = NULL;
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/ntp.c b/src/ntp.c
new file mode 100644 (file)
index 0000000..91a8832
--- /dev/null
+++ b/src/ntp.c
@@ -0,0 +1,393 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+struct ntp_short {
+       uint16_t seconds;
+       uint16_t fraction;
+} __attribute__ ((packed));
+
+struct ntp_time {
+       uint32_t seconds;
+       uint32_t fraction;
+} __attribute__ ((packed));
+
+struct ntp_msg {
+       uint8_t flags;                  /* Mode, version and leap indicator */
+       uint8_t stratum;                /* Stratum details */
+       int8_t poll;                    /* Maximum interval in log2 seconds */
+       int8_t precision;               /* Clock precision in log2 seconds */
+       struct ntp_short rootdelay;     /* Root delay */
+       struct ntp_short rootdisp;      /* Root dispersion */
+       uint32_t refid;                 /* Reference ID */
+       struct ntp_time reftime;        /* Reference timestamp */
+       struct ntp_time orgtime;        /* Origin timestamp */
+       struct ntp_time rectime;        /* Receive timestamp */
+       struct ntp_time xmttime;        /* Transmit timestamp */
+} __attribute__ ((packed));
+
+#define OFFSET_1900_1970  2208988800UL /* 1970 - 1900 in seconds */
+
+#define STEPTIME_MIN_OFFSET  0.128
+
+#define LOGTOD(a)  ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
+
+static guint channel_watch = 0;
+static struct timeval transmit_timeval;
+static int transmit_fd = 0;
+
+static char *timeserver = NULL;
+static gint poll_id = 0;
+static gint timeout_id = 0;
+
+static void send_packet(int fd, const char *server)
+{
+       struct ntp_msg msg;
+       struct sockaddr_in addr;
+       ssize_t len;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.flags = 0x23;
+       msg.poll = 4;   // min
+       msg.poll = 10;  // max
+       msg.xmttime.seconds = random();
+       msg.xmttime.fraction = random();
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(123);
+       addr.sin_addr.s_addr = inet_addr(server);
+
+       gettimeofday(&transmit_timeval, NULL);
+
+       len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
+                                               &addr, sizeof(addr));
+       if (len < 0) {
+               connman_error("Time request for server %s failed", server);
+               return;
+       }
+
+       if (len != sizeof(msg)) {
+               connman_error("Broken time request for server %s", server);
+               return;
+       }
+}
+
+static gboolean next_server(gpointer user_data)
+{
+       if (timeserver != NULL) {
+               g_free(timeserver);
+               timeserver = NULL;
+       }
+
+       __connman_timeserver_sync_next();
+
+       return FALSE;
+}
+
+static gboolean next_poll(gpointer user_data)
+{
+       if (timeserver == NULL || transmit_fd == 0)
+               return FALSE;
+
+       send_packet(transmit_fd, timeserver);
+
+       return FALSE;
+}
+
+static void decode_msg(void *base, size_t len, struct timeval *tv)
+{
+       struct ntp_msg *msg = base;
+       double org, rec, xmt, dst;
+       double delay, offset;
+       static guint transmit_delay;
+
+       if (len < sizeof(*msg)) {
+               connman_error("Invalid response from time server");
+               return;
+       }
+
+       if (tv == NULL) {
+               connman_error("Invalid packet timestamp from time server");
+               return;
+       }
+
+       DBG("flags      : 0x%02x", msg->flags);
+       DBG("stratum    : %u", msg->stratum);
+       DBG("poll       : %f seconds (%d)",
+                               LOGTOD(msg->poll), msg->poll);
+       DBG("precision  : %f seconds (%d)",
+                               LOGTOD(msg->precision), msg->precision);
+       DBG("root delay : %u seconds (fraction %u)",
+                       msg->rootdelay.seconds, msg->rootdelay.fraction);
+       DBG("root disp. : %u seconds (fraction %u)",
+                       msg->rootdisp.seconds, msg->rootdisp.fraction);
+       DBG("reference  : 0x%04x", msg->refid);
+
+       transmit_delay = LOGTOD(msg->poll);
+
+       if (msg->flags != 0x24)
+               return;
+
+       org = transmit_timeval.tv_sec +
+                       (1.0e-6 * transmit_timeval.tv_usec) + OFFSET_1900_1970;
+       rec = ntohl(msg->rectime.seconds) +
+                       ((double) ntohl(msg->rectime.fraction) / UINT_MAX);
+       xmt = ntohl(msg->xmttime.seconds) +
+                       ((double) ntohl(msg->xmttime.fraction) / UINT_MAX);
+       dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970;
+
+       DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst);
+
+       offset = ((rec - org) + (xmt - dst)) / 2;
+       delay = (dst - org) - (xmt - rec);
+
+       DBG("offset=%f delay=%f", offset, delay);
+
+       /* Remove the timeout, as timeserver has responded */
+       if (timeout_id > 0)
+               g_source_remove(timeout_id);
+
+       /*
+        * Now poll the server every transmit_delay seconds
+        * for time correction.
+        */
+       if (poll_id > 0)
+               g_source_remove(poll_id);
+
+       DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay);
+
+       poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL);
+
+       connman_info("ntp: time slew %+.6f s", offset);
+
+       if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
+               struct timeval adj;
+
+               adj.tv_sec = (long) offset;
+               adj.tv_usec = (offset - adj.tv_sec) * 1000000;
+
+               DBG("adjusting time");
+
+               if (adjtime(&adj, &adj) < 0) {
+                       connman_error("Failed to adjust time");
+                       return;
+               }
+
+               DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec);
+       } else {
+               struct timeval cur;
+               double dtime;
+
+               gettimeofday(&cur, NULL);
+               dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec;
+               cur.tv_sec = (long) dtime;
+               cur.tv_usec = (dtime - cur.tv_sec) * 1000000;
+
+               DBG("setting time");
+
+               if (settimeofday(&cur, NULL) < 0) {
+                       connman_error("Failed to set time");
+                       return;
+               }
+
+               DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec);
+       }
+}
+
+static gboolean received_data(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       unsigned char buf[128];
+       struct msghdr msg;
+       struct iovec iov;
+       struct cmsghdr *cmsg;
+       struct timeval *tv;
+       char aux[128];
+       ssize_t len;
+       int fd;
+
+       if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               connman_error("Problem with timer server channel");
+               channel_watch = 0;
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       iov.iov_base = buf;
+       iov.iov_len = sizeof(buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = aux;
+       msg.msg_controllen = sizeof(aux);
+
+       len = recvmsg(fd, &msg, MSG_DONTWAIT);
+       if (len < 0)
+               return TRUE;
+
+       tv = NULL;
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level != SOL_SOCKET)
+                       continue;
+
+               switch (cmsg->cmsg_type) {
+               case SCM_TIMESTAMP:
+                       tv = (struct timeval *) CMSG_DATA(cmsg);
+                       break;
+               }
+       }
+
+       decode_msg(iov.iov_base, iov.iov_len, tv);
+
+       return TRUE;
+}
+
+static void start_ntp(char *server)
+{
+       GIOChannel *channel;
+       struct sockaddr_in addr;
+       int tos = IPTOS_LOWDELAY, timestamp = 1;
+
+       if (server == NULL)
+               return;
+
+       DBG("server %s", server);
+
+       if (channel_watch > 0)
+               goto send;
+
+       transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (transmit_fd < 0) {
+               connman_error("Failed to open time server socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+
+       if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               connman_error("Failed to bind time server socket");
+               close(transmit_fd);
+               return;
+       }
+
+       if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+               connman_error("Failed to set type of service option");
+               close(transmit_fd);
+               return;
+       }
+
+       if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
+                                               sizeof(timestamp)) < 0) {
+               connman_error("Failed to enable timestamp support");
+               close(transmit_fd);
+               return;
+       }
+
+       channel = g_io_channel_unix_new(transmit_fd);
+       if (channel == NULL) {
+               close(transmit_fd);
+               return;
+       }
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               received_data, NULL, NULL);
+
+       g_io_channel_unref(channel);
+
+send:
+       send_packet(transmit_fd, server);
+}
+
+int __connman_ntp_start(char *server)
+{
+       DBG("%s", server);
+
+       if (server == NULL)
+               return -EINVAL;
+
+       if (timeserver != NULL)
+               g_free(timeserver);
+
+       timeserver = g_strdup(server);
+
+       start_ntp(timeserver);
+
+       /*
+        * Add a fallback timeout , preferably short, 5 sec here,
+        * to fallback on the next server.
+        */
+
+       timeout_id = g_timeout_add_seconds(5, next_server, NULL);
+
+       return 0;
+}
+
+void __connman_ntp_stop()
+{
+       DBG("");
+
+       if (poll_id > 0)
+               g_source_remove(poll_id);
+
+       if (timeout_id > 0)
+               g_source_remove(timeout_id);
+
+       if (channel_watch > 0) {
+               g_source_remove(channel_watch);
+               channel_watch = 0;
+               transmit_fd = 0;
+       }
+
+       if (timeserver != NULL) {
+               g_free(timeserver);
+               timeserver = NULL;
+       }
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644 (file)
index 0000000..65e0311
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *
+ *  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 <dlfcn.h>
+
+#include <glib.h>
+
+#ifdef CONNMAN_PLUGIN_BUILTIN
+#undef CONNMAN_PLUGIN_BUILTIN
+#endif
+
+#include "connman.h"
+
+/*
+ * Plugins that are using libraries with threads and their own mainloop
+ * will crash on exit. This is a bug inside these libraries, but there is
+ * nothing much that can be done about it.
+ */
+#ifdef NEED_THREADS
+#define PLUGINFLAG (RTLD_NOW | RTLD_NODELETE)
+#else
+#define PLUGINFLAG (RTLD_NOW)
+#endif
+
+static GSList *plugins = NULL;
+
+struct connman_plugin {
+       void *handle;
+       gboolean active;
+       struct connman_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_plugin *plugin1 = a;
+       const struct connman_plugin *plugin2 = b;
+
+       return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct connman_plugin_desc *desc)
+{
+       struct connman_plugin *plugin;
+
+       if (desc->init == NULL)
+               return FALSE;
+
+       if (g_str_equal(desc->version, CONNMAN_VERSION) == FALSE) {
+               connman_error("Invalid version %s for %s", desc->version,
+                                                       desc->description);
+               return FALSE;
+       }
+
+       plugin = g_try_new0(struct connman_plugin, 1);
+       if (plugin == NULL)
+               return FALSE;
+
+       plugin->handle = handle;
+       plugin->active = FALSE;
+       plugin->desc = desc;
+
+       __connman_log_enable(desc->debug_start, desc->debug_stop);
+
+       plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+       return TRUE;
+}
+
+static gboolean check_plugin(struct connman_plugin_desc *desc,
+                               char **patterns, char **excludes)
+{
+       if (excludes) {
+               for (; *excludes; excludes++)
+                       if (g_pattern_match_simple(*excludes, desc->name))
+                               break;
+               if (*excludes) {
+                       connman_info("Excluding %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       if (patterns) {
+               for (; *patterns; patterns++)
+                       if (g_pattern_match_simple(*patterns, desc->name))
+                               break;
+               if (!*patterns) {
+                       connman_info("Ignoring %s", desc->description);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+#include "builtin.h"
+
+int __connman_plugin_init(const char *pattern, const char *exclude)
+{
+       gchar **patterns = NULL;
+       gchar **excludes = NULL;
+       GSList *list;
+       GDir *dir;
+       const gchar *file;
+       gchar *filename;
+       unsigned int i;
+
+       DBG("");
+
+       if (pattern)
+               patterns = g_strsplit_set(pattern, ":, ", -1);
+
+       if (exclude)
+               excludes = g_strsplit_set(exclude, ":, ", -1);
+
+       for (i = 0; __connman_builtin[i]; i++) {
+               if (check_plugin(__connman_builtin[i],
+                                               patterns, excludes) == FALSE)
+                       continue;
+
+               add_plugin(NULL, __connman_builtin[i]);
+       }
+
+       dir = g_dir_open(PLUGINDIR, 0, NULL);
+       if (dir != NULL) {
+               while ((file = g_dir_read_name(dir)) != NULL) {
+                       void *handle;
+                       struct connman_plugin_desc *desc;
+
+                       if (g_str_has_prefix(file, "lib") == TRUE ||
+                                       g_str_has_suffix(file, ".so") == FALSE)
+                               continue;
+
+                       filename = g_build_filename(PLUGINDIR, file, NULL);
+
+                       handle = dlopen(filename, PLUGINFLAG);
+                       if (handle == NULL) {
+                               connman_error("Can't load %s: %s",
+                                                       filename, dlerror());
+                               g_free(filename);
+                               continue;
+                       }
+
+                       g_free(filename);
+
+                       desc = dlsym(handle, "connman_plugin_desc");
+                       if (desc == NULL) {
+                               connman_error("Can't load symbol: %s",
+                                                               dlerror());
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (check_plugin(desc, patterns, excludes) == FALSE) {
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (add_plugin(handle, desc) == FALSE)
+                               dlclose(handle);
+               }
+
+               g_dir_close(dir);
+       }
+
+       for (list = plugins; list; list = list->next) {
+               struct connman_plugin *plugin = list->data;
+
+               if (plugin->desc->init() < 0)
+                       continue;
+
+               plugin->active = TRUE;
+       }
+
+       g_strfreev(patterns);
+       g_strfreev(excludes);
+
+       return 0;
+}
+
+void __connman_plugin_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = plugins; list; list = list->next) {
+               struct connman_plugin *plugin = list->data;
+
+               if (plugin->active == TRUE && plugin->desc->exit)
+                       plugin->desc->exit();
+
+               if (plugin->handle != NULL)
+                       dlclose(plugin->handle);
+
+               g_free(plugin);
+       }
+
+       g_slist_free(plugins);
+}
diff --git a/src/provider.c b/src/provider.c
new file mode 100644 (file)
index 0000000..3f4bb1c
--- /dev/null
@@ -0,0 +1,1396 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection = NULL;
+
+static GHashTable *provider_hash = NULL;
+
+static GSList *driver_list = NULL;
+
+struct connman_route {
+       int family;
+       char *host;
+       char *netmask;
+       char *gateway;
+};
+
+struct connman_provider {
+       int refcount;
+       struct connman_service *vpn_service;
+       int index;
+       char *identifier;
+       char *name;
+       char *type;
+       char *host;
+       char *domain;
+       int family;
+       GHashTable *routes;
+       struct connman_provider_driver *driver;
+       void *driver_data;
+       GHashTable *setting_strings;
+       GHashTable *user_routes;
+       gchar **user_networks;
+       gsize num_user_networks;
+};
+
+void __connman_provider_append_properties(struct connman_provider *provider,
+                                                       DBusMessageIter *iter)
+{
+       if (provider->host != NULL)
+               connman_dbus_dict_append_basic(iter, "Host",
+                                       DBUS_TYPE_STRING, &provider->host);
+
+       if (provider->domain != NULL)
+               connman_dbus_dict_append_basic(iter, "Domain",
+                                       DBUS_TYPE_STRING, &provider->domain);
+
+       if (provider->type != NULL)
+               connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
+                                                &provider->type);
+}
+
+int __connman_provider_append_user_route(struct connman_provider *provider,
+                       int family, const char *network, const char *netmask)
+{
+       struct connman_route *route;
+       char *key = g_strdup_printf("%d/%s/%s", family, network, netmask);
+
+       DBG("family %d network %s netmask %s", family, network, netmask);
+
+       route = g_hash_table_lookup(provider->user_routes, key);
+       if (route == NULL) {
+               route = g_try_new0(struct connman_route, 1);
+               if (route == NULL) {
+                       connman_error("out of memory");
+                       return -ENOMEM;
+               }
+
+               route->family = family;
+               route->host = g_strdup(network);
+               route->netmask = g_strdup(netmask);
+
+               g_hash_table_replace(provider->user_routes, key, route);
+       } else
+               g_free(key);
+
+       return 0;
+}
+
+static void set_user_networks(struct connman_provider *provider,
+                                                       char **networks)
+{
+       int i = 0;
+
+       while (networks[i] != NULL) {
+               char **elems = g_strsplit(networks[i], "/", 0);
+               char *network, *netmask;
+               int family = PF_UNSPEC, ret;
+
+               if (elems == NULL)
+                       break;
+
+               network = elems[0];
+               if (network == NULL || *network == '\0') {
+                       DBG("no network/netmask set");
+                       g_strfreev(elems);
+                       break;
+               }
+
+               netmask = elems[1];
+               if (netmask != NULL && *netmask == '\0') {
+                       DBG("no netmask set");
+                       g_strfreev(elems);
+                       break;
+               }
+
+               if (g_strrstr(network, ":") != NULL)
+                       family = AF_INET6;
+               else if (g_strrstr(network, ".") != NULL) {
+                       family = AF_INET;
+
+                       if (g_strrstr(netmask, ".") == NULL) {
+                               /* We have netmask length */
+                               in_addr_t addr;
+                               struct in_addr netmask_in;
+                               unsigned char prefix_len = 32;
+
+                               if (netmask != NULL)
+                                       prefix_len = atoi(netmask);
+
+                               addr = 0xffffffff << (32 - prefix_len);
+                               netmask_in.s_addr = htonl(addr);
+                               netmask = inet_ntoa(netmask_in);
+
+                               DBG("network %s netmask %s", network, netmask);
+                       }
+               }
+
+               ret = __connman_provider_append_user_route(provider,
+                                               family, network, netmask);
+               g_strfreev(elems);
+
+               if (ret != 0)
+                       break;
+
+               i++;
+       }
+}
+
+static int provider_load_from_keyfile(struct connman_provider *provider,
+               GKeyFile *keyfile)
+{
+       gsize idx = 0;
+       gchar **settings;
+       gchar *key, *value;
+       gsize length;
+
+       settings = g_key_file_get_keys(keyfile, provider->identifier, &length,
+                               NULL);
+       if (settings == NULL) {
+               g_key_file_free(keyfile);
+               return -ENOENT;
+       }
+
+       while (idx < length) {
+               key = settings[idx];
+               if (key != NULL) {
+                       if (g_str_equal(key, "Networks") == TRUE) {
+                               g_strfreev(provider->user_networks);
+                               provider->user_networks =
+                                       g_key_file_get_string_list(keyfile,
+                                               provider->identifier,
+                                               key,
+                                               &provider->num_user_networks,
+                                               NULL);
+                       } else {
+                               value = g_key_file_get_string(keyfile,
+                                                       provider->identifier,
+                                                       key, NULL);
+                               connman_provider_set_string(provider, key,
+                                                       value);
+                               g_free(value);
+                       }
+               }
+               idx += 1;
+       }
+       g_strfreev(settings);
+
+       if (provider->user_networks != NULL)
+               set_user_networks(provider, provider->user_networks);
+
+       return 0;
+}
+
+static int connman_provider_load(struct connman_provider *provider)
+{
+       GKeyFile *keyfile;
+
+       DBG("provider %p", provider);
+
+       keyfile = __connman_storage_load_provider(provider->identifier);
+       if (keyfile == NULL)
+               return -ENOENT;
+
+       provider_load_from_keyfile(provider, keyfile);
+
+       g_key_file_free(keyfile);
+       return 0;
+}
+
+static int connman_provider_save(struct connman_provider *provider)
+{
+       GKeyFile *keyfile;
+
+       DBG("provider %p", provider);
+
+       keyfile = g_key_file_new();
+       if (keyfile == NULL)
+               return -ENOMEM;
+
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Name", provider->name);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Type", provider->type);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Host", provider->host);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "VPN.Domain", provider->domain);
+       if (provider->user_networks != NULL)
+               g_key_file_set_string_list(keyfile, provider->identifier,
+                               "Networks",
+                               (const gchar **)provider->user_networks,
+                               provider->num_user_networks);
+
+       if (provider->driver != NULL && provider->driver->save != NULL)
+               provider->driver->save(provider, keyfile);
+
+       __connman_storage_save_provider(keyfile, provider->identifier);
+        g_key_file_free(keyfile);
+
+       return 0;
+}
+
+static struct connman_provider *connman_provider_lookup(const char *identifier)
+{
+       struct connman_provider *provider = NULL;
+
+       provider = g_hash_table_lookup(provider_hash, identifier);
+
+       return provider;
+}
+
+static gboolean match_driver(struct connman_provider *provider,
+                               struct connman_provider_driver *driver)
+{
+       if (g_strcmp0(driver->name, provider->type) == 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+static int provider_probe(struct connman_provider *provider)
+{
+       GSList *list;
+
+       DBG("provider %p name %s", provider, provider->name);
+
+       if (provider->driver != NULL)
+               return -EALREADY;
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_provider_driver *driver = list->data;
+
+               if (match_driver(provider, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe != NULL && driver->probe(provider) == 0) {
+                       provider->driver = driver;
+                       break;
+               }
+       }
+
+       if (provider->driver == NULL)
+               return -ENODEV;
+
+       return 0;
+}
+
+static void provider_remove(struct connman_provider *provider)
+{
+       if (provider->driver != NULL) {
+               provider->driver->remove(provider);
+               provider->driver = NULL;
+       }
+}
+
+static int provider_register(struct connman_provider *provider)
+{
+       return provider_probe(provider);
+}
+
+static void provider_unregister(struct connman_provider *provider)
+{
+       provider_remove(provider);
+}
+
+struct connman_provider *
+connman_provider_ref_debug(struct connman_provider *provider,
+                       const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&provider->refcount, 1);
+
+       return provider;
+}
+
+static void provider_destruct(struct connman_provider *provider)
+{
+       DBG("provider %p", provider);
+
+       g_free(provider->name);
+       g_free(provider->type);
+       g_free(provider->host);
+       g_free(provider->domain);
+       g_free(provider->identifier);
+       g_strfreev(provider->user_networks);
+       g_hash_table_destroy(provider->routes);
+       g_hash_table_destroy(provider->user_routes);
+       g_hash_table_destroy(provider->setting_strings);
+       g_free(provider);
+}
+
+void connman_provider_unref_debug(struct connman_provider *provider,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&provider->refcount, 1) != 1)
+               return;
+
+       provider_remove(provider);
+
+       provider_destruct(provider);
+}
+
+static int provider_indicate_state(struct connman_provider *provider,
+                                       enum connman_service_state state)
+{
+       DBG("state %d", state);
+
+       __connman_service_ipconfig_indicate_state(provider->vpn_service, state,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       return __connman_service_ipconfig_indicate_state(provider->vpn_service,
+                                       state, CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
+int __connman_provider_disconnect(struct connman_provider *provider)
+{
+       int err;
+
+       DBG("provider %p", provider);
+
+       if (provider->driver != NULL && provider->driver->disconnect != NULL)
+               err = provider->driver->disconnect(provider);
+       else
+               return -EOPNOTSUPP;
+
+       if (provider->vpn_service != NULL)
+               provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT);
+
+       if (err < 0) {
+               if (err != -EINPROGRESS)
+                       return err;
+
+               return -EINPROGRESS;
+       }
+
+       return 0;
+}
+
+int __connman_provider_connect(struct connman_provider *provider)
+{
+       int err;
+
+       DBG("provider %p", provider);
+
+       if (provider->driver != NULL && provider->driver->connect != NULL)
+               err = provider->driver->connect(provider);
+       else
+               return -EOPNOTSUPP;
+
+       if (err < 0) {
+               if (err != -EINPROGRESS)
+                       return err;
+
+               provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION);
+
+               return -EINPROGRESS;
+       }
+
+       return 0;
+}
+
+int __connman_provider_remove(const char *path)
+{
+       struct connman_provider *provider;
+       GHashTableIter iter;
+       gpointer value, key;
+
+       DBG("path %s", path);
+
+       g_hash_table_iter_init(&iter, provider_hash);
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               const char *srv_path;
+               provider = value;
+
+               if (provider->vpn_service == NULL)
+                       continue;
+
+               srv_path = __connman_service_get_path(provider->vpn_service);
+
+               if (g_strcmp0(srv_path, path) == 0) {
+                       DBG("Removing VPN %s", provider->identifier);
+
+                       provider_unregister(provider);
+                       g_hash_table_remove(provider_hash,
+                                               provider->identifier);
+                       return 0;
+               }
+       }
+
+       return -ENXIO;
+}
+
+static void provider_append_routes(gpointer key, gpointer value,
+                                       gpointer user_data)
+{
+       struct connman_route *route = value;
+       struct connman_provider *provider = user_data;
+       int index = provider->index;
+
+       if (route->family == AF_INET6) {
+               unsigned char prefix_len = atoi(route->netmask);
+
+               connman_inet_add_ipv6_network_route(index, route->host,
+                                                       route->gateway,
+                                                       prefix_len);
+       } else {
+               connman_inet_add_network_route(index, route->host,
+                                               route->gateway,
+                                               route->netmask);
+       }
+}
+
+static int set_connected(struct connman_provider *provider,
+                                       connman_bool_t connected)
+{
+       struct connman_service *service = provider->vpn_service;
+       struct connman_ipconfig *ipconfig;
+
+       if (service == NULL)
+               return -ENODEV;
+
+       ipconfig = __connman_service_get_ipconfig(service, provider->family);
+
+       if (connected == TRUE) {
+               if (ipconfig == NULL) {
+                       provider_indicate_state(provider,
+                                               CONNMAN_SERVICE_STATE_FAILURE);
+                       return -EIO;
+               }
+
+               __connman_ipconfig_address_add(ipconfig);
+               __connman_ipconfig_gateway_add(ipconfig);
+
+               provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_READY);
+
+               g_hash_table_foreach(provider->routes, provider_append_routes,
+                                       provider);
+
+               g_hash_table_foreach(provider->user_routes, provider_append_routes,
+                                       provider);
+
+       } else {
+               if (ipconfig != NULL) {
+                       provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT);
+                       __connman_ipconfig_gateway_remove(ipconfig);
+               }
+
+               provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_IDLE);
+       }
+
+       return 0;
+}
+
+int connman_provider_set_state(struct connman_provider *provider,
+                                       enum connman_provider_state state)
+{
+       if (provider == NULL || provider->vpn_service == NULL)
+               return -EINVAL;
+
+       switch (state) {
+       case CONNMAN_PROVIDER_STATE_UNKNOWN:
+               return -EINVAL;
+       case CONNMAN_PROVIDER_STATE_IDLE:
+               return set_connected(provider, FALSE);
+       case CONNMAN_PROVIDER_STATE_CONNECT:
+               return provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION);
+       case CONNMAN_PROVIDER_STATE_READY:
+               return set_connected(provider, TRUE);
+       case CONNMAN_PROVIDER_STATE_DISCONNECT:
+               return provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT);
+       case CONNMAN_PROVIDER_STATE_FAILURE:
+               return provider_indicate_state(provider,
+                                       CONNMAN_SERVICE_STATE_FAILURE);
+       }
+
+       return -EINVAL;
+}
+
+int connman_provider_indicate_error(struct connman_provider *provider,
+                                       enum connman_provider_error error)
+{
+       enum connman_service_error service_error;
+
+       switch (error) {
+       case CONNMAN_PROVIDER_ERROR_LOGIN_FAILED:
+               service_error = CONNMAN_SERVICE_ERROR_LOGIN_FAILED;
+               break;
+       case CONNMAN_PROVIDER_ERROR_AUTH_FAILED:
+               service_error = CONNMAN_SERVICE_ERROR_AUTH_FAILED;
+               break;
+       case CONNMAN_PROVIDER_ERROR_CONNECT_FAILED:
+               service_error = CONNMAN_SERVICE_ERROR_CONNECT_FAILED;
+               break;
+       default:
+               service_error = CONNMAN_SERVICE_ERROR_UNKNOWN;
+               break;
+       }
+
+       return __connman_service_indicate_error(provider->vpn_service,
+                                                       service_error);
+}
+
+static void unregister_provider(gpointer data)
+{
+       struct connman_provider *provider = data;
+
+       DBG("provider %p service %p", provider, provider->vpn_service);
+
+       if (provider->vpn_service != NULL) {
+               connman_service_unref(provider->vpn_service);
+               provider->vpn_service = NULL;
+       }
+
+       connman_provider_unref(provider);
+}
+
+static void destroy_route(gpointer user_data)
+{
+       struct connman_route *route = user_data;
+
+       g_free(route->host);
+       g_free(route->netmask);
+       g_free(route->gateway);
+       g_free(route);
+}
+
+static void provider_initialize(struct connman_provider *provider)
+{
+       DBG("provider %p", provider);
+
+       provider->index = 0;
+       provider->name = NULL;
+       provider->type = NULL;
+       provider->domain = NULL;
+       provider->identifier = NULL;
+       provider->user_networks = NULL;
+       provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, destroy_route);
+       provider->user_routes = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       g_free, destroy_route);
+       provider->setting_strings = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+}
+
+static struct connman_provider *connman_provider_new(void)
+{
+       struct connman_provider *provider;
+
+       provider = g_try_new0(struct connman_provider, 1);
+       if (provider == NULL)
+               return NULL;
+
+       provider->refcount = 1;
+
+       DBG("provider %p", provider);
+       provider_initialize(provider);
+
+       return provider;
+}
+
+static struct connman_provider *connman_provider_get(const char *identifier)
+{
+       struct connman_provider *provider;
+
+       provider = g_hash_table_lookup(provider_hash, identifier);
+       if (provider != NULL)
+               return provider;
+
+       provider = connman_provider_new();
+       if (provider == NULL)
+               return NULL;
+
+       DBG("provider %p", provider);
+
+       provider->identifier = g_strdup(identifier);
+
+       g_hash_table_insert(provider_hash, provider->identifier, provider);
+
+       return provider;
+}
+
+static void provider_dbus_ident(char *ident)
+{
+       int i, len = strlen(ident);
+
+       for (i = 0; i < len; i++) {
+               if (ident[i] >= '0' && ident[i] <= '9')
+                       continue;
+               if (ident[i] >= 'a' && ident[i] <= 'z')
+                       continue;
+               if (ident[i] >= 'A' && ident[i] <= 'Z')
+                       continue;
+               ident[i] = '_';
+       }
+}
+
+static struct connman_provider *provider_create_from_keyfile(GKeyFile *keyfile,
+               const char *ident)
+{
+       struct connman_provider *provider;
+
+       if (keyfile == NULL || ident == NULL)
+               return NULL;
+
+       provider = connman_provider_lookup(ident);
+       if (provider == NULL) {
+               provider = connman_provider_get(ident);
+               if (provider == NULL) {
+                       DBG("can not create provider");
+                       return NULL;
+               }
+
+               provider_load_from_keyfile(provider, keyfile);
+
+               if (provider->name == NULL || provider->host == NULL ||
+                               provider->domain == NULL) {
+                       DBG("cannot get name, host or domain");
+                       connman_provider_unref(provider);
+                       return NULL;
+               }
+
+               provider_register(provider);
+       }
+       return provider;
+}
+
+static int provider_create_service(struct connman_provider *provider)
+{
+       if (provider->vpn_service != NULL)
+               return -EALREADY;
+
+       provider->vpn_service =
+               __connman_service_create_from_provider(provider);
+
+       if (provider->vpn_service == NULL)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+static void provider_create_all_from_type(const char *provider_type)
+{
+       unsigned int i;
+       char **providers;
+       char *id, *type;
+       GKeyFile *keyfile;
+       struct connman_provider *provider;
+
+       DBG("provider type %s", provider_type);
+
+       providers = __connman_storage_get_providers();
+
+       for (i = 0; providers[i] != NULL; i+=1) {
+
+               if (strncmp(providers[i], "provider_", 9) != 0)
+                       continue;
+
+               id = providers[i] + 9;
+               keyfile = __connman_storage_load_provider(id);
+
+               if (keyfile == NULL)
+                       continue;
+
+               type = g_key_file_get_string(keyfile, id, "Type", NULL);
+
+               DBG("keyfile %p id %s type %s", keyfile, id, type);
+
+               if (strcmp(provider_type, type) != 0) {
+                       g_free(type);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               provider = provider_create_from_keyfile(keyfile, id);
+               if (provider != NULL) {
+                       if (provider_create_service(provider) == -EOPNOTSUPP) {
+                               DBG("could not create service");
+                               connman_provider_unref(provider);
+                       }
+               }
+
+               g_free(type);
+               g_key_file_free(keyfile);
+       }
+       g_strfreev(providers);
+}
+
+static char **get_user_networks(DBusMessageIter *array, int *count)
+{
+       DBusMessageIter entry;
+       char **networks = NULL;
+       GSList *list = NULL, *l;
+       int len;
+
+       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);
+       }
+
+       len = g_slist_length(list);
+       if (len == 0)
+               goto out;
+
+       networks = g_try_new(char *, len + 1);
+       if (networks == NULL)
+               goto out;
+
+       *count = len;
+       networks[len] = 0;
+
+       for (l = list; l != NULL; l = g_slist_next(l))
+               networks[--len] = l->data;
+
+out:
+       g_slist_free(list);
+
+       return networks;
+}
+
+int __connman_provider_create_and_connect(DBusMessage *msg)
+{
+       struct connman_provider *provider;
+       DBusMessageIter iter, array;
+       const char *type = NULL, *name = NULL, *service_path;
+       const char *host = NULL, *domain = NULL;
+       char **networks = NULL;
+       char *ident;
+       int err, count = 0;
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_STRING:
+                       if (g_str_equal(key, "Type") == TRUE)
+                               dbus_message_iter_get_basic(&value, &type);
+                       else if (g_str_equal(key, "Name") == TRUE)
+                               dbus_message_iter_get_basic(&value, &name);
+                       else if (g_str_equal(key, "Host") == TRUE)
+                               dbus_message_iter_get_basic(&value, &host);
+                       else if (g_str_equal(key, "VPN.Domain") == TRUE)
+                               dbus_message_iter_get_basic(&value, &domain);
+                       break;
+               case DBUS_TYPE_ARRAY:
+                       if (g_str_equal(key, "Networks") == TRUE)
+                               networks = get_user_networks(&value, &count);
+                       break;
+               }
+
+               dbus_message_iter_next(&array);
+       }
+
+       if (host == NULL || domain == NULL)
+               return -EINVAL;
+
+       DBG("Type %s name %s networks %p", type, name, networks);
+
+       if (type == NULL || name == NULL)
+               return -EOPNOTSUPP;
+
+       ident = g_strdup_printf("%s_%s", host, domain);
+       provider_dbus_ident(ident);
+
+       DBG("ident %s", ident);
+
+       provider = connman_provider_lookup(ident);
+       if (provider == NULL) {
+               provider = connman_provider_get(ident);
+               if (provider == NULL) {
+                       DBG("can not create provider");
+                       g_free(ident);
+                       return -EOPNOTSUPP;
+               }
+
+               provider->host = g_strdup(host);
+               provider->domain = g_strdup(domain);
+               provider->name = g_strdup(name);
+               provider->type = g_strdup(type);
+
+               if (provider_register(provider) == 0)
+                       connman_provider_load(provider);
+       }
+
+       if (networks != NULL) {
+               g_strfreev(provider->user_networks);
+               provider->user_networks = networks;
+               provider->num_user_networks = count;
+               set_user_networks(provider, provider->user_networks);
+       }
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *str;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_STRING:
+                       dbus_message_iter_get_basic(&value, &str);
+                       connman_provider_set_string(provider, key, str);
+                       break;
+               }
+
+               dbus_message_iter_next(&array);
+       }
+
+       g_free(ident);
+
+       err = provider_create_service(provider);
+       if (err == -EALREADY) {
+               DBG("provider already connected");
+       } else {
+               if (err == -EOPNOTSUPP) {
+                       goto unref;
+               } else {
+                       err = __connman_service_connect(provider->vpn_service);
+
+                       if (err < 0 && err != -EINPROGRESS)
+                               goto failed;
+               }
+       }
+
+       connman_provider_save(provider);
+       service_path = __connman_service_get_path(provider->vpn_service);
+       g_dbus_send_reply(connection, msg,
+                               DBUS_TYPE_OBJECT_PATH, &service_path,
+                                                       DBUS_TYPE_INVALID);
+       return 0;
+
+failed:
+       connman_service_unref(provider->vpn_service);
+       provider->vpn_service = NULL;
+
+unref:
+       DBG("can not connect, delete provider");
+
+       g_hash_table_remove(provider_hash, provider->identifier);
+
+       return err;
+}
+
+const char * __connman_provider_get_ident(struct connman_provider *provider)
+{
+       if (provider == NULL)
+               return NULL;
+
+       return provider->identifier;
+}
+
+int connman_provider_set_string(struct connman_provider *provider,
+                                       const char *key, const char *value)
+{
+       DBG("provider %p key %s value %s", provider, key, value);
+
+       if (g_str_equal(key, "Type") == TRUE) {
+               g_free(provider->type);
+               provider->type = g_strdup(value);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               g_free(provider->name);
+               provider->name = g_strdup(value);
+       } else if (g_str_equal(key, "Host") == TRUE) {
+               g_free(provider->host);
+               provider->host = g_strdup(value);
+       } else if (g_str_equal(key, "VPN.Domain") == TRUE) {
+               g_free(provider->domain);
+               provider->domain = g_strdup(value);
+       } else
+               g_hash_table_replace(provider->setting_strings,
+                               g_strdup(key), g_strdup(value));
+       return 0;
+}
+
+const char *connman_provider_get_string(struct connman_provider *provider,
+                                                       const char *key)
+{
+       DBG("provider %p key %s", provider, key);
+
+       if (g_str_equal(key, "Type") == TRUE)
+               return provider->type;
+       else if (g_str_equal(key, "Name") == TRUE)
+               return provider->name;
+       else if (g_str_equal(key, "Host") == TRUE)
+               return provider->host;
+       else if (g_str_equal(key, "VPN.Domain") == TRUE)
+               return provider->domain;
+
+       return g_hash_table_lookup(provider->setting_strings, key);
+}
+
+connman_bool_t
+__connman_provider_check_routes(struct connman_provider *provider)
+{
+       if (provider == NULL)
+               return FALSE;
+
+       if (provider->user_routes != NULL &&
+                       g_hash_table_size(provider->user_routes) > 0)
+               return TRUE;
+
+       if (provider->routes != NULL &&
+                       g_hash_table_size(provider->routes) > 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+void *connman_provider_get_data(struct connman_provider *provider)
+{
+       return provider->driver_data;
+}
+
+void connman_provider_set_data(struct connman_provider *provider, void *data)
+{
+       provider->driver_data = data;
+}
+
+void connman_provider_set_index(struct connman_provider *provider, int index)
+{
+       struct connman_service *service = provider->vpn_service;
+       struct connman_ipconfig *ipconfig;
+
+       DBG("");
+
+       if (service == NULL)
+               return;
+
+       ipconfig = __connman_service_get_ip4config(service);
+
+       if (ipconfig == NULL) {
+               __connman_service_create_ip4config(service, index);
+
+               ipconfig = __connman_service_get_ip4config(service);
+               if (ipconfig == NULL) {
+                       DBG("Couldnt create ipconfig");
+                       goto done;
+               }
+       }
+
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
+       __connman_ipconfig_set_index(ipconfig, index);
+
+
+       ipconfig = __connman_service_get_ip6config(service);
+
+       if (ipconfig == NULL) {
+               __connman_service_create_ip6config(service, index);
+
+               ipconfig = __connman_service_get_ip6config(service);
+               if (ipconfig == NULL) {
+                       DBG("Couldnt create ipconfig for IPv6");
+                       goto done;
+               }
+       }
+
+       __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
+       __connman_ipconfig_set_index(ipconfig, index);
+
+done:
+       provider->index = index;
+}
+
+int connman_provider_get_index(struct connman_provider *provider)
+{
+       return provider->index;
+}
+
+int connman_provider_set_ipaddress(struct connman_provider *provider,
+                                       struct connman_ipaddress *ipaddress)
+{
+       struct connman_ipconfig *ipconfig = NULL;
+
+       ipconfig = __connman_service_get_ipconfig(provider->vpn_service,
+                                                       ipaddress->family);
+       if (ipconfig == NULL)
+               return -EINVAL;
+
+       provider->family = ipaddress->family;
+
+       __connman_ipconfig_set_local(ipconfig, ipaddress->local);
+       __connman_ipconfig_set_peer(ipconfig, ipaddress->peer);
+       __connman_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
+       __connman_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
+       __connman_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
+
+       return 0;
+}
+
+int connman_provider_set_pac(struct connman_provider *provider, const char *pac)
+{
+       DBG("provider %p pac %s", provider, pac);
+
+       __connman_service_set_pac(provider->vpn_service, pac);
+
+       return 0;
+}
+
+
+int connman_provider_set_domain(struct connman_provider *provider,
+                                       const char *domain)
+{
+       DBG("provider %p domain %s", provider, domain);
+
+       g_free(provider->domain);
+       provider->domain = g_strdup(domain);
+
+       __connman_service_set_domainname(provider->vpn_service, domain);
+
+       return 0;
+}
+
+int connman_provider_set_nameservers(struct connman_provider *provider,
+                                       const char *nameservers)
+{
+       int i;
+       char **nameservers_array = NULL;
+
+       DBG("provider %p nameservers %s", provider, nameservers);
+
+       __connman_service_nameserver_clear(provider->vpn_service);
+
+       if (nameservers == NULL)
+               return 0;
+
+       nameservers_array = g_strsplit(nameservers, " ", 0);
+
+       for (i = 0; nameservers_array[i] != NULL; i++) {
+               __connman_service_nameserver_append(provider->vpn_service,
+                                               nameservers_array[i], FALSE);
+       }
+
+       g_strfreev(nameservers_array);
+
+       return 0;
+}
+
+enum provider_route_type {
+       PROVIDER_ROUTE_TYPE_NONE = 0,
+       PROVIDER_ROUTE_TYPE_MASK = 1,
+       PROVIDER_ROUTE_TYPE_ADDR = 2,
+       PROVIDER_ROUTE_TYPE_GW   = 3,
+};
+
+static int route_env_parse(struct connman_provider *provider, const char *key,
+                               int *family, unsigned long *idx,
+                               enum provider_route_type *type)
+{
+       char *end;
+       const char *start;
+
+       DBG("name %s", provider->name);
+
+       if (!strcmp(provider->type, "openvpn")) {
+               if (g_str_has_prefix(key, "route_network_") == TRUE) {
+                       start = key + strlen("route_network_");
+                       *type = PROVIDER_ROUTE_TYPE_ADDR;
+               } else if (g_str_has_prefix(key, "route_netmask_") == TRUE) {
+                       start = key + strlen("route_netmask_");
+                       *type = PROVIDER_ROUTE_TYPE_MASK;
+               } else if (g_str_has_prefix(key, "route_gateway_") == TRUE) {
+                       start = key + strlen("route_gateway_");
+                       *type = PROVIDER_ROUTE_TYPE_GW;
+               } else
+                       return -EINVAL;
+
+               *family = AF_INET;
+               *idx = g_ascii_strtoull(start, &end, 10);
+
+       } else if (!strcmp(provider->type, "openconnect")) {
+               if (g_str_has_prefix(key, "CISCO_SPLIT_INC_") == TRUE) {
+                       *family = AF_INET;
+                       start = key + strlen("CISCO_SPLIT_INC_");
+               } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_") == TRUE) {
+                       *family = AF_INET6;
+                       start = key + strlen("CISCO_IPV6_SPLIT_INC_");
+               } else
+                       return -EINVAL;
+
+               *idx = g_ascii_strtoull(start, &end, 10);
+
+               if (strncmp(end, "_ADDR", 5) == 0)
+                       *type = PROVIDER_ROUTE_TYPE_ADDR;
+               else if (strncmp(end, "_MASK", 5) == 0)
+                       *type = PROVIDER_ROUTE_TYPE_MASK;
+               else if (strncmp(end, "_MASKLEN", 8) == 0 &&
+                               *family == AF_INET6) {
+                       *type = PROVIDER_ROUTE_TYPE_MASK;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+int connman_provider_append_route(struct connman_provider *provider,
+                                       const char *key, const char *value)
+{
+       struct connman_route *route;
+       int ret, family = 0;
+       unsigned long idx = 0;
+       enum provider_route_type type = PROVIDER_ROUTE_TYPE_NONE;
+
+       DBG("key %s value %s", key, value);
+
+       ret = route_env_parse(provider, key, &family, &idx, &type);
+       if (ret < 0)
+               return ret;
+
+       DBG("idx %lu family %d type %d", idx, family, type);
+
+       route = g_hash_table_lookup(provider->routes, GINT_TO_POINTER(idx));
+       if (route == NULL) {
+               route = g_try_new0(struct connman_route, 1);
+               if (route == NULL) {
+                       connman_error("out of memory");
+                       return -ENOMEM;
+               }
+
+               route->family = family;
+
+               g_hash_table_replace(provider->routes, GINT_TO_POINTER(idx),
+                                               route);
+       }
+
+       switch (type) {
+       case PROVIDER_ROUTE_TYPE_NONE:
+               break;
+       case PROVIDER_ROUTE_TYPE_MASK:
+               route->netmask = g_strdup(value);
+               break;
+       case PROVIDER_ROUTE_TYPE_ADDR:
+               route->host = g_strdup(value);
+               break;
+       case PROVIDER_ROUTE_TYPE_GW:
+               route->gateway = g_strdup(value);
+               break;
+       }
+
+       return 0;
+}
+
+const char *connman_provider_get_driver_name(struct connman_provider *provider)
+{
+       if (provider->driver == NULL)
+               return NULL;
+
+       return provider->driver->name;
+}
+
+const char *connman_provider_get_save_group(struct connman_provider *provider)
+{
+       return provider->identifier;
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       return 0;
+}
+
+static void clean_provider(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_provider *provider = value;
+
+       if (provider->driver != NULL && provider->driver->remove)
+               provider->driver->remove(provider);
+}
+
+int connman_provider_driver_register(struct connman_provider_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+       provider_create_all_from_type(driver->name);
+       return 0;
+}
+
+void connman_provider_driver_unregister(struct connman_provider_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+static void provider_remove_all(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       struct connman_provider *provider = value;
+
+       __connman_provider_remove(provider->identifier);
+}
+
+static void provider_offline_mode(connman_bool_t enabled)
+{
+       DBG("enabled %d", enabled);
+
+       if (enabled == TRUE)
+               g_hash_table_foreach(provider_hash, provider_remove_all, NULL);
+
+}
+
+static struct connman_provider *provider_get(int index)
+{
+       GHashTableIter iter;
+       gpointer value, key;
+
+       g_hash_table_iter_init(&iter, provider_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_provider *provider = value;
+
+               if (provider->index == index)
+                       return provider;
+       }
+
+       return NULL;
+}
+
+static void provider_service_changed(struct connman_service *service,
+                               enum connman_service_state state)
+{
+       struct connman_provider *provider;
+       int vpn_index, service_index;
+
+       if (service == NULL)
+               return;
+
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       }
+
+       service_index = __connman_service_get_index(service);
+
+       vpn_index = __connman_connection_get_vpn_index(service_index);
+
+       DBG("service %p %s state %d index %d/%d", service,
+               __connman_service_get_ident(service),
+               state, service_index, vpn_index);
+
+       if (vpn_index < 0)
+               return;
+
+       provider = provider_get(vpn_index);
+       if (provider == NULL)
+               return;
+
+       DBG("disconnect %p index %d", provider, vpn_index);
+
+       __connman_provider_disconnect(provider);
+
+       return;
+}
+
+static struct connman_notifier provider_notifier = {
+       .name                   = "provider",
+       .offline_mode           = provider_offline_mode,
+       .service_state_changed  = provider_service_changed,
+};
+
+int __connman_provider_init(void)
+{
+       int err;
+
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, unregister_provider);
+
+       err = connman_notifier_register(&provider_notifier);
+       if (err < 0) {
+               g_hash_table_destroy(provider_hash);
+               dbus_connection_unref(connection);
+       }
+
+       return err;
+}
+
+void __connman_provider_cleanup(void)
+{
+       DBG("");
+
+       connman_notifier_unregister(&provider_notifier);
+
+       g_hash_table_foreach(provider_hash, clean_provider, NULL);
+
+       g_hash_table_destroy(provider_hash);
+       provider_hash = NULL;
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/proxy.c b/src/proxy.c
new file mode 100644 (file)
index 0000000..fd58a70
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "connman.h"
+
+static unsigned int next_lookup_token = 1;
+
+static GSList *driver_list = NULL;
+static GSList *lookup_list = NULL;
+
+struct proxy_lookup {
+       unsigned int token;
+       connman_proxy_lookup_cb cb;
+       void *user_data;
+       struct connman_service *service;
+       char *url;
+       guint watch;
+       struct connman_proxy_driver *proxy;
+};
+
+static void remove_lookup(struct proxy_lookup *lookup)
+{
+       lookup_list = g_slist_remove(lookup_list, lookup);
+
+       connman_service_unref(lookup->service);
+       g_free(lookup->url);
+       g_free(lookup);
+}
+
+static void remove_lookups(GSList *lookups)
+{
+       GSList *list;
+
+       for (list = lookups; list; list = list->next) {
+               struct proxy_lookup *lookup = list->data;
+
+               remove_lookup(lookup);
+       }
+
+       g_slist_free(lookups);
+}
+
+static gboolean lookup_callback(gpointer user_data)
+{
+       struct proxy_lookup *lookup = user_data;
+       GSList *list;
+
+       if (lookup == NULL)
+               return FALSE;
+
+       lookup->watch = 0;
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_proxy_driver *proxy = list->data;
+
+               if (proxy->request_lookup == NULL)
+                       continue;
+
+               lookup->proxy = proxy;
+               break;
+       }
+
+       if (lookup->proxy == NULL ||
+               lookup->proxy->request_lookup(lookup->service,
+                                               lookup->url) < 0) {
+
+               if (lookup->cb)
+                       lookup->cb(NULL, lookup->user_data);
+
+               remove_lookup(lookup);
+       }
+
+       return FALSE;
+}
+
+unsigned int connman_proxy_lookup(const char *interface, const char *url,
+                                       struct connman_service *service,
+                                       connman_proxy_lookup_cb cb,
+                                       void *user_data)
+{
+       struct proxy_lookup *lookup;
+
+       DBG("interface %s url %s", interface, url);
+
+       if (interface == NULL)
+               return 0;
+
+       lookup = g_try_new0(struct proxy_lookup, 1);
+       if (lookup == NULL)
+               return 0;
+
+       lookup->token = next_lookup_token++;
+
+       lookup->cb = cb;
+       lookup->user_data = user_data;
+       lookup->url = g_strdup(url);
+       lookup->service = connman_service_ref(service);
+
+       lookup->watch = g_timeout_add_seconds(0, lookup_callback, lookup);
+       if (lookup->watch == 0) {
+               g_free(lookup->url);
+               g_free(lookup);
+               return 0;
+       }
+
+       DBG("token %u", lookup->token);
+       lookup_list = g_slist_append(lookup_list, lookup);
+
+       return lookup->token;
+}
+
+void connman_proxy_lookup_cancel(unsigned int token)
+{
+       GSList *list;
+       struct proxy_lookup *lookup = NULL;
+
+       DBG("token %u", token);
+
+       for (list = lookup_list; list; list = list->next) {
+               lookup = list->data;
+
+               if (lookup->token == token)
+                       break;
+       }
+
+       if (lookup != NULL) {
+               if (lookup->watch > 0) {
+                       g_source_remove(lookup->watch);
+                       lookup->watch = 0;
+               }
+
+               if (lookup->proxy != NULL &&
+                                       lookup->proxy->cancel_lookup != NULL)
+                       lookup->proxy->cancel_lookup(lookup->service,
+                                                               lookup->url);
+
+               remove_lookup(lookup);
+       }
+}
+
+void connman_proxy_driver_lookup_notify(struct connman_service *service,
+                                        const char *url, const char *result)
+{
+       GSList *list, *matches = NULL;
+
+       DBG("service %p url %s result %s", service, url, result);
+
+       for (list = lookup_list; list; list = list->next) {
+               struct proxy_lookup *lookup = list->data;
+
+               if (service != lookup->service)
+                       continue;
+
+               if (g_strcmp0(lookup->url, url) == 0) {
+                       if (lookup->cb)
+                               lookup->cb(result, lookup->user_data);
+
+                       matches = g_slist_append(matches, lookup);
+               }
+       }
+
+       if (matches != NULL)
+               remove_lookups(matches);
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_proxy_driver *driver1 = a;
+       const struct connman_proxy_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_proxy_driver_register:
+ * @driver: Proxy driver definition
+ *
+ * Register a new proxy driver
+ *
+ * Returns: %0 on success
+ */
+int connman_proxy_driver_register(struct connman_proxy_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_proxy_driver_unregister:
+ * @driver: Proxy driver definition
+ *
+ * Remove a previously registered proxy driver
+ */
+void connman_proxy_driver_unregister(struct connman_proxy_driver *driver)
+{
+       GSList *list;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       for (list = lookup_list; list; list = list->next) {
+               struct proxy_lookup *lookup = list->data;
+
+               if (lookup->proxy == driver)
+                       lookup->proxy = NULL;
+       }
+
+}
+
+int __connman_proxy_init(void)
+{
+       DBG("");
+
+       return 0;
+}
+
+void __connman_proxy_cleanup(void)
+{
+       DBG("");
+
+       remove_lookups(lookup_list);
+}
diff --git a/src/resolver.c b/src/resolver.c
new file mode 100644 (file)
index 0000000..47e4103
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <resolv.h>
+
+#include "connman.h"
+
+#define RESOLVER_FLAG_PUBLIC (1 << 0)
+
+/*
+ * Threshold for RDNSS lifetime. Will be used to trigger RS
+ * before RDNSS entries actually expire
+ */
+#define RESOLVER_LIFETIME_REFRESH_THRESHOLD 0.8
+
+struct entry_data {
+       char *interface;
+       char *domain;
+       char *server;
+       unsigned int flags;
+       unsigned int lifetime;
+       guint timeout;
+};
+
+static GSList *entry_list = NULL;
+static connman_bool_t dnsproxy_enabled = FALSE;
+
+struct resolvfile_entry {
+       char *interface;
+       char *domain;
+       char *server;
+};
+
+static GList *resolvfile_list = NULL;
+
+static void resolvfile_remove_entries(GList *entries)
+{
+       GList *list;
+
+       for (list = entries; list; list = list->next) {
+               struct resolvfile_entry *entry = list->data;
+
+               resolvfile_list = g_list_remove(resolvfile_list, entry);
+
+               g_free(entry->server);
+               g_free(entry->domain);
+               g_free(entry->interface);
+               g_free(entry);
+       }
+
+       g_list_free(entries);
+}
+
+static int resolvfile_export(void)
+{
+       GList *list;
+       GString *content;
+       int fd, err;
+       unsigned int count;
+       mode_t old_umask;
+
+       content = g_string_new("# Generated by Connection Manager\n");
+
+       /*
+        * Domains and nameservers are added in reverse so that the most
+        * recently appended entry is the primary one. No more than
+        * MAXDNSRCH/MAXNS entries are used.
+        */
+
+       for (count = 0, list = g_list_last(resolvfile_list);
+                                               list && (count < MAXDNSRCH);
+                                               list = g_list_previous(list)) {
+               struct resolvfile_entry *entry = list->data;
+
+               if (!entry->domain)
+                       continue;
+
+               if (count == 0)
+                       g_string_append_printf(content, "search ");
+
+               g_string_append_printf(content, "%s ", entry->domain);
+               count++;
+       }
+
+       if (count)
+               g_string_append_printf(content, "\n");
+
+       for (count = 0, list = g_list_last(resolvfile_list);
+                                               list && (count < MAXNS);
+                                               list = g_list_previous(list)) {
+               struct resolvfile_entry *entry = list->data;
+
+               if (!entry->server)
+                       continue;
+
+               g_string_append_printf(content, "nameserver %s\n",
+                                                               entry->server);
+               count++;
+       }
+
+       old_umask = umask(022);
+
+       fd = open(SYSCONFDIR "/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
+                                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       if (fd < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ftruncate(fd, 0) < 0) {
+               err = -errno;
+               goto failed;
+       }
+
+       err = 0;
+
+       if (write(fd, content->str, content->len) < 0)
+               err = -errno;
+
+failed:
+       close(fd);
+
+done:
+       g_string_free(content, TRUE);
+       umask(old_umask);
+
+       return err;
+}
+
+int __connman_resolvfile_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       struct resolvfile_entry *entry;
+
+       DBG("interface %s server %s", interface, server);
+
+       if (interface == NULL)
+               return -ENOENT;
+
+       entry = g_try_new0(struct resolvfile_entry, 1);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       entry->interface = g_strdup(interface);
+       entry->domain = g_strdup(domain);
+       entry->server = g_strdup(server);
+
+       resolvfile_list = g_list_append(resolvfile_list, entry);
+
+       return resolvfile_export();
+}
+
+int __connman_resolvfile_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       GList *list, *matches = NULL;
+
+       DBG("interface %s server %s", interface, server);
+
+       for (list = resolvfile_list; list; list = g_list_next(list)) {
+               struct resolvfile_entry *entry = list->data;
+
+               if (interface != NULL &&
+                               g_strcmp0(entry->interface, interface) != 0)
+                       continue;
+
+               if (domain != NULL && g_strcmp0(entry->domain, domain) != 0)
+                       continue;
+
+               if (g_strcmp0(entry->server, server) != 0)
+                       continue;
+
+               matches = g_list_append(matches, entry);
+       }
+
+       resolvfile_remove_entries(matches);
+
+       return resolvfile_export();
+}
+
+static void remove_entries(GSList *entries)
+{
+       GSList *list;
+
+       for (list = entries; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               entry_list = g_slist_remove(entry_list, entry);
+
+               if (dnsproxy_enabled == TRUE) {
+                       __connman_dnsproxy_remove(entry->interface, entry->domain,
+                                                       entry->server);
+               } else {
+                       __connman_resolvfile_remove(entry->interface, entry->domain,
+                                                       entry->server);
+               }
+
+               if (entry->timeout)
+                       g_source_remove(entry->timeout);
+               g_free(entry->server);
+               g_free(entry->domain);
+               g_free(entry->interface);
+               g_free(entry);
+       }
+
+       g_slist_free(entries);
+}
+
+static gboolean resolver_expire_cb(gpointer user_data)
+{
+       struct entry_data *entry = user_data;
+       GSList *list;
+       int index;
+
+       DBG("interface %s domain %s server %s",
+                       entry->interface, entry->domain, entry->server);
+
+       list = g_slist_append(NULL, entry);
+
+       index = connman_inet_ifindex(entry->interface);
+       if (index >= 0) {
+               struct connman_service *service;
+               service = __connman_service_lookup_from_index(index);
+               if (service != NULL)
+                       __connman_service_nameserver_remove(service,
+                                                       entry->server, TRUE);
+       }
+
+       remove_entries(list);
+
+       return FALSE;
+}
+
+static gboolean resolver_refresh_cb(gpointer user_data)
+{
+       struct entry_data *entry = user_data;
+       int index;
+       unsigned int interval;
+       struct connman_service *service = NULL;
+
+       /* Round up what we have left from lifetime */
+       interval = entry->lifetime *
+               (1 - RESOLVER_LIFETIME_REFRESH_THRESHOLD) + 1.0;
+
+       DBG("RDNSS start interface %s domain %s "
+                       "server %s remaining lifetime %d",
+                       entry->interface, entry->domain,
+                       entry->server, interval);
+
+       entry->timeout = g_timeout_add_seconds(interval,
+                       resolver_expire_cb, entry);
+
+       index = connman_inet_ifindex(entry->interface);
+       if (index >= 0) {
+               service = __connman_service_lookup_from_index(index);
+               if (service != NULL) {
+                       /*
+                        * Send Router Solicitation to refresh RDNSS entries
+                        * before their lifetime expires
+                        */
+                       __connman_refresh_rs_ipv6(
+                                       __connman_service_get_network(service),
+                                       index);
+               }
+       }
+       return FALSE;
+}
+
+static int append_resolver(const char *interface, const char *domain,
+                               const char *server, unsigned int lifetime,
+                                                       unsigned int flags)
+{
+       struct entry_data *entry;
+       unsigned int interval;
+
+       DBG("interface %s domain %s server %s lifetime %d flags %d",
+                               interface, domain, server, lifetime, flags);
+
+       if (server == NULL && domain == NULL)
+               return -EINVAL;
+
+       entry = g_try_new0(struct entry_data, 1);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       entry->interface = g_strdup(interface);
+       entry->domain = g_strdup(domain);
+       entry->server = g_strdup(server);
+       entry->flags = flags;
+       entry->lifetime = lifetime;
+       if (lifetime) {
+               int index;
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start interface %s domain %s "
+                               "server %s lifetime threshold %d",
+                               interface, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
+
+               /*
+                * We update the service only for those nameservers
+                * that are automagically added via netlink (lifetime > 0)
+                */
+               index = connman_inet_ifindex(interface);
+               if (server != NULL && index >= 0) {
+                       struct connman_service *service;
+                       service = __connman_service_lookup_from_index(index);
+                       if (service != NULL)
+                               __connman_service_nameserver_append(service,
+                                                               server, TRUE);
+               }
+       }
+       entry_list = g_slist_append(entry_list, entry);
+
+       if (dnsproxy_enabled == TRUE)
+               __connman_dnsproxy_append(interface, domain, server);
+       else
+               __connman_resolvfile_append(interface, domain, server);
+
+       return 0;
+}
+
+/**
+ * connman_resolver_append:
+ * @interface: network interface
+ * @domain: domain limitation
+ * @server: server address
+ *
+ * Append resolver server address to current list
+ */
+int connman_resolver_append(const char *interface, const char *domain,
+                                               const char *server)
+{
+       GSList *list;
+
+       DBG("interface %s domain %s server %s", interface, domain, server);
+
+       if (server == NULL && domain == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout > 0)
+                       continue;
+
+               if (g_strcmp0(entry->interface, interface) == 0 &&
+                               g_strcmp0(entry->domain, domain) == 0 &&
+                               g_strcmp0(entry->server, server) == 0)
+                       return -EEXIST;
+       }
+
+       return append_resolver(interface, domain, server, 0, 0);
+}
+
+/**
+ * connman_resolver_append_lifetime:
+ * @interface: network interface
+ * @domain: domain limitation
+ * @server: server address
+ * @timeout: server lifetime in seconds
+ *
+ * Append resolver server address to current list
+ */
+int connman_resolver_append_lifetime(const char *interface, const char *domain,
+                               const char *server, unsigned int lifetime)
+{
+       GSList *list;
+       unsigned int interval;
+
+       DBG("interface %s domain %s server %s lifetime %d",
+                               interface, domain, server, lifetime);
+
+       if (server == NULL && domain == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout == 0 ||
+                               g_strcmp0(entry->interface, interface) != 0 ||
+                               g_strcmp0(entry->domain, domain) != 0 ||
+                               g_strcmp0(entry->server, server) != 0)
+                       continue;
+
+               g_source_remove(entry->timeout);
+
+               if (lifetime == 0) {
+                       resolver_expire_cb(entry);
+                       return 0;
+               }
+
+               interval = lifetime * RESOLVER_LIFETIME_REFRESH_THRESHOLD;
+
+               DBG("RDNSS start interface %s domain %s "
+                               "server %s lifetime threshold %d",
+                               interface, domain, server, interval);
+
+               entry->timeout = g_timeout_add_seconds(interval,
+                               resolver_refresh_cb, entry);
+               return 0;
+       }
+
+       return append_resolver(interface, domain, server, lifetime, 0);
+}
+
+/**
+ * connman_resolver_remove:
+ * @interface: network interface
+ * @domain: domain limitation
+ * @server: server address
+ *
+ * Remover resolver server address from current list
+ */
+int connman_resolver_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       GSList *list, *matches = NULL;
+
+       DBG("interface %s domain %s server %s", interface, domain, server);
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (g_strcmp0(entry->interface, interface) != 0)
+                       continue;
+
+               if (g_strcmp0(entry->domain, domain) != 0)
+                       continue;
+
+               if (g_strcmp0(entry->server, server) != 0)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+               break;
+       }
+
+       if (matches == NULL)
+               return -ENOENT;
+
+       remove_entries(matches);
+
+       return 0;
+}
+
+/**
+ * connman_resolver_remove_all:
+ * @interface: network interface
+ *
+ * Remove all resolver server address for the specified interface
+ */
+int connman_resolver_remove_all(const char *interface)
+{
+       GSList *list, *matches = NULL;
+
+       DBG("interface %s", interface);
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (g_strcmp0(entry->interface, interface) != 0)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+       }
+
+       if (matches == NULL)
+               return -ENOENT;
+
+       remove_entries(matches);
+
+       return 0;
+}
+
+/**
+ * connman_resolver_flush:
+ *
+ * Flush pending resolver requests
+ */
+void connman_resolver_flush(void)
+{
+       if (dnsproxy_enabled == TRUE)
+               __connman_dnsproxy_flush();
+
+       return;
+}
+
+int __connman_resolver_redo_servers(const char *interface)
+{
+       GSList *list;
+
+       if (dnsproxy_enabled == FALSE)
+               return 0;
+
+       DBG("interface %s", interface);
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout == 0 ||
+                               g_strcmp0(entry->interface, interface) != 0)
+                       continue;
+
+               /*
+                * We remove the server, and then re-create so that it will
+                * use proper source addresses when sending DNS queries.
+                */
+               __connman_dnsproxy_remove(entry->interface, entry->domain,
+                                       entry->server);
+               /*
+                * Remove also the resolver timer for the old server entry.
+                * A new timer will be set for the new server entry
+                * when the next Router Advertisement message arrives
+                * with RDNSS/DNSSL settings.
+                */
+               g_source_remove(entry->timeout);
+
+               __connman_dnsproxy_append(entry->interface, entry->domain,
+                                       entry->server);
+       }
+
+       return 0;
+}
+
+static void free_entry(gpointer data)
+{
+       struct entry_data *entry = data;
+       g_free(entry->interface);
+       g_free(entry->domain);
+       g_free(entry->server);
+       g_free(entry);
+}
+
+static void free_resolvfile(gpointer data)
+{
+       struct resolvfile_entry *entry = data;
+       g_free(entry->interface);
+       g_free(entry->domain);
+       g_free(entry->server);
+       g_free(entry);
+}
+
+int __connman_resolver_init(connman_bool_t dnsproxy)
+{
+       int i;
+       char **ns;
+
+       DBG("dnsproxy %d", dnsproxy);
+
+       if (dnsproxy == FALSE)
+               return 0;
+
+       if (__connman_dnsproxy_init() < 0) {
+               /* Fall back to resolv.conf */
+               return 0;
+       }
+
+       dnsproxy_enabled = TRUE;
+
+       ns = connman_setting_get_string_list("FallbackNameservers");
+       for (i = 0; ns != NULL && ns[i] != NULL; i += 1) {
+               DBG("server %s", ns[i]);
+               append_resolver(NULL, NULL, ns[i], 0, RESOLVER_FLAG_PUBLIC);
+       }
+
+       return 0;
+}
+
+void __connman_resolver_cleanup(void)
+{
+       DBG("");
+
+       if (dnsproxy_enabled == TRUE)
+               __connman_dnsproxy_cleanup();
+       else {
+               GList *list;
+               GSList *slist;
+
+               for (list = resolvfile_list; list; list = g_list_next(list))
+                       free_resolvfile(list->data);
+               g_list_free(resolvfile_list);
+               resolvfile_list = NULL;
+
+               for (slist = entry_list; slist; slist = g_slist_next(slist))
+                       free_entry(slist->data);
+               g_slist_free(entry_list);
+               entry_list = NULL;
+       }
+}
diff --git a/src/rfkill.c b/src/rfkill.c
new file mode 100644 (file)
index 0000000..81530cf
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "connman.h"
+
+enum rfkill_type {
+       RFKILL_TYPE_ALL = 0,
+       RFKILL_TYPE_WLAN,
+       RFKILL_TYPE_BLUETOOTH,
+       RFKILL_TYPE_UWB,
+       RFKILL_TYPE_WIMAX,
+       RFKILL_TYPE_WWAN,
+       RFKILL_TYPE_GPS,
+       RFKILL_TYPE_FM,
+       NUM_RFKILL_TYPES,
+};
+
+enum rfkill_operation {
+       RFKILL_OP_ADD = 0,
+       RFKILL_OP_DEL,
+       RFKILL_OP_CHANGE,
+       RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+       uint32_t idx;
+       uint8_t  type;
+       uint8_t  op;
+       uint8_t  soft;
+       uint8_t  hard;
+};
+
+static enum connman_service_type convert_type(uint8_t type)
+{
+       switch (type) {
+       case RFKILL_TYPE_WLAN:
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       case RFKILL_TYPE_BLUETOOTH:
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       case RFKILL_TYPE_WIMAX:
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       case RFKILL_TYPE_WWAN:
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       }
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static enum rfkill_type convert_service_type(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return RFKILL_TYPE_WLAN;
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return RFKILL_TYPE_BLUETOOTH;
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return RFKILL_TYPE_WIMAX;
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return RFKILL_TYPE_WWAN;
+       case CONNMAN_SERVICE_TYPE_GPS:
+               return RFKILL_TYPE_GPS;
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+               return NUM_RFKILL_TYPES;
+       }
+
+       return NUM_RFKILL_TYPES;
+}
+
+static GIOStatus rfkill_process(GIOChannel *chan)
+{
+       unsigned char buf[32];
+       struct rfkill_event *event = (void *) buf;
+       enum connman_service_type type;
+       gsize len;
+       GIOStatus status;
+
+       DBG("");
+
+       memset(buf, 0, sizeof(buf));
+
+       status = g_io_channel_read_chars(chan, (gchar *) buf,
+                       sizeof(struct rfkill_event), &len, NULL);
+
+       if (status != G_IO_STATUS_NORMAL)
+               return status;
+
+       if (len != sizeof(struct rfkill_event))
+               return status;
+
+       DBG("idx %u type %u op %u soft %u hard %u", event->idx,
+                                               event->type, event->op,
+                                               event->soft, event->hard);
+
+       type = convert_type(event->type);
+
+       switch (event->op) {
+       case RFKILL_OP_ADD:
+               __connman_technology_add_rfkill(event->idx, type,
+                                               event->soft, event->hard);
+               break;
+       case RFKILL_OP_DEL:
+               __connman_technology_remove_rfkill(event->idx, type);
+               break;
+       case RFKILL_OP_CHANGE:
+               __connman_technology_update_rfkill(event->idx, type,
+                                               event->soft, event->hard);
+               break;
+       default:
+               break;
+       }
+
+       return status;
+}
+
+static gboolean rfkill_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       if (rfkill_process(chan) == G_IO_STATUS_ERROR)
+               return FALSE;
+
+       return TRUE;
+}
+
+static GIOChannel *channel = NULL;
+
+int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block)
+{
+       uint8_t rfkill_type;
+       struct rfkill_event event;
+       ssize_t len;
+       int fd;
+
+       DBG("type %d block %d", type, block);
+
+#if defined TIZEN_EXT
+       DBG("try to set rfkill block %d, but it's not permitted", block);
+#else
+       rfkill_type = convert_service_type(type);
+       if (rfkill_type == NUM_RFKILL_TYPES)
+               return -EINVAL;
+
+       fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
+       if (fd < 0)
+               return fd;
+
+       memset(&event, 0, sizeof(event));
+       event.op = RFKILL_OP_CHANGE_ALL;
+       event.type = rfkill_type;
+       event.soft = block;
+
+       len = write(fd, &event, sizeof(event));
+       if (len < 0)
+               connman_error("Failed to change RFKILL state");
+
+       close(fd);
+#endif
+
+       return 0;
+}
+
+int __connman_rfkill_init(void)
+{
+       GIOFlags flags;
+       int fd;
+
+       DBG("");
+
+       fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               connman_error("Failed to open RFKILL control device");
+               return -EIO;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       flags = g_io_channel_get_flags(channel);
+       flags |= G_IO_FLAG_NONBLOCK;
+       g_io_channel_set_flags(channel, flags, NULL);
+
+       /* Process current RFKILL events sent on device open */
+       while (rfkill_process(channel) == G_IO_STATUS_NORMAL);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       rfkill_event, NULL);
+
+       return 0;
+}
+
+void __connman_rfkill_cleanup(void)
+{
+       DBG("");
+
+       if (channel == NULL)
+               return;
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+}
diff --git a/src/rtnl.c b/src/rtnl.c
new file mode 100644 (file)
index 0000000..3c48879
--- /dev/null
@@ -0,0 +1,1688 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#ifndef ARPHDR_PHONET_PIPE
+#define ARPHDR_PHONET_PIPE (821)
+#endif
+
+#if defined TIZEN_EXT
+#ifndef ARPHDR_RMNET_USB
+#define ARPHDR_RMNET_USB (530)
+#endif
+#endif
+
+#define print(arg...) do { if (0) connman_info(arg); } while (0)
+//#define print(arg...) connman_info(arg)
+
+struct watch_data {
+       unsigned int id;
+       int index;
+       connman_rtnl_link_cb_t newlink;
+       void *user_data;
+};
+
+static GSList *watch_list = NULL;
+static unsigned int watch_id = 0;
+
+static GSList *update_list = NULL;
+static guint update_interval = G_MAXUINT;
+static guint update_timeout = 0;
+
+struct interface_data {
+       int index;
+       char *name;
+       char *ident;
+       enum connman_service_type service_type;
+       enum connman_device_type device_type;
+};
+
+static GHashTable *interface_list = NULL;
+
+static void free_interface(gpointer data)
+{
+       struct interface_data *interface = data;
+
+       __connman_technology_remove_interface(interface->service_type,
+                       interface->index, interface->name, interface->ident);
+
+       g_free(interface->ident);
+       g_free(interface->name);
+       g_free(interface);
+}
+
+static connman_bool_t ether_blacklisted(const char *name)
+{
+       if (name == NULL)
+               return TRUE;
+
+       if (__connman_device_isfiltered(name) == TRUE)
+               return TRUE;
+
+       return FALSE;
+}
+
+static connman_bool_t wext_interface(char *ifname)
+{
+       struct iwreq wrq;
+       int fd, err;
+
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return FALSE;
+
+       memset(&wrq, 0, sizeof(wrq));
+       strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
+
+       err = ioctl(fd, SIOCGIWNAME, &wrq);
+
+       close(fd);
+
+       if (err < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void read_uevent(struct interface_data *interface)
+{
+       char *filename, line[128];
+       connman_bool_t found_devtype;
+       FILE *f;
+
+       if (ether_blacklisted(interface->name) == TRUE) {
+               interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+               interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+       } else {
+               interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+               interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+       }
+
+       filename = g_strdup_printf("/sys/class/net/%s/uevent",
+                                               interface->name);
+
+       f = fopen(filename, "re");
+
+       g_free(filename);
+
+       if (f == NULL)
+               return;
+
+       found_devtype = FALSE;
+       while (fgets(line, sizeof(line), f)) {
+               char *pos;
+
+               pos = strchr(line, '\n');
+               if (pos == NULL)
+                       continue;
+               pos[0] = '\0';
+
+               if (strncmp(line, "DEVTYPE=", 8) != 0)
+                       continue;
+
+               found_devtype = TRUE;
+
+               if (strcmp(line + 8, "wlan") == 0) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_WIFI;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_WIFI;
+               } else if (strcmp(line + 8, "wwan") == 0) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
+               } else if (strcmp(line + 8, "bluetooth") == 0) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_BLUETOOTH;
+               } else if (strcmp(line + 8, "wimax") == 0) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_WIMAX;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_WIMAX;
+               } else if (strcmp(line + 8, "gadget") == 0) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_GADGET;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_GADGET;
+
+               } else {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               }
+       }
+
+       fclose(f);
+
+       if (found_devtype)
+               return;
+
+       /* We haven't got a DEVTYPE, let's check if it's a wireless device */
+       if (wext_interface(interface->name)) {
+               interface->service_type = CONNMAN_SERVICE_TYPE_WIFI;
+               interface->device_type = CONNMAN_DEVICE_TYPE_WIFI;
+
+               connman_error("%s runs an unsupported 802.11 driver",
+                               interface->name);
+       }
+}
+
+enum connman_device_type __connman_rtnl_get_device_type(int index)
+{
+       struct interface_data *interface;
+
+       interface = g_hash_table_lookup(interface_list,
+                                       GINT_TO_POINTER(index));
+       if (interface == NULL)
+               return CONNMAN_DEVICE_TYPE_UNKNOWN;
+
+       return interface->device_type;
+}
+
+/**
+ * connman_rtnl_add_newlink_watch:
+ * @index: network device index
+ * @callback: callback function
+ * @user_data: callback data;
+ *
+ * Add a new RTNL watch for newlink events
+ *
+ * Returns: %0 on failure and a unique id on success
+ */
+unsigned int connman_rtnl_add_newlink_watch(int index,
+                       connman_rtnl_link_cb_t callback, void *user_data)
+{
+       struct watch_data *watch;
+
+       watch = g_try_new0(struct watch_data, 1);
+       if (watch == NULL)
+               return 0;
+
+       watch->id = ++watch_id;
+       watch->index = index;
+
+       watch->newlink = callback;
+       watch->user_data = user_data;
+
+       watch_list = g_slist_prepend(watch_list, watch);
+
+       DBG("id %d", watch->id);
+
+       if (callback) {
+               unsigned int flags = __connman_ipconfig_get_flags_from_index(index);
+
+               if (flags > 0)
+                       callback(flags, 0, user_data);
+       }
+
+       return watch->id;
+}
+
+/**
+ * connman_rtnl_remove_watch:
+ * @id: watch identifier
+ *
+ * Remove the RTNL watch for the identifier
+ */
+void connman_rtnl_remove_watch(unsigned int id)
+{
+       GSList *list;
+
+       DBG("id %d", id);
+
+       if (id == 0)
+               return;
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               if (watch->id  == id) {
+                       watch_list = g_slist_remove(watch_list, watch);
+                       g_free(watch);
+                       break;
+               }
+       }
+}
+
+static void trigger_rtnl(int index, void *user_data)
+{
+       struct connman_rtnl *rtnl = user_data;
+
+       if (rtnl->newlink) {
+               unsigned short type = __connman_ipconfig_get_type_from_index(index);
+               unsigned int flags = __connman_ipconfig_get_flags_from_index(index);
+
+               rtnl->newlink(type, index, flags, 0);
+       }
+
+       if (rtnl->newgateway) {
+               const char *gateway =
+                       __connman_ipconfig_get_gateway_from_index(index,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+               if (gateway != NULL)
+                       rtnl->newgateway(index, gateway);
+       }
+}
+
+static GSList *rtnl_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_rtnl *rtnl1 = a;
+       const struct connman_rtnl *rtnl2 = b;
+
+       return rtnl2->priority - rtnl1->priority;
+}
+
+/**
+ * connman_rtnl_register:
+ * @rtnl: RTNL module
+ *
+ * Register a new RTNL module
+ *
+ * Returns: %0 on success
+ */
+int connman_rtnl_register(struct connman_rtnl *rtnl)
+{
+       DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+       rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
+                                                       compare_priority);
+
+       __connman_ipconfig_foreach(trigger_rtnl, rtnl);
+
+       return 0;
+}
+
+/**
+ * connman_rtnl_unregister:
+ * @rtnl: RTNL module
+ *
+ * Remove a previously registered RTNL module
+ */
+void connman_rtnl_unregister(struct connman_rtnl *rtnl)
+{
+       DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+       rtnl_list = g_slist_remove(rtnl_list, rtnl);
+}
+
+static const char *operstate2str(unsigned char operstate)
+{
+       switch (operstate) {
+       case IF_OPER_UNKNOWN:
+               return "UNKNOWN";
+       case IF_OPER_NOTPRESENT:
+               return "NOT-PRESENT";
+       case IF_OPER_DOWN:
+               return "DOWN";
+       case IF_OPER_LOWERLAYERDOWN:
+               return "LOWER-LAYER-DOWN";
+       case IF_OPER_TESTING:
+               return "TESTING";
+       case IF_OPER_DORMANT:
+               return "DORMANT";
+       case IF_OPER_UP:
+               return "UP";
+       }
+
+       return "";
+}
+
+static void extract_link(struct ifinfomsg *msg, int bytes,
+                               struct ether_addr *address, const char **ifname,
+                               unsigned int *mtu, unsigned char *operstate,
+                                               struct rtnl_link_stats *stats)
+{
+       struct rtattr *attr;
+
+       for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFLA_ADDRESS:
+                       if (address != NULL)
+                               memcpy(address, RTA_DATA(attr), ETH_ALEN);
+                       break;
+               case IFLA_IFNAME:
+                       if (ifname != NULL)
+                               *ifname = RTA_DATA(attr);
+                       break;
+               case IFLA_MTU:
+                       if (mtu != NULL)
+                               *mtu = *((unsigned int *) RTA_DATA(attr));
+                       break;
+               case IFLA_STATS:
+                       if (stats != NULL)
+                               memcpy(stats, RTA_DATA(attr),
+                                       sizeof(struct rtnl_link_stats));
+                       break;
+               case IFLA_OPERSTATE:
+                       if (operstate != NULL)
+                               *operstate = *((unsigned char *) RTA_DATA(attr));
+                       break;
+               case IFLA_LINKMODE:
+                       break;
+               }
+       }
+}
+
+static void process_newlink(unsigned short type, int index, unsigned flags,
+                       unsigned change, struct ifinfomsg *msg, int bytes)
+{
+       struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }};
+       struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }};
+       struct rtnl_link_stats stats;
+       unsigned char operstate = 0xff;
+       struct interface_data *interface;
+       const char *ifname = NULL;
+       unsigned int mtu = 0;
+       char ident[13], str[18];
+       GSList *list;
+
+       memset(&stats, 0, sizeof(stats));
+       extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats);
+
+       snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+                                               address.ether_addr_octet[0],
+                                               address.ether_addr_octet[1],
+                                               address.ether_addr_octet[2],
+                                               address.ether_addr_octet[3],
+                                               address.ether_addr_octet[4],
+                                               address.ether_addr_octet[5]);
+
+       snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+                                               address.ether_addr_octet[0],
+                                               address.ether_addr_octet[1],
+                                               address.ether_addr_octet[2],
+                                               address.ether_addr_octet[3],
+                                               address.ether_addr_octet[4],
+                                               address.ether_addr_octet[5]);
+
+       switch (type) {
+       case ARPHRD_ETHER:
+       case ARPHRD_LOOPBACK:
+       case ARPHDR_PHONET_PIPE:
+       case ARPHRD_NONE:
+#if defined TIZEN_EXT
+       case ARPHRD_PPP:
+       case ARPHDR_RMNET_USB:
+#endif
+               __connman_ipconfig_newlink(index, type, flags,
+                                                       str, mtu, &stats);
+               break;
+       }
+
+       if (memcmp(&address, &compare, ETH_ALEN) != 0)
+               connman_info("%s {newlink} index %d address %s mtu %u",
+                                               ifname, index, str, mtu);
+
+       if (operstate != 0xff)
+               connman_info("%s {newlink} index %d operstate %u <%s>",
+                                               ifname, index, operstate,
+                                               operstate2str(operstate));
+
+       interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index));
+       if (interface == NULL) {
+               interface = g_new0(struct interface_data, 1);
+               interface->index = index;
+               interface->name = g_strdup(ifname);
+               interface->ident = g_strdup(ident);
+
+               g_hash_table_insert(interface_list,
+                                       GINT_TO_POINTER(index), interface);
+
+               if (type == ARPHRD_ETHER)
+                       read_uevent(interface);
+
+#if defined TIZEN_EXT
+               if (type == ARPHRD_PPP || type == ARPHDR_RMNET_USB) {
+                       interface->service_type = CONNMAN_SERVICE_TYPE_CELLULAR;
+                       interface->device_type = CONNMAN_DEVICE_TYPE_CELLULAR;
+               }
+#endif
+
+               __connman_technology_add_interface(interface->service_type,
+                       interface->index, interface->name, interface->ident);
+       }
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->newlink)
+                       rtnl->newlink(type, index, flags, change);
+       }
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               if (watch->index != index)
+                       continue;
+
+               if (watch->newlink)
+                       watch->newlink(flags, change, watch->user_data);
+       }
+}
+
+static void process_dellink(unsigned short type, int index, unsigned flags,
+                       unsigned change, struct ifinfomsg *msg, int bytes)
+{
+       struct rtnl_link_stats stats;
+       unsigned char operstate = 0xff;
+       const char *ifname = NULL;
+       GSList *list;
+
+       memset(&stats, 0, sizeof(stats));
+       extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats);
+
+       if (operstate != 0xff)
+               connman_info("%s {dellink} index %d operstate %u <%s>",
+                                               ifname, index, operstate,
+                                               operstate2str(operstate));
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->dellink)
+                       rtnl->dellink(type, index, flags, change);
+       }
+
+       switch (type) {
+       case ARPHRD_ETHER:
+       case ARPHRD_LOOPBACK:
+       case ARPHRD_NONE:
+#if defined TIZEN_EXT
+       case ARPHRD_PPP:
+       case ARPHDR_RMNET_USB:
+               DBG("interface index(%d)", index);
+#endif
+               __connman_ipconfig_dellink(index, &stats);
+               break;
+       }
+
+       g_hash_table_remove(interface_list, GINT_TO_POINTER(index));
+}
+
+static void extract_ipv4_addr(struct ifaddrmsg *msg, int bytes,
+                                               const char **label,
+                                               struct in_addr *local,
+                                               struct in_addr *address,
+                                               struct in_addr *broadcast)
+{
+       struct rtattr *attr;
+
+       for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFA_ADDRESS:
+                       if (address != NULL)
+                               *address = *((struct in_addr *) RTA_DATA(attr));
+                       break;
+               case IFA_LOCAL:
+                       if (local != NULL)
+                               *local = *((struct in_addr *) RTA_DATA(attr));
+                       break;
+               case IFA_BROADCAST:
+                       if (broadcast != NULL)
+                               *broadcast = *((struct in_addr *) RTA_DATA(attr));
+                       break;
+               case IFA_LABEL:
+                       if (label != NULL)
+                               *label = RTA_DATA(attr);
+                       break;
+               }
+       }
+}
+
+static void extract_ipv6_addr(struct ifaddrmsg *msg, int bytes,
+                                               struct in6_addr *addr,
+                                               struct in6_addr *local)
+{
+       struct rtattr *attr;
+
+       for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFA_ADDRESS:
+                       if (addr != NULL)
+                               *addr = *((struct in6_addr *) RTA_DATA(attr));
+                       break;
+               case IFA_LOCAL:
+                       if (local != NULL)
+                               *local = *((struct in6_addr *) RTA_DATA(attr));
+                       break;
+               }
+       }
+}
+
+static void process_newaddr(unsigned char family, unsigned char prefixlen,
+                               int index, struct ifaddrmsg *msg, int bytes)
+{
+       const char *label = NULL;
+       void *src;
+       char ip_string[INET6_ADDRSTRLEN];
+
+       if (family == AF_INET) {
+               struct in_addr ipv4_addr = { INADDR_ANY };
+
+               extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
+               src = &ipv4_addr;
+       } else if (family == AF_INET6) {
+               struct in6_addr ipv6_address, ipv6_local;
+
+               extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
+               if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
+                       return;
+
+               src = &ipv6_address;
+       } else {
+               return;
+       }
+
+       if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL)
+               return;
+
+       __connman_ipconfig_newaddr(index, family, label,
+                                       prefixlen, ip_string);
+
+       if (family == AF_INET6) {
+               /*
+                * Re-create RDNSS configured servers if there are any
+                * for this interface. This is done because we might
+                * have now properly configured interface with proper
+                * autoconfigured address.
+                */
+               char *interface = connman_inet_ifname(index);
+
+               __connman_resolver_redo_servers(interface);
+
+               g_free(interface);
+       }
+}
+
+static void process_deladdr(unsigned char family, unsigned char prefixlen,
+                               int index, struct ifaddrmsg *msg, int bytes)
+{
+       const char *label = NULL;
+       void *src;
+       char ip_string[INET6_ADDRSTRLEN];
+
+       if (family == AF_INET) {
+               struct in_addr ipv4_addr = { INADDR_ANY };
+
+               extract_ipv4_addr(msg, bytes, &label, &ipv4_addr, NULL, NULL);
+               src = &ipv4_addr;
+       } else if (family == AF_INET6) {
+               struct in6_addr ipv6_address, ipv6_local;
+
+               extract_ipv6_addr(msg, bytes, &ipv6_address, &ipv6_local);
+               if (IN6_IS_ADDR_LINKLOCAL(&ipv6_address))
+                       return;
+
+               src = &ipv6_address;
+       } else {
+               return;
+       }
+
+       if (inet_ntop(family, src, ip_string, INET6_ADDRSTRLEN) == NULL)
+               return;
+
+       __connman_ipconfig_deladdr(index, family, label,
+                                       prefixlen, ip_string);
+}
+
+static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index,
+                                               struct in_addr *dst,
+                                               struct in_addr *gateway)
+{
+       struct rtattr *attr;
+
+       for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case RTA_DST:
+                       if (dst != NULL)
+                               *dst = *((struct in_addr *) RTA_DATA(attr));
+                       break;
+               case RTA_GATEWAY:
+                       if (gateway != NULL)
+                               *gateway = *((struct in_addr *) RTA_DATA(attr));
+                       break;
+               case RTA_OIF:
+                       if (index != NULL)
+                               *index = *((int *) RTA_DATA(attr));
+                       break;
+               }
+       }
+}
+
+static void extract_ipv6_route(struct rtmsg *msg, int bytes, int *index,
+                                               struct in6_addr *dst,
+                                               struct in6_addr *gateway)
+{
+       struct rtattr *attr;
+
+       for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case RTA_DST:
+                       if (dst != NULL)
+                               *dst = *((struct in6_addr *) RTA_DATA(attr));
+                       break;
+               case RTA_GATEWAY:
+                       if (gateway != NULL)
+                               *gateway =
+                                       *((struct in6_addr *) RTA_DATA(attr));
+                       break;
+               case RTA_OIF:
+                       if (index != NULL)
+                               *index = *((int *) RTA_DATA(attr));
+                       break;
+               }
+       }
+}
+
+static void process_newroute(unsigned char family, unsigned char scope,
+                                               struct rtmsg *msg, int bytes)
+{
+       GSList *list;
+       char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+       int index = -1;
+
+       if (family == AF_INET) {
+               struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+               extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+               inet_ntop(family, &dst, dststr, sizeof(dststr));
+               inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+               __connman_ipconfig_newroute(index, family, scope, dststr,
+                                                               gatewaystr);
+
+               /* skip host specific routes */
+               if (scope != RT_SCOPE_UNIVERSE &&
+                       !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+                       return;
+
+               if (dst.s_addr != INADDR_ANY)
+                       return;
+
+       } else if (family == AF_INET6) {
+               struct in6_addr dst = IN6ADDR_ANY_INIT,
+                               gateway = IN6ADDR_ANY_INIT;
+
+               extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+               inet_ntop(family, &dst, dststr, sizeof(dststr));
+               inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+               __connman_ipconfig_newroute(index, family, scope, dststr,
+                                                               gatewaystr);
+
+               /* skip host specific routes */
+               if (scope != RT_SCOPE_UNIVERSE &&
+                       !(scope == RT_SCOPE_LINK &&
+                               IN6_IS_ADDR_UNSPECIFIED(&dst)))
+                       return;
+
+               if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+                       return;
+       } else
+               return;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->newgateway)
+                       rtnl->newgateway(index, gatewaystr);
+       }
+}
+
+static void process_delroute(unsigned char family, unsigned char scope,
+                                               struct rtmsg *msg, int bytes)
+{
+       GSList *list;
+       char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+       int index = -1;
+
+       if (family == AF_INET) {
+               struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+               extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+               inet_ntop(family, &dst, dststr, sizeof(dststr));
+               inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+               __connman_ipconfig_delroute(index, family, scope, dststr,
+                                                               gatewaystr);
+
+               /* skip host specific routes */
+               if (scope != RT_SCOPE_UNIVERSE &&
+                       !(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+                       return;
+
+               if (dst.s_addr != INADDR_ANY)
+                       return;
+
+       }  else if (family == AF_INET6) {
+               struct in6_addr dst = IN6ADDR_ANY_INIT,
+                               gateway = IN6ADDR_ANY_INIT;
+
+               extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+               inet_ntop(family, &dst, dststr, sizeof(dststr));
+               inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+               __connman_ipconfig_delroute(index, family, scope, dststr,
+                                               gatewaystr);
+
+               /* skip host specific routes */
+               if (scope != RT_SCOPE_UNIVERSE &&
+                       !(scope == RT_SCOPE_LINK &&
+                               IN6_IS_ADDR_UNSPECIFIED(&dst)))
+                       return;
+
+               if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+                       return;
+       } else
+               return;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->delgateway)
+                       rtnl->delgateway(index, gatewaystr);
+       }
+}
+
+static inline void print_ether(struct rtattr *attr, const char *name)
+{
+       int len = (int) RTA_PAYLOAD(attr);
+
+       if (len == ETH_ALEN) {
+               struct ether_addr eth;
+               memcpy(&eth, RTA_DATA(attr), ETH_ALEN);
+               print("  attr %s (len %d) %s\n", name, len, ether_ntoa(&eth));
+       } else
+               print("  attr %s (len %d)\n", name, len);
+}
+
+static inline void print_inet(struct rtattr *attr, const char *name,
+                                                       unsigned char family)
+{
+       int len = (int) RTA_PAYLOAD(attr);
+
+       if (family == AF_INET && len == sizeof(struct in_addr)) {
+               struct in_addr addr;
+               addr = *((struct in_addr *) RTA_DATA(attr));
+               print("  attr %s (len %d) %s\n", name, len, inet_ntoa(addr));
+       } else
+               print("  attr %s (len %d)\n", name, len);
+}
+
+static inline void print_string(struct rtattr *attr, const char *name)
+{
+       print("  attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr),
+                                               (char *) RTA_DATA(attr));
+}
+
+static inline void print_byte(struct rtattr *attr, const char *name)
+{
+       print("  attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr),
+                                       *((unsigned char *) RTA_DATA(attr)));
+}
+
+static inline void print_integer(struct rtattr *attr, const char *name)
+{
+       print("  attr %s (len %d) %d\n", name, (int) RTA_PAYLOAD(attr),
+                                               *((int *) RTA_DATA(attr)));
+}
+
+static inline void print_attr(struct rtattr *attr, const char *name)
+{
+       int len = (int) RTA_PAYLOAD(attr);
+
+       if (name && len > 0)
+               print("  attr %s (len %d)\n", name, len);
+       else
+               print("  attr %d (len %d)\n", attr->rta_type, len);
+}
+
+static void rtnl_link(struct nlmsghdr *hdr)
+{
+       struct ifinfomsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+       bytes = IFLA_PAYLOAD(hdr);
+
+       print("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
+
+       for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFLA_ADDRESS:
+                       print_ether(attr, "address");
+                       break;
+               case IFLA_BROADCAST:
+                       print_ether(attr, "broadcast");
+                       break;
+               case IFLA_IFNAME:
+                       print_string(attr, "ifname");
+                       break;
+               case IFLA_MTU:
+                       print_integer(attr, "mtu");
+                       break;
+               case IFLA_LINK:
+                       print_attr(attr, "link");
+                       break;
+               case IFLA_QDISC:
+                       print_attr(attr, "qdisc");
+                       break;
+               case IFLA_STATS:
+                       print_attr(attr, "stats");
+                       break;
+               case IFLA_COST:
+                       print_attr(attr, "cost");
+                       break;
+               case IFLA_PRIORITY:
+                       print_attr(attr, "priority");
+                       break;
+               case IFLA_MASTER:
+                       print_attr(attr, "master");
+                       break;
+               case IFLA_WIRELESS:
+                       print_attr(attr, "wireless");
+                       break;
+               case IFLA_PROTINFO:
+                       print_attr(attr, "protinfo");
+                       break;
+               case IFLA_TXQLEN:
+                       print_integer(attr, "txqlen");
+                       break;
+               case IFLA_MAP:
+                       print_attr(attr, "map");
+                       break;
+               case IFLA_WEIGHT:
+                       print_attr(attr, "weight");
+                       break;
+               case IFLA_OPERSTATE:
+                       print_byte(attr, "operstate");
+                       break;
+               case IFLA_LINKMODE:
+                       print_byte(attr, "linkmode");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+}
+
+static void rtnl_newlink(struct nlmsghdr *hdr)
+{
+       struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+       rtnl_link(hdr);
+
+       process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+                               msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_dellink(struct nlmsghdr *hdr)
+{
+       struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+       rtnl_link(hdr);
+
+       process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+                               msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_addr(struct nlmsghdr *hdr)
+{
+       struct ifaddrmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+       bytes = IFA_PAYLOAD(hdr);
+
+       print("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
+
+       for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFA_ADDRESS:
+                       print_inet(attr, "address", msg->ifa_family);
+                       break;
+               case IFA_LOCAL:
+                       print_inet(attr, "local", msg->ifa_family);
+                       break;
+               case IFA_LABEL:
+                       print_string(attr, "label");
+                       break;
+               case IFA_BROADCAST:
+                       print_inet(attr, "broadcast", msg->ifa_family);
+                       break;
+               case IFA_ANYCAST:
+                       print_attr(attr, "anycast");
+                       break;
+               case IFA_CACHEINFO:
+                       print_attr(attr, "cacheinfo");
+                       break;
+               case IFA_MULTICAST:
+                       print_attr(attr, "multicast");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+}
+
+static void rtnl_newaddr(struct nlmsghdr *hdr)
+{
+       struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+
+       rtnl_addr(hdr);
+
+       process_newaddr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
+                                               msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_deladdr(struct nlmsghdr *hdr)
+{
+       struct ifaddrmsg *msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+
+       rtnl_addr(hdr);
+
+       process_deladdr(msg->ifa_family, msg->ifa_prefixlen, msg->ifa_index,
+                                               msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_route(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
+       bytes = RTM_PAYLOAD(hdr);
+
+       print("rtm_family %d rtm_table %d rtm_protocol %d",
+                       msg->rtm_family, msg->rtm_table, msg->rtm_protocol);
+       print("rtm_scope %d rtm_type %d rtm_flags 0x%04x",
+                               msg->rtm_scope, msg->rtm_type, msg->rtm_flags);
+
+       for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case RTA_DST:
+                       print_inet(attr, "dst", msg->rtm_family);
+                       break;
+               case RTA_SRC:
+                       print_inet(attr, "src", msg->rtm_family);
+                       break;
+               case RTA_IIF:
+                       print_string(attr, "iif");
+                       break;
+               case RTA_OIF:
+                       print_integer(attr, "oif");
+                       break;
+               case RTA_GATEWAY:
+                       print_inet(attr, "gateway", msg->rtm_family);
+                       break;
+               case RTA_PRIORITY:
+                       print_attr(attr, "priority");
+                       break;
+               case RTA_PREFSRC:
+                       print_inet(attr, "prefsrc", msg->rtm_family);
+                       break;
+               case RTA_METRICS:
+                       print_attr(attr, "metrics");
+                       break;
+               case RTA_TABLE:
+                       print_integer(attr, "table");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+}
+
+static connman_bool_t is_route_rtmsg(struct rtmsg *msg)
+{
+
+       if (msg->rtm_table != RT_TABLE_MAIN)
+               return FALSE;
+
+       if (msg->rtm_protocol != RTPROT_BOOT &&
+                       msg->rtm_protocol != RTPROT_KERNEL)
+               return FALSE;
+
+       if (msg->rtm_type != RTN_UNICAST)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void rtnl_newroute(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+       rtnl_route(hdr);
+
+       if (is_route_rtmsg(msg))
+               process_newroute(msg->rtm_family, msg->rtm_scope,
+                                               msg, RTM_PAYLOAD(hdr));
+}
+
+static void rtnl_delroute(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+       rtnl_route(hdr);
+
+       if (is_route_rtmsg(msg))
+               process_delroute(msg->rtm_family, msg->rtm_scope,
+                                               msg, RTM_PAYLOAD(hdr));
+}
+
+static void *rtnl_nd_opt_rdnss(struct nd_opt_hdr *opt, guint32 *lifetime,
+                              int *nr_servers)
+{
+       guint32 *optint = (void *)opt;
+
+       if (opt->nd_opt_len < 3)
+               return NULL;
+
+       if (*lifetime > ntohl(optint[1]))
+               *lifetime = ntohl(optint[1]);
+
+       /* nd_opt_len is in units of 8 bytes. The header is 1 unit (8 bytes)
+          and each address is another 2 units (16 bytes).
+          So the number of addresses (given rounding) is nd_opt_len/2 */
+       *nr_servers = opt->nd_opt_len / 2;
+
+       /* And they start 8 bytes into the packet, or two guint32s in. */
+       return optint + 2;
+}
+
+static const char **rtnl_nd_opt_dnssl(struct nd_opt_hdr *opt, guint32 *lifetime)
+{
+       const char **domains = NULL;
+       guint32 *optint = (void *)opt;
+       unsigned char *optc = (void *)&optint[2];
+       int data_len = (opt->nd_opt_len * 8) - 8;
+       int nr_domains = 0;
+       int i, tmp;
+
+       if (*lifetime > ntohl(optint[1]))
+               *lifetime = ntohl(optint[1]);
+
+       /* Turn it into normal strings by converting the length bytes into '.',
+          and count how many search domains there are while we're at it. */
+       i = 0;
+       while (i < data_len) {
+               if (optc[i] > 0x3f) {
+                       DBG("DNSSL contains compressed elements in violation of RFC6106");
+                       return NULL;
+               }
+
+               if (optc[i] == 0) {
+                       nr_domains++;
+                       i++;
+                       /* Check for double zero */
+                       if (i < data_len && optc[i] == 0)
+                               break;
+                       continue;
+               }
+
+               tmp = i;
+               i += optc[i] + 1;
+
+               if (i >= data_len) {
+                       DBG("DNSSL data overflows option length");
+                       return NULL;
+               }
+
+               optc[tmp] = '.';
+       }
+
+       domains = g_try_new0(const char *, nr_domains + 1);
+       if (!domains)
+               return NULL;
+
+       /* Now point to the normal strings, missing out the leading '.' that
+          each of them will have now. */
+       for (i = 0; i < nr_domains; i++) {
+               domains[i] = (char *)optc + 1;
+               optc += strlen((char *)optc) + 1;
+       }
+
+       return domains;
+}
+
+static void rtnl_newnduseropt(struct nlmsghdr *hdr)
+{
+       struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(hdr);
+       struct nd_opt_hdr *opt;
+       guint32 lifetime = -1;
+       const char **domains = NULL;
+       struct in6_addr *servers = NULL;
+       int i, nr_servers = 0;
+       int msglen = msg->nduseropt_opts_len;
+       char *interface;
+
+       DBG("family %d index %d len %d type %d code %d",
+               msg->nduseropt_family, msg->nduseropt_ifindex,
+               msg->nduseropt_opts_len, msg->nduseropt_icmp_type,
+               msg->nduseropt_icmp_code);
+
+       if (msg->nduseropt_family != AF_INET6 ||
+                       msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+                       msg->nduseropt_icmp_code != 0)
+               return;
+
+       interface = connman_inet_ifname(msg->nduseropt_ifindex);
+       if (!interface)
+               return;
+
+       for (opt = (void *)&msg[1];
+                       msglen > 0;
+                       msglen -= opt->nd_opt_len * 8,
+                       opt = ((void *)opt) + opt->nd_opt_len*8) {
+
+               DBG("remaining %d nd opt type %d len %d\n",
+                       msglen, opt->nd_opt_type, opt->nd_opt_len);
+
+               if (opt->nd_opt_type == 25) { /* ND_OPT_RDNSS */
+                       char buf[40];
+
+                       servers = rtnl_nd_opt_rdnss(opt, &lifetime,
+                                                               &nr_servers);
+                       for (i = 0; i < nr_servers; i++) {
+                               if (!inet_ntop(AF_INET6, servers + i, buf,
+                                                               sizeof(buf)))
+                                       continue;
+
+                               connman_resolver_append_lifetime(interface,
+                                                       NULL, buf, lifetime);
+                       }
+
+               } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */
+                       g_free(domains);
+
+                       domains = rtnl_nd_opt_dnssl(opt, &lifetime);
+                       for (i = 0; domains != NULL && domains[i] != NULL; i++)
+                               connman_resolver_append_lifetime(interface,
+                                               domains[i], NULL, lifetime);
+               }
+       }
+
+       g_free(domains);
+       g_free(interface);
+}
+
+static const char *type2string(uint16_t type)
+{
+       switch (type) {
+       case NLMSG_NOOP:
+               return "NOOP";
+       case NLMSG_ERROR:
+               return "ERROR";
+       case NLMSG_DONE:
+               return "DONE";
+       case NLMSG_OVERRUN:
+               return "OVERRUN";
+       case RTM_GETLINK:
+               return "GETLINK";
+       case RTM_NEWLINK:
+               return "NEWLINK";
+       case RTM_DELLINK:
+               return "DELLINK";
+       case RTM_GETADDR:
+               return "GETADDR";
+       case RTM_NEWADDR:
+               return "NEWADDR";
+       case RTM_DELADDR:
+               return "DELADDR";
+       case RTM_GETROUTE:
+               return "GETROUTE";
+       case RTM_NEWROUTE:
+               return "NEWROUTE";
+       case RTM_DELROUTE:
+               return "DELROUTE";
+       case RTM_NEWNDUSEROPT:
+               return "NEWNDUSEROPT";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+static GIOChannel *channel = NULL;
+
+struct rtnl_request {
+       struct nlmsghdr hdr;
+       struct rtgenmsg msg;
+};
+#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+
+static GSList *request_list = NULL;
+static guint32 request_seq = 0;
+
+static struct rtnl_request *find_request(guint32 seq)
+{
+       GSList *list;
+
+       for (list = request_list; list; list = list->next) {
+               struct rtnl_request *req = list->data;
+
+               if (req->hdr.nlmsg_seq == seq)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static int send_request(struct rtnl_request *req)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(req->hdr.nlmsg_type),
+                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+
+       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+                               (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int queue_request(struct rtnl_request *req)
+{
+       request_list = g_slist_append(request_list, req);
+
+       if (g_slist_length(request_list) > 1)
+               return 0;
+
+       return send_request(req);
+}
+
+static int process_response(guint32 seq)
+{
+       struct rtnl_request *req;
+
+       DBG("seq %d", seq);
+
+       req = find_request(seq);
+       if (req != NULL) {
+               request_list = g_slist_remove(request_list, req);
+               g_free(req);
+       }
+
+       req = g_slist_nth_data(request_list, 0);
+       if (req == NULL)
+               return 0;
+
+       return send_request(req);
+}
+
+static void rtnl_message(void *buf, size_t len)
+{
+       DBG("buf %p len %zd", buf, len);
+
+       while (len > 0) {
+               struct nlmsghdr *hdr = buf;
+               struct nlmsgerr *err;
+
+               if (!NLMSG_OK(hdr, len))
+                       break;
+
+               DBG("%s len %d type %d flags 0x%04x seq %d pid %d",
+                                       type2string(hdr->nlmsg_type),
+                                       hdr->nlmsg_len, hdr->nlmsg_type,
+                                       hdr->nlmsg_flags, hdr->nlmsg_seq,
+                                       hdr->nlmsg_pid);
+
+               switch (hdr->nlmsg_type) {
+               case NLMSG_NOOP:
+               case NLMSG_OVERRUN:
+                       return;
+               case NLMSG_DONE:
+                       process_response(hdr->nlmsg_seq);
+                       return;
+               case NLMSG_ERROR:
+                       err = NLMSG_DATA(hdr);
+                       DBG("error %d (%s)", -err->error,
+                                               strerror(-err->error));
+                       return;
+               case RTM_NEWLINK:
+                       rtnl_newlink(hdr);
+                       break;
+               case RTM_DELLINK:
+                       rtnl_dellink(hdr);
+                       break;
+               case RTM_NEWADDR:
+                       rtnl_newaddr(hdr);
+                       break;
+               case RTM_DELADDR:
+                       rtnl_deladdr(hdr);
+                       break;
+               case RTM_NEWROUTE:
+                       rtnl_newroute(hdr);
+                       break;
+               case RTM_DELROUTE:
+                       rtnl_delroute(hdr);
+                       break;
+               case RTM_NEWNDUSEROPT:
+                       rtnl_newnduseropt(hdr);
+                       break;
+               }
+
+               len -= hdr->nlmsg_len;
+               buf += hdr->nlmsg_len;
+       }
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[4096];
+       struct sockaddr_nl nladdr;
+       socklen_t addr_len = sizeof(nladdr);
+       ssize_t status;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       memset(buf, 0, sizeof(buf));
+       memset(&nladdr, 0, sizeof(nladdr));
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+       if (status < 0) {
+               if (errno == EINTR || errno == EAGAIN)
+                       return TRUE;
+
+               return FALSE;
+       }
+
+       if (status == 0)
+               return FALSE;
+
+       if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+               DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+               return TRUE;
+       }
+
+       rtnl_message(buf, status);
+
+       return TRUE;
+}
+
+static int send_getlink(void)
+{
+       struct rtnl_request *req;
+
+       DBG("");
+
+       req = g_try_malloc0(RTNL_REQUEST_SIZE);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+       req->hdr.nlmsg_type = RTM_GETLINK;
+       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       req->hdr.nlmsg_pid = 0;
+       req->hdr.nlmsg_seq = request_seq++;
+       req->msg.rtgen_family = AF_INET;
+
+       return queue_request(req);
+}
+
+static int send_getaddr(void)
+{
+       struct rtnl_request *req;
+
+       DBG("");
+
+       req = g_try_malloc0(RTNL_REQUEST_SIZE);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+       req->hdr.nlmsg_type = RTM_GETADDR;
+       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       req->hdr.nlmsg_pid = 0;
+       req->hdr.nlmsg_seq = request_seq++;
+       req->msg.rtgen_family = AF_INET;
+
+       return queue_request(req);
+}
+
+static int send_getroute(void)
+{
+       struct rtnl_request *req;
+
+       DBG("");
+
+       req = g_try_malloc0(RTNL_REQUEST_SIZE);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+       req->hdr.nlmsg_type = RTM_GETROUTE;
+       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       req->hdr.nlmsg_pid = 0;
+       req->hdr.nlmsg_seq = request_seq++;
+       req->msg.rtgen_family = AF_INET;
+
+       return queue_request(req);
+}
+
+static gboolean update_timeout_cb(gpointer user_data)
+{
+       __connman_rtnl_request_update();
+
+       return TRUE;
+}
+
+static void update_interval_callback(guint min)
+{
+       if (update_timeout > 0)
+               g_source_remove(update_timeout);
+
+       if (min < G_MAXUINT) {
+               update_interval = min;
+               update_timeout = g_timeout_add_seconds(update_interval,
+                                               update_timeout_cb, NULL);
+       } else {
+               update_timeout = 0;
+               update_interval = G_MAXUINT;
+       }
+}
+
+static gint compare_interval(gconstpointer a, gconstpointer b)
+{
+       guint val_a = GPOINTER_TO_UINT(a);
+       guint val_b = GPOINTER_TO_UINT(b);
+
+       return val_a - val_b;
+}
+
+unsigned int __connman_rtnl_update_interval_add(unsigned int interval)
+{
+       guint min;
+
+       if (interval == 0)
+               return 0;
+
+       update_list = g_slist_insert_sorted(update_list,
+                       GUINT_TO_POINTER(interval), compare_interval);
+
+       min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
+       if (min < update_interval) {
+               update_interval_callback(min);
+               __connman_rtnl_request_update();
+       }
+
+       return update_interval;
+}
+
+unsigned int __connman_rtnl_update_interval_remove(unsigned int interval)
+{
+       guint min = G_MAXUINT;
+
+       if (interval == 0)
+               return 0;
+
+       update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval));
+
+       if (update_list != NULL)
+               min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
+
+       if (min > update_interval)
+               update_interval_callback(min);
+
+       return min;
+}
+
+int __connman_rtnl_request_update(void)
+{
+       return send_getlink();
+}
+
+int __connman_rtnl_init(void)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("");
+
+       interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_interface);
+
+       sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+       if (sk < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+                               RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
+                               (1<<(RTNLGRP_ND_USEROPT-1));
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       channel = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       netlink_event, NULL);
+
+       return 0;
+}
+
+void __connman_rtnl_start(void)
+{
+       DBG("");
+
+       send_getlink();
+       send_getaddr();
+       send_getroute();
+}
+
+void __connman_rtnl_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               DBG("removing watch %d", watch->id);
+
+               g_free(watch);
+               list->data = NULL;
+       }
+
+       g_slist_free(watch_list);
+       watch_list = NULL;
+
+       g_slist_free(update_list);
+       update_list = NULL;
+
+       for (list = request_list; list; list = list->next) {
+               struct rtnl_request *req = list->data;
+
+               DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(req->hdr.nlmsg_type),
+                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+               g_free(req);
+               list->data = NULL;
+       }
+
+       g_slist_free(request_list);
+       request_list = NULL;
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+
+       g_hash_table_destroy(interface_list);
+}
diff --git a/src/service.c b/src/service.c
new file mode 100644 (file)
index 0000000..f81c633
--- /dev/null
@@ -0,0 +1,6906 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <gdbus.h>
+#include <ctype.h>
+
+#include <connman/storage.h>
+#include <connman/setting.h>
+
+#include "connman.h"
+
+#define CONNECT_TIMEOUT                120
+
+#if defined TIZEN_EXT
+#define WIFI_BSSID_STR_LEN     18
+#endif
+static DBusConnection *connection = NULL;
+
+static GSequence *service_list = NULL;
+static GHashTable *service_hash = NULL;
+static GSList *counter_list = NULL;
+static unsigned int autoconnect_timeout = 0;
+static struct connman_service *current_default = NULL;
+static connman_bool_t services_dirty = FALSE;
+
+struct connman_stats {
+       connman_bool_t valid;
+       connman_bool_t enabled;
+       struct connman_stats_data data_last;
+       struct connman_stats_data data;
+       GTimer *timer;
+};
+
+struct connman_stats_counter {
+       connman_bool_t append_all;
+       struct connman_stats stats;
+       struct connman_stats stats_roaming;
+};
+
+struct connman_service {
+       int refcount;
+       int session_usage_count;
+       char *identifier;
+       char *path;
+       enum connman_service_type type;
+       enum connman_service_security security;
+       enum connman_service_state state;
+       enum connman_service_state state_ipv4;
+       enum connman_service_state state_ipv6;
+       enum connman_service_error error;
+       connman_uint8_t strength;
+       connman_bool_t favorite;
+       connman_bool_t immutable;
+       connman_bool_t hidden;
+       connman_bool_t ignore;
+       connman_bool_t autoconnect;
+       connman_bool_t userconnect;
+       GTimeVal modified;
+       unsigned int order;
+       char *name;
+       char *passphrase;
+       char *agent_passphrase;
+       connman_bool_t roaming;
+       struct connman_ipconfig *ipconfig_ipv4;
+       struct connman_ipconfig *ipconfig_ipv6;
+       struct connman_network *network;
+       struct connman_provider *provider;
+       char **nameservers;
+       char **nameservers_config;
+       char **nameservers_auto;
+       char **domains;
+       char *domainname;
+       char **timeservers;
+       char **timeservers_config;
+       /* 802.1x settings from the config files */
+       char *eap;
+       char *identity;
+       char *agent_identity;
+       char *ca_cert_file;
+       char *client_cert_file;
+       char *private_key_file;
+       char *private_key_passphrase;
+       char *phase2;
+       DBusMessage *pending;
+       guint timeout;
+       struct connman_stats stats;
+       struct connman_stats stats_roaming;
+       GHashTable *counter_table;
+       enum connman_service_proxy_method proxy;
+       enum connman_service_proxy_method proxy_config;
+       char **proxies;
+       char **excludes;
+       char *pac;
+       connman_bool_t wps;
+       int online_check_count;
+       connman_bool_t do_split_routing;
+       connman_bool_t new_service;
+       connman_bool_t hidden_service;
+       char *config_file;
+       char *config_entry;
+#if defined TIZEN_EXT
+       /*
+        * Description: TIZEN implements system global connection management.
+        *              It's only for PDP (cellular) bearer. Wi-Fi is managed
+        *              by ConnMan automatically. Reference count can help to
+        *              manage open/close connection requests by each application.
+        */
+       int user_pdn_connection_refcount;
+#endif
+};
+
+struct find_data {
+       const char *path;
+       struct connman_service *service;
+};
+
+#if defined TIZEN_EXT
+/*
+ * Public APIs to use user_pdn_connection_refcount
+ */
+void connman_service_user_pdn_connection_ref(struct connman_service *service)
+{
+       __sync_fetch_and_add(&service->user_pdn_connection_refcount, 1);
+       DBG("User make a PDP connection with already refcount: %d",
+                               service->user_pdn_connection_refcount);
+}
+
+connman_bool_t connman_service_user_pdn_connection_unref_and_test(
+                                       struct connman_service *service)
+{
+       DBG("User disconnect PDP connection with already refcount: %d",
+                               service->user_pdn_connection_refcount);
+
+       if (__sync_sub_and_fetch(&service->user_pdn_connection_refcount, 1)
+                                                                       > 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+connman_bool_t connman_service_is_no_ref_user_pdn_connection(
+                                       struct connman_service *service)
+{
+       __sync_synchronize();
+       if (service->user_pdn_connection_refcount == 0)
+               return TRUE;
+
+       return FALSE;
+}
+#endif
+static void compare_path(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       struct find_data *data = user_data;
+
+       if (data->service != NULL)
+               return;
+
+       if (g_strcmp0(service->path, data->path) == 0)
+               data->service = service;
+}
+
+static struct connman_service *find_service(const char *path)
+{
+       struct find_data data = { .path = path, .service = NULL };
+
+       DBG("path %s", path);
+
+       g_sequence_foreach(service_list, compare_path, &data);
+
+       return data.service;
+}
+
+const char *__connman_service_type2string(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+               return "system";
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return "bluetooth";
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return "cellular";
+       case CONNMAN_SERVICE_TYPE_GPS:
+               return "gps";
+       case CONNMAN_SERVICE_TYPE_VPN:
+               return "vpn";
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return "gadget";
+       }
+
+       return NULL;
+}
+
+enum connman_service_type __connman_service_string2type(const char *str)
+{
+       if (str == NULL)
+               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+       if (strcmp(str, "ethernet") == 0)
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       if (strcmp(str, "gadget") == 0)
+               return CONNMAN_SERVICE_TYPE_GADGET;
+       if (strcmp(str, "wifi") == 0)
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       if (strcmp(str, "cellular") == 0)
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       if (strcmp(str, "bluetooth") == 0)
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       if (strcmp(str, "wimax") == 0)
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       if (strcmp(str, "vpn") == 0)
+               return CONNMAN_SERVICE_TYPE_VPN;
+       if (strcmp(str, "gps") == 0)
+               return CONNMAN_SERVICE_TYPE_GPS;
+       if (strcmp(str, "system") == 0)
+               return CONNMAN_SERVICE_TYPE_SYSTEM;
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static const char *security2string(enum connman_service_security security)
+{
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_SECURITY_NONE:
+               return "none";
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               return "wep";
+       case CONNMAN_SERVICE_SECURITY_PSK:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               return "psk";
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               return "ieee8021x";
+       }
+
+       return NULL;
+}
+
+static const char *state2string(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_STATE_IDLE:
+               return "idle";
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+               return "association";
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return "configuration";
+       case CONNMAN_SERVICE_STATE_READY:
+               return "ready";
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return "online";
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               return "disconnect";
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               return "failure";
+       }
+
+       return NULL;
+}
+
+static const char *error2string(enum connman_service_error error)
+{
+       switch (error) {
+       case CONNMAN_SERVICE_ERROR_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_ERROR_OUT_OF_RANGE:
+               return "out-of-range";
+       case CONNMAN_SERVICE_ERROR_PIN_MISSING:
+               return "pin-missing";
+       case CONNMAN_SERVICE_ERROR_DHCP_FAILED:
+               return "dhcp-failed";
+       case CONNMAN_SERVICE_ERROR_CONNECT_FAILED:
+               return "connect-failed";
+       case CONNMAN_SERVICE_ERROR_LOGIN_FAILED:
+               return "login-failed";
+       case CONNMAN_SERVICE_ERROR_AUTH_FAILED:
+               return "auth-failed";
+       case CONNMAN_SERVICE_ERROR_INVALID_KEY:
+               return "invalid-key";
+       }
+
+       return NULL;
+}
+
+static enum connman_service_error string2error(const char *error)
+{
+       if (g_strcmp0(error, "dhcp-failed") == 0)
+               return CONNMAN_SERVICE_ERROR_DHCP_FAILED;
+       else if (g_strcmp0(error, "pin-missing") == 0)
+               return CONNMAN_SERVICE_ERROR_PIN_MISSING;
+       else if (g_strcmp0(error, "invalid-key") == 0)
+               return CONNMAN_SERVICE_ERROR_INVALID_KEY;
+
+       return CONNMAN_SERVICE_ERROR_UNKNOWN;
+}
+
+static const char *proxymethod2string(enum connman_service_proxy_method method)
+{
+       switch (method) {
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               return "direct";
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               return "manual";
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               return "auto";
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               break;
+       }
+
+       return NULL;
+}
+
+static enum connman_service_proxy_method string2proxymethod(const char *method)
+{
+       if (g_strcmp0(method, "direct") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+       else if (g_strcmp0(method, "auto") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_AUTO;
+       else if (g_strcmp0(method, "manual") == 0)
+               return CONNMAN_SERVICE_PROXY_METHOD_MANUAL;
+       else
+               return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+}
+
+static int service_load(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+       GError *error = NULL;
+       gsize length;
+       gchar *str;
+       connman_bool_t autoconnect;
+       unsigned int ssid_len;
+       int err = 0;
+
+       DBG("service %p", service);
+
+       keyfile = connman_storage_load_service(service->identifier);
+       if (keyfile == NULL) {
+               service->new_service = TRUE;
+               return -EIO;
+       } else
+               service->new_service = FALSE;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_VPN:
+               service->do_split_routing = g_key_file_get_boolean(keyfile,
+                               service->identifier, "SplitRouting", NULL);
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               if (service->name == NULL) {
+                       gchar *name;
+
+                       name = g_key_file_get_string(keyfile,
+                                       service->identifier, "Name", NULL);
+                       if (name != NULL) {
+                               g_free(service->name);
+                               service->name = name;
+                       }
+
+                       if (service->network != NULL)
+                               connman_network_set_name(service->network,
+                                                                       name);
+               }
+
+               if (service->network &&
+                               connman_network_get_blob(service->network,
+                                       "WiFi.SSID", &ssid_len) == NULL) {
+                       gchar *hex_ssid;
+
+                       hex_ssid = g_key_file_get_string(keyfile,
+                                                       service->identifier,
+                                                               "SSID", NULL);
+
+                       if (hex_ssid != NULL) {
+                               gchar *ssid;
+                               unsigned int i, j = 0, hex;
+                               size_t hex_ssid_len = strlen(hex_ssid);
+
+                               ssid = g_try_malloc0(hex_ssid_len / 2);
+                               if (ssid == NULL) {
+                                       g_free(hex_ssid);
+                                       err = -ENOMEM;
+                                       goto done;
+                               }
+
+                               for (i = 0; i < hex_ssid_len; i += 2) {
+                                       sscanf(hex_ssid + i, "%02x", &hex);
+                                       ssid[j++] = hex;
+                               }
+
+                               connman_network_set_blob(service->network,
+                                       "WiFi.SSID", ssid, hex_ssid_len / 2);
+                       }
+
+                       g_free(hex_ssid);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               service->favorite = g_key_file_get_boolean(keyfile,
+                               service->identifier, "Favorite", NULL);
+
+               str = g_key_file_get_string(keyfile,
+                               service->identifier, "Failure", NULL);
+               if (str != NULL) {
+                       if (service->favorite == FALSE)
+                               service->state_ipv4 = service->state_ipv6 =
+                                       CONNMAN_SERVICE_STATE_FAILURE;
+                       service->error = string2error(str);
+                       g_free(str);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               autoconnect = g_key_file_get_boolean(keyfile,
+                               service->identifier, "AutoConnect", &error);
+               if (error == NULL)
+                       service->autoconnect = autoconnect;
+               g_clear_error(&error);
+               break;
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Modified", NULL);
+       if (str != NULL) {
+               g_time_val_from_iso8601(str, &service->modified);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Passphrase", NULL);
+       if (str != NULL) {
+               g_free(service->passphrase);
+               service->passphrase = str;
+       }
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
+                                       service->identifier, "IPv6.");
+
+       service->nameservers_config = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Nameservers", &length, NULL);
+       if (service->nameservers_config != NULL && length == 0) {
+               g_strfreev(service->nameservers_config);
+               service->nameservers_config = NULL;
+       }
+
+       service->timeservers_config = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Timeservers", &length, NULL);
+       if (service->timeservers_config != NULL && length == 0) {
+               g_strfreev(service->timeservers_config);
+               service->timeservers_config = NULL;
+       }
+
+       service->domains = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Domains", &length, NULL);
+       if (service->domains != NULL && length == 0) {
+               g_strfreev(service->domains);
+               service->domains = NULL;
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.Method", NULL);
+       if (str != NULL)
+               service->proxy_config = string2proxymethod(str);
+
+       g_free(str);
+
+       service->proxies = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Servers", &length, NULL);
+       if (service->proxies != NULL && length == 0) {
+               g_strfreev(service->proxies);
+               service->proxies = NULL;
+       }
+
+       service->excludes = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Excludes", &length, NULL);
+       if (service->excludes != NULL && length == 0) {
+               g_strfreev(service->excludes);
+               service->excludes = NULL;
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.URL", NULL);
+       if (str != NULL) {
+               g_free(service->pac);
+               service->pac = str;
+       }
+
+       service->hidden_service = g_key_file_get_boolean(keyfile,
+                                       service->identifier, "Hidden", NULL);
+
+done:
+       g_key_file_free(keyfile);
+
+       return err;
+}
+
+static int service_save(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+       gchar *str;
+       guint freq;
+       const char *cst_str = NULL;
+       int err = 0;
+
+       DBG("service %p new %d", service, service->new_service);
+
+       if (service->new_service == TRUE)
+               return -ESRCH;
+
+       keyfile = __connman_storage_open_service(service->identifier);
+       if (keyfile == NULL)
+               return -EIO;
+
+       if (service->name != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                                               "Name", service->name);
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_VPN:
+               g_key_file_set_boolean(keyfile, service->identifier,
+                               "SplitRouting", service->do_split_routing);
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               if (service->network) {
+                       const unsigned char *ssid;
+                       unsigned int ssid_len = 0;
+
+                       ssid = connman_network_get_blob(service->network,
+                                                       "WiFi.SSID", &ssid_len);
+
+                       if (ssid != NULL && ssid_len > 0 && ssid[0] != '\0') {
+                               char *identifier = service->identifier;
+                               GString *str;
+                               unsigned int i;
+
+                               str = g_string_sized_new(ssid_len * 2);
+                               if (str == NULL) {
+                                       err = -ENOMEM;
+                                       goto done;
+                               }
+
+                               for (i = 0; i < ssid_len; i++)
+                                       g_string_append_printf(str,
+                                                       "%02x", ssid[i]);
+
+                               g_key_file_set_string(keyfile, identifier,
+                                                       "SSID", str->str);
+
+                               g_string_free(str, TRUE);
+                       }
+
+                       freq = connman_network_get_frequency(service->network);
+                       g_key_file_set_integer(keyfile, service->identifier,
+                                               "Frequency", freq);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               g_key_file_set_boolean(keyfile, service->identifier,
+                                       "Favorite", service->favorite);
+
+               if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
+                       service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
+                       const char *failure = error2string(service->error);
+                       if (failure != NULL)
+                               g_key_file_set_string(keyfile,
+                                                       service->identifier,
+                                                       "Failure", failure);
+               } else {
+                       g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Failure", NULL);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               if (service->favorite == TRUE)
+                       g_key_file_set_boolean(keyfile, service->identifier,
+                                       "AutoConnect", service->autoconnect);
+               break;
+       }
+
+       str = g_time_val_to_iso8601(&service->modified);
+       if (str != NULL) {
+               g_key_file_set_string(keyfile, service->identifier,
+                                                       "Modified", str);
+               g_free(str);
+       }
+
+       if (service->passphrase != NULL && 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);
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv6, keyfile,
+                                               service->identifier, "IPv6.");
+
+       if (service->nameservers_config != NULL) {
+               guint len = g_strv_length(service->nameservers_config);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "Nameservers",
+                               (const gchar **) service->nameservers_config, len);
+       } else
+       g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Nameservers", NULL);
+
+       if (service->timeservers_config != NULL) {
+               guint len = g_strv_length(service->timeservers_config);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "Timeservers",
+                               (const gchar **) service->timeservers_config, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Timeservers", NULL);
+
+       if (service->domains != NULL) {
+               guint len = g_strv_length(service->domains);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "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 != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Proxy.Method", cst_str);
+
+       if (service->proxies != NULL) {
+               guint len = g_strv_length(service->proxies);
+
+               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 != NULL) {
+               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 != NULL && 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);
+
+       if (service->hidden_service == TRUE)
+               g_key_file_set_boolean(keyfile, service->identifier, "Hidden",
+                                                                       TRUE);
+
+       if (service->config_file != NULL && strlen(service->config_file) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Config.file", service->config_file);
+
+       if (service->config_entry != NULL &&
+                                       strlen(service->config_entry) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Config.ident", service->config_entry);
+
+done:
+       __connman_storage_save_service(keyfile, service->identifier);
+
+       g_key_file_free(keyfile);
+
+       return err;
+}
+
+void __connman_service_save(struct connman_service *service)
+{
+       service_save(service);
+}
+
+static enum connman_service_state combine_state(
+                                       enum connman_service_state state_a,
+                                       enum connman_service_state state_b)
+{
+       enum connman_service_state result;
+
+       if (state_a == state_b) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_READY) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_READY) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_b;
+               goto done;
+       }
+
+       result = CONNMAN_SERVICE_STATE_FAILURE;
+
+done:
+       return result;
+}
+
+static connman_bool_t is_connecting_state(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               if (service->network != NULL)
+                       return connman_network_get_connecting(service->network);
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_connected_state(const struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_idle_state(const struct connman_service *service,
+                               enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_IDLE:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_connecting(struct connman_service *service)
+{
+       return is_connecting_state(service, service->state);
+}
+
+static connman_bool_t is_connected(struct connman_service *service)
+{
+       return is_connected_state(service, service->state);
+}
+
+static const char *nameserver_get_ifname(struct connman_service *service)
+{
+       const char *ifname;
+
+       if (service->ipconfig_ipv4)
+               ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       else
+               ifname = NULL;
+
+       if (ifname == NULL)
+               return NULL;
+
+       switch (combine_state(service->state_ipv4, service->state_ipv6)) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               return NULL;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       }
+
+       return ifname;
+}
+
+static void remove_nameservers(struct connman_service *service,
+               const char* interface, char **ns)
+{
+       const char *ifname = interface;
+       int i;
+
+       if (ns == NULL)
+               return;
+
+       if (interface == NULL)
+               ifname = nameserver_get_ifname(service);
+
+       if (ifname == NULL)
+                       return;
+
+       for (i = 0; ns[i] != NULL; i++)
+               connman_resolver_remove(ifname, NULL, ns[i]);
+}
+
+static void remove_searchdomains(struct connman_service *service,
+               const char *interface, char **sd)
+{
+       const char *ifname = interface;
+       int i;
+
+       if (sd == NULL)
+               return;
+
+       if (interface == NULL)
+               ifname = nameserver_get_ifname(service);
+
+       if (ifname == NULL)
+               return;
+
+       for (i = 0; sd[i] != NULL; i++)
+               connman_resolver_remove(ifname, sd[i], NULL);
+}
+
+static void update_nameservers(struct connman_service *service)
+{
+       const char *ifname;
+
+       if (service->ipconfig_ipv4)
+               ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       else
+               ifname = NULL;
+
+       if (ifname == NULL)
+               return;
+
+       switch (combine_state(service->state_ipv4, service->state_ipv6)) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return;
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               connman_resolver_remove_all(ifname);
+               return;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       }
+
+       if (service->nameservers_config != NULL) {
+               int i;
+
+               remove_nameservers(service, ifname, service->nameservers);
+
+               i = g_strv_length(service->nameservers_config);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(ifname, NULL,
+                                       service->nameservers_config[i]);
+               }
+       } else if (service->nameservers != NULL) {
+               int i;
+
+               i = g_strv_length(service->nameservers);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(ifname, NULL,
+                                       service->nameservers[i]);
+               }
+       }
+
+       if (service->domains != NULL) {
+               char *searchdomains[2] = {NULL, NULL};
+               int i;
+
+               searchdomains[0] = service->domainname;
+               remove_searchdomains(service, ifname, searchdomains);
+
+               i = g_strv_length(service->domains);
+               while (i != 0) {
+                       i--;
+                       connman_resolver_append(ifname, service->domains[i],
+                                               NULL);
+               }
+       } else if (service->domainname != NULL)
+               connman_resolver_append(ifname, service->domainname, NULL);
+
+       connman_resolver_flush();
+}
+
+/*
+ * The is_auto variable is set to true when IPv6 autoconf nameservers are
+ * inserted to resolver via netlink message (see rtnl.c:rtnl_newnduseropt()
+ * for details) and not through service.c
+ */
+int __connman_service_nameserver_append(struct connman_service *service,
+                               const char *nameserver, gboolean is_auto)
+{
+       char **nameservers;
+       int len, i;
+
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
+
+       if (nameserver == NULL)
+               return -EINVAL;
+
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
+
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0)
+                       return -EEXIST;
+
+       if (nameservers != NULL) {
+               len = g_strv_length(nameservers);
+               nameservers = g_try_renew(char *, nameservers, len + 2);
+       } else {
+               len = 0;
+               nameservers = g_try_new0(char *, len + 2);
+       }
+
+       if (nameservers == NULL)
+               return -ENOMEM;
+
+       nameservers[len] = g_strdup(nameserver);
+       if (nameservers[len] == NULL)
+               return -ENOMEM;
+
+       nameservers[len + 1] = NULL;
+
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
+
+       return 0;
+}
+
+int __connman_service_nameserver_remove(struct connman_service *service,
+                               const char *nameserver, gboolean is_auto)
+{
+       char **servers, **nameservers;
+       gboolean found = FALSE;
+       int len, i, j;
+
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
+
+       if (nameserver == NULL)
+               return -EINVAL;
+
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
+
+       if (nameservers == NULL)
+               return 0;
+
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0) {
+                       found = TRUE;
+                       break;
+               }
+
+       if (found == FALSE)
+               return 0;
+
+       len = g_strv_length(nameservers);
+
+       if (len == 1) {
+               g_strfreev(nameservers);
+               if (is_auto == TRUE)
+                       service->nameservers_auto = NULL;
+               else
+                       service->nameservers = NULL;
+
+               return 0;
+       }
+
+       servers = g_try_new0(char *, len);
+       if (servers == NULL)
+               return -ENOMEM;
+
+       for (i = 0, j = 0; i < len; i++) {
+               if (g_strcmp0(nameservers[i], nameserver) != 0) {
+                       servers[j] = g_strdup(nameservers[i]);
+                       if (servers[j] == NULL)
+                               return -ENOMEM;
+                       j++;
+               }
+       }
+       servers[len - 1] = NULL;
+
+       g_strfreev(nameservers);
+       nameservers = servers;
+
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
+
+       return 0;
+}
+
+void __connman_service_nameserver_clear(struct connman_service *service)
+{
+       g_strfreev(service->nameservers);
+       service->nameservers = NULL;
+
+       update_nameservers(service);
+}
+
+static void add_nameserver_route(int family, int index, char *nameserver,
+                               const char *gw)
+{
+       switch (family) {
+       case AF_INET:
+               if (connman_inet_compare_subnet(index, nameserver) == TRUE)
+                       break;
+
+               if (connman_inet_add_host_route(index, nameserver, gw) < 0)
+                       /* For P-t-P link the above route add will fail */
+                       connman_inet_add_host_route(index, nameserver, NULL);
+               break;
+
+       case AF_INET6:
+               if (connman_inet_add_ipv6_host_route(index, nameserver,
+                                                               gw) < 0)
+                       connman_inet_add_ipv6_host_route(index, nameserver,
+                                                       NULL);
+               break;
+       }
+}
+
+static void nameserver_add_routes(int index, char **nameservers,
+                                       const char *gw)
+{
+       int i, family;
+
+       for (i = 0; nameservers[i] != NULL; i++) {
+               family = connman_inet_check_ipaddress(nameservers[i]);
+               if (family < 0)
+                       continue;
+
+               add_nameserver_route(family, index, nameservers[i], gw);
+       }
+}
+
+static void nameserver_del_routes(int index, char **nameservers,
+                               enum connman_ipconfig_type type)
+{
+       int i, family;
+
+       for (i = 0; nameservers[i] != NULL; i++) {
+               family = connman_inet_check_ipaddress(nameservers[i]);
+               if (family < 0)
+                       continue;
+
+               switch (family) {
+               case AF_INET:
+                       if (type != CONNMAN_IPCONFIG_TYPE_IPV6)
+                               connman_inet_del_host_route(index,
+                                                       nameservers[i]);
+                       break;
+               case AF_INET6:
+                       if (type != CONNMAN_IPCONFIG_TYPE_IPV4)
+                               connman_inet_del_ipv6_host_route(index,
+                                                       nameservers[i]);
+                       break;
+               }
+       }
+}
+
+void __connman_service_nameserver_add_routes(struct connman_service *service,
+                                               const char *gw)
+{
+       int index = -1;
+
+       if (service == NULL)
+               return;
+
+       if (service->network != NULL)
+               index = connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               index = connman_provider_get_index(service->provider);
+
+       if (service->nameservers_config != NULL) {
+               /*
+                * Configured nameserver takes preference over the
+                * discoverd nameserver gathered from DHCP, VPN, etc.
+                */
+               nameserver_add_routes(index, service->nameservers_config, gw);
+       } else if (service->nameservers != NULL) {
+               /*
+                * We add nameservers host routes for nameservers that
+                * are not on our subnet. For those who are, the subnet
+                * route will be installed by the time the dns proxy code
+                * tries to reach them. The subnet route is installed
+                * when setting the interface IP address.
+                */
+               nameserver_add_routes(index, service->nameservers, gw);
+       }
+}
+
+void __connman_service_nameserver_del_routes(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       int index = -1;
+
+       if (service == NULL)
+               return;
+
+       if (service->network != NULL)
+               index = connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               index = connman_provider_get_index(service->provider);
+
+       if (service->nameservers_config != NULL)
+               nameserver_del_routes(index, service->nameservers_config,
+                                       type);
+       else if (service->nameservers != NULL)
+               nameserver_del_routes(index, service->nameservers, type);
+}
+
+static struct connman_stats *stats_get(struct connman_service *service)
+{
+       if (service->roaming == TRUE)
+               return &service->stats_roaming;
+       else
+               return &service->stats;
+}
+
+static connman_bool_t stats_enabled(struct connman_service *service)
+{
+       struct connman_stats *stats = stats_get(service);
+
+       return stats->enabled;
+}
+
+static void stats_start(struct connman_service *service)
+{
+       struct connman_stats *stats = stats_get(service);
+
+       DBG("service %p", service);
+
+       if (stats->timer == NULL)
+               return;
+
+       stats->enabled = TRUE;
+       stats->data_last.time = stats->data.time;
+
+       g_timer_start(stats->timer);
+}
+
+static void stats_stop(struct connman_service *service)
+{
+       struct connman_stats *stats = stats_get(service);
+       unsigned int seconds;
+
+       DBG("service %p", service);
+
+       if (stats->timer == NULL)
+               return;
+
+       if (stats->enabled == FALSE)
+               return;
+
+       g_timer_stop(stats->timer);
+
+       seconds = g_timer_elapsed(stats->timer, NULL);
+       stats->data.time = stats->data_last.time + seconds;
+
+       stats->enabled = FALSE;
+}
+
+static void reset_stats(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       /* home */
+       service->stats.valid = FALSE;
+
+       service->stats.data.rx_packets = 0;
+       service->stats.data.tx_packets = 0;
+       service->stats.data.rx_bytes = 0;
+       service->stats.data.tx_bytes = 0;
+       service->stats.data.rx_errors = 0;
+       service->stats.data.tx_errors = 0;
+       service->stats.data.rx_dropped = 0;
+       service->stats.data.tx_dropped = 0;
+       service->stats.data.time = 0;
+       service->stats.data_last.time = 0;
+
+       g_timer_reset(service->stats.timer);
+
+       /* roaming */
+       service->stats_roaming.valid = FALSE;
+
+       service->stats_roaming.data.rx_packets = 0;
+       service->stats_roaming.data.tx_packets = 0;
+       service->stats_roaming.data.rx_bytes = 0;
+       service->stats_roaming.data.tx_bytes = 0;
+       service->stats_roaming.data.rx_errors = 0;
+       service->stats_roaming.data.tx_errors = 0;
+       service->stats_roaming.data.rx_dropped = 0;
+       service->stats_roaming.data.tx_dropped = 0;
+       service->stats_roaming.data.time = 0;
+       service->stats_roaming.data_last.time = 0;
+
+       g_timer_reset(service->stats_roaming.timer);
+}
+
+#if defined TIZEN_EXT
+static connman_bool_t __connman_service_is_internet_profile(
+               struct connman_service *cellular)
+{
+       DBG("Service path: %s", cellular->path);
+
+       const char internet_suffix[] = "_1";
+       char *suffix = NULL;
+
+       suffix = strrchr(cellular->path, '_');
+       if (strcmp(suffix, internet_suffix) == 0)
+               return TRUE;
+
+       DBG("Not Internet profile.");
+       return FALSE;
+}
+#endif
+
+struct connman_service *__connman_service_get_default(void)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       if (g_sequence_iter_is_end(iter) == TRUE)
+               return NULL;
+
+       service = g_sequence_get(iter);
+
+#if defined TIZEN_EXT
+       if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+               __connman_service_is_internet_profile(service) == FALSE) {
+               iter = g_sequence_iter_next(iter);
+
+               if (g_sequence_iter_is_end(iter) == TRUE)
+                       return NULL;
+
+               service = g_sequence_get(iter);
+       }
+#endif
+       if (is_connected(service) == FALSE)
+               return NULL;
+
+       return service;
+}
+
+static void default_changed(void)
+{
+       struct connman_service *service = __connman_service_get_default();
+
+       if (service == current_default)
+               return;
+
+       __connman_service_timeserver_changed(current_default, NULL);
+
+       current_default = service;
+
+       __connman_notifier_default_changed(service);
+}
+
+static void state_changed(struct connman_service *service)
+{
+       const char *str;
+
+       __connman_notifier_service_state_changed(service, service->state);
+
+       str = state2string(service->state);
+       if (str == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "State",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+static void strength_changed(struct connman_service *service)
+{
+       if (service->strength == 0)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Strength",
+                                       DBUS_TYPE_BYTE, &service->strength);
+}
+
+static void favorite_changed(struct connman_service *service)
+{
+       if (service->path == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Favorite",
+                                       DBUS_TYPE_BOOLEAN, &service->favorite);
+}
+
+static void immutable_changed(struct connman_service *service)
+{
+       if (service->path == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Immutable",
+                                       DBUS_TYPE_BOOLEAN, &service->immutable);
+}
+
+static void roaming_changed(struct connman_service *service)
+{
+       if (service->path == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Roaming",
+                                       DBUS_TYPE_BOOLEAN, &service->roaming);
+}
+
+static void autoconnect_changed(struct connman_service *service)
+{
+       if (service->path == NULL)
+               return;
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "AutoConnect",
+                               DBUS_TYPE_BOOLEAN, &service->autoconnect);
+}
+
+static void append_security(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       const char *str;
+
+       str = security2string(service->security);
+       if (str != NULL)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &str);
+
+       /*
+        * Some access points incorrectly advertise WPS even when they
+        * are configured as open or no security, so filter
+        * appropriately.
+        */
+       if (service->wps == TRUE) {
+               switch (service->security) {
+               case CONNMAN_SERVICE_SECURITY_PSK:
+               case CONNMAN_SERVICE_SECURITY_WPA:
+               case CONNMAN_SERVICE_SECURITY_RSN:
+                       str = "wps";
+                       dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &str);
+                       break;
+               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               case CONNMAN_SERVICE_SECURITY_NONE:
+               case CONNMAN_SERVICE_SECURITY_WEP:
+               case CONNMAN_SERVICE_SECURITY_8021X:
+                       break;
+               }
+       }
+}
+
+static void append_ethernet(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ethernet(service->ipconfig_ipv4,
+                                                                       iter);
+       else if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ethernet(service->ipconfig_ipv6,
+                                                                       iter);
+}
+
+static void append_ipv4(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("ipv4 %p state %s", service->ipconfig_ipv4,
+                               state2string(service->state_ipv4));
+
+       if (is_connected_state(service, service->state_ipv4) == FALSE)
+               return;
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ipv4(service->ipconfig_ipv4, iter);
+}
+
+static void append_ipv6(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("ipv6 %p state %s", service->ipconfig_ipv6,
+                               state2string(service->state_ipv6));
+
+       if (is_connected_state(service, service->state_ipv6) == FALSE)
+               return;
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ipv6(service->ipconfig_ipv6, iter,
+                                               service->ipconfig_ipv4);
+}
+
+static void append_ipv4config(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_append_ipv4config(service->ipconfig_ipv4,
+                                                       iter);
+}
+
+static void append_ipv6config(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_append_ipv6config(service->ipconfig_ipv6,
+                                                       iter);
+}
+
+static void append_nameservers(DBusMessageIter *iter, char **servers)
+{
+       int i;
+
+       DBG("%p", servers);
+
+       for (i = 0; servers[i] != NULL; i++) {
+               DBG("servers[%d] %s", i, servers[i]);
+               dbus_message_iter_append_basic(iter,
+                                       DBUS_TYPE_STRING, &servers[i]);
+       }
+}
+
+static void append_dns(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (is_connected(service) == FALSE)
+               return;
+
+       if (service->nameservers_config != NULL) {
+               append_nameservers(iter, service->nameservers_config);
+               return;
+       } else {
+               if (service->nameservers != NULL)
+                       append_nameservers(iter, service->nameservers);
+
+               if (service->nameservers_auto != NULL)
+                       append_nameservers(iter, service->nameservers_auto);
+       }
+}
+
+static void append_dnsconfig(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (service->nameservers_config == NULL)
+               return;
+
+       append_nameservers(iter, service->nameservers_config);
+}
+
+static void append_ts(DBusMessageIter *iter, void *user_data)
+{
+       GSList *list = user_data;
+
+       while (list != NULL) {
+               char *timeserver = list->data;
+
+               if (timeserver != NULL)
+                       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                       &timeserver);
+
+               list = g_slist_next(list);
+       }
+}
+
+static void append_tsconfig(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->timeservers_config == NULL)
+               return;
+
+       for (i = 0; service->timeservers_config[i]; i++) {
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING,
+                               &service->timeservers_config[i]);
+       }
+}
+
+static void append_domainconfig(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->domains == NULL)
+               return;
+
+       for (i = 0; service->domains[i]; i++)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->domains[i]);
+}
+
+static void append_domain(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       if (is_connected(service) == FALSE &&
+                               is_connecting(service) == FALSE)
+               return;
+
+       if (service->domains != NULL)
+               append_domainconfig(iter, user_data);
+       else if (service->domainname != NULL)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->domainname);
+}
+
+static void append_proxies(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->proxies == NULL)
+               return;
+
+       for (i = 0; service->proxies[i]; i++)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->proxies[i]);
+}
+
+static void append_excludes(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int i;
+
+       if (service->excludes == NULL)
+               return;
+
+       for (i = 0; service->excludes[i]; i++)
+               dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &service->excludes[i]);
+}
+
+static void append_proxy(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       enum connman_service_proxy_method proxy;
+       const char *pac = NULL;
+       const char *method = proxymethod2string(
+               CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
+
+       DBG("");
+
+       if (is_connected(service) == FALSE)
+               return;
+
+       proxy = connman_service_get_proxy_method(service);
+
+       switch (proxy) {
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               return;
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               goto done;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               connman_dbus_dict_append_array(iter, "Servers",
+                                       DBUS_TYPE_STRING, append_proxies,
+                                       service);
+
+               connman_dbus_dict_append_array(iter, "Excludes",
+                                       DBUS_TYPE_STRING, append_excludes,
+                                       service);
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               /* Maybe DHCP, or WPAD,  has provided an url for a pac file */
+               if (service->ipconfig_ipv4 != NULL)
+                       pac = __connman_ipconfig_get_proxy_autoconfig(
+                               service->ipconfig_ipv4);
+               else if (service->ipconfig_ipv6 != NULL)
+                       pac = __connman_ipconfig_get_proxy_autoconfig(
+                               service->ipconfig_ipv6);
+
+               if (service->pac == NULL && pac == NULL)
+                       goto done;
+
+               if (service->pac != NULL)
+                       pac = service->pac;
+
+               connman_dbus_dict_append_basic(iter, "URL",
+                                       DBUS_TYPE_STRING, &pac);
+               break;
+       }
+
+       method = proxymethod2string(proxy);
+
+done:
+       connman_dbus_dict_append_basic(iter, "Method",
+                                       DBUS_TYPE_STRING, &method);
+}
+
+static void append_proxyconfig(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       const char *method;
+
+       if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN)
+               return;
+
+       switch (service->proxy_config) {
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               return;
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               if (service->proxies != NULL)
+                       connman_dbus_dict_append_array(iter, "Servers",
+                                               DBUS_TYPE_STRING,
+                                               append_proxies, service);
+
+               if (service->excludes != NULL)
+                       connman_dbus_dict_append_array(iter, "Excludes",
+                                               DBUS_TYPE_STRING,
+                                               append_excludes, service);
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               if (service->pac != NULL)
+                       connman_dbus_dict_append_basic(iter, "URL",
+                                       DBUS_TYPE_STRING, &service->pac);
+               break;
+       }
+
+       method = proxymethod2string(service->proxy_config);
+
+       connman_dbus_dict_append_basic(iter, "Method",
+                               DBUS_TYPE_STRING, &method);
+}
+
+static void append_provider(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("%p %p", service, service->provider);
+
+       if (is_connected(service) == FALSE)
+               return;
+
+       if (service->provider != NULL)
+               __connman_provider_append_properties(service->provider, iter);
+}
+
+
+static void settings_changed(struct connman_service *service,
+                               struct connman_ipconfig *ipconfig)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "IPv4",
+                                                       append_ipv4, service);
+
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "IPv6",
+                                                       append_ipv6, service);
+
+       __connman_notifier_ipconfig_changed(service, ipconfig);
+}
+
+static void ipv4_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                                       "IPv4.Configuration",
+                                                       append_ipv4config,
+                                                       service);
+}
+
+static void ipv6_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                                       "IPv6.Configuration",
+                                                       append_ipv6config,
+                                                       service);
+}
+
+static void dns_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_array(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Nameservers",
+                                       DBUS_TYPE_STRING, append_dns, service);
+}
+
+static void dns_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_array(service->path,
+                               CONNMAN_SERVICE_INTERFACE,
+                               "Nameservers.Configuration",
+                               DBUS_TYPE_STRING, append_dnsconfig, service);
+
+       dns_changed(service);
+}
+
+static void domain_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_array(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Domains",
+                               DBUS_TYPE_STRING, append_domain, service);
+}
+
+static void domain_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_array(service->path,
+                               CONNMAN_SERVICE_INTERFACE,
+                               "Domains.Configuration",
+                               DBUS_TYPE_STRING, append_domainconfig, service);
+}
+
+static void proxy_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Proxy",
+                                                       append_proxy, service);
+}
+
+static void proxy_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "Proxy.Configuration",
+                                               append_proxyconfig, service);
+
+       proxy_changed(service);
+}
+
+static void timeservers_configuration_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_array(service->path,
+                       CONNMAN_SERVICE_INTERFACE,
+                       "Timeservers.Configuration",
+                       DBUS_TYPE_STRING,
+                       append_tsconfig, service);
+}
+
+static void link_changed(struct connman_service *service)
+{
+       connman_dbus_property_changed_dict(service->path,
+                                       CONNMAN_SERVICE_INTERFACE, "Ethernet",
+                                               append_ethernet, service);
+}
+
+static void stats_append_counters(DBusMessageIter *dict,
+                       struct connman_stats_data *stats,
+                       struct connman_stats_data *counters,
+                       connman_bool_t append_all)
+{
+       if (counters->rx_packets != stats->rx_packets || append_all) {
+               counters->rx_packets = stats->rx_packets;
+               connman_dbus_dict_append_basic(dict, "RX.Packets",
+                                       DBUS_TYPE_UINT32, &stats->rx_packets);
+       }
+
+       if (counters->tx_packets != stats->tx_packets || append_all) {
+               counters->tx_packets = stats->tx_packets;
+               connman_dbus_dict_append_basic(dict, "TX.Packets",
+                                       DBUS_TYPE_UINT32, &stats->tx_packets);
+       }
+
+       if (counters->rx_bytes != stats->rx_bytes || append_all) {
+               counters->rx_bytes = stats->rx_bytes;
+               connman_dbus_dict_append_basic(dict, "RX.Bytes",
+                                       DBUS_TYPE_UINT32, &stats->rx_bytes);
+       }
+
+       if (counters->tx_bytes != stats->tx_bytes || append_all) {
+               counters->tx_bytes = stats->tx_bytes;
+               connman_dbus_dict_append_basic(dict, "TX.Bytes",
+                                       DBUS_TYPE_UINT32, &stats->tx_bytes);
+       }
+
+       if (counters->rx_errors != stats->rx_errors || append_all) {
+               counters->rx_errors = stats->rx_errors;
+               connman_dbus_dict_append_basic(dict, "RX.Errors",
+                                       DBUS_TYPE_UINT32, &stats->rx_errors);
+       }
+
+       if (counters->tx_errors != stats->tx_errors || append_all) {
+               counters->tx_errors = stats->tx_errors;
+               connman_dbus_dict_append_basic(dict, "TX.Errors",
+                                       DBUS_TYPE_UINT32, &stats->tx_errors);
+       }
+
+       if (counters->rx_dropped != stats->rx_dropped || append_all) {
+               counters->rx_dropped = stats->rx_dropped;
+               connman_dbus_dict_append_basic(dict, "RX.Dropped",
+                                       DBUS_TYPE_UINT32, &stats->rx_dropped);
+       }
+
+       if (counters->tx_dropped != stats->tx_dropped || append_all) {
+               counters->tx_dropped = stats->tx_dropped;
+               connman_dbus_dict_append_basic(dict, "TX.Dropped",
+                                       DBUS_TYPE_UINT32, &stats->tx_dropped);
+       }
+
+       if (counters->time != stats->time || append_all) {
+               counters->time = stats->time;
+               connman_dbus_dict_append_basic(dict, "Time",
+                                       DBUS_TYPE_UINT32, &stats->time);
+       }
+}
+
+static void stats_append(struct connman_service *service,
+                               const char *counter,
+                               struct connman_stats_counter *counters,
+                               connman_bool_t append_all)
+{
+       DBusMessageIter array, dict;
+       DBusMessage *msg;
+
+       DBG("service %p counter %s", service, counter);
+
+       msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH,
+                               &service->path, DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(msg, &array);
+
+       /* home counter */
+       connman_dbus_dict_open(&array, &dict);
+
+       stats_append_counters(&dict, &service->stats.data,
+                               &counters->stats.data, append_all);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       /* roaming counter */
+       connman_dbus_dict_open(&array, &dict);
+
+       stats_append_counters(&dict, &service->stats_roaming.data,
+                               &counters->stats_roaming.data, append_all);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       __connman_counter_send_usage(counter, msg);
+}
+
+static void stats_update(struct connman_service *service,
+                               unsigned int rx_packets, unsigned int tx_packets,
+                               unsigned int rx_bytes, unsigned int tx_bytes,
+                               unsigned int rx_errors, unsigned int tx_errors,
+                               unsigned int rx_dropped, unsigned int tx_dropped)
+{
+       struct connman_stats *stats = stats_get(service);
+       struct connman_stats_data *data_last = &stats->data_last;
+       struct connman_stats_data *data = &stats->data;
+       unsigned int seconds;
+
+       DBG("service %p", service);
+
+       if (stats->valid == TRUE) {
+               data->rx_packets +=
+                       rx_packets - data_last->rx_packets;
+               data->tx_packets +=
+                       tx_packets - data_last->tx_packets;
+               data->rx_bytes +=
+                       rx_bytes - data_last->rx_bytes;
+               data->tx_bytes +=
+                       tx_bytes - data_last->tx_bytes;
+               data->rx_errors +=
+                       rx_errors - data_last->rx_errors;
+               data->tx_errors +=
+                       tx_errors - data_last->tx_errors;
+               data->rx_dropped +=
+                       rx_dropped - data_last->rx_dropped;
+               data->tx_dropped +=
+                       tx_dropped - data_last->tx_dropped;
+       } else {
+               stats->valid = TRUE;
+       }
+
+       data_last->rx_packets = rx_packets;
+       data_last->tx_packets = tx_packets;
+       data_last->rx_bytes = rx_bytes;
+       data_last->tx_bytes = tx_bytes;
+       data_last->rx_errors = rx_errors;
+       data_last->tx_errors = tx_errors;
+       data_last->rx_dropped = rx_dropped;
+       data_last->tx_dropped = tx_dropped;
+
+       seconds = g_timer_elapsed(stats->timer, NULL);
+       stats->data.time = stats->data_last.time + seconds;
+}
+
+void __connman_service_notify(struct connman_service *service,
+                       unsigned int rx_packets, unsigned int tx_packets,
+                       unsigned int rx_bytes, unsigned int tx_bytes,
+                       unsigned int rx_errors, unsigned int tx_errors,
+                       unsigned int rx_dropped, unsigned int tx_dropped)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *counter;
+       struct connman_stats_counter *counters;
+       struct connman_stats_data *data;
+       int err;
+
+       if (service == NULL)
+               return;
+
+       if (is_connected(service) == FALSE)
+               return;
+
+       stats_update(service,
+               rx_packets, tx_packets,
+               rx_bytes, tx_bytes,
+               rx_errors, tx_errors,
+               rx_dropped, tx_dropped);
+
+       data = &stats_get(service)->data;
+       err = __connman_stats_update(service, service->roaming, data);
+       if (err < 0)
+               connman_error("Failed to store statistics for %s",
+                               service->identifier);
+
+       g_hash_table_iter_init(&iter, service->counter_table);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               counter = key;
+               counters = value;
+
+               stats_append(service, counter, counters, counters->append_all);
+               counters->append_all = FALSE;
+       }
+}
+
+int __connman_service_counter_register(const char *counter)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+       struct connman_stats_counter *counters;
+
+       DBG("counter %s", counter);
+
+       counter_list = g_slist_append(counter_list, (gpointer)counter);
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               counters = g_try_new0(struct connman_stats_counter, 1);
+               if (counters == NULL)
+                       return -ENOMEM;
+
+               counters->append_all = TRUE;
+
+               g_hash_table_replace(service->counter_table, (gpointer)counter,
+                                       counters);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return 0;
+}
+
+void __connman_service_counter_unregister(const char *counter)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       DBG("counter %s", counter);
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               g_hash_table_remove(service->counter_table, counter);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       counter_list = g_slist_remove(counter_list, counter);
+}
+
+GSequence *__connman_service_get_list(struct connman_session *session,
+                               service_match_cb service_match,
+                               create_service_entry_cb create_service_entry,
+                               GDestroyNotify destroy_service_entry)
+{
+       GSequence *list;
+       GSequenceIter *iter;
+       struct connman_service *service;
+       struct service_entry *entry;
+
+       list = g_sequence_new(destroy_service_entry);
+       if (list == NULL)
+               return NULL;
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               if (service_match(session, service) == TRUE) {
+                       entry = create_service_entry(service, service->name,
+                                                       service->state);
+                       if (entry == NULL)
+                               return list;
+
+                       g_sequence_append(list, entry);
+               }
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return list;
+}
+
+void __connman_service_session_inc(struct connman_service *service)
+{
+       DBG("service %p ref count %d", service,
+               service->session_usage_count + 1);
+
+       __sync_fetch_and_add(&service->session_usage_count, 1);
+}
+
+connman_bool_t __connman_service_session_dec(struct connman_service *service)
+{
+       DBG("service %p ref count %d", service,
+               service->session_usage_count - 1);
+
+       if (__sync_fetch_and_sub(&service->session_usage_count, 1) != 1)
+               return FALSE;
+
+       return TRUE;
+}
+
+#if defined TIZEN_EXT
+static void append_wifi_ext_info(DBusMessageIter *dict,
+                                       struct connman_network *network)
+{
+       char bssid_buff[WIFI_BSSID_STR_LEN] = {0,};
+       unsigned char *bssid_str = bssid_buff;
+       unsigned char *bssid;
+       unsigned int maxrate;
+       connman_uint16_t frequency;
+       const char *enc_mode;
+       const char *str;
+
+       bssid = connman_network_get_bssid(network);
+       maxrate = connman_network_get_maxrate(network);
+       frequency = connman_network_get_frequency(network);
+       enc_mode = connman_network_get_enc_mode(network);
+
+       snprintf(bssid_str, WIFI_BSSID_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
+                               bssid[0], bssid[1], bssid[2],
+                               bssid[3], bssid[4], bssid[5]);
+
+       connman_dbus_dict_append_basic(dict, "BSSID",
+                                       DBUS_TYPE_STRING, &bssid_str);
+       connman_dbus_dict_append_basic(dict, "MaxRate",
+                                       DBUS_TYPE_UINT32, &maxrate);
+       connman_dbus_dict_append_basic(dict, "Frequency",
+                                       DBUS_TYPE_UINT16, &frequency);
+       connman_dbus_dict_append_basic(dict, "EncryptionMode",
+                                       DBUS_TYPE_STRING, &enc_mode);
+
+       str = connman_network_get_string(network, "WiFi.Security");
+
+       if (str != NULL && g_str_equal(str, "ieee8021x") == TRUE) {
+               str = connman_network_get_string(network, "WiFi.EAP");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "EAP",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network, "WiFi.Phase2");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "Phase2",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network, "WiFi.Identity");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "Identity",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network, "WiFi.Passphrase");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "Password",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network, "WiFi.CACertFile");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "CACertFile",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network,
+                                                       "WiFi.ClientCertFile");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "ClientCertFile",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network,
+                                                       "WiFi.PrivateKeyFile");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict, "PrivateKeyFile",
+                                       DBUS_TYPE_STRING, &str);
+
+               str = connman_network_get_string(network,
+                                               "WiFi.PrivateKeyPassphrase");
+               if (str != NULL)
+                       connman_dbus_dict_append_basic(dict,
+                                               "PrivateKeyPassphrase",
+                                               DBUS_TYPE_STRING, &str);
+       }
+}
+#endif
+
+static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
+                                       struct connman_service *service)
+{
+       const char *str;
+       GSList *list;
+
+       str = __connman_service_type2string(service->type);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(dict, "Type",
+                                               DBUS_TYPE_STRING, &str);
+
+       connman_dbus_dict_append_array(dict, "Security",
+                               DBUS_TYPE_STRING, append_security, service);
+
+       str = state2string(service->state);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(dict, "State",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = error2string(service->error);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(dict, "Error",
+                                               DBUS_TYPE_STRING, &str);
+
+       if (service->strength > 0)
+               connman_dbus_dict_append_basic(dict, "Strength",
+                                       DBUS_TYPE_BYTE, &service->strength);
+
+       connman_dbus_dict_append_basic(dict, "Favorite",
+                                       DBUS_TYPE_BOOLEAN, &service->favorite);
+
+       connman_dbus_dict_append_basic(dict, "Immutable",
+                                       DBUS_TYPE_BOOLEAN, &service->immutable);
+
+       if (service->favorite == TRUE)
+               connman_dbus_dict_append_basic(dict, "AutoConnect",
+                               DBUS_TYPE_BOOLEAN, &service->autoconnect);
+       else
+               connman_dbus_dict_append_basic(dict, "AutoConnect",
+                                       DBUS_TYPE_BOOLEAN, &service->favorite);
+
+       if (service->name != NULL)
+               connman_dbus_dict_append_basic(dict, "Name",
+                                       DBUS_TYPE_STRING, &service->name);
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               connman_dbus_dict_append_basic(dict, "Roaming",
+                                       DBUS_TYPE_BOOLEAN, &service->roaming);
+
+               connman_dbus_dict_append_dict(dict, "Ethernet",
+                                               append_ethernet, service);
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+#if defined TIZEN_EXT
+                if (service->network != NULL)
+                        append_wifi_ext_info(dict, service->network);
+
+               connman_dbus_dict_append_dict(dict, "Ethernet",
+                                               append_ethernet, service);
+               break;
+#endif
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               connman_dbus_dict_append_dict(dict, "Ethernet",
+                                               append_ethernet, service);
+               break;
+       }
+
+       connman_dbus_dict_append_dict(dict, "IPv4", append_ipv4, service);
+
+       connman_dbus_dict_append_dict(dict, "IPv4.Configuration",
+                                               append_ipv4config, service);
+
+       connman_dbus_dict_append_dict(dict, "IPv6", append_ipv6, service);
+
+       connman_dbus_dict_append_dict(dict, "IPv6.Configuration",
+                                               append_ipv6config, service);
+
+       connman_dbus_dict_append_array(dict, "Nameservers",
+                               DBUS_TYPE_STRING, append_dns, service);
+
+       connman_dbus_dict_append_array(dict, "Nameservers.Configuration",
+                               DBUS_TYPE_STRING, append_dnsconfig, service);
+
+       if (service->state == CONNMAN_SERVICE_STATE_READY ||
+                       service->state == CONNMAN_SERVICE_STATE_ONLINE)
+               list = __connman_timeserver_get_all(service);
+       else
+               list = NULL;
+
+       connman_dbus_dict_append_array(dict, "Timeservers",
+                               DBUS_TYPE_STRING, append_ts, list);
+
+       g_slist_free_full(list, g_free);
+
+       connman_dbus_dict_append_array(dict, "Timeservers.Configuration",
+                               DBUS_TYPE_STRING, append_tsconfig, service);
+
+       connman_dbus_dict_append_array(dict, "Domains",
+                               DBUS_TYPE_STRING, append_domain, service);
+
+       connman_dbus_dict_append_array(dict, "Domains.Configuration",
+                               DBUS_TYPE_STRING, append_domainconfig, service);
+
+       connman_dbus_dict_append_dict(dict, "Proxy", append_proxy, service);
+
+       connman_dbus_dict_append_dict(dict, "Proxy.Configuration",
+                                               append_proxyconfig, service);
+
+       connman_dbus_dict_append_dict(dict, "Provider",
+                                               append_provider, service);
+}
+
+static void append_struct_service(DBusMessageIter *iter,
+               connman_dbus_append_cb_t function,
+               struct connman_service *service)
+{
+       DBusMessageIter entry, dict;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                                                       &service->path);
+
+       connman_dbus_dict_open(&entry, &dict);
+       if (function != NULL)
+               function(&dict, service);
+       connman_dbus_dict_close(&entry, &dict);
+
+       dbus_message_iter_close_container(iter, &entry);
+}
+
+static void append_dict_properties(DBusMessageIter *dict, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       append_properties(dict, TRUE, service);
+}
+
+static void append_struct(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       DBusMessageIter *iter = user_data;
+
+       if (service->path == NULL)
+               return;
+
+       append_struct_service(iter, append_dict_properties, service);
+}
+
+void __connman_service_list_struct(DBusMessageIter *iter)
+{
+       g_sequence_foreach(service_list, append_struct, iter);
+}
+
+connman_bool_t __connman_service_is_hidden(struct connman_service *service)
+{
+       return service->hidden;
+}
+
+connman_bool_t
+__connman_service_is_split_routing(struct connman_service *service)
+{
+       return service->do_split_routing;
+}
+
+int __connman_service_get_index(struct connman_service *service)
+{
+       if (service == NULL)
+               return -1;
+
+       if (service->network != NULL)
+               return connman_network_get_index(service->network);
+       else if (service->provider != NULL)
+               return connman_provider_get_index(service->provider);
+
+       return -1;
+}
+
+void __connman_service_set_hidden(struct connman_service *service)
+{
+       if (service == NULL || service->hidden == TRUE)
+               return;
+
+       service->hidden_service = TRUE;
+}
+
+void __connman_service_set_domainname(struct connman_service *service,
+                                               const char *domainname)
+{
+       if (service == NULL || service->hidden == TRUE)
+               return;
+
+       g_free(service->domainname);
+       service->domainname = g_strdup(domainname);
+
+       domain_changed(service);
+}
+
+const char *connman_service_get_domainname(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->domains != NULL)
+               return service->domains[0];
+       else
+               return service->domainname;
+}
+
+char **connman_service_get_nameservers(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->nameservers_config != NULL)
+               return g_strdupv(service->nameservers_config);
+       else if (service->nameservers != NULL ||
+                                       service->nameservers_auto != NULL) {
+               int len = 0, len_auto = 0, i;
+               char **nameservers;
+
+               if (service->nameservers != NULL)
+                       len = g_strv_length(service->nameservers);
+               if (service->nameservers_auto != NULL)
+                       len_auto = g_strv_length(service->nameservers_auto);
+
+               nameservers = g_try_new0(char *, len + len_auto + 1);
+               if (nameservers == NULL)
+                       return NULL;
+
+               for (i = 0; i < len; i++)
+                       nameservers[i] = g_strdup(service->nameservers[i]);
+
+               for (i = 0; i < len_auto; i++)
+                       nameservers[i + len] =
+                               g_strdup(service->nameservers_auto[i]);
+
+               return nameservers;
+       }
+
+       return NULL;
+}
+
+char **connman_service_get_timeservers_config(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->timeservers_config;
+}
+
+char **connman_service_get_timeservers(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->timeservers != NULL)
+               return service->timeservers;
+
+       return NULL;
+}
+
+void connman_service_set_proxy_method(struct connman_service *service,
+                                       enum connman_service_proxy_method method)
+{
+       if (service == NULL || service->hidden == TRUE)
+               return;
+
+       service->proxy = method;
+
+       proxy_changed(service);
+
+       if (method != CONNMAN_SERVICE_PROXY_METHOD_AUTO)
+               __connman_notifier_proxy_changed(service);
+}
+
+enum connman_service_proxy_method connman_service_get_proxy_method(
+                                       struct connman_service *service)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+
+       if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) {
+               if (service->proxy_config == CONNMAN_SERVICE_PROXY_METHOD_AUTO &&
+                               service->pac == NULL)
+                       return service->proxy;
+
+               return service->proxy_config;
+       }
+
+       return service->proxy;
+}
+
+char **connman_service_get_proxy_servers(struct connman_service *service)
+{
+       return g_strdupv(service->proxies);
+}
+
+char **connman_service_get_proxy_excludes(struct connman_service *service)
+{
+       return g_strdupv(service->excludes);
+}
+
+const char *connman_service_get_proxy_url(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->pac;
+}
+
+void __connman_service_set_proxy_autoconfig(struct connman_service *service,
+                                                       const char *url)
+{
+       if (service == NULL || service->hidden == TRUE)
+               return;
+
+       service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO;
+
+       if (service->ipconfig_ipv4) {
+               if (__connman_ipconfig_set_proxy_autoconfig(
+                           service->ipconfig_ipv4, url) < 0)
+                       return;
+       } else if (service->ipconfig_ipv6) {
+               if (__connman_ipconfig_set_proxy_autoconfig(
+                           service->ipconfig_ipv6, url) < 0)
+                       return;
+       } else
+               return;
+
+       proxy_changed(service);
+
+       __connman_notifier_proxy_changed(service);
+}
+
+const char *connman_service_get_proxy_autoconfig(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       if (service->ipconfig_ipv4)
+               return __connman_ipconfig_get_proxy_autoconfig(
+                                               service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               return __connman_ipconfig_get_proxy_autoconfig(
+                                               service->ipconfig_ipv6);
+       return NULL;
+}
+
+int __connman_service_timeserver_append(struct connman_service *service,
+                                               const char *timeserver)
+{
+       int len;
+
+       DBG("service %p timeserver %s", service, timeserver);
+
+       if (timeserver == NULL)
+               return -EINVAL;
+
+       if (service->timeservers != NULL) {
+               int i;
+
+               for (i = 0; service->timeservers[i] != NULL; i++)
+                       if (g_strcmp0(service->timeservers[i], timeserver) == 0)
+                               return -EEXIST;
+
+               len = g_strv_length(service->timeservers);
+               service->timeservers = g_try_renew(char *, service->timeservers,
+                                                       len + 2);
+       } else {
+               len = 0;
+               service->timeservers = g_try_new0(char *, len + 2);
+       }
+
+       if (service->timeservers == NULL)
+               return -ENOMEM;
+
+       service->timeservers[len] = g_strdup(timeserver);
+       service->timeservers[len + 1] = NULL;
+
+       return 0;
+}
+
+int __connman_service_timeserver_remove(struct connman_service *service,
+                                               const char *timeserver)
+{
+       char **servers;
+       int len, i, j, found = 0;
+
+       DBG("service %p timeserver %s", service, timeserver);
+
+       if (timeserver == NULL)
+               return -EINVAL;
+
+       if (service->timeservers == NULL)
+               return 0;
+
+       for (i = 0; service->timeservers != NULL &&
+                                       service->timeservers[i] != NULL; i++)
+               if (g_strcmp0(service->timeservers[i], timeserver) == 0) {
+                       found = 1;
+                       break;
+               }
+
+       if (found == 0)
+               return 0;
+
+       len = g_strv_length(service->timeservers);
+
+       if (len == 1) {
+               g_strfreev(service->timeservers);
+               service->timeservers = NULL;
+
+               return 0;
+       }
+
+       servers = g_try_new0(char *, len);
+       if (servers == NULL)
+               return -ENOMEM;
+
+       for (i = 0, j = 0; i < len; i++) {
+               if (g_strcmp0(service->timeservers[i], timeserver) != 0) {
+                       servers[j] = g_strdup(service->timeservers[i]);
+                       if (servers[j] == NULL)
+                               return -ENOMEM;
+                       j++;
+               }
+       }
+       servers[len - 1] = NULL;
+
+       g_strfreev(service->timeservers);
+       service->timeservers = servers;
+
+       return 0;
+}
+
+void __connman_service_timeserver_changed(struct connman_service *service,
+               GSList *ts_list)
+{
+       if (service == NULL)
+               return;
+
+       connman_dbus_property_changed_array(service->path,
+                       CONNMAN_SERVICE_INTERFACE, "Timeservers",
+                       DBUS_TYPE_STRING, append_ts, ts_list);
+}
+
+void __connman_service_set_pac(struct connman_service *service,
+                                       const char *pac)
+{
+       if (service->hidden == TRUE)
+               return;
+       g_free(service->pac);
+       service->pac = g_strdup(pac);
+
+       proxy_changed(service);
+}
+
+#if defined TIZEN_EXT
+void __connman_service_set_proxy(struct connman_service *service,
+                                       const char *proxies)
+{
+       char **proxies_array = NULL;
+
+       g_strfreev(service->proxies);
+       service->proxies = NULL;
+
+       if (proxies != NULL)
+               proxies_array = g_strsplit(proxies, " ", 0);
+
+       service->proxies = proxies_array;
+}
+#endif
+
+void __connman_service_set_identity(struct connman_service *service,
+                                       const char *identity)
+{
+       if (service->immutable || service->hidden == TRUE)
+               return;
+
+       g_free(service->identity);
+       service->identity = g_strdup(identity);
+
+       if (service->network != NULL)
+               connman_network_set_string(service->network,
+                                       "WiFi.Identity",
+                                       service->identity);
+}
+
+void __connman_service_set_agent_identity(struct connman_service *service,
+                                               const char *agent_identity)
+{
+       if (service->hidden == TRUE)
+               return;
+       g_free(service->agent_identity);
+       service->agent_identity = g_strdup(agent_identity);
+
+       if (service->network != NULL)
+               connman_network_set_string(service->network,
+                                       "WiFi.AgentIdentity",
+                                       service->agent_identity);
+}
+
+static int check_passphrase(struct connman_service *service,
+                               enum connman_service_security security,
+                               const char *passphrase)
+{
+       guint i;
+       gsize length;
+
+       if (passphrase == NULL) {
+               /*
+                * This will prevent __connman_service_set_passphrase() to
+                * wipe the passphrase out in case of -ENOKEY error for a
+                * favorite service. */
+               if (service->favorite == TRUE)
+                       return 1;
+               else
+                       return 0;
+       }
+
+       length = strlen(passphrase);
+
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_PSK:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               /* A raw key is always 64 bytes length,
+                * its content is in hex representation.
+                * A PSK key must be between [8..63].
+                */
+               if (length == 64) {
+                       for (i = 0; i < 64; i++)
+                               if (!isxdigit((unsigned char)
+                                             passphrase[i]))
+                                       return -ENOKEY;
+               } else if (length < 8 || length > 63)
+                       return -ENOKEY;
+               break;
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               /* length of WEP key is 10 or 26
+                * length of WEP passphrase is 5 or 13
+                */
+               if (length == 10 || length == 26) {
+                       for (i = 0; i < length; i++)
+                               if (!isxdigit((unsigned char)
+                                             passphrase[i]))
+                                       return -ENOKEY;
+               } else if (length != 5 && length != 13)
+                       return -ENOKEY;
+               break;
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+       case CONNMAN_SERVICE_SECURITY_NONE:
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               break;
+       }
+
+       return 0;
+}
+
+int __connman_service_set_passphrase(struct connman_service *service,
+                                       const char *passphrase)
+{
+       int err = 0;
+
+       if (service->immutable == TRUE || service->hidden == TRUE)
+               return -EINVAL;
+
+       err = check_passphrase(service, service->security, passphrase);
+
+       if (err == 0) {
+               g_free(service->passphrase);
+               service->passphrase = g_strdup(passphrase);
+
+               if (service->network != NULL)
+                       connman_network_set_string(service->network,
+                                                       "WiFi.Passphrase",
+                                                       service->passphrase);
+               service_save(service);
+       }
+
+       return err;
+}
+
+const char *__connman_service_get_passphrase(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->passphrase;
+}
+
+void __connman_service_set_agent_passphrase(struct connman_service *service,
+                                               const char *agent_passphrase)
+{
+       if (service->hidden == TRUE)
+               return;
+       g_free(service->agent_passphrase);
+       service->agent_passphrase = g_strdup(agent_passphrase);
+
+       if (service->network != NULL)
+               connman_network_set_string(service->network,
+                                       "WiFi.AgentPassphrase",
+                                       service->agent_passphrase);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("service %p", service);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       connman_dbus_dict_open(&array, &dict);
+       append_properties(&dict, FALSE, service);
+       connman_dbus_dict_close(&array, &dict);
+
+       return reply;
+}
+
+static int update_proxy_configuration(struct connman_service *service,
+                               DBusMessageIter *array)
+{
+       DBusMessageIter dict;
+       enum connman_service_proxy_method method;
+       GString *servers_str = NULL;
+       GString *excludes_str = NULL;
+       const char *url = NULL;
+
+       method = CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
+
+       dbus_message_iter_recurse(array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, variant;
+               const char *key;
+               int type;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       goto error;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       goto error;
+
+               dbus_message_iter_recurse(&entry, &variant);
+
+               type = dbus_message_iter_get_arg_type(&variant);
+
+               if (g_str_equal(key, "Method") == TRUE) {
+                       const char *val;
+
+                       if (type != DBUS_TYPE_STRING)
+                               goto error;
+
+                       dbus_message_iter_get_basic(&variant, &val);
+                       method = string2proxymethod(val);
+               } else if (g_str_equal(key, "URL") == TRUE) {
+                       if (type != DBUS_TYPE_STRING)
+                               goto error;
+
+                       dbus_message_iter_get_basic(&variant, &url);
+               } else if (g_str_equal(key, "Servers") == TRUE) {
+                       DBusMessageIter str_array;
+
+                       if (type != DBUS_TYPE_ARRAY)
+                               goto error;
+
+                       servers_str = g_string_new(NULL);
+                       if (servers_str == NULL)
+                               goto error;
+
+                       dbus_message_iter_recurse(&variant, &str_array);
+
+                       while (dbus_message_iter_get_arg_type(&str_array) ==
+                                                       DBUS_TYPE_STRING) {
+                               char *val = NULL;
+
+                               dbus_message_iter_get_basic(&str_array, &val);
+
+                               if (servers_str->len > 0)
+                                       g_string_append_printf(servers_str,
+                                                       " %s", val);
+                               else
+                                       g_string_append(servers_str, val);
+
+                               dbus_message_iter_next(&str_array);
+                       }
+               } else if (g_str_equal(key, "Excludes") == TRUE) {
+                       DBusMessageIter str_array;
+
+                       if (type != DBUS_TYPE_ARRAY)
+                               goto error;
+
+                       excludes_str = g_string_new(NULL);
+                       if (excludes_str == NULL)
+                               goto error;
+
+                       dbus_message_iter_recurse(&variant, &str_array);
+
+                       while (dbus_message_iter_get_arg_type(&str_array) ==
+                                                       DBUS_TYPE_STRING) {
+                               char *val = NULL;
+
+                               dbus_message_iter_get_basic(&str_array, &val);
+
+                               if (excludes_str->len > 0)
+                                       g_string_append_printf(excludes_str,
+                                                       " %s", val);
+                               else
+                                       g_string_append(excludes_str, val);
+
+                               dbus_message_iter_next(&str_array);
+                       }
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       switch (method) {
+       case CONNMAN_SERVICE_PROXY_METHOD_DIRECT:
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_MANUAL:
+               if (servers_str == NULL && service->proxies == NULL)
+                       goto error;
+
+               if (servers_str != NULL) {
+                       g_strfreev(service->proxies);
+
+                       if (servers_str->len > 0)
+                               service->proxies = g_strsplit_set(
+                                       servers_str->str, " ", 0);
+                       else
+                               service->proxies = NULL;
+               }
+
+               if (excludes_str != NULL) {
+                       g_strfreev(service->excludes);
+
+                       if (excludes_str->len > 0)
+                               service->excludes = g_strsplit_set(
+                                       excludes_str->str, " ", 0);
+                       else
+                               service->excludes = NULL;
+               }
+
+               if (service->proxies == NULL)
+                       method = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
+
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_AUTO:
+               g_free(service->pac);
+
+               if (url != NULL && strlen(url) > 0)
+                       service->pac = g_strdup(url);
+               else
+                       service->pac = NULL;
+
+               /* if we are connected:
+                  - if service->pac == NULL
+                  - if __connman_ipconfig_get_proxy_autoconfig(
+                  service->ipconfig) == NULL
+                  --> We should start WPAD */
+
+               break;
+       case CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN:
+               goto error;
+       }
+
+       if (servers_str != NULL)
+               g_string_free(servers_str, TRUE);
+
+       if (excludes_str != NULL)
+               g_string_free(excludes_str, TRUE);
+
+       service->proxy_config = method;
+
+       return 0;
+
+error:
+       if (servers_str != NULL)
+               g_string_free(servers_str, TRUE);
+
+       if (excludes_str != NULL)
+               g_string_free(excludes_str, TRUE);
+
+       return -EINVAL;
+}
+
+static int set_ipconfig(struct connman_service *service,
+                       struct connman_ipconfig *ipconfig,
+                       DBusMessageIter *array,
+                       enum connman_service_state state,
+                       enum connman_service_state *new_state)
+{
+       enum connman_ipconfig_method old_method;
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type;
+       int err;
+
+       if (ipconfig == NULL)
+               return -EINVAL;
+
+       old_method = __connman_ipconfig_get_method(ipconfig);
+
+       if (is_connecting_state(service, state) ||
+                                       is_connected_state(service, state))
+               __connman_network_clear_ipconfig(service->network, ipconfig);
+
+       err = __connman_ipconfig_set_config(ipconfig, array);
+       method = __connman_ipconfig_get_method(ipconfig);
+       type = __connman_ipconfig_get_config_type(ipconfig);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF &&
+                               method == CONNMAN_IPCONFIG_METHOD_DHCP) {
+                       *new_state = service->state_ipv4 =
+                               CONNMAN_SERVICE_STATE_CONFIGURATION;
+                       __connman_ipconfig_enable(ipconfig);
+               }
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               if (err == 0 && old_method == CONNMAN_IPCONFIG_METHOD_OFF &&
+                               method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+                       *new_state = service->state_ipv6;
+                       __connman_ipconfig_enable(ipconfig);
+               }
+       }
+
+       DBG("err %d ipconfig %p type %d method %d state %s", err, ipconfig,
+               type, method, state2string(*new_state));
+
+       return err;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("service %p", service);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "AutoConnect") == TRUE) {
+               connman_bool_t autoconnect;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (service->favorite == FALSE)
+                       return __connman_error_invalid_service(msg);
+
+               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);
+
+               service_save(service);
+       } else if (g_str_equal(name, "Nameservers.Configuration") == TRUE) {
+               DBusMessageIter entry;
+               GString *str;
+               int index;
+               const char *gw;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               str = g_string_new(NULL);
+               if (str == NULL)
+                       return __connman_error_invalid_arguments(msg);
+
+               index = connman_network_get_index(service->network);
+               gw = __connman_ipconfig_get_gateway_from_index(index,
+                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+               if (gw && strlen(gw))
+                       __connman_service_nameserver_del_routes(service,
+                                               CONNMAN_IPCONFIG_TYPE_ALL);
+
+               dbus_message_iter_recurse(&value, &entry);
+
+               while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+                       const char *val;
+                       dbus_message_iter_get_basic(&entry, &val);
+                       dbus_message_iter_next(&entry);
+                       if (connman_inet_check_ipaddress(val) > 0) {
+                               if (str->len > 0)
+                                       g_string_append_printf(str, " %s", val);
+                               else
+                                       g_string_append(str, val);
+                       }
+               }
+
+               remove_nameservers(service, NULL, service->nameservers_config);
+               g_strfreev(service->nameservers_config);
+
+               if (str->len > 0) {
+                       service->nameservers_config =
+                               g_strsplit_set(str->str, " ", 0);
+               } else {
+                       service->nameservers_config = NULL;
+               }
+
+               g_string_free(str, TRUE);
+
+               if (gw && strlen(gw))
+                       __connman_service_nameserver_add_routes(service, gw);
+
+               update_nameservers(service);
+               dns_configuration_changed(service);
+
+               service_save(service);
+       } else if (g_str_equal(name, "Timeservers.Configuration") == TRUE) {
+               DBusMessageIter entry;
+               GSList *list = NULL;
+               int count = 0;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_recurse(&value, &entry);
+
+               while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+                       const char *val;
+                       GSList *new_head;
+
+                       dbus_message_iter_get_basic(&entry, &val);
+
+                       new_head = __connman_timeserver_add_list(list, val);
+                       if (list != new_head) {
+                               count++;
+                               list = new_head;
+                       }
+
+                       dbus_message_iter_next(&entry);
+               }
+
+               g_strfreev(service->timeservers_config);
+               service->timeservers_config = NULL;
+
+               if (list != NULL) {
+                       service->timeservers_config = g_new0(char *, count+1);
+
+                       while (list != NULL) {
+                               count--;
+                               service->timeservers_config[count] = list->data;
+                               list = g_slist_delete_link(list, list);
+                       };
+               }
+
+               service_save(service);
+               timeservers_configuration_changed(service);
+
+               __connman_timeserver_sync(service);
+       } else if (g_str_equal(name, "Domains.Configuration") == TRUE) {
+               DBusMessageIter entry;
+               GString *str;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               str = g_string_new(NULL);
+               if (str == NULL)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_recurse(&value, &entry);
+
+               while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+                       const char *val;
+                       dbus_message_iter_get_basic(&entry, &val);
+                       dbus_message_iter_next(&entry);
+                       if (str->len > 0)
+                               g_string_append_printf(str, " %s", val);
+                       else
+                               g_string_append(str, val);
+               }
+
+               remove_searchdomains(service, NULL, service->domains);
+               g_strfreev(service->domains);
+
+               if (str->len > 0)
+                       service->domains = g_strsplit_set(str->str, " ", 0);
+               else
+                       service->domains = NULL;
+
+               g_string_free(str, TRUE);
+
+               update_nameservers(service);
+               domain_configuration_changed(service);
+
+               service_save(service);
+       } else if (g_str_equal(name, "Proxy.Configuration") == TRUE) {
+               int err;
+
+               if (type != DBUS_TYPE_ARRAY)
+                       return __connman_error_invalid_arguments(msg);
+
+               err = update_proxy_configuration(service, &value);
+
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+
+               proxy_configuration_changed(service);
+
+               __connman_notifier_proxy_changed(service);
+
+               service_save(service);
+       } else if (g_str_equal(name, "IPv4.Configuration") == TRUE ||
+                       g_str_equal(name, "IPv6.Configuration")) {
+
+               struct connman_ipconfig *ipv4 = NULL, *ipv6 = NULL;
+               enum connman_service_state state =
+                                               CONNMAN_SERVICE_STATE_UNKNOWN;
+               int err = 0;
+
+               DBG("%s", name);
+
+               if (service->ipconfig_ipv4 == NULL &&
+                                       service->ipconfig_ipv6 == NULL)
+                       return __connman_error_invalid_property(msg);
+
+               if (g_str_equal(name, "IPv4.Configuration") == TRUE) {
+                       ipv4 = service->ipconfig_ipv4;
+                       err = set_ipconfig(service, ipv4, &value,
+                                       service->state_ipv4, &state);
+
+               } else if (g_str_equal(name, "IPv6.Configuration") == TRUE) {
+                       ipv6 = service->ipconfig_ipv6;
+                       err = set_ipconfig(service, ipv6, &value,
+                                       service->state_ipv6, &state);
+               }
+
+               if (err < 0) {
+                       if (is_connected_state(service, state) ||
+                                       is_connecting_state(service, state))
+                               __connman_network_set_ipconfig(service->network,
+                                                               ipv4, ipv6);
+                       return __connman_error_failed(msg, -err);
+               }
+
+               if (ipv4)
+                       ipv4_configuration_changed(service);
+               else if (ipv6)
+                       ipv6_configuration_changed(service);
+
+               if (is_connecting(service) || is_connected(service))
+                       __connman_network_set_ipconfig(service->network,
+                                                       ipv4, ipv6);
+
+               service_save(service);
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void set_error(struct connman_service *service,
+                                       enum connman_service_error error)
+{
+       const char *str;
+
+       if (service->error == error)
+               return;
+
+       service->error = error;
+
+       if (service->path == NULL)
+               return;
+
+       str = error2string(service->error);
+
+       if (str == NULL)
+               str = "";
+
+       connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Error",
+                               DBUS_TYPE_STRING, &str);
+}
+
+static void set_idle(struct connman_service *service)
+{
+       service->state = service->state_ipv4 = service->state_ipv6 =
+                                               CONNMAN_SERVICE_STATE_IDLE;
+       set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+       state_changed(service);
+}
+
+static DBusMessage *clear_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       const char *name;
+
+       DBG("service %p", service);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       if (g_str_equal(name, "Error") == TRUE) {
+               set_idle(service);
+
+               g_get_current_time(&service->modified);
+               service_save(service);
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static connman_bool_t is_ignore(struct connman_service *service)
+{
+       if (service->autoconnect == FALSE)
+               return TRUE;
+
+       if (service->roaming == TRUE)
+               return TRUE;
+
+       if (service->ignore == TRUE)
+               return TRUE;
+
+       if (service->state == CONNMAN_SERVICE_STATE_FAILURE)
+               return TRUE;
+
+       return FALSE;
+}
+
+struct preferred_tech_data {
+       GSequence *preferred_list;
+       enum connman_service_type type;
+};
+
+static void preferred_tech_add_by_type(gpointer data, gpointer user_data)
+{
+       struct connman_service *service = data;
+       struct preferred_tech_data *tech_data = user_data;
+
+       if (service->type == tech_data->type) {
+               g_sequence_append(tech_data->preferred_list, service);
+
+               DBG("type %d service %p %s", tech_data->type, service,
+                               service->name);
+       }
+}
+
+static GSequence* preferred_tech_list_get(GSequence *list)
+{
+       unsigned int *tech_array;
+       struct preferred_tech_data tech_data;
+       int i;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array == NULL)
+               return NULL;
+
+       if (connman_setting_get_bool("SingleConnectedTechnology") == TRUE) {
+               GSequenceIter *iter = g_sequence_get_begin_iter(service_list);
+               while (g_sequence_iter_is_end(iter) == FALSE) {
+                       struct connman_service *service;
+
+                       service = g_sequence_get(iter);
+
+                       if (is_connected(service) == FALSE)
+                               break;
+
+                       if (service->userconnect == TRUE) {
+                               DBG("service %p name %s is user connected",
+                                               service, service->name);
+#if defined TIZEN_EXT
+                               /* We can connect to a favorite service like
+                                * wifi even we have a userconnect for cellular
+                                * because we have refount for cellular service
+                                */
+                               if (service->type ==
+                                               CONNMAN_SERVICE_TYPE_CELLULAR)
+                                       break;
+#endif
+                               return NULL;
+                       }
+
+                       iter = g_sequence_iter_next(iter);
+               }
+       }
+
+       tech_data.preferred_list = g_sequence_new(NULL);
+
+       for (i = 0; tech_array[i] != 0; i += 1) {
+               tech_data.type = tech_array[i];
+               g_sequence_foreach(service_list, preferred_tech_add_by_type,
+                               &tech_data);
+       }
+
+       return tech_data.preferred_list;
+}
+
+static connman_bool_t auto_connect_service(GSequenceIter* iter,
+               connman_bool_t preferred)
+{
+       struct connman_service *service = NULL;
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               if (service->pending != NULL)
+                       return TRUE;
+
+               if (is_connecting(service) == TRUE)
+                       return TRUE;
+
+               if (service->favorite == FALSE) {
+                       if (preferred == TRUE)
+                               goto next_service;
+                       return FALSE;
+               }
+
+               if (is_connected(service) == TRUE)
+                       return TRUE;
+
+#if defined TIZEN_EXT
+               if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                       __connman_service_is_internet_profile(service) == FALSE)
+                       goto next_service;
+#endif
+               if (is_ignore(service) == FALSE && service->state ==
+                               CONNMAN_SERVICE_STATE_IDLE)
+                       break;
+
+       next_service:
+               service = NULL;
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       if (service != NULL) {
+
+               DBG("service %p %s %s", service, service->name,
+                               (preferred == TRUE)? "preferred": "auto");
+
+               service->userconnect = FALSE;
+               __connman_service_connect(service);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static gboolean run_auto_connect(gpointer data)
+{
+       GSequenceIter *iter = NULL;
+       GSequence *preferred_tech;
+
+       autoconnect_timeout = 0;
+
+       DBG("");
+
+       preferred_tech = preferred_tech_list_get(service_list);
+       if (preferred_tech != NULL)
+               iter = g_sequence_get_begin_iter(preferred_tech);
+
+       if (iter == NULL || auto_connect_service(iter, TRUE) == FALSE)
+               iter = g_sequence_get_begin_iter(service_list);
+
+       if (iter != NULL)
+               auto_connect_service(iter, FALSE);
+
+       if (preferred_tech != NULL)
+               g_sequence_free(preferred_tech);
+
+       return FALSE;
+}
+
+void __connman_service_auto_connect(void)
+{
+       DBG("");
+
+       if (__connman_session_mode() == TRUE) {
+               DBG("Session mode enabled: auto connect disabled");
+               return;
+       }
+
+       if (autoconnect_timeout != 0)
+               return;
+
+       autoconnect_timeout = g_timeout_add_seconds(0, run_auto_connect, NULL);
+}
+
+static void remove_timeout(struct connman_service *service)
+{
+       if (service->timeout > 0) {
+               g_source_remove(service->timeout);
+               service->timeout = 0;
+       }
+}
+
+void __connman_service_reply_dbus_pending(DBusMessage *pending, int error)
+{
+       if (pending != NULL) {
+               if (error > 0) {
+                       DBusMessage *reply;
+
+                       reply = __connman_error_failed(pending, error);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               } else {
+                       const char *sender, *path;
+
+                       sender = dbus_message_get_interface(pending);
+                       path = dbus_message_get_path(pending);
+
+                       DBG("sender %s path %s", sender, path);
+
+                       if (g_strcmp0(sender, CONNMAN_MANAGER_INTERFACE) == 0)
+                               g_dbus_send_reply(connection, pending,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+                       else
+                               g_dbus_send_reply(connection, pending,
+                                                       DBUS_TYPE_INVALID);
+               }
+
+               dbus_message_unref(pending);
+       }
+}
+
+static void reply_pending(struct connman_service *service, int error)
+{
+       remove_timeout(service);
+
+       if (service->pending != NULL) {
+               __connman_service_reply_dbus_pending(service->pending, error);
+               service->pending = NULL;
+       }
+}
+
+static void check_pending_msg(struct connman_service *service)
+{
+       if (service->pending == NULL)
+               return;
+
+       DBG("service %p pending msg %p already exists", service,
+                                               service->pending);
+       dbus_message_unref(service->pending);
+}
+
+void __connman_service_set_hidden_data(struct connman_service *service,
+                                                       gpointer user_data)
+{
+       DBusMessage *pending = user_data;
+
+       DBG("service %p pending %p", service, pending);
+
+       check_pending_msg(service);
+
+       service->pending = pending;
+}
+
+void __connman_service_return_error(struct connman_service *service,
+                               int error, gpointer user_data)
+{
+       DBG("service %p error %d user_data %p", service, error, user_data);
+
+       __connman_service_set_hidden_data(service, user_data);
+
+       reply_pending(service, error);
+}
+
+static gboolean connect_timeout(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+       connman_bool_t autoconnect = FALSE;
+
+       DBG("service %p", service);
+
+       service->timeout = 0;
+
+       if (service->network != NULL)
+               __connman_network_disconnect(service->network);
+
+       __connman_ipconfig_disable(service->ipconfig_ipv4);
+       __connman_ipconfig_disable(service->ipconfig_ipv6);
+
+       __connman_stats_service_unregister(service);
+
+       if (service->pending != NULL) {
+               DBusMessage *reply;
+
+               reply = __connman_error_operation_timeout(service->pending);
+               if (reply != NULL)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(service->pending);
+               service->pending = NULL;
+       } else
+               autoconnect = TRUE;
+
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       if (autoconnect == TRUE && service->userconnect == FALSE)
+               __connman_service_auto_connect();
+
+       return FALSE;
+}
+
+static void set_reconnect_state(struct connman_service *service,
+                                               connman_bool_t reconnect)
+{
+       struct connman_device *device;
+
+       if (service->network == NULL)
+               return;
+
+       device = connman_network_get_device(service->network);
+       if (device == NULL)
+               return;
+
+       __connman_device_set_reconnect(device, reconnect);
+}
+
+static connman_bool_t get_reconnect_state(struct connman_service *service)
+{
+       struct connman_device *device;
+
+       if (service->network == NULL)
+               return FALSE;
+
+       device = connman_network_get_device(service->network);
+       if (device == NULL)
+               return FALSE;
+
+       return __connman_device_get_reconnect(device);
+}
+
+static DBusMessage *connect_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       GSequenceIter *iter;
+       int err;
+
+       DBG("service %p", service);
+
+#if defined TIZEN_EXT
+       if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+               connman_service_user_pdn_connection_ref(service);
+#endif
+       if (service->pending != NULL)
+               return __connman_error_in_progress(msg);
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *temp = g_sequence_get(iter);
+
+               if (service->type == temp->type && is_connecting(temp) == TRUE)
+                       return __connman_error_in_progress(msg);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       service->ignore = FALSE;
+
+       service->userconnect = TRUE;
+
+       service->pending = dbus_message_ref(msg);
+
+       set_reconnect_state(service, FALSE);
+
+       err = __connman_service_connect(service);
+       if (err < 0) {
+               if (service->pending == NULL)
+                       return NULL;
+
+               if (err != -EINPROGRESS) {
+                       dbus_message_unref(service->pending);
+                       service->pending = NULL;
+
+                       return __connman_error_failed(msg, -err);
+               }
+
+               return NULL;
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       int err;
+
+       DBG("service %p", service);
+
+#if defined TIZEN_EXT
+       if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) {
+               if (connman_service_user_pdn_connection_unref_and_test(service) != TRUE)
+                       return __connman_error_failed(msg, EISCONN);
+
+               if (is_connected(service) == TRUE &&
+                               service == __connman_service_get_default())
+                       return __connman_error_failed(msg, EISCONN);
+       }
+#endif
+       reply_pending(service, ECONNABORTED);
+
+       service->ignore = TRUE;
+
+       set_reconnect_state(service, FALSE);
+
+       err = __connman_service_disconnect(service);
+       if (err < 0) {
+               if (err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+
+#if defined TIZEN_EXT
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+#else
+               return NULL;
+#endif
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+gboolean __connman_service_remove(struct connman_service *service)
+{
+       if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+               return FALSE;
+
+       if (service->immutable == TRUE || service->hidden == TRUE)
+               return FALSE;
+
+       if (service->favorite == FALSE && service->state !=
+                                               CONNMAN_SERVICE_STATE_FAILURE)
+               return FALSE;
+
+       set_reconnect_state(service, FALSE);
+
+       __connman_service_disconnect(service);
+
+       g_free(service->passphrase);
+       service->passphrase = NULL;
+
+       g_free(service->agent_passphrase);
+       service->agent_passphrase = NULL;
+
+       g_free(service->identity);
+       service->identity = NULL;
+
+       g_free(service->agent_identity);
+       service->agent_identity = NULL;
+
+       g_free(service->eap);
+       service->eap = NULL;
+
+       set_idle(service);
+
+       __connman_service_set_favorite(service, FALSE);
+
+       service_save(service);
+
+       return TRUE;
+}
+
+static DBusMessage *remove_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       if (__connman_service_remove(service) == FALSE)
+               return __connman_error_not_supported(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static gboolean check_suitable_state(enum connman_service_state a,
+                                       enum connman_service_state b)
+{
+       /*
+        * Special check so that "ready" service can be moved before
+        * "online" one.
+        */
+       if ((a == CONNMAN_SERVICE_STATE_ONLINE &&
+                       b == CONNMAN_SERVICE_STATE_READY) ||
+               (b == CONNMAN_SERVICE_STATE_ONLINE &&
+                       a == CONNMAN_SERVICE_STATE_READY))
+               return TRUE;
+
+       return a == b;
+}
+
+static void downgrade_state(struct connman_service *service)
+{
+       if (service == NULL)
+               return;
+
+       DBG("service %p state4 %d state6 %d", service, service->state_ipv4,
+                                               service->state_ipv6);
+
+       if (service->state_ipv4 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (service->state_ipv6 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
+static void apply_relevant_default_downgrade(struct connman_service *service)
+{
+       struct connman_service *def_service;
+
+       def_service = __connman_service_get_default();
+       if (def_service == NULL)
+               return;
+
+       if (def_service == service &&
+                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE)
+               def_service->state = CONNMAN_SERVICE_STATE_READY;
+}
+
+static void switch_default_service(struct connman_service *default_service,
+               struct connman_service *downgrade_service)
+{
+       GSequenceIter *src, *dst;
+
+       apply_relevant_default_downgrade(default_service);
+       src = g_hash_table_lookup(service_hash, downgrade_service->identifier);
+       dst = g_hash_table_lookup(service_hash, default_service->identifier);
+       g_sequence_move(src, dst);
+       downgrade_state(downgrade_service);
+}
+
+static DBusMessage *move_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data,
+                                                               gboolean before)
+{
+       struct connman_service *service = user_data;
+       struct connman_service *target;
+       const char *path;
+       enum connman_ipconfig_method target4, target6;
+       enum connman_ipconfig_method service4, service6;
+
+       DBG("service %p", service);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       if (service->favorite == FALSE)
+               return __connman_error_not_supported(msg);
+
+       target = find_service(path);
+       if (target == NULL || target->favorite == FALSE || target == service)
+               return __connman_error_invalid_service(msg);
+
+       if (target->type == CONNMAN_SERVICE_TYPE_VPN) {
+               /*
+                * We only allow VPN route splitting if there are
+                * routes defined for a given VPN.
+                */
+               if (__connman_provider_check_routes(target->provider)
+                                                               == FALSE) {
+                       connman_info("Cannot move service. "
+                               "No routes defined for provider %s",
+                               __connman_provider_get_ident(target->provider));
+                       return __connman_error_invalid_service(msg);
+               }
+
+               target->do_split_routing = TRUE;
+       } else
+               target->do_split_routing = FALSE;
+
+       service->do_split_routing = FALSE;
+
+       target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
+       target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
+       service4 = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+       service6 = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+
+       DBG("target %s method %d/%d state %d/%d split %d", target->identifier,
+               target4, target6, target->state_ipv4, target->state_ipv6,
+               target->do_split_routing);
+
+       DBG("service %s method %d/%d state %d/%d", service->identifier,
+                               service4, service6,
+                               service->state_ipv4, service->state_ipv6);
+
+       /*
+        * If method is OFF, then we do not need to check the corresponding
+        * ipconfig state.
+        */
+       if (target4 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv6,
+                                               service->state_ipv6) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (target6 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv4,
+                                               service->state_ipv4) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (service4 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv6,
+                                               service->state_ipv6) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       if (service6 == CONNMAN_IPCONFIG_METHOD_OFF) {
+               if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
+                       if (check_suitable_state(target->state_ipv4,
+                                               service->state_ipv4) == FALSE)
+                               return __connman_error_invalid_service(msg);
+               }
+       }
+
+       g_get_current_time(&service->modified);
+       service_save(service);
+       service_save(target);
+
+       /*
+        * If the service which goes down is the default service and is
+        * online, we downgrade directly its state to ready so:
+        * the service which goes up, needs to recompute its state which
+        * is triggered via downgrading it - if relevant - to state ready.
+        */
+       if (before == TRUE)
+               switch_default_service(target, service);
+       else
+               switch_default_service(service, target);
+
+       __connman_connection_update_gateway();
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *move_before(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       return move_service(conn, msg, user_data, TRUE);
+}
+
+static DBusMessage *move_after(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       return move_service(conn, msg, user_data, FALSE);
+}
+
+static DBusMessage *reset_counters(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       reset_stats(service);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct _services_notify {
+       int id;
+       GHashTable *add;
+       GHashTable *remove;
+} *services_notify;
+
+static void service_append_added_foreach(gpointer data, gpointer user_data)
+{
+       struct connman_service *service = data;
+       DBusMessageIter *iter = user_data;
+
+       if (service == NULL || service->path == NULL) {
+               DBG("service %p or path is NULL", service);
+               return;
+       }
+
+       if (g_hash_table_lookup(services_notify->add, service->path) != NULL) {
+               DBG("new %s", service->path);
+
+               append_struct(service, iter);
+               g_hash_table_remove(services_notify->add, service->path);
+       } else {
+               DBG("changed %s", service->path);
+
+               append_struct_service(iter, NULL, service);
+       }
+}
+
+static void service_append_ordered(DBusMessageIter *iter, void *user_data)
+{
+       if (service_list != NULL)
+               g_sequence_foreach(service_list,
+                                       service_append_added_foreach, iter);
+}
+
+static void append_removed(gpointer key, gpointer value, gpointer user_data)
+{
+       char *objpath = key;
+       DBusMessageIter *iter = user_data;
+
+       DBG("removed %s", objpath);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
+}
+
+static gboolean service_send_changed(gpointer data)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, array;
+
+       DBG("");
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "ServicesChanged");
+       if (signal == NULL)
+               return FALSE;
+
+       __connman_dbus_append_objpath_dict_array(signal,
+                       service_append_ordered, NULL);
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
+
+       g_hash_table_foreach(services_notify->remove, append_removed, &array);
+
+       dbus_message_iter_close_container(&iter, &array);
+
+       dbus_connection_send(connection, signal, NULL);
+       dbus_message_unref(signal);
+
+       g_hash_table_remove_all(services_notify->remove);
+       g_hash_table_remove_all(services_notify->add);
+
+       services_notify->id = 0;
+       return FALSE;
+}
+
+static void service_schedule_changed(void)
+{
+       if (services_notify->id != 0)
+               return;
+
+       services_notify->id = g_timeout_add(100, service_send_changed, NULL);
+}
+
+static void service_schedule_added(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       g_hash_table_remove(services_notify->remove, service->path);
+       g_hash_table_replace(services_notify->add, service->path, service);
+
+       service_schedule_changed();
+}
+
+static void service_schedule_removed(struct connman_service *service)
+{
+       DBG("service %p %s", service, service->path);
+
+       if (service == NULL || service->path == NULL) {
+               DBG("service %p or path is NULL", service);
+               return;
+       }
+
+       g_hash_table_remove(services_notify->add, service->path);
+       g_hash_table_replace(services_notify->remove, g_strdup(service->path),
+                       NULL);
+
+       service_schedule_changed();
+}
+
+static const GDBusMethodTable service_methods[] = {
+       { GDBUS_DEPRECATED_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_METHOD("ClearProperty",
+                       GDBUS_ARGS({ "name", "s" }), NULL,
+                       clear_property) },
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL,
+                             connect_service) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL,
+                       disconnect_service) },
+       { GDBUS_METHOD("Remove", NULL, NULL, remove_service) },
+       { GDBUS_METHOD("MoveBefore",
+                       GDBUS_ARGS({ "service", "o" }), NULL,
+                       move_before) },
+       { GDBUS_METHOD("MoveAfter",
+                       GDBUS_ARGS({ "service", "o" }), NULL,
+                       move_after) },
+       { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) },
+       { },
+};
+
+static const GDBusSignalTable service_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { },
+};
+
+static void service_free(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+       char *path = service->path;
+
+       DBG("service %p", service);
+
+       reply_pending(service, ENOENT);
+
+       g_hash_table_remove(service_hash, service->identifier);
+
+       __connman_notifier_service_remove(service);
+       service_schedule_removed(service);
+
+       stats_stop(service);
+
+       service->path = NULL;
+
+       if (path != NULL) {
+               __connman_connection_update_gateway();
+
+               g_dbus_unregister_interface(connection, path,
+                                               CONNMAN_SERVICE_INTERFACE);
+               g_free(path);
+       }
+
+       g_hash_table_destroy(service->counter_table);
+
+       if (service->network != NULL) {
+               __connman_network_disconnect(service->network);
+               connman_network_unref(service->network);
+               service->network = NULL;
+       }
+
+       if (service->provider != NULL)
+               connman_provider_unref(service->provider);
+
+       if (service->ipconfig_ipv4 != NULL) {
+               __connman_ipconfig_set_ops(service->ipconfig_ipv4, NULL);
+               __connman_ipconfig_set_data(service->ipconfig_ipv4, NULL);
+               __connman_ipconfig_unref(service->ipconfig_ipv4);
+               service->ipconfig_ipv4 = NULL;
+       }
+
+       if (service->ipconfig_ipv6 != NULL) {
+               __connman_ipconfig_set_ops(service->ipconfig_ipv6, NULL);
+               __connman_ipconfig_set_data(service->ipconfig_ipv6, NULL);
+               __connman_ipconfig_unref(service->ipconfig_ipv6);
+               service->ipconfig_ipv6 = NULL;
+       }
+
+       g_strfreev(service->timeservers);
+       g_strfreev(service->timeservers_config);
+       g_strfreev(service->nameservers);
+       g_strfreev(service->nameservers_config);
+       g_strfreev(service->nameservers_auto);
+       g_strfreev(service->domains);
+       g_strfreev(service->proxies);
+       g_strfreev(service->excludes);
+
+       g_free(service->domainname);
+       g_free(service->pac);
+       g_free(service->name);
+       g_free(service->passphrase);
+       g_free(service->agent_passphrase);
+       g_free(service->identifier);
+       g_free(service->eap);
+       g_free(service->identity);
+       g_free(service->agent_identity);
+       g_free(service->ca_cert_file);
+       g_free(service->client_cert_file);
+       g_free(service->private_key_file);
+       g_free(service->private_key_passphrase);
+       g_free(service->phase2);
+       g_free(service->config_file);
+       g_free(service->config_entry);
+
+       if (service->stats.timer != NULL)
+               g_timer_destroy(service->stats.timer);
+       if (service->stats_roaming.timer != NULL)
+               g_timer_destroy(service->stats_roaming.timer);
+
+       g_free(service);
+}
+
+static void stats_init(struct connman_service *service)
+{
+       /* home */
+       service->stats.valid = FALSE;
+       service->stats.enabled = FALSE;
+       service->stats.timer = g_timer_new();
+
+       /* roaming */
+       service->stats_roaming.valid = FALSE;
+       service->stats_roaming.enabled = FALSE;
+       service->stats_roaming.timer = g_timer_new();
+}
+
+static void service_initialize(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       service->refcount = 1;
+       service->session_usage_count = 0;
+
+       service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;
+
+       service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
+       service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
+
+       service->state = CONNMAN_SERVICE_STATE_UNKNOWN;
+       service->state_ipv4 = CONNMAN_SERVICE_STATE_UNKNOWN;
+       service->state_ipv6 = CONNMAN_SERVICE_STATE_UNKNOWN;
+
+       service->favorite  = FALSE;
+       service->immutable = FALSE;
+       service->hidden = FALSE;
+
+       service->ignore = FALSE;
+
+       service->userconnect = FALSE;
+
+       service->order = 0;
+
+       stats_init(service);
+
+       service->provider = NULL;
+
+       service->wps = FALSE;
+#if defined TIZEN_EXT
+       service->user_pdn_connection_refcount = 0;
+#endif
+}
+
+/**
+ * connman_service_create:
+ *
+ * Allocate a new service.
+ *
+ * Returns: a newly-allocated #connman_service structure
+ */
+struct connman_service *connman_service_create(void)
+{
+       GSList *list;
+       struct connman_stats_counter *counters;
+       const char *counter;
+
+       struct connman_service *service;
+
+       service = g_try_new0(struct connman_service, 1);
+       if (service == NULL)
+               return NULL;
+
+       DBG("service %p", service);
+
+       service->counter_table = g_hash_table_new_full(g_str_hash,
+                                               g_str_equal, NULL, g_free);
+
+       for (list = counter_list; list; list = list->next) {
+               counter = list->data;
+
+               counters = g_try_new0(struct connman_stats_counter, 1);
+               if (counters == NULL) {
+                       g_hash_table_destroy(service->counter_table);
+                       g_free(service);
+                       return NULL;
+               }
+
+               counters->append_all = TRUE;
+
+               g_hash_table_replace(service->counter_table, (gpointer)counter,
+                               counters);
+       }
+
+       service_initialize(service);
+
+       return service;
+}
+
+/**
+ * connman_service_ref:
+ * @service: service structure
+ *
+ * Increase reference counter of service
+ */
+struct connman_service *
+connman_service_ref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", service, service->refcount + 1,
+               file, line, caller);
+
+       __sync_fetch_and_add(&service->refcount, 1);
+
+       return service;
+}
+
+/**
+ * connman_service_unref:
+ * @service: service structure
+ *
+ * Decrease reference counter of service and release service if no
+ * longer needed.
+ */
+void connman_service_unref_debug(struct connman_service *service,
+                       const char *file, int line, const char *caller)
+{
+       GSequenceIter *iter;
+
+       DBG("%p ref %d by %s:%d:%s()", service, service->refcount - 1,
+               file, line, caller);
+
+       if (__sync_fetch_and_sub(&service->refcount, 1) != 1)
+               return;
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL) {
+               reply_pending(service, ECONNABORTED);
+
+               __connman_service_disconnect(service);
+
+               g_sequence_remove(iter);
+       } else {
+               service_free(service);
+       }
+}
+
+static gint service_compare(gconstpointer a, gconstpointer b,
+                                                       gpointer user_data)
+{
+       struct connman_service *service_a = (void *) a;
+       struct connman_service *service_b = (void *) b;
+       enum connman_service_state state_a, state_b;
+
+       state_a = service_a->state;
+       state_b = service_b->state;
+
+       if (state_a != state_b) {
+               gboolean a_connected = is_connected(service_a);
+               gboolean b_connected = is_connected(service_b);
+
+               if (a_connected == TRUE && b_connected == TRUE) {
+                       /* We prefer online over ready state */
+                       if (state_a == CONNMAN_SERVICE_STATE_ONLINE)
+                               return -1;
+
+                       if (state_b == CONNMAN_SERVICE_STATE_ONLINE)
+                               return 1;
+               }
+
+               if (a_connected == TRUE)
+                       return -1;
+               if (b_connected == TRUE)
+                       return 1;
+
+               if (is_connecting(service_a) == TRUE)
+                       return -1;
+               if (is_connecting(service_b) == TRUE)
+                       return 1;
+       }
+
+       if (service_a->order > service_b->order)
+               return -1;
+
+       if (service_a->order < service_b->order)
+               return 1;
+
+       if (service_a->favorite == TRUE && service_b->favorite == FALSE)
+               return -1;
+
+       if (service_a->favorite == FALSE && service_b->favorite == TRUE)
+               return 1;
+
+       if (service_a->type != service_b->type) {
+               switch (service_a->type) {
+               case CONNMAN_SERVICE_TYPE_UNKNOWN:
+               case CONNMAN_SERVICE_TYPE_SYSTEM:
+               case CONNMAN_SERVICE_TYPE_ETHERNET:
+               case CONNMAN_SERVICE_TYPE_GPS:
+               case CONNMAN_SERVICE_TYPE_VPN:
+               case CONNMAN_SERVICE_TYPE_GADGET:
+                       break;
+               case CONNMAN_SERVICE_TYPE_WIFI:
+                       return 1;
+               case CONNMAN_SERVICE_TYPE_WIMAX:
+               case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               case CONNMAN_SERVICE_TYPE_CELLULAR:
+                       return -1;
+               }
+       }
+
+       return (gint) service_b->strength - (gint) service_a->strength;
+}
+
+/**
+ * connman_service_get_type:
+ * @service: service structure
+ *
+ * Get the type of service
+ */
+enum connman_service_type connman_service_get_type(struct connman_service *service)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+       return service->type;
+}
+
+/**
+ * connman_service_get_interface:
+ * @service: service structure
+ *
+ * Get network interface of service
+ */
+char *connman_service_get_interface(struct connman_service *service)
+{
+       int index;
+
+       if (service == NULL)
+               return NULL;
+
+       if (service->type == CONNMAN_SERVICE_TYPE_VPN) {
+               if (service->ipconfig_ipv4)
+                       index = __connman_ipconfig_get_index(
+                                               service->ipconfig_ipv4);
+               else if (service->ipconfig_ipv6)
+                       index = __connman_ipconfig_get_index(
+                                               service->ipconfig_ipv6);
+               else
+                       return NULL;
+
+               return connman_inet_ifname(index);
+       }
+
+       if (service->network == NULL)
+               return NULL;
+
+       index = connman_network_get_index(service->network);
+
+       return connman_inet_ifname(index);
+}
+
+/**
+ * connman_service_get_network:
+ * @service: service structure
+ *
+ * Get the service network
+ */
+struct connman_network *
+__connman_service_get_network(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->network;
+}
+
+struct connman_ipconfig *
+__connman_service_get_ip4config(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->ipconfig_ipv4;
+}
+
+struct connman_ipconfig *
+__connman_service_get_ip6config(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->ipconfig_ipv6;
+}
+
+struct connman_ipconfig *
+__connman_service_get_ipconfig(struct connman_service *service, int family)
+{
+       if (family == AF_INET)
+               return __connman_service_get_ip4config(service);
+       else if (family == AF_INET6)
+               return __connman_service_get_ip6config(service);
+       else
+               return NULL;
+
+}
+
+connman_bool_t __connman_service_is_connected_state(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       if (service == NULL)
+               return FALSE;
+
+       switch (type) {
+       case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+               break;
+       case CONNMAN_IPCONFIG_TYPE_IPV4:
+               return is_connected_state(service, service->state_ipv4);
+       case CONNMAN_IPCONFIG_TYPE_IPV6:
+               return is_connected_state(service, service->state_ipv6);
+       }
+
+       return FALSE;
+}
+enum connman_service_security __connman_service_get_security(struct connman_service *service)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+
+       return service->security;
+}
+
+const char *__connman_service_get_phase2(struct connman_service *service)
+{
+       if (service == NULL)
+               return NULL;
+
+       return service->phase2;
+}
+
+connman_bool_t __connman_service_wps_enabled(struct connman_service *service)
+{
+       if (service == NULL)
+               return FALSE;
+
+       return service->wps;
+}
+
+void __connman_service_mark_dirty()
+ {
+       services_dirty = TRUE;
+ }
+
+#if defined TIZEN_EXT
+/**
+  * Returns profile count if there is any connected profiles
+  * that use same interface
+  */
+int __connman_service_get_connected_count_of_iface(
+                                       struct connman_service *service)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *ifname1;
+       const char *ifname2;
+       int count = 0;
+
+       DBG("");
+
+       if (service->ipconfig_ipv4)
+               ifname1 = __connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname1 = __connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       else
+               ifname1 = NULL;
+
+       if (ifname1 == NULL)
+               return 0;
+
+       g_hash_table_iter_init(&iter, service_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               GSequenceIter *iter = value;
+               struct connman_service *iter_service = g_sequence_get(iter);
+
+               if (service == iter_service)
+                       continue;
+
+               if (iter_service->ipconfig_ipv4)
+                       ifname2 = __connman_ipconfig_get_ifname(
+                                       iter_service->ipconfig_ipv4);
+               else if (iter_service->ipconfig_ipv6)
+                       ifname2 = __connman_ipconfig_get_ifname(
+                                       iter_service->ipconfig_ipv6);
+               else
+                       ifname2 = NULL;
+
+               if (is_connected(iter_service) && ifname2 != NULL &&
+                               g_strcmp0(ifname1, ifname2) == 0)
+                       count++;
+       }
+
+       DBG("Interface %s, count %d", ifname1, count);
+
+       return count;
+}
+#endif
+
+/**
+ * __connman_service_set_favorite_delayed:
+ * @service: service structure
+ * @favorite: favorite value
+ * @delay_ordering: do not order service sequence
+ *
+ * Change the favorite setting of service
+ */
+int __connman_service_set_favorite_delayed(struct connman_service *service,
+                                       connman_bool_t favorite,
+                                       gboolean delay_ordering)
+{
+       GSequenceIter *iter;
+
+       if (service->hidden == TRUE)
+               return -EOPNOTSUPP;
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter == NULL)
+               return -ENOENT;
+
+       if (service->favorite == favorite)
+               return -EALREADY;
+
+       service->favorite = favorite;
+
+       if (delay_ordering == FALSE)
+               service->order = __connman_service_get_order(service);
+
+       favorite_changed(service);
+
+       if (delay_ordering == FALSE) {
+
+               if (g_sequence_get_length(service_list) > 1) {
+                       g_sequence_sort_changed(iter, service_compare, NULL);
+                       service_schedule_changed();
+               }
+
+               __connman_connection_update_gateway();
+       }
+
+#if defined TIZEN_EXT
+       {
+               struct connman_device *device = connman_network_get_device(
+                                                       service->network);
+
+               if (connman_device_get_type(device) ==
+                                       CONNMAN_DEVICE_TYPE_WIFI) {
+                       if (service->favorite == TRUE)
+                               connman_device_sig_wifi_profile_ref(device);
+                       else
+                               connman_device_sig_wifi_profile_unref_and_test
+                                                               (device);
+               }
+
+               connman_device_save_sig_wifi_profile_refcount2storage(device);
+       }
+#endif
+       return 0;
+}
+
+/**
+ * __connman_service_set_favorite:
+ * @service: service structure
+ * @favorite: favorite value
+ *
+ * Change the favorite setting of service
+ */
+int __connman_service_set_favorite(struct connman_service *service,
+                                               connman_bool_t favorite)
+{
+       return __connman_service_set_favorite_delayed(service, favorite,
+                                                       FALSE);
+}
+
+int __connman_service_set_immutable(struct connman_service *service,
+                                               connman_bool_t immutable)
+{
+       if (service->hidden == TRUE)
+               return -EOPNOTSUPP;
+       service->immutable = immutable;
+
+       immutable_changed(service);
+
+       return 0;
+}
+
+void __connman_service_set_string(struct connman_service *service,
+                                 const char *key, const char *value)
+{
+       if (service->hidden == TRUE)
+               return;
+       if (g_str_equal(key, "EAP") == TRUE) {
+               g_free(service->eap);
+               service->eap = g_strdup(value);
+       } else if (g_str_equal(key, "Identity") == TRUE) {
+               g_free(service->identity);
+               service->identity = g_strdup(value);
+       } else if (g_str_equal(key, "CACertFile") == TRUE) {
+               g_free(service->ca_cert_file);
+               service->ca_cert_file = g_strdup(value);
+       } else if (g_str_equal(key, "ClientCertFile") == TRUE) {
+               g_free(service->client_cert_file);
+               service->client_cert_file = g_strdup(value);
+       } else if (g_str_equal(key, "PrivateKeyFile") == TRUE) {
+               g_free(service->private_key_file);
+               service->private_key_file = g_strdup(value);
+       } else if (g_str_equal(key, "PrivateKeyPassphrase") == TRUE) {
+               g_free(service->private_key_passphrase);
+               service->private_key_passphrase = g_strdup(value);
+       } else if (g_str_equal(key, "Phase2") == TRUE) {
+               g_free(service->phase2);
+               service->phase2 = g_strdup(value);
+       } else if (g_str_equal(key, "Passphrase") == TRUE) {
+               g_free(service->passphrase);
+               service->passphrase = g_strdup(value);
+       }
+}
+
+void __connman_service_set_userconnect(struct connman_service *service,
+                                               connman_bool_t userconnect)
+{
+       if (service != NULL)
+               service->userconnect = userconnect;
+}
+
+static void service_complete(struct connman_service *service)
+{
+       reply_pending(service, EIO);
+
+       if (service->userconnect == FALSE)
+               __connman_service_auto_connect();
+
+       g_get_current_time(&service->modified);
+       service_save(service);
+}
+
+static void report_error_cb(struct connman_service *service,
+                       gboolean retry, void *user_data)
+{
+       if (retry == TRUE)
+               __connman_service_connect(service);
+       else {
+               /* It is not relevant to stay on Failure state
+                * when failing is due to wrong user input */
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+
+               service_complete(service);
+               __connman_connection_update_gateway();
+       }
+}
+
+int __connman_service_add_passphrase(struct connman_service *service,
+                               const gchar *passphrase)
+{
+       int err = 0;
+
+       switch (service->security) {
+       case CONNMAN_SERVICE_SECURITY_WEP:
+       case CONNMAN_SERVICE_SECURITY_PSK:
+               err = __connman_service_set_passphrase(service, passphrase);
+               break;
+       case CONNMAN_SERVICE_SECURITY_8021X:
+               __connman_service_set_agent_passphrase(service,
+                                               passphrase);
+               break;
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+       case CONNMAN_SERVICE_SECURITY_NONE:
+       case CONNMAN_SERVICE_SECURITY_WPA:
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               DBG("service security '%s' (%d) not handled",
+                               security2string(service->security),
+                               service->security);
+               break;
+       }
+
+       return err;
+}
+
+static int check_wpspin(struct connman_service *service, const char *wpspin)
+{
+       int length;
+       guint i;
+
+       if (wpspin == NULL)
+               return 0;
+
+       length = strlen(wpspin);
+
+       /* If 0, it will mean user wants to use PBC method */
+       if (length == 0) {
+               connman_network_set_string(service->network,
+                                                       "WiFi.PinWPS", NULL);
+               return 0;
+       }
+
+       /* A WPS PIN is always 8 chars length,
+        * its content is in digit representation.
+        */
+       if (length != 8)
+               return -ENOKEY;
+
+       for (i = 0; i < 8; i++)
+               if (!isdigit((unsigned char) wpspin[i]))
+                       return -ENOKEY;
+
+       connman_network_set_string(service->network, "WiFi.PinWPS", wpspin);
+
+       return 0;
+}
+
+static void request_input_cb (struct connman_service *service,
+                       connman_bool_t values_received,
+                       const char *name, int name_len,
+                       const char *identity, const char *passphrase,
+                       gboolean wps, const char *wpspin,
+                       const char *error, void *user_data)
+{
+       struct connman_device *device;
+       int err = 0;
+
+       DBG ("RequestInput return, %p", service);
+
+       if (error != NULL) {
+               DBG("error: %s", error);
+
+               if (g_strcmp0(error,
+                               "net.connman.Agent.Error.Canceled") == 0) {
+                       err = -EINVAL;
+
+                       if (service->hidden == TRUE)
+                               __connman_service_return_error(service,
+                                                       ECANCELED, user_data);
+                       goto done;
+               } else {
+                       if (service->hidden == TRUE)
+                               __connman_service_return_error(service,
+                                                       ETIMEDOUT, user_data);
+               }
+       }
+
+       if (service->hidden == TRUE && name_len > 0 && name_len <= 32) {
+               device = connman_network_get_device(service->network);
+               err = __connman_device_request_hidden_scan(device,
+                                               name, name_len,
+                                               identity, passphrase,
+                                               user_data);
+               if (err < 0)
+                       __connman_service_return_error(service, -err,
+                                                       user_data);
+       }
+
+       if (values_received == FALSE || service->hidden == TRUE) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (wps == TRUE && service->network != NULL) {
+               err = check_wpspin(service, wpspin);
+               if (err < 0)
+                       goto done;
+
+               connman_network_set_bool(service->network, "WiFi.UseWPS", wps);
+       }
+
+       if (identity != NULL)
+               __connman_service_set_agent_identity(service, identity);
+
+       if (passphrase != NULL)
+               err = __connman_service_add_passphrase(service, passphrase);
+
+ done:
+       if (err >= 0) {
+               /* We forget any previous error. */
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+
+               __connman_service_connect(service);
+
+               /* Never cache agent provided credentials */
+               __connman_service_set_agent_identity(service, NULL);
+               __connman_service_set_agent_passphrase(service, NULL);
+       } else if (err == -ENOKEY) {
+               __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY);
+       } else {
+               /* It is not relevant to stay on Failure state
+                * when failing is due to wrong user input */
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+
+               if (service->hidden == FALSE) {
+                       /*
+                        * If there was a real error when requesting
+                        * hidden scan, then that error is returned already
+                        * to the user somewhere above so do not try to
+                        * do this again.
+                        */
+                       __connman_service_return_error(service, -err,
+                                                       user_data);
+               }
+
+               service_complete(service);
+               __connman_connection_update_gateway();
+       }
+}
+
+static void downgrade_connected_services(void)
+{
+       struct connman_service *up_service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               up_service = g_sequence_get(iter);
+
+               if (is_connected(up_service) == FALSE) {
+                       iter = g_sequence_iter_next(iter);
+                       continue;
+               }
+
+               if (up_service->state == CONNMAN_SERVICE_STATE_ONLINE)
+                       return;
+
+               downgrade_state(up_service);
+
+               iter = g_sequence_iter_next(iter);
+       }
+}
+
+static int service_update_preferred_order(struct connman_service *default_service,
+               struct connman_service *new_service,
+               enum connman_service_state new_state)
+{
+       unsigned int *tech_array;
+       int i;
+
+       if (default_service == NULL || default_service == new_service ||
+                       default_service->state != new_state )
+               return 0;
+
+       tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+       if (tech_array != NULL) {
+
+               for (i = 0; tech_array[i] != 0; i += 1) {
+                       if (default_service->type == tech_array[i])
+                               return -EALREADY;
+
+                       if (new_service->type == tech_array[i]) {
+                               switch_default_service(default_service,
+                                               new_service);
+                               __connman_connection_update_gateway();
+                               return 0;
+                       }
+               }
+       }
+
+       return -EALREADY;
+}
+
+#if defined TIZEN_EXT
+static connman_bool_t __connman_service_can_drop_cellular(
+               struct connman_service *cellular)
+{
+       if (cellular->type == CONNMAN_SERVICE_TYPE_CELLULAR &&
+                               is_connected(cellular) == TRUE)
+               if (connman_service_is_no_ref_user_pdn_connection(cellular)
+                                                               == TRUE)
+                       return TRUE;
+       return FALSE;
+}
+#endif
+
+static void single_connected_tech(struct connman_service *allowed)
+{
+       GSList *services = NULL;
+       GSequenceIter *iter;
+       GSList *list;
+
+#if defined TIZEN_EXT
+       if (allowed->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+               return;
+#endif
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *service = g_sequence_get(iter);
+
+               if (service != allowed && is_connected(service))
+#if defined TIZEN_EXT
+                       if (__connman_service_can_drop_cellular(service) == TRUE)
+#endif
+                       services = g_slist_prepend(services, service);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       DBG("keeping %p %s", allowed, allowed->path);
+
+       for (list = services; list != NULL; list = list->next) {
+               struct connman_service *service = list->data;
+
+               DBG("disconnecting %p %s", service, service->path);
+               __connman_service_disconnect(service);
+       }
+
+       g_slist_free(services);
+}
+
+static int service_indicate_state(struct connman_service *service)
+{
+       enum connman_service_state old_state, new_state;
+       struct connman_service *def_service;
+       int result;
+       GSequenceIter *iter;
+
+       if (service == NULL)
+               return -EINVAL;
+
+       old_state = service->state;
+       new_state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       DBG("service %p old %s - new %s/%s => %s",
+                                       service,
+                                       state2string(old_state),
+                                       state2string(service->state_ipv4),
+                                       state2string(service->state_ipv6),
+                                       state2string(new_state));
+
+       if (old_state == new_state)
+               return -EALREADY;
+
+       def_service = __connman_service_get_default();
+
+       if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = service_update_preferred_order(def_service,
+                               service, new_state);
+               if (result == -EALREADY)
+                       return result;
+       }
+
+       if (old_state == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_notifier_leave_online(service->type);
+
+       service->state = new_state;
+       state_changed(service);
+
+       if (new_state == CONNMAN_SERVICE_STATE_IDLE &&
+                       old_state != CONNMAN_SERVICE_STATE_DISCONNECT) {
+#if !defined TIZEN_EXT
+               /*
+                * Description: 'service->pending' should be cleared whenever
+                * connection is finished regardless success or failure.
+                * If the service is disconnected in configuration state by
+                * dhcp failure or by the other part of connman, new state is
+                * 'idle' but old state is 'disconnect'. So it's not cleared.
+                */
+               reply_pending(service, ECONNABORTED);
+#endif
+
+               __connman_service_disconnect(service);
+       }
+
+       if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               if (service->new_service == FALSE &&
+                               __connman_stats_service_register(service) == 0) {
+                       /*
+                        * For new services the statistics are updated after
+                        * we have successfully connected.
+                        */
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+       }
+
+       if (new_state == CONNMAN_SERVICE_STATE_IDLE) {
+               connman_bool_t reconnect;
+
+#if defined TIZEN_EXT
+               reply_pending(service, ECONNABORTED);
+#endif
+               reconnect = get_reconnect_state(service);
+               if (reconnect == TRUE)
+                       __connman_service_auto_connect();
+       }
+
+       if (new_state == CONNMAN_SERVICE_STATE_READY) {
+               enum connman_ipconfig_method method;
+
+               if (service->new_service == TRUE &&
+                               __connman_stats_service_register(service) == 0) {
+                       /*
+                        * This is normally done after configuring state
+                        * but for new service do this after we have connected
+                        * successfully.
+                        */
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+
+               service->new_service = FALSE;
+
+               service_update_preferred_order(def_service, service, new_state);
+
+               set_reconnect_state(service, TRUE);
+
+               __connman_service_set_favorite(service, TRUE);
+
+               reply_pending(service, 0);
+
+               g_get_current_time(&service->modified);
+               service_save(service);
+
+               update_nameservers(service);
+               dns_changed(service);
+               domain_changed(service);
+
+               __connman_notifier_connect(service->type);
+
+               if (service->type == CONNMAN_SERVICE_TYPE_WIFI &&
+                       connman_network_get_bool(service->network,
+                                               "WiFi.UseWPS") == TRUE) {
+                       const char *pass;
+
+                       pass = connman_network_get_string(service->network,
+                                                       "WiFi.Passphrase");
+
+                       __connman_service_set_passphrase(service, pass);
+
+                       connman_network_set_bool(service->network,
+                                                       "WiFi.UseWPS", FALSE);
+               }
+
+               default_changed();
+
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF)
+                       __connman_ipconfig_disable_ipv6(
+                                               service->ipconfig_ipv6);
+
+               if (connman_setting_get_bool("SingleConnectedTechnology")
+                               == TRUE)
+                       single_connected_tech(service);
+
+       } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               def_service = __connman_service_get_default();
+
+               if (__connman_notifier_is_connected() == FALSE &&
+                       def_service != NULL &&
+                               def_service->provider != NULL)
+                       __connman_provider_disconnect(def_service->provider);
+
+               default_changed();
+
+               __connman_wispr_stop(service);
+
+               __connman_wpad_stop(service);
+
+#if defined TIZEN_EXT
+               /**
+                 * Skip the functions if there is any connected profiles
+                 * that use same interface
+                 */
+               if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR ||
+                       __connman_service_get_connected_count_of_iface(
+                                                       service) <= 0) {
+#endif
+               update_nameservers(service);
+               dns_changed(service);
+               domain_changed(service);
+
+               __connman_notifier_disconnect(service->type);
+#if defined TIZEN_EXT
+               }
+#endif
+
+               /*
+                * Previous services which are connected and which states
+                * are set to online should reset relevantly ipconfig_state
+                * to ready so wispr/portal will be rerun on those
+                */
+               downgrade_connected_services();
+       }
+
+       if (new_state == CONNMAN_SERVICE_STATE_FAILURE) {
+               if (service->userconnect == TRUE &&
+                       __connman_agent_report_error(service,
+                                       error2string(service->error),
+                                       report_error_cb, NULL) == -EINPROGRESS)
+                       return 0;
+               service_complete(service);
+       } else
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+
+#if defined TIZEN_EXT
+       def_service = __connman_service_get_default();
+       if (def_service == NULL)
+               __connman_service_auto_connect();
+#endif
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
+               g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
+
+       __connman_connection_update_gateway();
+
+       if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
+               __connman_notifier_enter_online(service->type);
+               default_changed();
+       }
+
+       return 0;
+}
+
+int __connman_service_indicate_error(struct connman_service *service,
+                                       enum connman_service_error error)
+{
+       DBG("service %p error %d", service, error);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       set_error(service, error);
+
+       if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY)
+               __connman_service_set_passphrase(service, NULL);
+
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_FAILURE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_FAILURE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       return 0;
+}
+
+int __connman_service_clear_error(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       if (service->state != CONNMAN_SERVICE_STATE_FAILURE)
+               return -EINVAL;
+
+       service->state_ipv4 = service->state_ipv6 =
+                                               CONNMAN_SERVICE_STATE_UNKNOWN;
+       set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+
+       if (service->favorite == TRUE)
+               set_reconnect_state(service, TRUE);
+
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_IDLE,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       /*
+        * Toggling the IPv6 state to IDLE could trigger the auto connect
+        * machinery and consequently the IPv4 state.
+        */
+       if (service->state_ipv4 != CONNMAN_SERVICE_STATE_UNKNOWN &&
+                       service->state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
+               return 0;
+
+       return __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+}
+
+int __connman_service_indicate_default(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       default_changed();
+
+       return 0;
+}
+
+enum connman_service_state __connman_service_ipconfig_get_state(
+                                       struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_STATE_UNKNOWN;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               return service->state_ipv4;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               return service->state_ipv6;
+
+       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 != NULL))
+               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_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
+ * connectivity works ok. This is only done for IPv4 networks as IPv6
+ * does not have rp_filter knob.
+ */
+static int connected_networks_count;
+static int original_rp_filter;
+
+static void service_rp_filter(struct connman_service *service,
+                               gboolean connected)
+{
+       enum connman_ipconfig_method method;
+
+       method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               break;
+       }
+
+       if (connected == TRUE) {
+               if (connected_networks_count == 1) {
+                       int filter_value;
+                       filter_value = __connman_ipconfig_set_rp_filter();
+                       if (filter_value < 0)
+                               return;
+
+                       original_rp_filter = filter_value;
+               }
+               connected_networks_count++;
+
+       } else {
+               if (connected_networks_count == 2)
+                       __connman_ipconfig_unset_rp_filter(original_rp_filter);
+
+               connected_networks_count--;
+               if (connected_networks_count < 0)
+                       connected_networks_count = 0;
+       }
+
+       DBG("%s %s ipconfig %p method %d count %d filter %d",
+               connected ? "connected" : "disconnected", service->identifier,
+               service->ipconfig_ipv4, method,
+               connected_networks_count, original_rp_filter);
+}
+
+static gboolean redo_wispr(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("");
+
+       __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       return FALSE;
+}
+
+int __connman_service_online_check_failed(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       DBG("service %p type %d count %d", service, type,
+                                               service->online_check_count);
+
+       /* currently we only retry IPv6 stuff */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 ||
+                       service->online_check_count != 1) {
+               connman_warn("Online check failed for %p %s", service,
+                       service->name);
+               return 0;
+       }
+
+       service->online_check_count = 0;
+
+       /*
+        * We set the timeout to 1 sec so that we have a chance to get
+        * necessary IPv6 router advertisement messages that might have
+        * DNS data etc.
+        */
+       g_timeout_add_seconds(1, redo_wispr, service);
+
+       return EAGAIN;
+}
+
+int __connman_service_ipconfig_indicate_state(struct connman_service *service,
+                                       enum connman_service_state new_state,
+                                       enum connman_ipconfig_type type)
+{
+       struct connman_ipconfig *ipconfig = NULL;
+       enum connman_service_state old_state;
+       int ret;
+
+       if (service == NULL)
+               return -EINVAL;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               old_state = service->state_ipv4;
+               ipconfig = service->ipconfig_ipv4;
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               old_state = service->state_ipv6;
+               ipconfig = service->ipconfig_ipv6;
+       }
+
+       if (ipconfig == NULL)
+               return -EINVAL;
+
+#if defined TIZEN_EXT
+       if (new_state == CONNMAN_SERVICE_STATE_FAILURE &&
+           service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
+               service->user_pdn_connection_refcount = 0;
+#endif
+       /* Any change? */
+       if (old_state == new_state)
+               return -EALREADY;
+
+       DBG("service %p (%s) state %d (%s) type %d (%s)",
+               service, service ? service->identifier : NULL,
+               new_state, state2string(new_state),
+               type, __connman_ipconfig_type2string(type));
+
+       switch (new_state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+               if (service->state == CONNMAN_SERVICE_STATE_FAILURE)
+                       return -EINVAL;
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+               break;
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               __connman_ipconfig_enable(ipconfig);
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+               update_nameservers(service);
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+                       check_proxy_setup(service);
+                       service_rp_filter(service, TRUE);
+               } else {
+                       service->online_check_count = 1;
+                       __connman_wispr_start(service, type);
+               }
+               break;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               if (service->state == CONNMAN_SERVICE_STATE_IDLE)
+                       return -EINVAL;
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                       service_rp_filter(service, FALSE);
+
+               break;
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       }
+
+       /* We keep that state */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               service->state_ipv4 = new_state;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               service->state_ipv6 = new_state;
+
+       ret = service_indicate_state(service);
+
+       /*
+        * If the ipconfig method is OFF, then we set the state to IDLE
+        * so that it will not affect the combined state in the future.
+        */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               enum connman_ipconfig_method method;
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
+                               method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) {
+                       service->state_ipv4 = CONNMAN_SERVICE_STATE_IDLE;
+                       ret = service_indicate_state(service);
+               }
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               enum connman_ipconfig_method method;
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
+                               method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) {
+                       service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+                       ret = service_indicate_state(service);
+               }
+       }
+
+       return ret;
+}
+
+static connman_bool_t prepare_network(struct connman_service *service)
+{
+       enum connman_network_type type;
+       unsigned int ssid_len;
+
+       type = connman_network_get_type(service->network);
+
+       switch (type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               return FALSE;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               if (connman_network_get_blob(service->network, "WiFi.SSID",
+                                                       &ssid_len) == NULL)
+                       return FALSE;
+
+               if (service->passphrase != NULL)
+                       connman_network_set_string(service->network,
+                               "WiFi.Passphrase", service->passphrase);
+               break;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+               break;
+       }
+
+       return TRUE;
+}
+
+static void prepare_8021x(struct connman_service *service)
+{
+       if (service->eap != NULL)
+               connman_network_set_string(service->network, "WiFi.EAP",
+                                                               service->eap);
+
+       if (service->identity != NULL)
+               connman_network_set_string(service->network, "WiFi.Identity",
+                                                       service->identity);
+
+       if (service->ca_cert_file != NULL)
+               connman_network_set_string(service->network, "WiFi.CACertFile",
+                                                       service->ca_cert_file);
+
+       if (service->client_cert_file != NULL)
+               connman_network_set_string(service->network,
+                                               "WiFi.ClientCertFile",
+                                               service->client_cert_file);
+
+       if (service->private_key_file != NULL)
+               connman_network_set_string(service->network,
+                                               "WiFi.PrivateKeyFile",
+                                               service->private_key_file);
+
+       if (service->private_key_passphrase != NULL)
+               connman_network_set_string(service->network,
+                                       "WiFi.PrivateKeyPassphrase",
+                                       service->private_key_passphrase);
+
+       if (service->phase2 != NULL)
+               connman_network_set_string(service->network, "WiFi.Phase2",
+                                                       service->phase2);
+}
+
+static int service_connect(struct connman_service *service)
+{
+       int err;
+
+       if (service->hidden == TRUE)
+               return -EPERM;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EINVAL;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_VPN:
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               switch (service->security) {
+               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               case CONNMAN_SERVICE_SECURITY_NONE:
+                       break;
+               case CONNMAN_SERVICE_SECURITY_WEP:
+               case CONNMAN_SERVICE_SECURITY_PSK:
+               case CONNMAN_SERVICE_SECURITY_WPA:
+               case CONNMAN_SERVICE_SECURITY_RSN:
+                       if (service->passphrase == NULL) {
+                               if (service->network == NULL)
+                                       return -EOPNOTSUPP;
+
+                               if (service->wps == FALSE ||
+                                       connman_network_get_bool(
+                                                       service->network,
+                                                       "WiFi.UseWPS") == FALSE)
+                                       return -ENOKEY;
+                       } else if (service->error ==
+                                       CONNMAN_SERVICE_ERROR_INVALID_KEY)
+                               return -ENOKEY;
+                       break;
+               case CONNMAN_SERVICE_SECURITY_8021X:
+                       if (service->eap == NULL)
+                               return -EINVAL;
+
+#if defined TIZEN_EXT
+                       /*
+                        * never request credentials if using EAP-TLS, EAP-SIM
+                        * or EAP-AKA (EAP-TLS, EAP-SIM and EAP-AKA networks
+                        * need to be fully provisioned)
+                        */
+                       if (g_str_equal(service->eap, "tls") == TRUE ||
+                               g_str_equal(service->eap, "sim") == TRUE ||
+                               g_str_equal(service->eap, "aka") == TRUE)
+                               break;
+#else
+                       /*
+                        * never request credentials if using EAP-TLS
+                        * (EAP-TLS networks need to be fully provisioned)
+                        */
+                       if (g_str_equal(service->eap, "tls") == TRUE)
+                               break;
+#endif
+
+                       /*
+                        * Return -ENOKEY if either identity or passphrase is
+                        * missing. Agent provided credentials can be used as
+                        * fallback if needed.
+                        */
+                       if ((service->identity == NULL &&
+                                       service->agent_identity == NULL) ||
+                                       (service->passphrase == NULL &&
+                                       service->agent_passphrase == NULL))
+                               return -ENOKEY;
+
+                       break;
+               }
+               break;
+       }
+
+       if (service->network != NULL) {
+               if (prepare_network(service) == FALSE)
+                       return -EINVAL;
+
+               switch (service->security) {
+               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               case CONNMAN_SERVICE_SECURITY_NONE:
+               case CONNMAN_SERVICE_SECURITY_WEP:
+               case CONNMAN_SERVICE_SECURITY_PSK:
+               case CONNMAN_SERVICE_SECURITY_WPA:
+               case CONNMAN_SERVICE_SECURITY_RSN:
+                       break;
+               case CONNMAN_SERVICE_SECURITY_8021X:
+                       prepare_8021x(service);
+                       break;
+               }
+
+               if (__connman_stats_service_register(service) == 0) {
+                       __connman_stats_get(service, FALSE,
+                                               &service->stats.data);
+                       __connman_stats_get(service, TRUE,
+                                               &service->stats_roaming.data);
+               }
+
+               if (service->ipconfig_ipv4)
+                       __connman_ipconfig_enable(service->ipconfig_ipv4);
+               if (service->ipconfig_ipv6)
+                       __connman_ipconfig_enable(service->ipconfig_ipv6);
+
+               err = __connman_network_connect(service->network);
+       } else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                                       service->provider != NULL)
+               err = __connman_provider_connect(service->provider);
+       else
+               return -EOPNOTSUPP;
+
+       if (err < 0) {
+               if (err != -EINPROGRESS) {
+                       __connman_ipconfig_disable(service->ipconfig_ipv4);
+                       __connman_ipconfig_disable(service->ipconfig_ipv6);
+                       __connman_stats_service_unregister(service);
+               }
+       }
+
+       return err;
+}
+
+
+int __connman_service_connect(struct connman_service *service)
+{
+       int err;
+
+       DBG("service %p state %s", service, state2string(service->state));
+
+       if (is_connected(service) == TRUE)
+               return -EISCONN;
+
+       if (is_connecting(service) == TRUE)
+               return -EALREADY;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EINVAL;
+       default:
+               err = service_connect(service);
+       }
+
+       if (err >= 0) {
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+               return 0;
+       }
+
+       if (err == -EINPROGRESS) {
+               if (service->timeout == 0)
+                       service->timeout = g_timeout_add_seconds(
+                               CONNECT_TIMEOUT, connect_timeout, service);
+               set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+               return -EINPROGRESS;
+       }
+
+       if (service->network != NULL)
+               __connman_network_disconnect(service->network);
+       else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                               service->provider != NULL)
+                       __connman_provider_disconnect(service->provider);
+
+       if (service->userconnect == TRUE) {
+               if (err == -ENOKEY || err == -EPERM) {
+                       DBusMessage *pending = NULL;
+
+                       /*
+                        * We steal the reply here. The idea is that the
+                        * connecting client will see the connection status
+                        * after the real hidden network is connected or
+                        * connection failed.
+                        */
+                       if (service->hidden == TRUE) {
+                               pending = service->pending;
+                               service->pending = NULL;
+                       }
+
+                       err = __connman_agent_request_passphrase_input(service,
+                                       request_input_cb, pending);
+                       if (service->hidden == TRUE && err != -EINPROGRESS)
+                               service->pending = pending;
+
+                       return err;
+               }
+               reply_pending(service, -err);
+       }
+
+       return err;
+}
+
+int __connman_service_disconnect(struct connman_service *service)
+{
+       int err;
+
+       DBG("service %p", service);
+
+       service->userconnect = FALSE;
+
+       if (service->network != NULL) {
+               err = __connman_network_disconnect(service->network);
+       } else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                                       service->provider != NULL)
+               err = __connman_provider_disconnect(service->provider);
+       else
+               return -EOPNOTSUPP;
+
+       if (err < 0 && err != -EINPROGRESS)
+               return err;
+
+       __connman_6to4_remove(service->ipconfig_ipv4);
+
+       if (service->ipconfig_ipv4)
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv4,
+                                                       NULL);
+       else
+               __connman_ipconfig_set_proxy_autoconfig(service->ipconfig_ipv6,
+                                                       NULL);
+
+#if defined TIZEN_EXT
+       /**
+         * Skip the functions If there is any connected profiles
+         * that use same interface
+         */
+       if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR ||
+               __connman_service_get_connected_count_of_iface(service) <= 0) {
+#endif
+       __connman_ipconfig_address_remove(service->ipconfig_ipv4);
+       __connman_ipconfig_address_remove(service->ipconfig_ipv6);
+
+       __connman_ipconfig_disable(service->ipconfig_ipv4);
+       __connman_ipconfig_disable(service->ipconfig_ipv6);
+#if defined TIZEN_EXT
+       }
+#endif
+
+       __connman_stats_service_unregister(service);
+
+       return err;
+}
+
+int __connman_service_disconnect_all(void)
+{
+       GSequenceIter *iter;
+       GSList *services = NULL, *list;
+
+       DBG("");
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               struct connman_service *service = g_sequence_get(iter);
+
+               services = g_slist_prepend(services, service);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       for (list = services; list != NULL; list = list->next) {
+               struct connman_service *service = list->data;
+
+               service->ignore = TRUE;
+
+               set_reconnect_state(service, FALSE);
+
+               __connman_service_disconnect(service);
+       }
+
+       g_slist_free(list);
+
+       return 0;
+
+}
+
+/**
+ * lookup_by_identifier:
+ * @identifier: service identifier
+ *
+ * Look up a service by identifier (reference count will not be increased)
+ */
+static struct connman_service *lookup_by_identifier(const char *identifier)
+{
+       GSequenceIter *iter;
+
+       iter = g_hash_table_lookup(service_hash, identifier);
+       if (iter != NULL)
+               return g_sequence_get(iter);
+
+       return NULL;
+}
+
+struct provision_user_data {
+       const char *ident;
+       int ret;
+};
+
+static void provision_changed(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       struct provision_user_data *data = user_data;
+       const char *path = data->ident;
+       int ret;
+
+       ret = __connman_config_provision_service_ident(service, path,
+                       service->config_file, service->config_entry);
+       if (ret > 0)
+               data->ret = ret;
+}
+
+int __connman_service_provision_changed(const char *ident)
+{
+       struct provision_user_data data = {
+               .ident = ident,
+               .ret = 0
+       };
+
+       g_sequence_foreach(service_list, provision_changed, (void *)&data);
+
+       /*
+        * Because the provision_changed() might have set some services
+        * as favorite, we must sort the sequence now.
+        */
+       if (services_dirty == TRUE) {
+               services_dirty = FALSE;
+
+               if (g_sequence_get_length(service_list) > 1) {
+                       g_sequence_sort(service_list, service_compare, NULL);
+                       service_schedule_changed();
+               }
+
+               __connman_connection_update_gateway();
+       }
+
+       return data.ret;
+}
+
+void __connman_service_set_config(struct connman_service *service,
+                               const char *file_id, const char *entry)
+{
+       if (service == NULL)
+               return;
+
+       g_free(service->config_file);
+       service->config_file = g_strdup(file_id);
+
+       g_free(service->config_entry);
+       service->config_entry = g_strdup(entry);
+}
+
+/**
+ * __connman_service_get:
+ * @identifier: service identifier
+ *
+ * Look up a service by identifier or create a new one if not found
+ */
+static struct connman_service *service_get(const char *identifier)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_hash_table_lookup(service_hash, identifier);
+       if (iter != NULL) {
+               service = g_sequence_get(iter);
+               if (service != NULL)
+                       connman_service_ref(service);
+               return service;
+       }
+
+       service = connman_service_create();
+       if (service == NULL)
+               return NULL;
+
+       DBG("service %p", service);
+
+       service->identifier = g_strdup(identifier);
+
+       iter = g_sequence_insert_sorted(service_list, service,
+                                               service_compare, NULL);
+
+       g_hash_table_insert(service_hash, service->identifier, iter);
+
+       return service;
+}
+
+static int service_register(struct connman_service *service)
+{
+       GSequenceIter *iter;
+
+       DBG("service %p", service);
+
+       if (service->path != NULL)
+               return -EALREADY;
+
+       service->path = g_strdup_printf("%s/service/%s", CONNMAN_PATH,
+                                               service->identifier);
+
+       DBG("path %s", service->path);
+
+       __connman_config_provision_service(service);
+
+       service_load(service);
+
+       g_dbus_register_interface(connection, service->path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                       service_methods, service_signals,
+                                                       NULL, service, NULL);
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
+               g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
+
+       __connman_connection_update_gateway();
+
+       return 0;
+}
+
+static void service_up(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
+
+       DBG("%s up", __connman_ipconfig_get_ifname(ipconfig));
+
+       link_changed(service);
+
+       service->stats.valid = FALSE;
+       service->stats_roaming.valid = FALSE;
+}
+
+static void service_down(struct connman_ipconfig *ipconfig)
+{
+       DBG("%s down", __connman_ipconfig_get_ifname(ipconfig));
+}
+
+static void service_lower_up(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
+
+       DBG("%s lower up", __connman_ipconfig_get_ifname(ipconfig));
+
+       stats_start(service);
+}
+
+static void service_lower_down(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
+
+       DBG("%s lower down", __connman_ipconfig_get_ifname(ipconfig));
+
+       if (is_idle_state(service, service->state_ipv4) == FALSE)
+               __connman_ipconfig_disable(service->ipconfig_ipv4);
+
+       if (is_idle_state(service, service->state_ipv6) == FALSE)
+               __connman_ipconfig_disable(service->ipconfig_ipv6);
+
+       stats_stop(service);
+       service_save(service);
+}
+
+static void service_ip_bound(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+
+       DBG("%s ip bound", __connman_ipconfig_get_ifname(ipconfig));
+
+       type = __connman_ipconfig_get_config_type(ipconfig);
+       method = __connman_ipconfig_get_method(ipconfig);
+
+       DBG("service %p ipconfig %p type %d method %d", service, ipconfig,
+                                                       type, method);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       method == CONNMAN_IPCONFIG_METHOD_AUTO)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       settings_changed(service, ipconfig);
+}
+
+static void service_ip_release(struct connman_ipconfig *ipconfig)
+{
+       struct connman_service *service = __connman_ipconfig_get_data(ipconfig);
+       enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+
+       DBG("%s ip release", __connman_ipconfig_get_ifname(ipconfig));
+
+       type = __connman_ipconfig_get_config_type(ipconfig);
+       method = __connman_ipconfig_get_method(ipconfig);
+
+       DBG("service %p ipconfig %p type %d method %d", service, ipconfig,
+                                                       type, method);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
+                       method == CONNMAN_IPCONFIG_METHOD_OFF)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV6);
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4 &&
+                       method == CONNMAN_IPCONFIG_METHOD_OFF)
+               __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       settings_changed(service, ipconfig);
+}
+
+static const struct connman_ipconfig_ops service_ops = {
+       .up             = service_up,
+       .down           = service_down,
+       .lower_up       = service_lower_up,
+       .lower_down     = service_lower_down,
+       .ip_bound       = service_ip_bound,
+       .ip_release     = service_ip_release,
+};
+
+static void setup_ip4config(struct connman_service *service, int index,
+                       enum connman_ipconfig_method method)
+{
+       service->ipconfig_ipv4 = __connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (service->ipconfig_ipv4 == NULL)
+               return;
+
+       __connman_ipconfig_set_method(service->ipconfig_ipv4, method);
+
+       __connman_ipconfig_set_data(service->ipconfig_ipv4, service);
+
+       __connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops);
+}
+
+static void setup_ip6config(struct connman_service *service, int index)
+{
+       service->ipconfig_ipv6 = __connman_ipconfig_create(index,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (service->ipconfig_ipv6 == NULL)
+               return;
+
+       __connman_ipconfig_set_data(service->ipconfig_ipv6, service);
+
+       __connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops);
+}
+
+void __connman_service_read_ip4config(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+
+       if (service->ipconfig_ipv4 == NULL)
+               return;
+
+       keyfile = connman_storage_load_service(service->identifier);
+       if (keyfile == NULL)
+               return;
+
+       __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                               service->identifier, "IPv4.");
+
+       g_key_file_free(keyfile);
+}
+
+void __connman_service_create_ip4config(struct connman_service *service,
+                                       int index)
+{
+       DBG("ipv4 %p", service->ipconfig_ipv4);
+
+       if (service->ipconfig_ipv4 != NULL)
+               return;
+
+       setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_DHCP);
+       __connman_service_read_ip4config(service);
+}
+
+void __connman_service_read_ip6config(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+
+       if (service->ipconfig_ipv6 == NULL)
+               return;
+
+       keyfile = connman_storage_load_service(service->identifier);
+       if (keyfile == NULL)
+               return;
+
+       __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
+                               service->identifier, "IPv6.");
+
+       g_key_file_free(keyfile);
+}
+
+void __connman_service_create_ip6config(struct connman_service *service,
+                                                               int index)
+{
+       DBG("ipv6 %p", service->ipconfig_ipv6);
+
+       if (service->ipconfig_ipv6 != NULL)
+               return;
+
+       setup_ip6config(service, index);
+
+       __connman_service_read_ip6config(service);
+}
+
+/**
+ * __connman_service_lookup_from_network:
+ * @network: network structure
+ *
+ * Look up a service by network (reference count will not be increased)
+ */
+struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
+       const char *ident, *group;
+       char *name;
+
+       DBG("network %p", network);
+
+       if (network == NULL)
+               return NULL;
+
+       ident = __connman_network_get_ident(network);
+       if (ident == NULL)
+               return NULL;
+
+       group = connman_network_get_group(network);
+       if (group == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s_%s",
+                       __connman_network_get_type(network), ident, group);
+       service = lookup_by_identifier(name);
+       g_free(name);
+
+       return service;
+}
+
+struct connman_service *__connman_service_lookup_from_index(int index)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               service = g_sequence_get(iter);
+
+               if (__connman_ipconfig_get_index(service->ipconfig_ipv4)
+                                                       == index)
+                       return service;
+
+               if (__connman_ipconfig_get_index(service->ipconfig_ipv6)
+                                                       == index)
+                       return service;
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       return NULL;
+}
+
+struct connman_service *__connman_service_lookup_from_ident(const char *identifier)
+{
+       return lookup_by_identifier(identifier);
+}
+
+const char *__connman_service_get_ident(struct connman_service *service)
+{
+       return service->identifier;
+}
+
+const char *__connman_service_get_path(struct connman_service *service)
+{
+       return service->path;
+}
+
+unsigned int __connman_service_get_order(struct connman_service *service)
+{
+       GSequenceIter *iter;
+
+       if (service == NULL)
+               return 0;
+
+       if (service->favorite == FALSE) {
+               service->order = 0;
+               goto done;
+       }
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL) {
+               if (g_sequence_iter_get_position(iter) == 0)
+                       service->order = 1;
+               else if (service->type == CONNMAN_SERVICE_TYPE_VPN &&
+                               service->do_split_routing == FALSE)
+                       service->order = 10;
+               else
+                       service->order = 0;
+       }
+
+       DBG("service %p name %s order %d split %d", service, service->name,
+               service->order, service->do_split_routing);
+
+done:
+       return service->order;
+}
+
+void __connman_service_update_ordering(void)
+{
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+       if (iter != NULL && g_sequence_get_length(service_list) > 1)
+               g_sequence_sort_changed(iter, service_compare, NULL);
+}
+
+static enum connman_service_type convert_network_type(struct connman_network *network)
+{
+       enum connman_network_type type = connman_network_get_type(network);
+
+       switch (type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               break;
+       case CONNMAN_NETWORK_TYPE_ETHERNET:
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       case CONNMAN_NETWORK_TYPE_CELLULAR:
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       }
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static enum connman_service_security convert_wifi_security(const char *security)
+{
+       if (security == NULL)
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+       else if (g_str_equal(security, "none") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_NONE;
+       else if (g_str_equal(security, "wep") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_WEP;
+       else if (g_str_equal(security, "psk") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_PSK;
+       else if (g_str_equal(security, "ieee8021x") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_8021X;
+       else if (g_str_equal(security, "wpa") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_WPA;
+       else if (g_str_equal(security, "rsn") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_RSN;
+       else
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+}
+
+static void update_from_network(struct connman_service *service,
+                                       struct connman_network *network)
+{
+       connman_uint8_t strength = service->strength;
+       GSequenceIter *iter;
+       const char *str;
+
+       DBG("service %p network %p", service, network);
+
+       if (is_connected(service) == TRUE)
+               return;
+
+       if (is_connecting(service) == TRUE)
+               return;
+
+       str = connman_network_get_string(network, "Name");
+       if (str != NULL) {
+               g_free(service->name);
+               service->name = g_strdup(str);
+               service->hidden = FALSE;
+       } else {
+               g_free(service->name);
+               service->name = NULL;
+               service->hidden = TRUE;
+       }
+
+       service->strength = connman_network_get_strength(network);
+       service->roaming = connman_network_get_bool(network, "Roaming");
+
+       if (service->strength == 0) {
+               /*
+                * Filter out 0-values; it's unclear what they mean
+                * and they cause anomalous sorting of the priority list.
+                */
+               service->strength = strength;
+       }
+
+       str = connman_network_get_string(network, "WiFi.Security");
+       service->security = convert_wifi_security(str);
+
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
+
+       if (service->strength > strength && service->network != NULL) {
+               connman_network_unref(service->network);
+               service->network = connman_network_ref(network);
+
+               strength_changed(service);
+       }
+
+       if (service->network == NULL)
+               service->network = connman_network_ref(network);
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL && g_sequence_get_length(service_list) > 1) {
+               g_sequence_sort_changed(iter, service_compare, NULL);
+               service_schedule_changed();
+       }
+}
+
+/**
+ * __connman_service_create_from_network:
+ * @network: network structure
+ *
+ * Look up service by network and if not found, create one
+ */
+struct connman_service * __connman_service_create_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
+       struct connman_device *device;
+       const char *ident, *group;
+       char *name;
+       unsigned int *auto_connect_types;
+       int i, index;
+
+       DBG("network %p", network);
+
+       if (network == NULL)
+               return NULL;
+
+       ident = __connman_network_get_ident(network);
+       if (ident == NULL)
+               return NULL;
+
+       group = connman_network_get_group(network);
+       if (group == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s_%s",
+                       __connman_network_get_type(network), ident, group);
+       service = service_get(name);
+       g_free(name);
+
+       if (service == NULL)
+               return NULL;
+
+       if (__connman_network_get_weakness(network) == TRUE)
+               return service;
+
+       if (service->path != NULL) {
+               update_from_network(service, network);
+               __connman_connection_update_gateway();
+               return service;
+       }
+
+       service->type = convert_network_type(network);
+
+       auto_connect_types = connman_setting_get_uint_list("DefaultAutoConnectTechnologies");
+       service->autoconnect = FALSE;
+       for (i = 0; auto_connect_types != NULL &&
+                    auto_connect_types[i] != 0; i++) {
+               if (service->type == auto_connect_types[i]) {
+                       service->autoconnect = TRUE;
+                       break;
+               }
+       }
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               break;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               service->favorite = TRUE;
+               break;
+       }
+
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+       service->state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       update_from_network(service, network);
+
+       index = connman_network_get_index(network);
+
+       if (service->ipconfig_ipv4 == NULL)
+               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_DHCP);
+
+       if (service->ipconfig_ipv6 == NULL)
+               setup_ip6config(service, index);
+
+       service_register(service);
+
+       if (service->favorite == TRUE) {
+               device = connman_network_get_device(service->network);
+               if (device && connman_device_get_scanning(device) == FALSE)
+                       __connman_service_auto_connect();
+       }
+
+       __connman_notifier_service_add(service, service->name);
+       service_schedule_added(service);
+
+       return service;
+}
+
+void __connman_service_update_from_network(struct connman_network *network)
+{
+       connman_bool_t need_sort = FALSE;
+       struct connman_service *service;
+       connman_uint8_t strength;
+       connman_bool_t roaming;
+       GSequenceIter *iter;
+       const char *name;
+       connman_bool_t stats_enable;
+
+       DBG("network %p", network);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       if (service->network == NULL)
+               return;
+
+       name = connman_network_get_string(service->network, "Name");
+       if (g_strcmp0(service->name, name) != 0) {
+               g_free(service->name);
+               service->name = g_strdup(name);
+               connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Name",
+                               DBUS_TYPE_STRING, &service->name);
+       }
+
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
+
+       strength = connman_network_get_strength(service->network);
+       if (strength == service->strength)
+               goto roaming;
+
+       service->strength = strength;
+       need_sort = TRUE;
+
+       strength_changed(service);
+
+roaming:
+       roaming = connman_network_get_bool(service->network, "Roaming");
+       if (roaming == service->roaming)
+               goto sorting;
+
+       stats_enable = stats_enabled(service);
+       if (stats_enable == TRUE)
+               stats_stop(service);
+
+       service->roaming = roaming;
+       need_sort = TRUE;
+
+       if (stats_enable == TRUE)
+               stats_start(service);
+
+       roaming_changed(service);
+
+sorting:
+       if (need_sort == TRUE) {
+               iter = g_hash_table_lookup(service_hash, service->identifier);
+               if (iter != NULL && g_sequence_get_length(service_list) > 1) {
+                       g_sequence_sort_changed(iter, service_compare, NULL);
+                       service_schedule_changed();
+               }
+       }
+}
+
+void __connman_service_remove_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       DBG("network %p", network);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
+
+       service->ignore = TRUE;
+
+       __connman_connection_gateway_remove(service,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+       connman_service_unref(service);
+}
+
+/**
+ * __connman_service_create_from_provider:
+ * @provider: provider structure
+ *
+ * Look up service by provider and if not found, create one
+ */
+struct connman_service *
+__connman_service_create_from_provider(struct connman_provider *provider)
+{
+       struct connman_service *service;
+       const char *ident, *str;
+       char *name;
+       int index = connman_provider_get_index(provider);
+
+       DBG("provider %p", provider);
+
+       ident = __connman_provider_get_ident(provider);
+       if (ident == NULL)
+               return NULL;
+
+       name = g_strdup_printf("vpn_%s", ident);
+       service = service_get(name);
+       g_free(name);
+
+       if (service == NULL)
+               return NULL;
+
+       service->type = CONNMAN_SERVICE_TYPE_VPN;
+       service->provider = connman_provider_ref(provider);
+       service->autoconnect = FALSE;
+       service->userconnect = TRUE;
+
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+       service->state = combine_state(service->state_ipv4, service->state_ipv6);
+
+       str = connman_provider_get_string(provider, "Name");
+       if (str != NULL) {
+               g_free(service->name);
+               service->name = g_strdup(str);
+               service->hidden = FALSE;
+       } else {
+               g_free(service->name);
+               service->name = NULL;
+               service->hidden = TRUE;
+       }
+
+       service->strength = 0;
+
+       if (service->ipconfig_ipv4 == NULL)
+               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_MANUAL);
+
+       if (service->ipconfig_ipv6 == NULL)
+               setup_ip6config(service, index);
+
+       service_register(service);
+
+       __connman_notifier_service_add(service, service->name);
+       service_schedule_added(service);
+
+       return service;
+}
+
+static void remove_unprovisioned_services()
+{
+       gchar **services;
+       GKeyFile *keyfile, *configkeyfile;
+       char *file, *section;
+       int i = 0;
+
+       services = connman_storage_get_services();
+       if (services == NULL)
+               return;
+
+       for (;services[i] != NULL; i++) {
+               file = section = NULL;
+               keyfile = configkeyfile = NULL;
+
+               keyfile = connman_storage_load_service(services[i]);
+               if (keyfile == NULL)
+                       continue;
+
+               file = g_key_file_get_string(keyfile, services[i],
+                                       "Config.file", NULL);
+               if (file == NULL)
+                       goto next;
+
+               section = g_key_file_get_string(keyfile, services[i],
+                                       "Config.ident", NULL);
+               if (section == NULL)
+                       goto next;
+
+               configkeyfile = __connman_storage_load_config(file);
+               if (configkeyfile == NULL) {
+                       /*
+                        * Config file is missing, remove the provisioned
+                        * service.
+                        */
+                       __connman_storage_remove_service(services[i]);
+                       goto next;
+               }
+
+               if (g_key_file_has_group(configkeyfile, section) == FALSE)
+                       /*
+                        * Config section is missing, remove the provisioned
+                        * service.
+                        */
+                       __connman_storage_remove_service(services[i]);
+
+       next:
+               if (keyfile != NULL)
+                       g_key_file_free(keyfile);
+
+               if (configkeyfile != NULL)
+                       g_key_file_free(configkeyfile);
+
+               g_free(section);
+               g_free(file);
+       }
+
+       g_strfreev(services);
+}
+
+int __connman_service_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       service_list = g_sequence_new(service_free);
+
+       services_notify = g_new0(struct _services_notify, 1);
+       services_notify->remove = g_hash_table_new_full(g_str_hash,
+                       g_str_equal, g_free, NULL);
+       services_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
+
+       remove_unprovisioned_services();
+
+       return 0;
+}
+
+void __connman_service_cleanup(void)
+{
+       GSequence *list;
+
+       DBG("");
+
+       if (autoconnect_timeout != 0) {
+               g_source_remove(autoconnect_timeout);
+               autoconnect_timeout = 0;
+       }
+
+       list = service_list;
+       service_list = NULL;
+       g_sequence_free(list);
+
+       g_hash_table_destroy(service_hash);
+       service_hash = NULL;
+
+       g_slist_free(counter_list);
+       counter_list = NULL;
+
+       if (services_notify->id != 0) {
+               g_source_remove(services_notify->id);
+               service_send_changed(NULL);
+               g_hash_table_destroy(services_notify->remove);
+               g_hash_table_destroy(services_notify->add);
+       }
+       g_free(services_notify);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/session.c b/src/session.c
new file mode 100644 (file)
index 0000000..a5db7ad
--- /dev/null
@@ -0,0 +1,2017 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  BWM CarIT GmbH. 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 <errno.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+static GHashTable *session_hash;
+static connman_bool_t sessionmode;
+static struct session_info *ecall_info;
+
+enum connman_session_trigger {
+       CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
+       CONNMAN_SESSION_TRIGGER_SETTING         = 1,
+       CONNMAN_SESSION_TRIGGER_CONNECT         = 2,
+       CONNMAN_SESSION_TRIGGER_DISCONNECT      = 3,
+       CONNMAN_SESSION_TRIGGER_PERIODIC        = 4,
+       CONNMAN_SESSION_TRIGGER_SERVICE         = 5,
+       CONNMAN_SESSION_TRIGGER_ECALL           = 6,
+};
+
+enum connman_session_reason {
+       CONNMAN_SESSION_REASON_UNKNOWN          = 0,
+       CONNMAN_SESSION_REASON_CONNECT          = 1,
+       CONNMAN_SESSION_REASON_DISCONNECT       = 2,
+       CONNMAN_SESSION_REASON_FREE_RIDE        = 3,
+       CONNMAN_SESSION_REASON_PERIODIC         = 4,
+};
+
+enum connman_session_state {
+       CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
+       CONNMAN_SESSION_STATE_CONNECTED      = 1,
+       CONNMAN_SESSION_STATE_ONLINE         = 2,
+};
+
+enum connman_session_type {
+       CONNMAN_SESSION_TYPE_ANY      = 0,
+       CONNMAN_SESSION_TYPE_LOCAL    = 1,
+       CONNMAN_SESSION_TYPE_INTERNET = 2,
+};
+
+enum connman_session_roaming_policy {
+       CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
+       CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
+       CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
+       CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
+       CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
+       CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
+};
+
+struct service_entry {
+       /* track why this service was selected */
+       enum connman_session_reason reason;
+       enum connman_service_state state;
+       const char *name;
+       struct connman_service *service;
+       char *ifname;
+       const char *bearer;
+       GSList *pending_timeouts;
+};
+
+struct session_info {
+       enum connman_session_state state;
+       enum connman_session_type type;
+       connman_bool_t priority;
+       GSList *allowed_bearers;
+       connman_bool_t avoid_handover;
+       connman_bool_t stay_connected;
+       unsigned int periodic_connect;
+       unsigned int idle_timeout;
+       connman_bool_t ecall;
+       enum connman_session_roaming_policy roaming_policy;
+       unsigned int marker;
+
+       struct service_entry *entry;
+       enum connman_session_reason reason;
+};
+
+struct connman_session {
+       char *owner;
+       char *session_path;
+       char *notify_path;
+       guint notify_watch;
+
+       connman_bool_t append_all;
+       struct session_info *info;
+       struct session_info *info_last;
+
+       GSequence *service_list;
+       GHashTable *service_hash;
+};
+
+struct bearer_info {
+       char *name;
+       connman_bool_t match_all;
+       enum connman_service_type service_type;
+};
+
+static const char *trigger2string(enum connman_session_trigger trigger)
+{
+       switch (trigger) {
+       case CONNMAN_SESSION_TRIGGER_UNKNOWN:
+               break;
+       case CONNMAN_SESSION_TRIGGER_SETTING:
+               return "setting";
+       case CONNMAN_SESSION_TRIGGER_CONNECT:
+               return "connect";
+       case CONNMAN_SESSION_TRIGGER_DISCONNECT:
+               return "disconnect";
+       case CONNMAN_SESSION_TRIGGER_PERIODIC:
+               return "periodic";
+       case CONNMAN_SESSION_TRIGGER_SERVICE:
+               return "service";
+       case CONNMAN_SESSION_TRIGGER_ECALL:
+               return "ecall";
+       }
+
+       return NULL;
+}
+
+static const char *reason2string(enum connman_session_reason reason)
+{
+       switch (reason) {
+       case CONNMAN_SESSION_REASON_UNKNOWN:
+               return "unknown";
+       case CONNMAN_SESSION_REASON_CONNECT:
+               return "connect";
+       case CONNMAN_SESSION_REASON_DISCONNECT:
+               return "disconnect";
+       case CONNMAN_SESSION_REASON_FREE_RIDE:
+               return "free-ride";
+       case CONNMAN_SESSION_REASON_PERIODIC:
+               return "periodic";
+       }
+
+       return NULL;
+}
+
+static const char *state2string(enum connman_session_state state)
+{
+       switch (state) {
+       case CONNMAN_SESSION_STATE_DISCONNECTED:
+               return "disconnected";
+       case CONNMAN_SESSION_STATE_CONNECTED:
+               return "connected";
+       case CONNMAN_SESSION_STATE_ONLINE:
+               return "online";
+       }
+
+       return NULL;
+}
+
+static const char *type2string(enum connman_session_type type)
+{
+       switch (type) {
+       case CONNMAN_SESSION_TYPE_ANY:
+               return "";
+       case CONNMAN_SESSION_TYPE_LOCAL:
+               return "local";
+       case CONNMAN_SESSION_TYPE_INTERNET:
+               return "internet";
+       }
+
+       return NULL;
+}
+
+static enum connman_session_type string2type(const char *type)
+{
+       if (g_strcmp0(type, "local") == 0)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+       else if (g_strcmp0(type, "internet") == 0)
+               return CONNMAN_SESSION_TYPE_INTERNET;
+
+       return CONNMAN_SESSION_TYPE_ANY;
+}
+
+static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
+{
+       switch (policy) {
+       case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
+               return "unknown";
+       case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
+               return "default";
+       case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
+               return "always";
+       case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
+               return "forbidden";
+       case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
+               return "national";
+       case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
+               return "international";
+       }
+
+       return NULL;
+}
+
+static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
+{
+       if (g_strcmp0(policy, "default") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
+       else if (g_strcmp0(policy, "always") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
+       else if (g_strcmp0(policy, "forbidden") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
+       else if (g_strcmp0(policy, "national") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
+       else if (g_strcmp0(policy, "international") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
+       else
+               return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
+}
+
+static enum connman_service_type bearer2service(const char *bearer)
+{
+       if (bearer == NULL)
+               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+       if (g_strcmp0(bearer, "ethernet") == 0)
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       else if (g_strcmp0(bearer, "wifi") == 0)
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       else if (g_strcmp0(bearer, "wimax") == 0)
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       else if (g_strcmp0(bearer, "bluetooth") == 0)
+               return CONNMAN_SERVICE_TYPE_BLUETOOTH;
+       else if (g_strcmp0(bearer, "cellular") == 0)
+               return CONNMAN_SERVICE_TYPE_CELLULAR;
+       else if (g_strcmp0(bearer, "vpn") == 0)
+               return CONNMAN_SERVICE_TYPE_VPN;
+       else
+               return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static char *service2bearer(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return "bluetooth";
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return "cellular";
+       case CONNMAN_SERVICE_TYPE_VPN:
+               return "vpn";
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return "";
+       }
+
+       return "";
+}
+
+static void cleanup_bearer_info(gpointer data, gpointer user_data)
+{
+       struct bearer_info *info = data;
+
+       g_free(info->name);
+       g_free(info);
+}
+
+static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
+{
+       struct bearer_info *info;
+       DBusMessageIter array;
+       GSList *list = NULL;
+
+       dbus_message_iter_recurse(iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+               char *bearer = NULL;
+
+               dbus_message_iter_get_basic(&array, &bearer);
+
+               info = g_try_new0(struct bearer_info, 1);
+               if (info == NULL) {
+                       g_slist_foreach(list, cleanup_bearer_info, NULL);
+                       g_slist_free(list);
+
+                       return NULL;
+               }
+
+               info->name = g_strdup(bearer);
+               info->service_type = bearer2service(info->name);
+
+               if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
+                               g_strcmp0(info->name, "*") == 0) {
+                       info->match_all = TRUE;
+               } else {
+                       info->match_all = FALSE;
+               }
+
+               list = g_slist_append(list, info);
+
+               dbus_message_iter_next(&array);
+       }
+
+       return list;
+}
+
+static GSList *session_allowed_bearers_any(void)
+{
+       struct bearer_info *info;
+       GSList *list = NULL;
+
+       info = g_try_new0(struct bearer_info, 1);
+       if (info == NULL) {
+               g_slist_free(list);
+
+               return NULL;
+       }
+
+       info->name = g_strdup("");
+       info->match_all = TRUE;
+       info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
+
+       list = g_slist_append(list, info);
+
+       return list;
+}
+
+static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
+{
+       struct session_info *info = user_data;
+       GSList *list;
+
+       for (list = info->allowed_bearers;
+                       list != NULL; list = list->next) {
+               struct bearer_info *info = list->data;
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                               &info->name);
+       }
+}
+
+static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       struct connman_ipconfig *ipconfig_ipv4;
+
+       if (service == NULL)
+               return;
+
+       if (__connman_service_is_connected_state(service,
+                               CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
+               return;
+       }
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       if (ipconfig_ipv4 == NULL)
+               return;
+
+       __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
+}
+
+static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
+{
+       struct connman_service *service = user_data;
+       struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+
+       if (service == NULL)
+               return;
+
+       if (__connman_service_is_connected_state(service,
+                               CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
+               return;
+       }
+
+       ipconfig_ipv4 = __connman_service_get_ip4config(service);
+       ipconfig_ipv6 = __connman_service_get_ip6config(service);
+       if (ipconfig_ipv6 == NULL)
+               return;
+
+       __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
+}
+
+static void append_notify(DBusMessageIter *dict,
+                                       struct connman_session *session)
+{
+       struct session_info *info = session->info;
+       struct session_info *info_last = session->info_last;
+       const char *policy;
+       struct connman_service *service;
+       const char *name, *ifname, *bearer;
+
+       if (session->append_all == TRUE ||
+                       info->state != info_last->state) {
+               const char *state = state2string(info->state);
+
+               connman_dbus_dict_append_basic(dict, "State",
+                                               DBUS_TYPE_STRING,
+                                               &state);
+               info_last->state = info->state;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->entry != info_last->entry) {
+               if (info->entry == NULL) {
+                       name = "";
+                       ifname = "";
+                       service = NULL;
+                       bearer = "";
+               } else {
+                       name = info->entry->name;
+                       ifname = info->entry->ifname;
+                       service = info->entry->service;
+                       bearer = info->entry->bearer;
+               }
+
+               connman_dbus_dict_append_basic(dict, "Name",
+                                               DBUS_TYPE_STRING,
+                                               &name);
+
+               connman_dbus_dict_append_dict(dict, "IPv4",
+                                               append_ipconfig_ipv4,
+                                               service);
+
+               connman_dbus_dict_append_dict(dict, "IPv6",
+                                               append_ipconfig_ipv6,
+                                               service);
+
+               connman_dbus_dict_append_basic(dict, "Interface",
+                                               DBUS_TYPE_STRING,
+                                               &ifname);
+
+               connman_dbus_dict_append_basic(dict, "Bearer",
+                                               DBUS_TYPE_STRING,
+                                               &bearer);
+
+               info_last->entry = info->entry;
+       }
+
+       if (session->append_all == TRUE || info->type != info_last->type) {
+               const char *type = type2string(info->type);
+
+               connman_dbus_dict_append_basic(dict, "ConnectionType",
+                                               DBUS_TYPE_STRING,
+                                               &type);
+               info_last->type = info->type;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->priority != info_last->priority) {
+               connman_dbus_dict_append_basic(dict, "Priority",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->priority);
+               info_last->priority = info->priority;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->allowed_bearers != info_last->allowed_bearers) {
+               connman_dbus_dict_append_array(dict, "AllowedBearers",
+                                               DBUS_TYPE_STRING,
+                                               append_allowed_bearers,
+                                               info);
+               info_last->allowed_bearers = info->allowed_bearers;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->avoid_handover != info_last->avoid_handover) {
+               connman_dbus_dict_append_basic(dict, "AvoidHandover",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->avoid_handover);
+               info_last->avoid_handover = info->avoid_handover;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->stay_connected != info_last->stay_connected) {
+               connman_dbus_dict_append_basic(dict, "StayConnected",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->stay_connected);
+               info_last->stay_connected = info->stay_connected;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->periodic_connect != info_last->periodic_connect) {
+               connman_dbus_dict_append_basic(dict, "PeriodicConnect",
+                                               DBUS_TYPE_UINT32,
+                                               &info->periodic_connect);
+               info_last->periodic_connect = info->periodic_connect;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->idle_timeout != info_last->idle_timeout) {
+               connman_dbus_dict_append_basic(dict, "IdleTimeout",
+                                               DBUS_TYPE_UINT32,
+                                               &info->idle_timeout);
+               info_last->idle_timeout = info->idle_timeout;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->ecall != info_last->ecall) {
+               connman_dbus_dict_append_basic(dict, "EmergencyCall",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->ecall);
+               info_last->ecall = info->ecall;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->roaming_policy != info_last->roaming_policy) {
+               policy = roamingpolicy2string(info->roaming_policy);
+               connman_dbus_dict_append_basic(dict, "RoamingPolicy",
+                                               DBUS_TYPE_STRING,
+                                               &policy);
+               info_last->roaming_policy = info->roaming_policy;
+       }
+
+       if (session->append_all == TRUE ||
+                       info->marker != info_last->marker) {
+               connman_dbus_dict_append_basic(dict, "SessionMarker",
+                                               DBUS_TYPE_UINT32,
+                                               &info->marker);
+               info_last->marker = info->marker;
+       }
+
+       session->append_all = FALSE;
+}
+
+static connman_bool_t is_type_matching_state(enum connman_session_state *state,
+                                               enum connman_session_type type)
+{
+       switch (type) {
+       case CONNMAN_SESSION_TYPE_ANY:
+               return TRUE;
+       case CONNMAN_SESSION_TYPE_LOCAL:
+               if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
+                       *state = CONNMAN_SESSION_STATE_CONNECTED;
+                       return TRUE;
+               }
+
+               break;
+       case CONNMAN_SESSION_TYPE_INTERNET:
+               if (*state == CONNMAN_SESSION_STATE_ONLINE)
+                       return TRUE;
+               break;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t compute_notifiable_changes(struct connman_session *session)
+{
+       struct session_info *info_last = session->info_last;
+       struct session_info *info = session->info;
+
+       if (session->append_all == TRUE)
+               return TRUE;
+
+       if (info->state != info_last->state)
+               return TRUE;
+
+       if (info->entry != info_last->entry &&
+                       info->state >= CONNMAN_SESSION_STATE_CONNECTED)
+               return TRUE;
+
+       if (info->periodic_connect != info_last->periodic_connect ||
+                       info->allowed_bearers != info_last->allowed_bearers ||
+                       info->avoid_handover != info_last->avoid_handover ||
+                       info->stay_connected != info_last->stay_connected ||
+                       info->roaming_policy != info_last->roaming_policy ||
+                       info->idle_timeout != info_last->idle_timeout ||
+                       info->priority != info_last->priority ||
+                       info->marker != info_last->marker ||
+                       info->ecall != info_last->ecall ||
+                       info->type != info_last->type)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean session_notify(gpointer user_data)
+{
+       struct connman_session *session = user_data;
+       DBusMessage *msg;
+       DBusMessageIter array, dict;
+
+       if (compute_notifiable_changes(session) == FALSE)
+               return FALSE;
+
+       DBG("session %p owner %s notify_path %s", session,
+               session->owner, session->notify_path);
+
+       msg = dbus_message_new_method_call(session->owner, session->notify_path,
+                                               CONNMAN_NOTIFICATION_INTERFACE,
+                                               "Update");
+       if (msg == NULL)
+               return FALSE;
+
+       dbus_message_iter_init_append(msg, &array);
+       connman_dbus_dict_open(&array, &dict);
+
+       append_notify(&dict, session);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       g_dbus_send_message(connection, msg);
+
+       return FALSE;
+}
+
+static void ipconfig_ipv4_changed(struct connman_session *session)
+{
+       struct session_info *info = session->info;
+
+       connman_dbus_setting_changed_dict(session->owner, session->notify_path,
+                                               "IPv4", append_ipconfig_ipv4,
+                                               info->entry->service);
+}
+
+static void ipconfig_ipv6_changed(struct connman_session *session)
+{
+       struct session_info *info = session->info;
+
+       connman_dbus_setting_changed_dict(session->owner, session->notify_path,
+                                               "IPv6", append_ipconfig_ipv6,
+                                               info->entry->service);
+}
+
+static connman_bool_t service_type_match(struct connman_session *session,
+                                       struct connman_service *service)
+{
+       struct session_info *info = session->info;
+       GSList *list;
+
+       for (list = info->allowed_bearers;
+                       list != NULL; list = list->next) {
+               struct bearer_info *info = list->data;
+               enum connman_service_type service_type;
+
+               if (info->match_all == TRUE)
+                       return TRUE;
+
+               service_type = connman_service_get_type(service);
+               if (info->service_type == service_type)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t service_match(struct connman_session *session,
+                                       struct connman_service *service)
+{
+       if (service_type_match(session, service) == FALSE)
+               return FALSE;
+
+       return TRUE;
+}
+
+static int service_type_weight(enum connman_service_type type)
+{
+       /*
+        * The session doesn't care which service
+        * to use. Nevertheless we have to sort them
+        * according their type. The ordering is
+        *
+        * 1. Ethernet
+        * 2. Bluetooth
+        * 3. WiFi/WiMAX
+        * 4. Cellular
+        */
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               return 4;
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return 3;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return 2;
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return 1;
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       }
+
+       return 0;
+}
+
+static gint sort_allowed_bearers(struct connman_service *service_a,
+                                       struct connman_service *service_b,
+                                       struct connman_session *session)
+{
+       struct session_info *info = session->info;
+       GSList *list;
+       enum connman_service_type type_a, type_b;
+       int weight_a, weight_b;
+
+       type_a = connman_service_get_type(service_a);
+       type_b = connman_service_get_type(service_b);
+
+       for (list = info->allowed_bearers;
+                       list != NULL; list = list->next) {
+               struct bearer_info *info = list->data;
+
+               if (info->match_all == TRUE) {
+                       if (type_a != type_b) {
+                               weight_a = service_type_weight(type_a);
+                               weight_b = service_type_weight(type_b);
+
+                               if (weight_a > weight_b)
+                                       return -1;
+
+                               if (weight_a < weight_b)
+                                       return 1;
+
+                               return 0;
+                       }
+               }
+
+               if (type_a == info->service_type &&
+                               type_b == info->service_type) {
+                       return 0;
+               }
+
+               if (type_a == info->service_type &&
+                               type_b != info->service_type) {
+                       return -1;
+               }
+
+               if (type_a != info->service_type &&
+                               type_b == info->service_type) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       struct service_entry *entry_a = (void *)a;
+       struct service_entry *entry_b = (void *)b;
+       struct connman_session *session = user_data;
+
+       return sort_allowed_bearers(entry_a->service, entry_b->service,
+                               session);
+}
+
+static void cleanup_session(gpointer user_data)
+{
+       struct connman_session *session = user_data;
+       struct session_info *info = session->info;
+
+       DBG("remove %s", session->session_path);
+
+       g_hash_table_destroy(session->service_hash);
+       g_sequence_free(session->service_list);
+
+       if (info->entry != NULL &&
+                       info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
+               __connman_service_disconnect(info->entry->service);
+       }
+
+       g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
+       g_slist_free(info->allowed_bearers);
+
+       g_free(session->owner);
+       g_free(session->session_path);
+       g_free(session->notify_path);
+       g_free(session->info);
+       g_free(session->info_last);
+
+       g_free(session);
+}
+
+static enum connman_session_state service_to_session_state(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+               return CONNMAN_SESSION_STATE_CONNECTED;
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return CONNMAN_SESSION_STATE_ONLINE;
+       }
+
+       return CONNMAN_SESSION_STATE_DISCONNECTED;
+}
+
+static connman_bool_t is_connected(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_connecting(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return TRUE;
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t explicit_connect(enum connman_session_reason reason)
+{
+       switch (reason) {
+       case CONNMAN_SESSION_REASON_UNKNOWN:
+       case CONNMAN_SESSION_REASON_FREE_RIDE:
+       case CONNMAN_SESSION_REASON_DISCONNECT:
+               break;
+       case CONNMAN_SESSION_REASON_CONNECT:
+       case CONNMAN_SESSION_REASON_PERIODIC:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t explicit_disconnect(struct session_info *info)
+{
+       if (info->entry == NULL)
+               return FALSE;
+
+       DBG("reason %s service %p state %d",
+               reason2string(info->entry->reason),
+               info->entry->service, info->entry->state);
+
+       if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
+               return FALSE;
+
+       if (explicit_connect(info->entry->reason) == FALSE)
+               return FALSE;
+
+       if (__connman_service_session_dec(info->entry->service) == FALSE)
+               return FALSE;
+
+       if (ecall_info != NULL && ecall_info != info)
+               return FALSE;
+
+       return TRUE;
+}
+
+struct pending_data {
+       unsigned int timeout;
+       struct service_entry *entry;
+       gboolean (*cb)(gpointer);
+};
+
+static void pending_timeout_free(gpointer data, gpointer user_data)
+{
+       struct pending_data *pending = data;
+
+       DBG("pending %p timeout %d", pending, pending->timeout);
+       g_source_remove(pending->timeout);
+       g_free(pending);
+}
+
+static void pending_timeout_remove_all(struct service_entry *entry)
+{
+       DBG("");
+
+       g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
+       g_slist_free(entry->pending_timeouts);
+       entry->pending_timeouts = NULL;
+}
+
+static gboolean pending_timeout_cb(gpointer data)
+{
+       struct pending_data *pending = data;
+       struct service_entry *entry = pending->entry;
+       gboolean ret;
+
+       DBG("pending %p timeout %d", pending, pending->timeout);
+
+       ret = pending->cb(pending->entry);
+       if (ret == FALSE) {
+               entry->pending_timeouts =
+                       g_slist_remove(entry->pending_timeouts,
+                                       pending);
+               g_free(pending);
+       }
+       return ret;
+}
+
+static connman_bool_t pending_timeout_add(unsigned int seconds,
+                                       gboolean (*cb)(gpointer),
+                                       struct service_entry *entry)
+{
+       struct pending_data *pending = g_try_new0(struct pending_data, 1);
+
+       if (pending == NULL || cb == NULL || entry == NULL) {
+               g_free(pending);
+               return FALSE;
+       }
+
+       pending->cb = cb;
+       pending->entry = entry;
+       pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
+                                               pending);
+       entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
+                                               pending);
+
+       DBG("pending %p entry %p timeout id %d", pending, entry,
+               pending->timeout);
+
+       return TRUE;
+}
+
+static gboolean call_disconnect(gpointer user_data)
+{
+       struct service_entry *entry = user_data;
+       struct connman_service *service = entry->service;
+
+       /*
+        * TODO: We should mark this entry as pending work. In case
+        * disconnect fails we just unassign this session from the
+        * service and can't do anything later on it
+        */
+       DBG("disconnect service %p", service);
+       __connman_service_disconnect(service);
+
+       return FALSE;
+}
+
+static gboolean call_connect(gpointer user_data)
+{
+       struct service_entry *entry = user_data;
+       struct connman_service *service = entry->service;
+
+       DBG("connect service %p", service);
+       __connman_service_connect(service);
+
+       return FALSE;
+}
+
+static void deselect_service(struct session_info *info)
+{
+       struct service_entry *entry;
+       connman_bool_t disconnect, connected;
+
+       DBG("");
+
+       if (info->entry == NULL)
+               return;
+
+       disconnect = explicit_disconnect(info);
+
+       connected = is_connecting(info->entry->state) == TRUE ||
+                       is_connected(info->entry->state) == TRUE;
+
+       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
+       info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
+
+       entry = info->entry;
+       info->entry = NULL;
+
+       DBG("disconnect %d connected %d", disconnect, connected);
+
+       if (disconnect == TRUE && connected == TRUE)
+               pending_timeout_add(0, call_disconnect, entry);
+}
+
+static void deselect_and_disconnect(struct connman_session *session,
+                                       enum connman_session_reason reason)
+{
+       struct session_info *info = session->info;
+
+       deselect_service(info);
+
+       info->reason = reason;
+}
+
+static void select_connected_service(struct session_info *info,
+                                       struct service_entry *entry)
+{
+       enum connman_session_state state;
+
+       state = service_to_session_state(entry->state);
+       if (is_type_matching_state(&state, info->type) == FALSE)
+               return;
+
+       info->state = state;
+
+       info->entry = entry;
+       info->entry->reason = info->reason;
+
+       if (explicit_connect(info->reason) == FALSE)
+               return;
+
+       __connman_service_session_inc(info->entry->service);
+}
+
+static void select_offline_service(struct session_info *info,
+                                       struct service_entry *entry)
+{
+       if (explicit_connect(info->reason) == FALSE)
+               return;
+
+       info->state = service_to_session_state(entry->state);
+
+       info->entry = entry;
+       info->entry->reason = info->reason;
+
+       __connman_service_session_inc(info->entry->service);
+       pending_timeout_add(0, call_connect, entry);
+}
+
+static void select_service(struct session_info *info,
+                               struct service_entry *entry)
+{
+       DBG("service %p", entry->service);
+
+       if (is_connected(entry->state) == TRUE)
+               select_connected_service(info, entry);
+       else
+               select_offline_service(info, entry);
+}
+
+static void select_and_connect(struct connman_session *session,
+                               enum connman_session_reason reason)
+{
+       struct session_info *info = session->info;
+       struct service_entry *entry = NULL;
+       GSequenceIter *iter;
+
+       DBG("session %p reason %s", session, reason2string(reason));
+
+       info->reason = reason;
+
+       iter = g_sequence_get_begin_iter(session->service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               entry = g_sequence_get(iter);
+
+               switch (entry->state) {
+               case CONNMAN_SERVICE_STATE_ASSOCIATION:
+               case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               case CONNMAN_SERVICE_STATE_READY:
+               case CONNMAN_SERVICE_STATE_ONLINE:
+               case CONNMAN_SERVICE_STATE_IDLE:
+               case CONNMAN_SERVICE_STATE_DISCONNECT:
+                       select_service(info, entry);
+                       return;
+               case CONNMAN_SERVICE_STATE_UNKNOWN:
+               case CONNMAN_SERVICE_STATE_FAILURE:
+                       break;
+               }
+
+               iter = g_sequence_iter_next(iter);
+       }
+}
+
+static struct service_entry *create_service_entry(struct connman_service *service,
+                                       const char *name,
+                                       enum connman_service_state state)
+{
+       struct service_entry *entry;
+       enum connman_service_type type;
+       int idx;
+
+       entry = g_try_new0(struct service_entry, 1);
+       if (entry == NULL)
+               return entry;
+
+       entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
+       entry->state = state;
+       if (name != NULL)
+               entry->name = name;
+       else
+               entry->name = "";
+       entry->service = service;
+
+       idx = __connman_service_get_index(entry->service);
+       entry->ifname = connman_inet_ifname(idx);
+       if (entry->ifname == NULL)
+               entry->ifname = g_strdup("");
+
+       type = connman_service_get_type(entry->service);
+       entry->bearer = service2bearer(type);
+
+       return entry;
+}
+
+static void destroy_service_entry(gpointer data)
+{
+       struct service_entry *entry = data;
+
+       pending_timeout_remove_all(entry);
+       g_free(entry->ifname);
+
+       g_free(entry);
+}
+
+static void populate_service_list(struct connman_session *session)
+{
+       struct service_entry *entry;
+       GSequenceIter *iter;
+
+       session->service_hash =
+               g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, NULL);
+       session->service_list = __connman_service_get_list(session,
+                                                       service_match,
+                                                       create_service_entry,
+                                                       destroy_service_entry);
+
+       g_sequence_sort(session->service_list, sort_services, session);
+
+       iter = g_sequence_get_begin_iter(session->service_list);
+
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               entry = g_sequence_get(iter);
+
+               DBG("service %p type %s name %s", entry->service,
+                       service2bearer(connman_service_get_type(entry->service)),
+                       entry->name);
+
+               g_hash_table_replace(session->service_hash,
+                                       entry->service, iter);
+
+               iter = g_sequence_iter_next(iter);
+       }
+}
+
+static void session_changed(struct connman_session *session,
+                               enum connman_session_trigger trigger)
+{
+       struct session_info *info = session->info;
+       struct session_info *info_last = session->info_last;
+       GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
+       GSequence *service_list_last;
+       GHashTable *service_hash_last;
+
+       /*
+        * TODO: This only a placeholder for the 'real' algorithm to
+        * play a bit around. So we are going to improve it step by step.
+        */
+
+       DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
+                                               reason2string(info->reason));
+
+       if (info->entry != NULL) {
+               enum connman_session_state state;
+
+               state = service_to_session_state(info->entry->state);
+
+               if (is_type_matching_state(&state, info->type) == TRUE)
+                       info->state = state;
+       }
+
+       switch (trigger) {
+       case CONNMAN_SESSION_TRIGGER_UNKNOWN:
+               DBG("ignore session changed event");
+               return;
+       case CONNMAN_SESSION_TRIGGER_SETTING:
+               if (info->allowed_bearers != info_last->allowed_bearers) {
+
+                       service_hash_last = session->service_hash;
+                       service_list_last = session->service_list;
+
+                       populate_service_list(session);
+
+                       if (info->entry != NULL) {
+                               service_iter_last = g_hash_table_lookup(
+                                                       service_hash_last,
+                                                       info->entry->service);
+                               service_iter = g_hash_table_lookup(
+                                                       session->service_hash,
+                                                       info->entry->service);
+                       }
+
+                       if (service_iter == NULL && service_iter_last != NULL) {
+                               /*
+                                * The currently selected service is
+                                * not part of this session anymore.
+                                */
+                               deselect_and_disconnect(session, info->reason);
+                       }
+
+                       g_hash_table_remove_all(service_hash_last);
+                       g_sequence_free(service_list_last);
+               }
+
+               if (info->type != info_last->type) {
+                       if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
+                                       is_type_matching_state(&info->state,
+                                                       info->type) == FALSE)
+                               deselect_and_disconnect(session, info->reason);
+               }
+
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
+                       select_and_connect(session,
+                                       CONNMAN_SESSION_REASON_FREE_RIDE);
+               }
+
+               break;
+       case CONNMAN_SESSION_TRIGGER_CONNECT:
+               if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
+                       if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
+                               break;
+                       info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
+                       __connman_service_session_inc(info->entry->service);
+                       break;
+               }
+
+               if (info->entry != NULL &&
+                               is_connecting(info->entry->state) == TRUE) {
+                       break;
+               }
+
+               select_and_connect(session,
+                               CONNMAN_SESSION_REASON_CONNECT);
+
+               break;
+       case CONNMAN_SESSION_TRIGGER_DISCONNECT:
+               deselect_and_disconnect(session,
+                                       CONNMAN_SESSION_REASON_DISCONNECT);
+
+               break;
+       case CONNMAN_SESSION_TRIGGER_PERIODIC:
+               if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
+                       info->entry->reason = CONNMAN_SESSION_REASON_PERIODIC;
+                       __connman_service_session_inc(info->entry->service);
+                       break;
+               }
+
+               select_and_connect(session,
+                               CONNMAN_SESSION_REASON_PERIODIC);
+
+               break;
+       case CONNMAN_SESSION_TRIGGER_SERVICE:
+               if (info->entry != NULL &&
+                       (is_connecting(info->entry->state) == TRUE ||
+                               is_connected(info->entry->state) == TRUE)) {
+                       break;
+               }
+
+               deselect_and_disconnect(session, info->reason);
+
+               if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
+                               info->stay_connected == TRUE) {
+                       select_and_connect(session, info->reason);
+               }
+
+               break;
+       case CONNMAN_SESSION_TRIGGER_ECALL:
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED &&
+                               info->entry != NULL &&
+                               info->entry->service != NULL) {
+                       deselect_and_disconnect(session, info->reason);
+               }
+
+               break;
+       }
+
+       session_notify(session);
+}
+
+static DBusMessage *connect_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_session *session = user_data;
+       struct session_info *info = session->info;
+
+       DBG("session %p", session);
+
+       if (ecall_info != NULL && ecall_info != info)
+               return __connman_error_failed(msg, EBUSY);
+
+       session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_session *session = user_data;
+       struct session_info *info = session->info;
+
+       DBG("session %p", session);
+
+       if (ecall_info != NULL && ecall_info != info)
+               return __connman_error_failed(msg, EBUSY);
+
+       session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void update_ecall_sessions(struct connman_session *session)
+{
+       struct session_info *info = session->info;
+       struct connman_session *session_iter;
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               session_iter = value;
+
+               if (session_iter == session)
+                       continue;
+
+               session_iter->info->ecall = info->ecall;
+
+               session_changed(session_iter, CONNMAN_SESSION_TRIGGER_ECALL);
+       }
+}
+
+static void update_ecall(struct connman_session *session)
+{
+       struct session_info *info = session->info;
+       struct session_info *info_last = session->info_last;
+
+       DBG("session %p ecall_info %p ecall %d -> %d", session,
+               ecall_info, info_last->ecall, info->ecall);
+
+       if (ecall_info == NULL) {
+               if (!(info_last->ecall == FALSE && info->ecall == TRUE))
+                       goto err;
+
+               ecall_info = info;
+       } else if (ecall_info == info) {
+               if (!(info_last->ecall == TRUE && info->ecall == FALSE))
+                       goto err;
+
+               ecall_info = NULL;
+       } else {
+               goto err;
+       }
+
+       update_ecall_sessions(session);
+
+       return;
+
+err:
+       /* not a valid transition */
+       info->ecall = info_last->ecall;
+}
+
+static DBusMessage *change_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_session *session = user_data;
+       struct session_info *info = session->info;
+       DBusMessageIter iter, value;
+       const char *name;
+       const char *val;
+       GSList *allowed_bearers;
+
+       DBG("session %p", session);
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       switch (dbus_message_iter_get_arg_type(&value)) {
+       case DBUS_TYPE_ARRAY:
+               if (g_str_equal(name, "AllowedBearers") == TRUE) {
+                       allowed_bearers = session_parse_allowed_bearers(&value);
+
+                       g_slist_foreach(info->allowed_bearers,
+                                       cleanup_bearer_info, NULL);
+                       g_slist_free(info->allowed_bearers);
+
+                       if (allowed_bearers == NULL) {
+                               allowed_bearers = session_allowed_bearers_any();
+
+                               if (allowed_bearers == NULL)
+                                       return __connman_error_failed(msg, ENOMEM);
+                       }
+
+                       info->allowed_bearers = allowed_bearers;
+               } else {
+                       goto err;
+               }
+               break;
+       case DBUS_TYPE_BOOLEAN:
+               if (g_str_equal(name, "Priority") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->priority);
+               } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->avoid_handover);
+               } else if (g_str_equal(name, "StayConnected") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->stay_connected);
+               } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->ecall);
+
+                       update_ecall(session);
+               } else {
+                       goto err;
+               }
+               break;
+       case DBUS_TYPE_UINT32:
+               if (g_str_equal(name, "PeriodicConnect") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->periodic_connect);
+               } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
+                       dbus_message_iter_get_basic(&value,
+                                       &info->idle_timeout);
+               } else {
+                       goto err;
+               }
+               break;
+       case DBUS_TYPE_STRING:
+               if (g_str_equal(name, "ConnectionType") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+                       info->type = string2type(val);
+               } else if (g_str_equal(name, "RoamingPolicy") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
+                       info->roaming_policy =
+                                       string2roamingpolicy(val);
+               } else {
+                       goto err;
+               }
+               break;
+       default:
+               goto err;
+       }
+
+       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+err:
+       return __connman_error_invalid_arguments(msg);
+}
+
+static void release_session(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_session *session = value;
+       DBusMessage *message;
+
+       DBG("owner %s path %s", session->owner, session->notify_path);
+
+       if (session->notify_watch > 0)
+               g_dbus_remove_watch(connection, session->notify_watch);
+
+       g_dbus_unregister_interface(connection, session->session_path,
+                                               CONNMAN_SESSION_INTERFACE);
+
+       message = dbus_message_new_method_call(session->owner,
+                                               session->notify_path,
+                                               CONNMAN_NOTIFICATION_INTERFACE,
+                                               "Release");
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(connection, message);
+}
+
+static int session_disconnect(struct connman_session *session)
+{
+       DBG("session %p, %s", session, session->owner);
+
+       if (session->notify_watch > 0)
+               g_dbus_remove_watch(connection, session->notify_watch);
+
+       g_dbus_unregister_interface(connection, session->session_path,
+                                               CONNMAN_SESSION_INTERFACE);
+
+       deselect_and_disconnect(session,
+                               CONNMAN_SESSION_REASON_DISCONNECT);
+
+       g_hash_table_remove(session_hash, session->session_path);
+
+       return 0;
+}
+
+static void owner_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct connman_session *session = user_data;
+
+       DBG("session %p, %s died", session, session->owner);
+
+       session_disconnect(session);
+}
+
+static DBusMessage *destroy_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_session *session = user_data;
+       struct session_info *info = session->info;
+
+       DBG("session %p", session);
+
+       if (ecall_info != NULL && ecall_info != info)
+               return __connman_error_failed(msg, EBUSY);
+
+       session_disconnect(session);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable session_methods[] = {
+       { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
+       { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL,
+                       disconnect_session ) },
+       { GDBUS_METHOD("Change",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, change_session) },
+       { },
+};
+
+int __connman_session_create(DBusMessage *msg)
+{
+       const char *owner, *notify_path;
+       char *session_path = NULL;
+       DBusMessageIter iter, array;
+       struct connman_session *session = NULL;
+       struct session_info *info, *info_last;
+
+       enum connman_session_type type = CONNMAN_SESSION_TYPE_ANY;
+       connman_bool_t priority = FALSE, avoid_handover = FALSE;
+       connman_bool_t stay_connected = FALSE, ecall = FALSE;
+       enum connman_session_roaming_policy roaming_policy =
+                               CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
+       GSList *allowed_bearers = NULL;
+       unsigned int periodic_connect = 0;
+       unsigned int idle_timeout = 0;
+
+       int err;
+
+       owner = dbus_message_get_sender(msg);
+
+       DBG("owner %s", owner);
+
+       if (ecall_info != NULL) {
+               /*
+                * If there is an emergency call already going on,
+                * ignore session creation attempt
+                */
+               err = -EBUSY;
+               goto err;
+       }
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *val;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_ARRAY:
+                       if (g_str_equal(key, "AllowedBearers") == TRUE) {
+                               allowed_bearers =
+                                       session_parse_allowed_bearers(&value);
+                       } else {
+                               return -EINVAL;
+                       }
+                       break;
+               case DBUS_TYPE_BOOLEAN:
+                       if (g_str_equal(key, "Priority") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &priority);
+                       } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &avoid_handover);
+                       } else if (g_str_equal(key, "StayConnected") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &stay_connected);
+                       } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &ecall);
+                       } else {
+                               return -EINVAL;
+                       }
+                       break;
+               case DBUS_TYPE_UINT32:
+                       if (g_str_equal(key, "PeriodicConnect") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &periodic_connect);
+                       } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &idle_timeout);
+                       } else {
+                               return -EINVAL;
+                       }
+                       break;
+               case DBUS_TYPE_STRING:
+                       if (g_str_equal(key, "ConnectionType") == TRUE) {
+                               dbus_message_iter_get_basic(&value, &val);
+                               type = string2type(val);
+                       } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
+                               dbus_message_iter_get_basic(&value, &val);
+                               roaming_policy = string2roamingpolicy(val);
+                       } else {
+                               return -EINVAL;
+                       }
+               }
+               dbus_message_iter_next(&array);
+       }
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &notify_path);
+
+       if (notify_path == NULL) {
+               err = -EINVAL;
+               goto err;
+       }
+
+       session_path = g_strdup_printf("/sessions%s", notify_path);
+       if (session_path == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       session = g_hash_table_lookup(session_hash, session_path);
+       if (session != NULL) {
+               session = NULL;
+               err = -EEXIST;
+               goto err;
+       }
+
+       session = g_try_new0(struct connman_session, 1);
+       if (session == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       session->info = g_try_new0(struct session_info, 1);
+       if (session->info == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       session->info_last = g_try_new0(struct session_info, 1);
+       if (session->info_last == NULL) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       info = session->info;
+       info_last = session->info_last;
+
+       session->owner = g_strdup(owner);
+       session->session_path = session_path;
+       session->notify_path = g_strdup(notify_path);
+       session->notify_watch =
+               g_dbus_add_disconnect_watch(connection, session->owner,
+                                       owner_disconnect, session, NULL);
+
+       info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
+       info->type = type;
+       info->priority = priority;
+       info->avoid_handover = avoid_handover;
+       info->stay_connected = stay_connected;
+       info->periodic_connect = periodic_connect;
+       info->idle_timeout = idle_timeout;
+       info->ecall = ecall;
+       info->roaming_policy = roaming_policy;
+       info->entry = NULL;
+       info->marker = 0;
+
+       if (allowed_bearers == NULL) {
+               info->allowed_bearers =
+                               session_allowed_bearers_any();
+
+               if (info->allowed_bearers == NULL) {
+                       err = -ENOMEM;
+                       goto err;
+               }
+       } else {
+               info->allowed_bearers = allowed_bearers;
+       }
+
+       g_hash_table_replace(session_hash, session->session_path, session);
+
+       DBG("add %s", session->session_path);
+
+       if (g_dbus_register_interface(connection, session->session_path,
+                                       CONNMAN_SESSION_INTERFACE,
+                                       session_methods, NULL,
+                                       NULL, session, NULL) == FALSE) {
+               connman_error("Failed to register %s", session->session_path);
+               g_hash_table_remove(session_hash, session->session_path);
+               session = NULL;
+
+               err = -EINVAL;
+               goto err;
+       }
+
+       g_dbus_send_reply(connection, msg,
+                               DBUS_TYPE_OBJECT_PATH, &session->session_path,
+                               DBUS_TYPE_INVALID);
+
+
+       populate_service_list(session);
+       if (info->ecall == TRUE) {
+               ecall_info = info;
+               update_ecall_sessions(session);
+       }
+
+       info_last->state = info->state;
+       info_last->priority = info->priority;
+       info_last->avoid_handover = info->avoid_handover;
+       info_last->stay_connected = info->stay_connected;
+       info_last->periodic_connect = info->periodic_connect;
+       info_last->idle_timeout = info->idle_timeout;
+       info_last->ecall = info->ecall;
+       info_last->roaming_policy = info->roaming_policy;
+       info_last->entry = info->entry;
+       info_last->marker = info->marker;
+       info_last->allowed_bearers = info->allowed_bearers;
+
+       session->append_all = TRUE;
+
+       session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
+
+       return 0;
+
+err:
+       connman_error("Failed to create session");
+
+       if (session != NULL) {
+               if (session->info_last != NULL)
+                       g_free(session->info_last);
+               if (session->info != NULL)
+                       g_free(session->info);
+               g_free(session);
+       }
+
+       g_free(session_path);
+
+       g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
+       g_slist_free(allowed_bearers);
+
+       return err;
+}
+
+int __connman_session_destroy(DBusMessage *msg)
+{
+       const char *owner, *session_path;
+       struct connman_session *session;
+
+       owner = dbus_message_get_sender(msg);
+
+       DBG("owner %s", owner);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
+                                                       DBUS_TYPE_INVALID);
+       if (session_path == NULL)
+               return -EINVAL;
+
+       session = g_hash_table_lookup(session_hash, session_path);
+       if (session == NULL)
+               return -EINVAL;
+
+       if (g_strcmp0(owner, session->owner) != 0)
+               return -EACCES;
+
+       session_disconnect(session);
+
+       return 0;
+}
+
+connman_bool_t __connman_session_mode()
+{
+       return sessionmode;
+}
+
+void __connman_session_set_mode(connman_bool_t enable)
+{
+       DBG("enable %d", enable);
+
+       if (sessionmode != enable) {
+               sessionmode = enable;
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "SessionMode",
+                               DBUS_TYPE_BOOLEAN, &sessionmode);
+       }
+
+       if (sessionmode == TRUE)
+               __connman_service_disconnect_all();
+}
+
+static void service_add(struct connman_service *service,
+                       const char *name)
+{
+       GHashTableIter iter;
+       GSequenceIter *iter_service_list;
+       gpointer key, value;
+       struct connman_session *session;
+       struct service_entry *entry;
+
+       DBG("service %p", service);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               session = value;
+
+               if (service_match(session, service) == FALSE)
+                       continue;
+
+               entry = create_service_entry(service, name,
+                                               CONNMAN_SERVICE_STATE_IDLE);
+               if (entry == NULL)
+                       continue;
+
+               iter_service_list =
+                       g_sequence_insert_sorted(session->service_list,
+                                                       entry, sort_services,
+                                                       session);
+
+               g_hash_table_replace(session->service_hash, service,
+                                       iter_service_list);
+
+               session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
+       }
+}
+
+static void service_remove(struct connman_service *service)
+{
+
+       GHashTableIter iter;
+       gpointer key, value;
+       struct connman_session *session;
+       struct session_info *info;
+
+       DBG("service %p", service);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               GSequenceIter *iter;
+               session = value;
+               info = session->info;
+
+               iter = g_hash_table_lookup(session->service_hash, service);
+               if (iter == NULL)
+                       continue;
+
+               g_sequence_remove(iter);
+
+               if (info->entry != NULL && info->entry->service == service)
+                       info->entry = NULL;
+               session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
+       }
+}
+
+static void service_state_changed(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("service %p state %d", service, state);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_session *session = value;
+               GSequenceIter *service_iter;
+
+               service_iter = g_hash_table_lookup(session->service_hash, service);
+               if (service_iter != NULL) {
+                       struct service_entry *entry;
+
+                       entry = g_sequence_get(service_iter);
+                       entry->state = state;
+               }
+
+               session_changed(session,
+                               CONNMAN_SESSION_TRIGGER_SERVICE);
+       }
+}
+
+static void ipconfig_changed(struct connman_service *service,
+                               struct connman_ipconfig *ipconfig)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       struct connman_session *session;
+       struct session_info *info;
+       enum connman_ipconfig_type type;
+
+       DBG("service %p ipconfig %p", service, ipconfig);
+
+       type = __connman_ipconfig_get_config_type(ipconfig);
+
+       g_hash_table_iter_init(&iter, session_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               session = value;
+               info = session->info;
+
+               if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
+                       continue;
+
+               if (info->entry != NULL && info->entry->service == service) {
+                       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                               ipconfig_ipv4_changed(session);
+                       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+                               ipconfig_ipv6_changed(session);
+               }
+       }
+}
+
+static struct connman_notifier session_notifier = {
+       .name                   = "session",
+       .service_add            = service_add,
+       .service_remove         = service_remove,
+       .service_state_changed  = service_state_changed,
+       .ipconfig_changed       = ipconfig_changed,
+};
+
+int __connman_session_init(void)
+{
+       int err;
+
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -1;
+
+       err = connman_notifier_register(&session_notifier);
+       if (err < 0) {
+               dbus_connection_unref(connection);
+               return err;
+       }
+
+       session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, cleanup_session);
+
+       sessionmode = FALSE;
+       return 0;
+}
+
+void __connman_session_cleanup(void)
+{
+       DBG("");
+
+       if (connection == NULL)
+               return;
+
+       connman_notifier_unregister(&session_notifier);
+
+       g_hash_table_foreach(session_hash, release_session, NULL);
+       g_hash_table_destroy(session_hash);
+       session_hash = NULL;
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/stats.c b/src/stats.c
new file mode 100644 (file)
index 0000000..828c1ef
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "connman.h"
+
+#define MODE           (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
+                       S_IXGRP | S_IROTH | S_IXOTH)
+
+#ifdef TEMP_FAILURE_RETRY
+#define TFR TEMP_FAILURE_RETRY
+#else
+#define TFR
+#endif
+
+#define MAGIC 0xFA00B916
+
+/*
+ * Statistics counters are stored into a ring buffer which is stored
+ * into a file
+ *
+ * File properties:
+ *   The ring buffer is mmap to a file
+ *   Initialy 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
+ *   Each file has a header where the indexes are stored
+ *
+ * Entries properties:
+ *   Each entry has a timestamp
+ *   A flag to mark if the entry is either home (0) or roaming (1) entry
+ *   The entries are fixed sized (stats_record)
+ *
+ * Ring buffer properties:
+ *   There are to indexes 'begin', 'end', 'home' and 'roaming'
+ *   'begin' points to the oldest entry
+ *   'end' points to the newest/current entry
+ *   'home' points to the current home entry
+ *   'roaming' points to the current roaming entry
+ *   If 'begin' == 'end' then the buffer is empty
+ *   If 'end' + 1 == 'begin then it's full
+ *   The ring buffer is valid in the range (begin, end]
+ *   If 'home' has the value UINT_MAX', 'home' is invalid
+ *   if 'roaming' has the value UINT_MAX', 'roaming' is invalid
+ *   'first' points to the first entry in the ring buffer
+ *   'last' points to the last entry in the ring buffer
+ *
+ * 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
+ */
+
+
+struct stats_file_header {
+       unsigned int magic;
+       unsigned int begin;
+       unsigned int end;
+       unsigned int home;
+       unsigned int roaming;
+};
+
+struct stats_record {
+       time_t ts;
+       unsigned int roaming;
+       struct connman_stats_data data;
+};
+
+struct stats_file {
+       int fd;
+       char *name;
+       char *addr;
+       size_t len;
+       size_t max_len;
+
+       /* cached values */
+       struct stats_record *first;
+       struct stats_record *last;
+       struct stats_record *home;
+       struct stats_record *roaming;
+
+       /* history */
+       char *history_name;
+       int account_period_offset;
+};
+
+struct stats_iter {
+       struct stats_file *file;
+       struct stats_record *begin;
+       struct stats_record *end;
+       struct stats_record *it;
+};
+
+GHashTable *stats_hash = NULL;
+
+static struct stats_file_header *get_hdr(struct stats_file *file)
+{
+       return (struct stats_file_header *)file->addr;
+}
+
+static struct stats_record *get_begin(struct stats_file *file)
+{
+       unsigned int off = get_hdr(file)->begin;
+
+       return (struct stats_record *)(file->addr + off);
+}
+
+static struct stats_record *get_end(struct stats_file *file)
+{
+       unsigned int off = get_hdr(file)->end;
+
+       return (struct stats_record *)(file->addr + off);
+}
+
+static struct stats_record *get_home(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+
+       if (hdr->home == UINT_MAX)
+               return NULL;
+
+       return (struct stats_record *)(file->addr + hdr->home);
+}
+
+static struct stats_record *get_roaming(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+
+       if (hdr->roaming == UINT_MAX)
+               return NULL;
+
+       return (struct stats_record *)(file->addr + hdr->roaming);
+}
+
+static void set_end(struct stats_file *file, struct stats_record *end)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+       hdr->end = (char *)end - file->addr;
+}
+
+static void set_home(struct stats_file *file, struct stats_record *home)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+       hdr->home = (char *)home - file->addr;
+}
+
+static void set_roaming(struct stats_file *file, struct stats_record *roaming)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+       hdr->roaming = (char *)roaming - file->addr;
+}
+
+static struct stats_record *get_next(struct stats_file *file,
+                                       struct stats_record *cur)
+{
+       cur++;
+
+       if (cur > file->last)
+               cur = file->first;
+
+       return cur;
+}
+
+static struct stats_record *get_iterator_begin(struct stats_file *file)
+{
+       return get_next(file, get_begin(file));
+}
+
+static struct stats_record *get_iterator_end(struct stats_file *file)
+{
+       return get_next(file, get_end(file));
+}
+
+static void stats_free(gpointer user_data)
+{
+       struct stats_file *file = user_data;
+
+       if (file == NULL)
+               return;
+
+       msync(file->addr, file->len, MS_SYNC);
+
+       munmap(file->addr, file->len);
+       file->addr = NULL;
+
+       TFR(close(file->fd));
+       file->fd = -1;
+
+       if (file->history_name != NULL) {
+               g_free(file->history_name);
+               file->history_name = NULL;
+       }
+
+       if (file->name != NULL) {
+               g_free(file->name);
+               file->name = NULL;
+       }
+
+       g_free(file);
+}
+
+static void update_first(struct stats_file *file)
+{
+       file->first = (struct stats_record *)
+                       (file->addr + sizeof(struct stats_file_header));
+}
+
+static void update_last(struct stats_file *file)
+{
+       unsigned int max_entries;
+
+       max_entries = (file->len - sizeof(struct stats_file_header)) /
+                       sizeof(struct stats_record);
+       file->last = file->first + max_entries - 1;
+}
+
+static void update_home(struct stats_file *file)
+{
+       file->home = get_home(file);
+}
+
+static void update_roaming(struct stats_file *file)
+{
+       file->roaming = get_roaming(file);
+}
+
+static void stats_file_update_cache(struct stats_file *file)
+{
+       update_first(file);
+       update_last(file);
+       update_home(file);
+       update_roaming(file);
+}
+
+static int stats_file_remap(struct stats_file *file, size_t size)
+{
+       size_t page_size, new_size;
+       void *addr;
+       int err;
+
+       DBG("file %p size %zu addr %p len %zu", file, size, file->addr,
+               file->len);
+
+       page_size = sysconf(_SC_PAGESIZE);
+       new_size = (size + page_size - 1) & ~(page_size - 1);
+
+       err = ftruncate(file->fd, new_size);
+       if (err < 0) {
+               connman_error("ftrunctate error %s for %s",
+                               strerror(errno), file->name);
+               return -errno;
+       }
+
+       if (file->addr == NULL) {
+               /*
+                * Though the buffer is not shared between processes, we still
+                * have to take MAP_SHARED because MAP_PRIVATE does not
+                * guarantee that writes will hit the file without an explicit
+                * call to munmap or msync. For more details please read the
+                * mmap man pages.
+                */
+               addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
+                               MAP_SHARED, file->fd, 0);
+       } else {
+               addr = mremap(file->addr, file->len, new_size, MREMAP_MAYMOVE);
+       }
+
+       if (addr == MAP_FAILED) {
+               connman_error("mmap error %s for %s",
+                               strerror(errno), file->name);
+               if (errno == EINVAL) {
+                       connman_error("%s might be on a file system, such as "
+                                       "JFFS2, that does not allow shared "
+                                       "writable mappings.", file->name);
+               }
+               return -errno;
+       }
+
+       file->addr = addr;
+       file->len = new_size;
+
+       stats_file_update_cache(file);
+
+       return 0;
+}
+
+static int stats_open(struct stats_file *file,
+                       const char *name)
+{
+       DBG("file %p name %s", file, name);
+
+       file->name = g_strdup(name);
+
+       file->fd = TFR(open(file->name, O_RDWR | O_CREAT | O_CLOEXEC, 0644));
+       if (file->fd < 0) {
+               connman_error("open error %s for %s",
+                               strerror(errno), file->name);
+               g_free(file->name);
+               file->name = NULL;
+               return -errno;
+       }
+
+       return 0;
+}
+
+static int stats_open_temp(struct stats_file *file)
+{
+       file->name = g_strdup_printf("%s/stats.XXXXXX.tmp",
+                                       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",
+                               strerror(errno), file->name);
+               g_free(file->name);
+               file->name = NULL;
+               return -errno;
+       }
+
+       return 0;
+}
+
+static int stats_file_setup(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+       struct stat st;
+       size_t size = 0;
+       int err;
+
+       DBG("file %p fd %d name %s", file, file->fd, file->name);
+
+       err = fstat(file->fd, &st);
+       if (err < 0) {
+               connman_error("fstat error %s for %s\n",
+                       strerror(errno), file->name);
+
+               TFR(close(file->fd));
+               g_free(file->name);
+               file->name = NULL;
+
+               return -errno;
+       }
+
+       size = (size_t)st.st_size;
+       file->max_len = STATS_MAX_FILE_SIZE;
+
+       if (size < (size_t)sysconf(_SC_PAGESIZE))
+               size = sysconf(_SC_PAGESIZE);
+
+       err = stats_file_remap(file, size);
+       if (err < 0) {
+               TFR(close(file->fd));
+               g_free(file->name);
+               file->name = NULL;
+
+               return err;
+       }
+
+       hdr = get_hdr(file);
+
+       if (hdr->magic != MAGIC ||
+                       hdr->begin < sizeof(struct stats_file_header) ||
+                       hdr->end < sizeof(struct stats_file_header) ||
+                       hdr->home < sizeof(struct stats_file_header) ||
+                       hdr->roaming < sizeof(struct stats_file_header) ||
+                       hdr->begin > file->len ||
+                       hdr->end > file->len) {
+               hdr->magic = MAGIC;
+               hdr->begin = sizeof(struct stats_file_header);
+               hdr->end = sizeof(struct stats_file_header);
+               hdr->home = UINT_MAX;
+               hdr->roaming = UINT_MAX;
+
+               stats_file_update_cache(file);
+       }
+
+       return 0;
+}
+
+
+static struct stats_record *get_next_record(struct stats_iter *iter)
+{
+       if (iter->it != iter->end) {
+               struct stats_record *tmp;
+
+               tmp = iter->it;
+               iter->it = get_next(iter->file, iter->it);
+
+               return tmp;
+       }
+
+       return NULL;
+}
+
+static int append_record(struct stats_file *file,
+                               struct stats_record *rec)
+{
+       struct stats_record *cur, *next;
+       int err;
+
+       if (file->last == get_end(file)) {
+               err = stats_file_remap(file, file->len +
+                                       sysconf(_SC_PAGESIZE));
+               if (err < 0)
+                       return err;
+
+               stats_file_update_cache(file);
+       }
+
+       cur = get_end(file);
+       next = get_next(file, cur);
+
+       memcpy(next, rec, sizeof(struct stats_record));
+
+       set_end(file, next);
+
+       return 0;
+}
+
+static struct stats_record *process_file(struct stats_iter *iter,
+                                       struct stats_file *temp_file,
+                                       struct stats_record *cur,
+                                       GDate *date_change_step_size,
+                                       int account_period_offset)
+{
+       struct stats_record *home, *roaming;
+       struct stats_record *next;
+
+       home = NULL;
+       roaming = NULL;
+
+       if (cur == NULL)
+               cur = get_next_record(iter);
+       next = get_next_record(iter);
+
+       while (next != NULL) {
+               GDate date_cur;
+               GDate date_next;
+               int append;
+
+               append = FALSE;
+
+               if (cur->roaming == TRUE)
+                       roaming = cur;
+               else
+                       home = cur;
+
+               g_date_set_time_t(&date_cur, cur->ts);
+               g_date_set_time_t(&date_next, next->ts);
+
+               if (g_date_compare(&date_cur, date_change_step_size) < 0) {
+                       /* month period size */
+                       GDateDay day_cur, day_next;
+                       GDateMonth month_cur, month_next;
+
+                       month_cur = g_date_get_month(&date_cur);
+                       month_next = g_date_get_month(&date_next);
+
+                       day_cur = g_date_get_day(&date_cur);
+                       day_next = g_date_get_day(&date_next);
+
+                       if (day_cur == day_next && month_cur != month_next) {
+                               append = TRUE;
+                       } else if (day_cur < account_period_offset &&
+                                       day_next >= account_period_offset) {
+                               append = TRUE;
+                       }
+               } else {
+                       /* day period size */
+                       if (g_date_days_between(&date_cur, &date_next) > 0)
+                               append = TRUE;
+               }
+
+               if (append == TRUE) {
+                       if (home != NULL) {
+                               append_record(temp_file, home);
+                               home = NULL;
+                       }
+
+                       if (roaming != NULL) {
+                               append_record(temp_file, roaming);
+                               roaming = NULL;
+                       }
+               }
+
+               cur = next;
+               next = get_next_record(iter);
+       }
+
+       return cur;
+}
+
+static int summarize(struct stats_file *data_file,
+                       struct stats_file *history_file,
+                       struct stats_file *temp_file)
+{
+       struct stats_iter data_iter;
+       struct stats_iter history_iter;
+       struct stats_record *cur, *next;
+
+       GDate today, date_change_step_size;
+
+       /*
+        * First calculate the date when switch from monthly
+        * accounting period size to daily size
+        */
+       g_date_set_time_t(&today, time(NULL));
+
+       date_change_step_size = today;
+       if (g_date_get_day(&today) - data_file->account_period_offset >= 0)
+               g_date_subtract_months(&date_change_step_size, 2);
+       else
+               g_date_subtract_months(&date_change_step_size, 3);
+
+       g_date_set_day(&date_change_step_size,
+                       data_file->account_period_offset);
+
+
+       /* Now process history file */
+       cur = NULL;
+
+       if (history_file != NULL) {
+               history_iter.file = history_file;
+               history_iter.begin = get_iterator_begin(history_iter.file);
+               history_iter.end = get_iterator_end(history_iter.file);
+               history_iter.it = history_iter.begin;
+
+               cur = process_file(&history_iter, temp_file, NULL,
+                                       &date_change_step_size,
+                                       data_file->account_period_offset);
+       }
+
+       data_iter.file = data_file;
+       data_iter.begin = get_iterator_begin(data_iter.file);
+       data_iter.end = get_iterator_end(data_iter.file);
+       data_iter.it = data_iter.begin;
+
+       /*
+        * Ensure date_file records are newer than the history_file
+        * record
+        */
+       if (cur != NULL) {
+               next = get_next_record(&data_iter);
+               while (next != NULL && cur->ts > next->ts)
+                       next = get_next_record(&data_iter);
+       }
+
+       /* And finally process the new data records */
+       cur = process_file(&data_iter, temp_file, cur,
+                               &date_change_step_size,
+                               data_file->account_period_offset);
+
+       if (cur != NULL)
+               append_record(temp_file, cur);
+
+       return 0;
+}
+
+static void stats_file_unmap(struct stats_file *file)
+{
+       msync(file->addr, file->len, MS_SYNC);
+       munmap(file->addr, file->len);
+       file->addr = NULL;
+}
+
+static void stats_file_cleanup(struct stats_file *file)
+{
+       file->fd = -1;
+       g_free(file->name);
+       file->name = NULL;
+}
+
+static int stats_file_close_swap(struct stats_file *history_file,
+                                       struct stats_file *temp_file)
+{
+       int err;
+
+       stats_file_unmap(history_file);
+       stats_file_unmap(temp_file);
+
+       TFR(close(temp_file->fd));
+
+       unlink(history_file->name);
+
+       err = link(temp_file->name, history_file->name);
+
+       unlink(temp_file->name);
+
+       TFR(close(history_file->fd));
+
+       stats_file_cleanup(history_file);
+       stats_file_cleanup(temp_file);
+
+       return err;
+}
+
+static int stats_file_history_update(struct stats_file *data_file)
+{
+       struct stats_file _history_file, *history_file;
+       struct stats_file _temp_file, *temp_file;
+       int err;
+
+       history_file = &_history_file;
+       temp_file = &_temp_file;
+
+       bzero(history_file, sizeof(struct stats_file));
+       bzero(temp_file, sizeof(struct stats_file));
+
+       err = stats_open(history_file, data_file->history_name);
+       if (err < 0)
+               return err;
+       stats_file_setup(history_file);
+
+       err = stats_open_temp(temp_file);
+       if (err < 0) {
+               stats_free(history_file);
+               return err;
+       }
+       stats_file_setup(temp_file);
+
+       summarize(data_file, history_file, temp_file);
+
+       err = stats_file_close_swap(history_file, temp_file);
+
+       return err;
+}
+
+int __connman_stats_service_register(struct connman_service *service)
+{
+       struct stats_file *file;
+       char *name, *dir;
+       int err;
+
+       DBG("service %p", service);
+
+       file = g_hash_table_lookup(stats_hash, service);
+       if (file == NULL) {
+               file = g_try_new0(struct stats_file, 1);
+               if (file == NULL)
+                       return -ENOMEM;
+
+               g_hash_table_insert(stats_hash, service, file);
+       } else {
+               return -EALREADY;
+       }
+
+       dir = g_strdup_printf("%s/%s", STORAGEDIR,
+                               __connman_service_get_ident(service));
+
+       /* If the dir doesn't exist, create it */
+       if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
+               if(mkdir(dir, MODE) < 0) {
+                       if (errno != EEXIST) {
+                               g_free(dir);
+
+                               err = -errno;
+                               goto err;
+                       }
+               }
+       }
+
+       g_free(dir);
+
+       name = g_strdup_printf("%s/%s/data", STORAGEDIR,
+                               __connman_service_get_ident(service));
+       file->history_name = g_strdup_printf("%s/%s/history", STORAGEDIR,
+                               __connman_service_get_ident(service));
+
+       /* TODO: Use a global config file instead of hard coded value. */
+       file->account_period_offset = 1;
+
+       err = stats_open(file, name);
+       g_free(name);
+       if (err < 0)
+               goto err;
+
+       err = stats_file_setup(file);
+       if (err < 0)
+               goto err;
+
+       return 0;
+
+err:
+       g_hash_table_remove(stats_hash, service);
+
+       return err;
+}
+
+void __connman_stats_service_unregister(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       g_hash_table_remove(stats_hash, service);
+}
+
+int  __connman_stats_update(struct connman_service *service,
+                               connman_bool_t roaming,
+                               struct connman_stats_data *data)
+{
+       struct stats_file *file;
+       struct stats_record *next;
+       int err;
+
+       file = g_hash_table_lookup(stats_hash, service);
+       if (file == NULL)
+               return -EEXIST;
+
+       if (file->len < file->max_len &&
+                       file->last == get_end(file)) {
+               DBG("grow file %s", file->name);
+
+               err = stats_file_remap(file, file->len + sysconf(_SC_PAGESIZE));
+               if (err < 0)
+                       return err;
+       }
+
+       next = get_next(file, get_end(file));
+
+       if (next == get_begin(file)) {
+               DBG("ring buffer is full, update history file");
+
+               if (stats_file_history_update(file) < 0) {
+                       connman_warn("history file update failed %s",
+                                       file->history_name);
+               }
+       }
+
+       next->ts = time(NULL);
+       next->roaming = roaming;
+       memcpy(&next->data, data, sizeof(struct connman_stats_data));
+
+       if (roaming != TRUE)
+               set_home(file, next);
+       else
+               set_roaming(file, next);
+
+       set_end(file, next);
+
+       return 0;
+}
+
+int __connman_stats_get(struct connman_service *service,
+                               connman_bool_t roaming,
+                               struct connman_stats_data *data)
+{
+       struct stats_file *file;
+       struct stats_record *rec;
+
+       file = g_hash_table_lookup(stats_hash, service);
+       if (file == NULL)
+               return -EEXIST;
+
+       if (roaming != TRUE)
+               rec = file->home;
+       else
+               rec = file->roaming;
+
+       if (rec != NULL) {
+               memcpy(data, &rec->data,
+                       sizeof(struct connman_stats_data));
+       }
+
+       return 0;
+}
+
+int __connman_stats_init(void)
+{
+       DBG("");
+
+       stats_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, stats_free);
+
+       return 0;
+}
+
+void __connman_stats_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(stats_hash);
+       stats_hash = NULL;
+}
diff --git a/src/storage.c b/src/storage.c
new file mode 100644 (file)
index 0000000..dc71527
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <connman/storage.h>
+
+#include "connman.h"
+
+#define SETTINGS       "settings"
+#define DEFAULT                "default.profile"
+
+#define MODE           (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
+                       S_IXGRP | S_IROTH | S_IXOTH)
+
+static GKeyFile *storage_load(const char *pathname)
+{
+       GKeyFile *keyfile = NULL;
+       GError *error = NULL;
+
+       DBG("Loading %s", pathname);
+
+       keyfile = g_key_file_new();
+
+       if (!g_key_file_load_from_file(keyfile, pathname, 0, &error)) {
+               DBG("Unable to load %s: %s", pathname, error->message);
+               g_clear_error(&error);
+
+               g_key_file_free(keyfile);
+               keyfile = NULL;
+       }
+
+       return keyfile;
+}
+
+static int storage_save(GKeyFile *keyfile, char *pathname)
+{
+       gchar *data = NULL;
+       gsize length = 0;
+       GError *error = NULL;
+       int ret = 0;
+
+       data = g_key_file_to_data(keyfile, &length, NULL);
+
+       if (!g_file_set_contents(pathname, data, length, &error)) {
+               DBG("Failed to store information: %s", error->message);
+               g_error_free(error);
+               ret = -EIO;
+       }
+
+       g_free(data);
+
+       return ret;
+}
+
+static void storage_delete(const char *pathname)
+{
+       DBG("file path %s", pathname);
+
+       if (unlink(pathname) < 0)
+               connman_error("Failed to remove %s", pathname);
+}
+
+GKeyFile *__connman_storage_load_global()
+{
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
+               return NULL;
+
+       keyfile = storage_load(pathname);
+
+       g_free(pathname);
+
+       return keyfile;
+}
+
+int __connman_storage_save_global(GKeyFile *keyfile)
+{
+       gchar *pathname;
+       int ret;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
+               return -ENOMEM;
+
+       ret = storage_save(keyfile, pathname);
+
+       g_free(pathname);
+
+       return ret;
+}
+
+void __connman_storage_delete_global()
+{
+       gchar *pathname;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
+               return;
+
+       storage_delete(pathname);
+
+       g_free(pathname);
+}
+
+GKeyFile *__connman_storage_load_config(const char *ident)
+{
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
+
+       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
+       if(pathname == NULL)
+               return NULL;
+
+       keyfile = storage_load(pathname);
+
+       g_free(pathname);
+
+       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 == NULL)
+               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()
+{
+       struct dirent *d;
+       gchar *str;
+       DIR *dir;
+       GString *result;
+       gchar **services = NULL;
+       struct stat buf;
+       int ret;
+
+       dir = opendir(STORAGEDIR);
+       if (dir == NULL)
+               return NULL;
+
+       result = g_string_new(NULL);
+
+       while ((d = readdir(dir))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                               strcmp(d->d_name, "..") == 0 ||
+                               strncmp(d->d_name, "provider_", 9) == 0)
+                       continue;
+
+               switch (d->d_type) {
+               case DT_DIR:
+                       /*
+                        * If the settings file is not found, then
+                        * assume this directory is not a services dir.
+                        */
+                       str = g_strdup_printf("%s/%s/settings", STORAGEDIR,
+                                                               d->d_name);
+                       ret = stat(str, &buf);
+                       g_free(str);
+                       if (ret < 0)
+                               continue;
+
+                       g_string_append_printf(result, "%s/", d->d_name);
+                       break;
+               }
+       }
+
+       closedir(dir);
+
+       str = g_string_free(result, FALSE);
+       if (str && str[0] != '\0') {
+               /*
+               * Remove the trailing separator so that services doesn't end up
+               * with an empty element.
+               */
+               str[strlen(str) - 1] = '\0';
+               services = g_strsplit(str, "/", -1);
+       }
+       g_free(str);
+
+       return services;
+}
+
+GKeyFile *connman_storage_load_service(const char *service_id)
+{
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
+
+       pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, SETTINGS);
+       if(pathname == NULL)
+               return NULL;
+
+       keyfile =  storage_load(pathname);
+       g_free(pathname);
+
+       return keyfile;
+}
+
+int __connman_storage_save_service(GKeyFile *keyfile, const char *service_id)
+{
+       int ret = 0;
+       gchar *pathname, *dirname;
+
+       dirname = g_strdup_printf("%s/%s", STORAGEDIR, service_id);
+       if(dirname == NULL)
+               return -ENOMEM;
+
+       /* If the dir doesn't exist, create it */
+       if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
+               if(mkdir(dirname, MODE) < 0) {
+                       if (errno != EEXIST) {
+                               g_free(dirname);
+                               return -errno;
+                       }
+               }
+       }
+
+       pathname = g_strdup_printf("%s/%s", dirname, SETTINGS);
+
+       g_free(dirname);
+
+       ret = storage_save(keyfile, pathname);
+
+       g_free(pathname);
+
+       return ret;
+}
+
+static gboolean remove_file(const char *service_id, const char *file)
+{
+       gchar *pathname;
+       gboolean ret = FALSE;
+
+       pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, file);
+       if(pathname == NULL)
+               return FALSE;
+
+       if (g_file_test(pathname, G_FILE_TEST_EXISTS) == FALSE) {
+               ret = TRUE;
+       } else if (g_file_test(pathname, G_FILE_TEST_IS_REGULAR) == TRUE) {
+               unlink(pathname);
+               ret = TRUE;
+       }
+
+       g_free(pathname);
+       return ret;
+}
+
+static gboolean remove_dir(const char *service_id)
+{
+       gchar *pathname;
+       gboolean ret = FALSE;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, service_id);
+       if(pathname == NULL)
+               return FALSE;
+
+       if (g_file_test(pathname, G_FILE_TEST_EXISTS) == FALSE) {
+               ret = TRUE;
+       } else if (g_file_test(pathname, G_FILE_TEST_IS_DIR) == TRUE) {
+               rmdir(pathname);
+               ret = TRUE;
+       }
+
+       g_free(pathname);
+       return ret;
+}
+
+gboolean __connman_storage_remove_service(const char *service_id)
+{
+       gboolean removed;
+
+       /* Remove service configuration file */
+       removed = remove_file(service_id, SETTINGS);
+       if (removed == FALSE)
+               return FALSE;
+
+       /* Remove the statistics file also */
+       removed = remove_file(service_id, "data");
+       if (removed == FALSE)
+               return FALSE;
+
+       removed = remove_dir(service_id);
+       if (removed == FALSE)
+               return FALSE;
+
+       DBG("Removed service dir %s/%s", STORAGEDIR, service_id);
+
+       return TRUE;
+}
+
+GKeyFile *__connman_storage_load_provider(const char *identifier)
+{
+       gchar *pathname;
+       GKeyFile *keyfile;
+
+       pathname = g_strdup_printf("%s/%s_%s/%s", STORAGEDIR, "provider",
+                       identifier, SETTINGS);
+       if (pathname == NULL)
+               return NULL;
+
+       keyfile = storage_load(pathname);
+       g_free(pathname);
+
+       return keyfile;
+}
+
+void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier)
+{
+       gchar *pathname, *dirname;
+
+       dirname = g_strdup_printf("%s/%s_%s", STORAGEDIR,
+                       "provider", identifier);
+       if (dirname == NULL)
+               return;
+
+       if (g_file_test(dirname, G_FILE_TEST_IS_DIR) == FALSE &&
+                       mkdir(dirname, MODE) < 0) {
+               g_free(dirname);
+               return;
+       }
+
+       pathname = g_strdup_printf("%s/%s", dirname, SETTINGS);
+       g_free(dirname);
+
+       storage_save(keyfile, pathname);
+       g_free(pathname);
+}
+
+gchar **__connman_storage_get_providers(void)
+{
+       GSList *list = NULL;
+       int num = 0, i = 0;
+       struct dirent *d;
+       gchar *str;
+       DIR *dir;
+       struct stat buf;
+       int ret;
+       char **providers;
+       GSList *iter;
+
+       dir = opendir(STORAGEDIR);
+       if (dir == NULL)
+               return NULL;
+
+       while ((d = readdir(dir))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                               strcmp(d->d_name, "..") == 0 ||
+                               strncmp(d->d_name, "provider_", 9) != 0)
+                       continue;
+
+               if (d->d_type == DT_DIR) {
+                       str = g_strdup_printf("%s/%s/settings", STORAGEDIR,
+                                       d->d_name);
+                       ret = stat(str, &buf);
+                       g_free(str);
+                       if (ret < 0)
+                               continue;
+                       list = g_slist_prepend(list, g_strdup(d->d_name));
+                       num += 1;
+               }
+       }
+
+       closedir(dir);
+
+       providers = g_try_new0(char *, num + 1);
+       for (iter = list; iter != NULL; iter = g_slist_next(iter)) {
+               if (providers != NULL)
+                       providers[i] = iter->data;
+               else
+                       g_free(iter->data);
+               i += 1;
+       }
+       g_slist_free(list);
+
+       return providers;
+}
+
+/*
+ * This function migrates keys from default.profile to settings file.
+ * This can be removed once the migration is over.
+*/
+void __connman_storage_migrate()
+{
+       gchar *pathname;
+       GKeyFile *keyfile_def = NULL;
+       GKeyFile *keyfile = NULL;
+       GError *error = NULL;
+       connman_bool_t delete_old_config = TRUE;
+       char **services, **keys, *value;
+       int i, k, err;
+       connman_bool_t val;
+
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, DEFAULT);
+       if (pathname == NULL)
+               return;
+
+       /* If setting file exists, migration has been done. */
+       keyfile = __connman_storage_load_global();
+       if (keyfile) {
+               g_key_file_free(keyfile);
+               unlink(pathname);
+               g_free(pathname);
+               return;
+       }
+
+       /* If default.profile exists, create new settings file */
+       keyfile_def = storage_load(pathname);
+       if (keyfile_def == NULL)
+               goto done;
+
+       services = g_key_file_get_groups(keyfile_def, NULL);
+       for (i = 0; services != NULL && services[i] != NULL; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0 &&
+                               strncmp(services[i], "ethernet_", 9) != 0 &&
+                               strncmp(services[i], "cellular_", 9) != 0 &&
+                               strncmp(services[i], "bluetooth_", 10) != 0 &&
+                               strncmp(services[i], "wimax_", 6) != 0 &&
+                               strncmp(services[i], "vpn_", 4) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+               if (keyfile != NULL) {
+                       g_key_file_free(keyfile);
+                       DBG("already exists %s", services[i]);
+                       continue;
+               }
+
+               keyfile = g_key_file_new();
+               if (keyfile == NULL) {
+                       connman_warn("Migrating %s failed", services[i]);
+                       delete_old_config = FALSE;
+                       continue;
+               }
+
+               keys = g_key_file_get_keys(keyfile_def, services[i],
+                               NULL, NULL);
+
+               for (k = 0; keys != NULL && keys[k] != NULL; k++) {
+                       value = g_key_file_get_value(keyfile_def, services[i],
+                                       keys[k], NULL);
+                       g_key_file_set_value(keyfile, services[i],
+                                       keys[k], value);
+                       g_free(value);
+               }
+
+               if (keys != NULL && keys[0] != NULL) {
+                       err = __connman_storage_save_service(keyfile,
+                                       services[i]);
+                       if (err >= 0)
+                               DBG("migrated %s", services[i]);
+                       else {
+                               connman_warn("Migrating %s failed %s",
+                                               services[i], strerror(-err));
+                               delete_old_config = FALSE;
+                       }
+               } else
+                       DBG("no keys in %s", services[i]);
+
+               g_strfreev(keys);
+               g_key_file_free(keyfile);
+       }
+       g_strfreev(services);
+
+       /* Copy global settings from default.profile to settings. */
+       keyfile = g_key_file_new();
+
+       val = g_key_file_get_boolean(keyfile_def, "global",
+                                       "OfflineMode", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
+       }
+
+       g_key_file_set_boolean(keyfile, "global",
+                                       "OfflineMode", val);
+
+       /* Migrate Powered/Enable state key/value pairs from legacy
+        * settings
+        */
+
+       val = g_key_file_get_boolean(keyfile_def, "WiFi",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = g_key_file_get_boolean(keyfile_def, "device_Wireless", "Powered", &error);
+               if (error != NULL) {
+                       g_clear_error(&error);
+                       val = FALSE;
+               }
+       }
+
+       g_key_file_set_boolean(keyfile, "WiFi",
+                                       "Enable", val);
+
+       val = g_key_file_get_boolean(keyfile_def, "Bluetooth",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = g_key_file_get_boolean(keyfile_def, "device_Bluetooth", "Powered", &error);
+               if (error != NULL) {
+                       g_clear_error(&error);
+                       val = FALSE;
+               }
+       }
+
+       g_key_file_set_boolean(keyfile, "Bluetooth",
+                                       "Enable", val);
+
+       val = g_key_file_get_boolean(keyfile_def, "Wired",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = g_key_file_get_boolean(keyfile_def, "device_Ethernet", "Powered", &error);
+               if (error != NULL) {
+                       g_clear_error(&error);
+                       val = FALSE;
+               }
+       }
+
+       g_key_file_set_boolean(keyfile, "Wired",
+                                       "Enable", val);
+
+       val = g_key_file_get_boolean(keyfile_def, "Cellular",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = g_key_file_get_boolean(keyfile_def, "device_Cellular", "Powered", &error);
+               if (error != NULL) {
+                       g_clear_error(&error);
+                       val = FALSE;
+               }
+       }
+
+       g_key_file_set_boolean(keyfile, "Cellular",
+                                       "Enable", val);
+
+       val = g_key_file_get_boolean(keyfile_def, "WiMAX",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = g_key_file_get_boolean(keyfile_def, "device_WiMAX", "Powered", &error);
+               if (error != NULL) {
+                       g_clear_error(&error);
+                       val = FALSE;
+               }
+       }
+
+       g_key_file_set_boolean(keyfile, "WiMAX",
+                                       "Enable", val);
+
+       if (__connman_storage_save_global(keyfile) < 0) {
+               connman_warn("Migrating global config failed");
+               delete_old_config = FALSE;
+       }
+
+       g_key_file_free(keyfile);
+
+       g_key_file_free(keyfile_def);
+
+       if (delete_old_config == TRUE) {
+               DBG("migration done for %s", pathname);
+               unlink(pathname);
+       }
+done:
+       g_free(pathname);
+}
diff --git a/src/task.c b/src/task.c
new file mode 100644 (file)
index 0000000..97de7e3
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+struct notify_data {
+       connman_task_notify_t func;
+       void *data;
+};
+
+struct connman_task {
+       char *path;
+       pid_t pid;
+       guint child_watch;
+       GPtrArray *argv;
+       GPtrArray *envp;
+       connman_task_exit_t exit_func;
+       void *exit_data;
+       GHashTable *notify;
+};
+
+static GHashTable *task_hash = NULL;
+
+static volatile int task_counter;
+
+static DBusConnection *connection;
+
+static void free_pointer(gpointer data, gpointer user_data)
+{
+       g_free(data);
+}
+
+static void free_task(gpointer data)
+{
+       struct connman_task *task = data;
+
+       DBG("task %p", task);
+
+       g_hash_table_destroy(task->notify);
+       task->notify = NULL;
+
+       if (task->pid > 0)
+               kill(task->pid, SIGTERM);
+
+       if (task->child_watch > 0)
+               g_source_remove(task->child_watch);
+
+       g_ptr_array_foreach(task->envp, free_pointer, NULL);
+       g_ptr_array_free(task->envp, TRUE);
+
+       g_ptr_array_foreach(task->argv, free_pointer, NULL);
+       g_ptr_array_free(task->argv, TRUE);
+
+       g_free(task->path);
+       g_free(task);
+}
+
+/**
+ * connman_task_create:
+ * @program: name of executable
+ *
+ * Allocate a new task of given #program
+ *
+ * Returns: a newly-allocated #connman_task structure
+ */
+struct connman_task *connman_task_create(const char *program)
+{
+       struct connman_task *task;
+       gint counter;
+       char *str;
+
+       DBG("");
+
+       task = g_try_new0(struct connman_task, 1);
+       if (task == NULL)
+               return NULL;
+
+       counter = __sync_fetch_and_add(&task_counter, 1);
+
+       task->path = g_strdup_printf("/task/%d", counter);
+       task->pid = -1;
+
+       task->argv = g_ptr_array_new();
+       task->envp = g_ptr_array_new();
+
+       str = g_strdup(program);
+       g_ptr_array_add(task->argv, str);
+
+       task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+
+       DBG("task %p", task);
+
+       g_hash_table_insert(task_hash, task->path, task);
+
+       return task;
+}
+
+/**
+ * connman_task_destory:
+ * @task: task structure
+ *
+ * Remove and destory #task
+ */
+void connman_task_destroy(struct connman_task *task)
+{
+       DBG("task %p", task);
+
+       g_hash_table_remove(task_hash, task->path);
+}
+
+/**
+ * connman_task_get_path:
+ * @task: task structure
+ *
+ * Get object path
+ */
+const char *connman_task_get_path(struct connman_task *task)
+{
+       return task->path;
+}
+
+/**
+ * connman_task_add_argument:
+ * @task: task structure
+ * @name: argument name
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Add a new command line argument
+ */
+int connman_task_add_argument(struct connman_task *task,
+                               const char *name, const char *format, ...)
+{
+       va_list ap;
+       char *str;
+
+       DBG("task %p arg %s", task, name);
+
+       if (name == NULL)
+               return -EINVAL;
+
+       str = g_strdup(name);
+       g_ptr_array_add(task->argv, str);
+
+       va_start(ap, format);
+
+       if (format != NULL) {
+               str = g_strdup_vprintf(format, ap);
+               g_ptr_array_add(task->argv, str);
+       }
+
+       va_end(ap);
+
+       return 0;
+}
+
+/**
+ * connman_task_add_variable:
+ * @task: task structure
+ * @key: variable name
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Add a new environment variable
+ */
+int connman_task_add_variable(struct connman_task *task,
+                               const char *key, const char *format, ...)
+{
+       va_list ap;
+       char *str, *val;
+
+       DBG("task %p key %s", task, key);
+
+       if (key == NULL)
+               return -EINVAL;
+
+       va_start(ap, format);
+
+       val = g_strdup_vprintf(format, ap);
+       str = g_strdup_printf("%s=%s", key, format ? format : "");
+       g_ptr_array_add(task->envp, str);
+       g_free(val);
+
+       va_end(ap);
+
+       return 0;
+}
+
+/**
+ * connman_task_set_notify:
+ * @task: task structure
+ * @member: notifcation method name
+ * @function: notification callback
+ * @user_data: optional notification user data
+ *
+ * Set notification handler for #member
+ */
+int connman_task_set_notify(struct connman_task *task, const char *member,
+                       connman_task_notify_t function, void *user_data)
+{
+       struct notify_data *notify;
+
+       DBG("task %p", task);
+
+       notify = g_try_new0(struct notify_data, 1);
+       if (notify == NULL)
+               return -ENOMEM;
+
+       notify->func = function;
+       notify->data = user_data;
+
+       g_hash_table_replace(task->notify, g_strdup(member), notify);
+
+       return 0;
+}
+
+static void task_died(GPid pid, gint status, gpointer user_data)
+{
+       struct connman_task *task = user_data;
+       int exit_code;
+
+       if (WIFEXITED(status)) {
+               exit_code = WEXITSTATUS(status);
+               DBG("task %p exit status %d", task, exit_code);
+       } else {
+               exit_code = 0;
+               DBG("task %p signal %d", task, WTERMSIG(status));
+       }
+
+       g_spawn_close_pid(pid);
+       task->pid = -1;
+
+       task->child_watch = 0;
+
+       if (task->exit_func)
+               task->exit_func(task, exit_code, task->exit_data);
+}
+
+static void task_setup(gpointer user_data)
+{
+       sigset_t mask;
+       struct connman_task *task = user_data;
+
+       DBG("task %p", task);
+
+       sigemptyset(&mask);
+       if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
+               connman_error("Failed to clean signal mask");
+}
+
+/**
+ * connman_task_run:
+ * @task: task structure
+ * @function: exit callback
+ * @user_data: optional exit user data
+ * @fd: optional spawn with pipe
+ *
+ * Execute program specified by #task
+ */
+int connman_task_run(struct connman_task *task,
+                       connman_task_exit_t function, void *user_data,
+                       int *stdin_fd, int *stdout_fd, int *stderr_fd)
+{
+       GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
+       gboolean result;
+       char **argv, **envp;
+
+       DBG("task %p", task);
+
+       if (task->pid > 0)
+               return -EALREADY;
+
+       if (stdout_fd == NULL)
+               flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
+
+       if (stderr_fd == NULL)
+               flags |= G_SPAWN_STDERR_TO_DEV_NULL;
+
+       task->exit_func = function;
+       task->exit_data = user_data;
+
+       if (g_ptr_array_index(task->argv, task->argv->len - 1) != NULL)
+               g_ptr_array_add(task->argv, NULL);
+
+       if (task->envp->len == 0 || g_ptr_array_index(task->envp,
+                                       task->envp->len - 1) != NULL) {
+               if (g_hash_table_size(task->notify) > 0) {
+                       const char *busname;
+                       char *str;
+
+                       busname = dbus_bus_get_unique_name(connection);
+                       str = g_strdup_printf("CONNMAN_BUSNAME=%s", busname);
+                       g_ptr_array_add(task->envp, str);
+
+                       str = g_strdup_printf("CONNMAN_INTERFACE=%s",
+                                               CONNMAN_TASK_INTERFACE);
+                       g_ptr_array_add(task->envp, str);
+
+                       str = g_strdup_printf("CONNMAN_PATH=%s", task->path);
+                       g_ptr_array_add(task->envp, str);
+               }
+
+               g_ptr_array_add(task->envp, NULL);
+       }
+
+       argv = (char **) task->argv->pdata;
+       envp = (char **) task->envp->pdata;
+
+       result = g_spawn_async_with_pipes(NULL, argv, envp, flags,
+                                       task_setup, task, &task->pid,
+                                       stdin_fd, stdout_fd, stderr_fd, NULL);
+       if (result == FALSE) {
+               connman_error("Failed to spawn %s", argv[0]);
+               return -EIO;
+       }
+
+       task->child_watch = g_child_watch_add(task->pid, task_died, task);
+
+       return 0;
+}
+
+static gboolean force_kill_timeout(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, SIGKILL) == 0)
+                       connman_warn("killing pid %d by force", pid);
+       }
+
+       return FALSE;
+}
+
+static gboolean kill_timeout(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, SIGINT) == 0)
+                       g_timeout_add_seconds(1, force_kill_timeout,
+                                       GINT_TO_POINTER(pid));
+       }
+
+       return FALSE;
+}
+
+static gboolean check_kill(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, 0) == 0) {
+                       connman_info("pid %d was not killed, "
+                                       "retrying after 2 sec", pid);
+                       g_timeout_add_seconds(2, kill_timeout,
+                                       GINT_TO_POINTER(pid));
+               }
+       }
+
+       return FALSE;
+}
+
+/**
+ * connman_task_stop:
+ * @task: task structure
+ *
+ * Stop program specified by #task
+ */
+int connman_task_stop(struct connman_task *task)
+{
+       DBG("task %p", task);
+
+       if (task->pid > 0) {
+               kill(task->pid, SIGTERM);
+
+               g_timeout_add_seconds(0, check_kill,
+                               GINT_TO_POINTER(task->pid));
+       }
+
+       return 0;
+}
+
+static DBusHandlerResult task_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct connman_task *task;
+       struct notify_data *notify;
+       const char *path, *member;
+       DBusMessage *reply = NULL;
+
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (dbus_message_has_interface(message,
+                                       CONNMAN_TASK_INTERFACE) == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       path = dbus_message_get_path(message);
+       if (path == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       task = g_hash_table_lookup(task_hash, path);
+       if (task == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       member = dbus_message_get_member(message);
+       if (member == NULL)
+               goto send_reply;
+
+       notify = g_hash_table_lookup(task->notify, member);
+       if (notify == NULL)
+               goto send_reply;
+
+       if (notify->func)
+               reply = notify->func(task, message, notify->data);
+
+send_reply:
+       if (dbus_message_get_no_reply(message) == FALSE &&
+                                               reply == NULL) {
+
+               reply = dbus_message_new_method_return(message);
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       if (reply != NULL) {
+               dbus_connection_send(connection, reply, NULL);
+
+               dbus_message_unref(reply);
+       }
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static const char *task_rule = "type=method_call"
+                                       ",interface=" CONNMAN_TASK_INTERFACE;
+
+int __connman_task_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       dbus_connection_add_filter(connection, task_filter, NULL, NULL);
+
+       task_counter = 0;
+       __sync_synchronize();
+
+       task_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, free_task);
+
+       dbus_bus_add_match(connection, task_rule, NULL);
+       dbus_connection_flush(connection);
+
+       return 0;
+}
+
+void __connman_task_cleanup(void)
+{
+       DBG("");
+
+       dbus_bus_remove_match(connection, task_rule, NULL);
+       dbus_connection_flush(connection);
+
+       g_hash_table_destroy(task_hash);
+       task_hash = NULL;
+
+       dbus_connection_remove_filter(connection, task_filter, NULL);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/technology.c b/src/technology.c
new file mode 100644 (file)
index 0000000..3781b0c
--- /dev/null
@@ -0,0 +1,1382 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+
+static GSList *technology_list = NULL;
+
+/*
+ * List of devices with no technology associated with them either because of
+ * no compiled in support or the driver is not yet loaded.
+*/
+static GSList *techless_device_list = NULL;
+static GHashTable *rfkill_list;
+
+static connman_bool_t global_offlinemode;
+
+struct connman_rfkill {
+       unsigned int index;
+       enum connman_service_type type;
+       connman_bool_t softblock;
+       connman_bool_t hardblock;
+};
+
+struct connman_technology {
+       int refcount;
+       enum connman_service_type type;
+       char *path;
+       GSList *device_list;
+       int enabled;
+       char *regdom;
+       connman_bool_t connected;
+
+       connman_bool_t tethering;
+       char *tethering_ident;
+       char *tethering_passphrase;
+
+       connman_bool_t enable_persistent; /* Save the tech state */
+
+       struct connman_technology_driver *driver;
+       void *driver_data;
+
+       DBusMessage *pending_reply;
+       guint pending_timeout;
+
+       GSList *scan_pending;
+};
+
+static GSList *driver_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_technology_driver *driver1 = a;
+       const struct connman_technology_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+static void rfkill_check(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_rfkill *rfkill = value;
+       enum connman_service_type type = GPOINTER_TO_INT(user_data);
+
+       /* Calling _technology_rfkill_add will update the tech. */
+       if (rfkill->type == type)
+               __connman_technology_add_rfkill(rfkill->index, type,
+                               rfkill->softblock, rfkill->hardblock);
+}
+
+/**
+ * connman_technology_driver_register:
+ * @driver: Technology driver definition
+ *
+ * Register a new technology driver
+ *
+ * Returns: %0 on success
+ */
+int connman_technology_driver_register(struct connman_technology_driver *driver)
+{
+       GSList *list;
+       struct connman_device *device;
+       enum connman_service_type type;
+
+       DBG("Registering %s driver", driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       if (techless_device_list == NULL)
+               goto check_rfkill;
+
+       /*
+        * Check for technology less devices if this driver
+        * can service any of them.
+       */
+       for (list = techless_device_list; list; list = list->next) {
+               device = list->data;
+
+               type = __connman_device_get_service_type(device);
+               if (type != driver->type)
+                       continue;
+
+               techless_device_list = g_slist_remove(techless_device_list,
+                                                               device);
+
+               __connman_technology_add_device(device);
+       }
+
+check_rfkill:
+       /* Check for orphaned rfkill switches. */
+       g_hash_table_foreach(rfkill_list, rfkill_check,
+                                       GINT_TO_POINTER(driver->type));
+
+       return 0;
+}
+
+/**
+ * connman_technology_driver_unregister:
+ * @driver: Technology driver definition
+ *
+ * Remove a previously registered technology driver
+ */
+void connman_technology_driver_unregister(struct connman_technology_driver *driver)
+{
+       GSList *list;
+       struct connman_technology *technology;
+
+       DBG("Unregistering driver %p name %s", driver, driver->name);
+
+       for (list = technology_list; list; list = list->next) {
+               technology = list->data;
+
+               if (technology->driver == NULL)
+                       continue;
+
+               if (technology->type == driver->type) {
+                       technology->driver->remove(technology);
+                       technology->driver = NULL;
+               }
+       }
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+static void tethering_changed(struct connman_technology *technology)
+{
+       connman_bool_t tethering = technology->tethering;
+
+       connman_dbus_property_changed_basic(technology->path,
+                               CONNMAN_TECHNOLOGY_INTERFACE, "Tethering",
+                                               DBUS_TYPE_BOOLEAN, &tethering);
+}
+
+void connman_technology_tethering_notify(struct connman_technology *technology,
+                                                       connman_bool_t enabled)
+{
+       GSList *list;
+
+       DBG("technology %p enabled %u", technology, enabled);
+
+       if (technology->tethering == enabled)
+               return;
+
+       technology->tethering = enabled;
+
+       tethering_changed(technology);
+
+       if (enabled == TRUE)
+               __connman_tethering_set_enabled();
+       else {
+               for (list = technology_list; list; list = list->next) {
+                       struct connman_technology *other_tech = list->data;
+                       if (other_tech->tethering == TRUE)
+                               break;
+               }
+               if (list == NULL)
+                       __connman_tethering_set_disabled();
+       }
+}
+
+static int set_tethering(struct connman_technology *technology,
+                               connman_bool_t enabled)
+{
+       const char *ident, *passphrase, *bridge;
+
+       ident = technology->tethering_ident;
+       passphrase = technology->tethering_passphrase;
+
+       if (technology->driver == NULL ||
+                       technology->driver->set_tethering == NULL)
+               return -EOPNOTSUPP;
+
+       bridge = __connman_tethering_get_bridge();
+       if (bridge == NULL)
+               return -EOPNOTSUPP;
+
+       if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
+           (ident == NULL || passphrase == NULL))
+               return -EINVAL;
+
+       return technology->driver->set_tethering(technology, ident, passphrase,
+                                                       bridge, enabled);
+}
+
+void connman_technology_regdom_notify(struct connman_technology *technology,
+                                                       const char *alpha2)
+{
+       DBG("");
+
+       if (alpha2 == NULL)
+               connman_error("Failed to set regulatory domain");
+       else
+               DBG("Regulatory domain set to %s", alpha2);
+
+       g_free(technology->regdom);
+       technology->regdom = g_strdup(alpha2);
+}
+
+int connman_technology_set_regdom(const char *alpha2)
+{
+       GSList *list;
+
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (technology->driver == NULL)
+                       continue;
+
+               if (technology->driver->set_regdom)
+                       technology->driver->set_regdom(technology, alpha2);
+       }
+
+       return 0;
+}
+
+static void free_rfkill(gpointer data)
+{
+       struct connman_rfkill *rfkill = data;
+
+       g_free(rfkill);
+}
+
+static const char *get_name(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               return "Wired";
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return "WiFi";
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return "WiMAX";
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return "Bluetooth";
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return "Cellular";
+       }
+
+       return NULL;
+}
+
+static void save_state(struct connman_technology *technology)
+{
+       GKeyFile *keyfile;
+       gchar *identifier;
+
+       DBG("technology %p", technology);
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       identifier = g_strdup_printf("%s", get_name(technology->type));
+       if (identifier == NULL)
+               goto done;
+
+       g_key_file_set_boolean(keyfile, identifier, "Enable",
+                               technology->enable_persistent);
+
+done:
+       g_free(identifier);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+static void load_state(struct connman_technology *technology)
+{
+       GKeyFile *keyfile;
+       gchar *identifier;
+       GError *error = NULL;
+       connman_bool_t enable;
+
+       DBG("technology %p", technology);
+
+       keyfile = __connman_storage_load_global();
+       /* Fallback on disabling technology if file not found. */
+       if (keyfile == NULL) {
+               if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+                       /* We enable ethernet by default */
+                       technology->enable_persistent = TRUE;
+               else
+                       technology->enable_persistent = FALSE;
+               return;
+       }
+
+       identifier = g_strdup_printf("%s", get_name(technology->type));
+       if (identifier == NULL)
+               goto done;
+
+       enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
+       if (error == NULL)
+               technology->enable_persistent = enable;
+       else {
+               if (technology->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+                       technology->enable_persistent = TRUE;
+               else
+                       technology->enable_persistent = FALSE;
+
+               save_state(technology);
+               g_clear_error(&error);
+       }
+done:
+       g_free(identifier);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+connman_bool_t __connman_technology_get_offlinemode(void)
+{
+       return global_offlinemode;
+}
+
+static void connman_technology_save_offlinemode()
+{
+       GKeyFile *keyfile;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       g_key_file_set_boolean(keyfile, "global",
+                                       "OfflineMode", global_offlinemode);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+static connman_bool_t connman_technology_load_offlinemode()
+{
+       GKeyFile *keyfile;
+       GError *error = NULL;
+       connman_bool_t offlinemode;
+
+       /* If there is a error, we enable offlinemode */
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               return FALSE;
+
+       offlinemode = g_key_file_get_boolean(keyfile, "global",
+                                               "OfflineMode", &error);
+       if (error != NULL) {
+               offlinemode = FALSE;
+               g_clear_error(&error);
+       }
+
+       g_key_file_free(keyfile);
+
+       return offlinemode;
+}
+
+static void append_properties(DBusMessageIter *iter,
+               struct connman_technology *technology)
+{
+       DBusMessageIter dict;
+       const char *str;
+       connman_bool_t powered;
+
+       connman_dbus_dict_open(iter, &dict);
+
+       str = get_name(technology->type);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(&dict, "Name",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = __connman_service_type2string(technology->type);
+       if (str != NULL)
+               connman_dbus_dict_append_basic(&dict, "Type",
+                                               DBUS_TYPE_STRING, &str);
+
+       __sync_synchronize();
+       if (technology->enabled > 0)
+               powered = TRUE;
+       else
+               powered = FALSE;
+       connman_dbus_dict_append_basic(&dict, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &powered);
+
+       connman_dbus_dict_append_basic(&dict, "Connected",
+                                       DBUS_TYPE_BOOLEAN,
+                                       &technology->connected);
+
+       connman_dbus_dict_append_basic(&dict, "Tethering",
+                                       DBUS_TYPE_BOOLEAN,
+                                       &technology->tethering);
+
+       if (technology->tethering_ident != NULL)
+               connman_dbus_dict_append_basic(&dict, "TetheringIdentifier",
+                                               DBUS_TYPE_STRING,
+                                               &technology->tethering_ident);
+
+       if (technology->tethering_passphrase != NULL)
+               connman_dbus_dict_append_basic(&dict, "TetheringPassphrase",
+                                               DBUS_TYPE_STRING,
+                                               &technology->tethering_passphrase);
+
+       connman_dbus_dict_close(iter, &dict);
+}
+
+static void technology_added_signal(struct connman_technology *technology)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "TechnologyAdded");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &technology->path);
+       append_properties(&iter, technology);
+
+       dbus_connection_send(connection, signal, NULL);
+       dbus_message_unref(signal);
+}
+
+static void technology_removed_signal(struct connman_technology *technology)
+{
+       g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
+                       CONNMAN_MANAGER_INTERFACE, "TechnologyRemoved",
+                       DBUS_TYPE_OBJECT_PATH, &technology->path,
+                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *message, void *user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       append_properties(&iter, technology);
+
+       return reply;
+}
+
+void __connman_technology_list_struct(DBusMessageIter *array)
+{
+       GSList *list;
+       DBusMessageIter entry;
+
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (technology->path == NULL)
+                       continue;
+
+               dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
+                               NULL, &entry);
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+                               &technology->path);
+               append_properties(&entry, technology);
+               dbus_message_iter_close_container(array, &entry);
+       }
+}
+
+static gboolean technology_pending_reply(gpointer user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusMessage *reply;
+
+       /* Power request timedout, send ETIMEDOUT. */
+       if (technology->pending_reply != NULL) {
+               reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
+               if (reply != NULL)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(technology->pending_reply);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
+       return FALSE;
+}
+
+static int technology_enable(struct connman_technology *technology,
+               DBusMessage *msg)
+{
+       GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("technology %p enable", technology);
+
+       __sync_synchronize();
+       if (technology->enabled > 0) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (msg != NULL) {
+               /*
+                * This is a bit of a trick. When msg is not NULL it means
+                * thats technology_enable was invoked from the manager API.
+                * Hence we save the state here.
+                */
+               technology->enable_persistent = TRUE;
+               save_state(technology);
+       }
+
+       __connman_rfkill_block(technology->type, FALSE);
+
+       /*
+        * An empty device list means that devices in the technology
+        * were rfkill blocked. The unblock above will enable the devs.
+        */
+       if (technology->device_list == NULL) {
+               ret = 0;
+               goto done;
+       }
+
+       for (list = technology->device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               err = __connman_device_enable(device);
+               /*
+                * err = 0 : Device was enabled right away.
+                * If atleast one device gets enabled, we consider
+                * the technology to be enabled.
+                */
+               if (err == 0)
+                       ret = 0;
+       }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       if (err == -EALREADY)
+                               reply = __connman_error_already_enabled(msg);
+                       else
+                               reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
+static int technology_disable(struct connman_technology *technology,
+               DBusMessage *msg)
+{
+       GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("technology %p disable", technology);
+
+       __sync_synchronize();
+       if (technology->enabled == 0) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (technology->tethering == TRUE)
+               set_tethering(technology, FALSE);
+
+       if (msg != NULL) {
+               technology->enable_persistent = FALSE;
+               save_state(technology);
+       }
+
+       __connman_rfkill_block(technology->type, TRUE);
+
+       for (list = technology->device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               err = __connman_device_disable(device);
+               if (err == 0)
+                       ret = 0;
+       }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       if (err == -EALREADY)
+                               reply = __connman_error_already_disabled(msg);
+                       else
+                               reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_technology *technology = data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       DBG("property %s", name);
+
+       if (g_str_equal(name, "Tethering") == TRUE) {
+               int err;
+               connman_bool_t tethering;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &tethering);
+
+               if (technology->tethering == tethering)
+                       return __connman_error_in_progress(msg);
+
+               err = set_tethering(technology, tethering);
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+
+       } else if (g_str_equal(name, "TetheringIdentifier") == TRUE) {
+               const char *str;
+
+               dbus_message_iter_get_basic(&value, &str);
+
+               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       return __connman_error_not_supported(msg);
+
+               technology->tethering_ident = g_strdup(str);
+       } else if (g_str_equal(name, "TetheringPassphrase") == TRUE) {
+               const char *str;
+
+               dbus_message_iter_get_basic(&value, &str);
+
+               if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                       return __connman_error_not_supported(msg);
+
+               if (strlen(str) < 8)
+                       return __connman_error_invalid_arguments(msg);
+
+               technology->tethering_passphrase = g_strdup(str);
+       } else if (g_str_equal(name, "Powered") == TRUE) {
+               connman_bool_t enable;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &enable);
+               if (enable == TRUE)
+                       technology_enable(technology, msg);
+               else
+                       technology_disable(technology, msg);
+
+       } else
+               return __connman_error_invalid_property(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct connman_technology *technology_find(enum connman_service_type type)
+{
+       GSList *list;
+
+       DBG("type %d", type);
+
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (technology->type == type)
+                       return technology;
+       }
+
+       return NULL;
+}
+
+static void reply_scan_pending(struct connman_technology *technology, int err)
+{
+       DBusMessage *reply;
+
+       DBG("technology %p err %d", technology, err);
+
+       while (technology->scan_pending != NULL) {
+               DBusMessage *msg = technology->scan_pending->data;
+
+               DBG("reply to %s", dbus_message_get_sender(msg));
+
+               if (err == 0)
+                       reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               else
+                       reply = __connman_error_failed(msg, -err);
+               g_dbus_send_message(connection, reply);
+               dbus_message_unref(msg);
+
+               technology->scan_pending =
+                       g_slist_delete_link(technology->scan_pending,
+                                       technology->scan_pending);
+       }
+}
+
+void __connman_technology_scan_started(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+void __connman_technology_scan_stopped(struct connman_device *device)
+{
+       int count = 0;
+       struct connman_technology *technology;
+       enum connman_service_type type;
+       GSList *list;
+
+       type = __connman_device_get_service_type(device);
+       technology = technology_find(type);
+
+       DBG("technology %p device %p", technology, device);
+
+       if (technology == NULL)
+               return;
+
+       for (list = technology->device_list; list != NULL; list = list->next) {
+               struct connman_device *other_device = list->data;
+
+               if (device == other_device)
+                       continue;
+
+               if (__connman_device_get_service_type(other_device) != type)
+                       continue;
+
+               if (connman_device_get_scanning(other_device) == TRUE)
+                       count += 1;
+       }
+
+       if (count == 0)
+               reply_scan_pending(technology, 0);
+}
+
+static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+       struct connman_technology *technology = data;
+       int err;
+
+       DBG ("technology %p request from %s", technology,
+                       dbus_message_get_sender(msg));
+
+       dbus_message_ref(msg);
+       technology->scan_pending =
+               g_slist_prepend(technology->scan_pending, msg);
+
+       err = __connman_device_request_scan(technology->type);
+       if (err < 0)
+               reply_scan_pending(technology, err);
+
+       return NULL;
+}
+
+static const GDBusMethodTable technology_methods[] = {
+       { GDBUS_DEPRECATED_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { GDBUS_ASYNC_METHOD("Scan", NULL, NULL, scan) },
+       { },
+};
+
+static const GDBusSignalTable technology_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { },
+};
+
+static struct connman_technology *technology_get(enum connman_service_type type)
+{
+       struct connman_technology *technology;
+       struct connman_technology_driver *driver = NULL;
+       const char *str;
+       GSList *list;
+       int err;
+
+       DBG("type %d", type);
+
+       str = __connman_service_type2string(type);
+       if (str == NULL)
+               return NULL;
+
+       technology = technology_find(type);
+       if (technology != NULL) {
+               __sync_fetch_and_add(&technology->refcount, 1);
+               return technology;
+       }
+
+       /* First check if we have a driver for this technology type */
+       for (list = driver_list; list; list = list->next) {
+               driver = list->data;
+
+               if (driver->type == type)
+                       break;
+               else
+                       driver = NULL;
+       }
+
+       if (driver == NULL) {
+               DBG("No matching driver found for %s.",
+                               __connman_service_type2string(type));
+               return NULL;
+       }
+
+       technology = g_try_new0(struct connman_technology, 1);
+       if (technology == NULL)
+               return NULL;
+
+       technology->refcount = 1;
+
+       technology->type = type;
+       technology->path = g_strdup_printf("%s/technology/%s",
+                                                       CONNMAN_PATH, str);
+
+       technology->device_list = NULL;
+
+       technology->pending_reply = NULL;
+
+       load_state(technology);
+
+       if (g_dbus_register_interface(connection, technology->path,
+                                       CONNMAN_TECHNOLOGY_INTERFACE,
+                                       technology_methods, technology_signals,
+                                       NULL, technology, NULL) == FALSE) {
+               connman_error("Failed to register %s", technology->path);
+               g_free(technology);
+               return NULL;
+       }
+
+       technology_list = g_slist_append(technology_list, technology);
+
+       technology_added_signal(technology);
+
+       technology->driver = driver;
+       err = driver->probe(technology);
+       if (err != 0)
+               DBG("Driver probe failed for technology %p", technology);
+
+       DBG("technology %p", technology);
+
+       return technology;
+}
+
+static void technology_put(struct connman_technology *technology)
+{
+       DBG("technology %p", technology);
+
+       if (__sync_sub_and_fetch(&technology->refcount, 1) > 0)
+               return;
+
+       reply_scan_pending(technology, -EINTR);
+
+       if (technology->driver) {
+               technology->driver->remove(technology);
+               technology->driver = NULL;
+       }
+
+       technology_list = g_slist_remove(technology_list, technology);
+
+       technology_removed_signal(technology);
+
+       g_dbus_unregister_interface(connection, technology->path,
+                                               CONNMAN_TECHNOLOGY_INTERFACE);
+
+       g_slist_free(technology->device_list);
+
+       g_free(technology->path);
+       g_free(technology->regdom);
+       g_free(technology);
+}
+
+void __connman_technology_add_interface(enum connman_service_type type,
+                               int index, const char *name, const char *ident)
+{
+       struct connman_technology *technology;
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+               return;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       }
+
+       connman_info("Adding interface %s [ %s ]", name,
+                               __connman_service_type2string(type));
+
+       technology = technology_find(type);
+
+       if (technology == NULL || technology->driver == NULL
+                       || technology->driver->add_interface == NULL)
+               return;
+
+       technology->driver->add_interface(technology,
+                                       index, name, ident);
+}
+
+void __connman_technology_remove_interface(enum connman_service_type type,
+                               int index, const char *name, const char *ident)
+{
+       struct connman_technology *technology;
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+               return;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       }
+
+       connman_info("Remove interface %s [ %s ]", name,
+                               __connman_service_type2string(type));
+
+       technology = technology_find(type);
+
+       if (technology == NULL || technology->driver == NULL)
+               return;
+
+       if (technology->driver->remove_interface)
+               technology->driver->remove_interface(technology, index);
+}
+
+int __connman_technology_add_device(struct connman_device *device)
+{
+       struct connman_technology *technology;
+       enum connman_service_type type;
+
+       DBG("device %p", device);
+
+       type = __connman_device_get_service_type(device);
+
+       technology = technology_get(type);
+       if (technology == NULL) {
+               /*
+                * Since no driver can be found for this device at the moment we
+                * add it to the techless device list.
+               */
+               techless_device_list = g_slist_prepend(techless_device_list,
+                                                               device);
+
+               return -ENXIO;
+       }
+
+       if (technology->enable_persistent && !global_offlinemode) {
+               int err = __connman_device_enable(device);
+               /*
+                * connman_technology_add_device() calls __connman_device_enable()
+                * but since the device is already enabled, the calls does not
+                * propagate through to connman_technology_enabled via
+                * connman_device_set_powered.
+                */
+               if (err == -EALREADY)
+                       __connman_technology_enabled(type);
+       }
+       /* if technology persistent state is offline */
+       if (!technology->enable_persistent)
+               __connman_device_disable(device);
+
+       technology->device_list = g_slist_append(technology->device_list,
+                                                               device);
+
+       return 0;
+}
+
+int __connman_technology_remove_device(struct connman_device *device)
+{
+       struct connman_technology *technology;
+       enum connman_service_type type;
+
+       DBG("device %p", device);
+
+       type = __connman_device_get_service_type(device);
+
+       technology = technology_find(type);
+       if (technology == NULL) {
+               techless_device_list = g_slist_remove(techless_device_list,
+                                                               device);
+               return -ENXIO;
+       }
+
+       technology->device_list = g_slist_remove(technology->device_list,
+                                                               device);
+       technology_put(technology);
+
+       return 0;
+}
+
+static void powered_changed(struct connman_technology *technology)
+{
+       connman_bool_t powered;
+
+       __sync_synchronize();
+       if (technology->enabled >0)
+               powered = TRUE;
+       else
+               powered = FALSE;
+
+       connman_dbus_property_changed_basic(technology->path,
+                       CONNMAN_TECHNOLOGY_INTERFACE, "Powered",
+                       DBUS_TYPE_BOOLEAN, &powered);
+}
+
+int __connman_technology_enabled(enum connman_service_type type)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return -ENXIO;
+
+       if (__sync_fetch_and_add(&technology->enabled, 1) != 0)
+               return -EALREADY;
+
+       powered_changed(technology);
+
+       if (technology->pending_reply != NULL) {
+               g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
+               dbus_message_unref(technology->pending_reply);
+               g_source_remove(technology->pending_timeout);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
+       return 0;
+}
+
+int __connman_technology_disabled(enum connman_service_type type)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return -ENXIO;
+
+       if (__sync_fetch_and_sub(&technology->enabled, 1) != 1)
+               return -EINPROGRESS;
+
+       if (technology->pending_reply != NULL) {
+               g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
+               dbus_message_unref(technology->pending_reply);
+               g_source_remove(technology->pending_timeout);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
+       powered_changed(technology);
+
+       return 0;
+}
+
+int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
+{
+       GSList *list;
+       int err = -EINVAL;
+
+       if (global_offlinemode == offlinemode)
+               return 0;
+
+       DBG("offlinemode %s", offlinemode ? "On" : "Off");
+
+       /*
+        * This is a bit tricky. When you set offlinemode, there is no
+        * way to differentiate between attempting offline mode and
+        * resuming offlinemode from last saved profile. We need that
+        * information in rfkill_update, otherwise it falls back on the
+        * technology's persistent state. Hence we set the offline mode here
+        * but save it & call the notifier only if its successful.
+        */
+
+       global_offlinemode = offlinemode;
+
+       /* Traverse technology list, enable/disable each technology. */
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (offlinemode)
+                       err = technology_disable(technology, NULL);
+
+               if (!offlinemode && technology->enable_persistent)
+                       err = technology_enable(technology, NULL);
+       }
+
+       if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
+               connman_technology_save_offlinemode();
+               __connman_notifier_offlinemode(offlinemode);
+       } else
+               global_offlinemode = connman_technology_load_offlinemode();
+
+       return err;
+}
+
+void __connman_technology_set_connected(enum connman_service_type type,
+               connman_bool_t connected)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return;
+
+       DBG("technology %p connected %d", technology, connected);
+
+       technology->connected = connected;
+
+       connman_dbus_property_changed_basic(technology->path,
+                       CONNMAN_TECHNOLOGY_INTERFACE, "Connected",
+                       DBUS_TYPE_BOOLEAN, &connected);
+}
+
+int __connman_technology_add_rfkill(unsigned int index,
+                                       enum connman_service_type type,
+                                               connman_bool_t softblock,
+                                               connman_bool_t hardblock)
+{
+       struct connman_technology *technology;
+       struct connman_rfkill *rfkill;
+
+       DBG("index %u type %d soft %u hard %u", index, type,
+                                                       softblock, hardblock);
+
+       rfkill = g_hash_table_lookup(rfkill_list, &index);
+       if (rfkill != NULL)
+               goto done;
+
+       rfkill = g_try_new0(struct connman_rfkill, 1);
+       if (rfkill == NULL)
+               return -ENOMEM;
+
+       rfkill->index = index;
+       rfkill->type = type;
+       rfkill->softblock = softblock;
+       rfkill->hardblock = hardblock;
+
+       g_hash_table_insert(rfkill_list, &rfkill->index, rfkill);
+
+done:
+       technology = technology_get(type);
+       /* If there is no driver for this type, ignore it. */
+       if (technology == NULL)
+               return -ENXIO;
+
+       if (hardblock) {
+               DBG("%s is switched off.", get_name(type));
+               return 0;
+       }
+
+       /*
+        * If Offline mode is on, we softblock the device if it isnt already.
+        * If Offline mode is off, we rely on the persistent state of tech.
+        */
+       if (global_offlinemode) {
+               if (!softblock)
+                       return __connman_rfkill_block(type, TRUE);
+       } else {
+               if (technology->enable_persistent && softblock)
+                       return __connman_rfkill_block(type, FALSE);
+               /* if technology persistent state is offline */
+               if (!technology->enable_persistent && !softblock)
+                       return __connman_rfkill_block(type, TRUE);
+       }
+
+       return 0;
+}
+
+int __connman_technology_update_rfkill(unsigned int index,
+                                       enum connman_service_type type,
+                                               connman_bool_t softblock,
+                                               connman_bool_t hardblock)
+{
+       struct connman_technology *technology;
+       struct connman_rfkill *rfkill;
+
+       DBG("index %u soft %u hard %u", index, softblock, hardblock);
+
+       rfkill = g_hash_table_lookup(rfkill_list, &index);
+       if (rfkill == NULL)
+               return -ENXIO;
+
+       if (rfkill->softblock == softblock &&
+               rfkill->hardblock == hardblock)
+               return 0;
+
+       rfkill->softblock = softblock;
+       rfkill->hardblock = hardblock;
+
+       if (hardblock) {
+               DBG("%s is switched off.", get_name(type));
+               return 0;
+       }
+
+       technology = technology_find(type);
+       /* If there is no driver for this type, ignore it. */
+       if (technology == NULL)
+               return -ENXIO;
+
+       if (!global_offlinemode) {
+               if (technology->enable_persistent && softblock)
+                       return __connman_rfkill_block(type, FALSE);
+               if (!technology->enable_persistent && !softblock)
+                       return __connman_rfkill_block(type, TRUE);
+       }
+
+       return 0;
+}
+
+int __connman_technology_remove_rfkill(unsigned int index,
+                                       enum connman_service_type type)
+{
+       struct connman_technology *technology;
+       struct connman_rfkill *rfkill;
+
+       DBG("index %u", index);
+
+       rfkill = g_hash_table_lookup(rfkill_list, &index);
+       if (rfkill == NULL)
+               return -ENXIO;
+
+       g_hash_table_remove(rfkill_list, &index);
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return -ENXIO;
+
+       technology_put(technology);
+
+       return 0;
+}
+
+int __connman_technology_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       rfkill_list = g_hash_table_new_full(g_int_hash, g_int_equal,
+                                                       NULL, free_rfkill);
+
+       global_offlinemode = connman_technology_load_offlinemode();
+
+       /* This will create settings file if it is missing */
+       connman_technology_save_offlinemode();
+
+       return 0;
+}
+
+void __connman_technology_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(rfkill_list);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/tethering.c b/src/tethering.c
new file mode 100644 (file)
index 0000000..265408b
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011 ProFUSION embedded systems
+ *
+ *  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 <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <string.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
+
+#include "connman.h"
+
+#include <gdhcp/gdhcp.h>
+
+#include <gdbus.h>
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define BRIDGE_PROC_DIR "/proc/sys/net/bridge"
+
+#define BRIDGE_NAME "tether"
+#define BRIDGE_DNS "8.8.8.8"
+
+#define DEFAULT_MTU    1500
+
+#define PRIVATE_NETWORK_PRIMARY_DNS BRIDGE_DNS
+#define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
+
+static volatile int tethering_enabled;
+static GDHCPServer *tethering_dhcp_server = NULL;
+static struct connman_ippool *dhcp_ippool = NULL;
+static DBusConnection *connection;
+static GHashTable *pn_hash;
+
+struct connman_private_network {
+       char *owner;
+       char *path;
+       guint watch;
+       DBusMessage *msg;
+       DBusMessage *reply;
+       int fd;
+       char *interface;
+       int index;
+       guint iface_watch;
+       struct connman_ippool *pool;
+       const char *primary_dns;
+       const char *secondary_dns;
+};
+
+const char *__connman_tethering_get_bridge(void)
+{
+       struct stat st;
+
+       if (stat(BRIDGE_PROC_DIR, &st) < 0) {
+               connman_error("Missing support for 802.1d ethernet bridging");
+               return NULL;
+       }
+
+       return BRIDGE_NAME;
+}
+
+static void dhcp_server_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static void dhcp_server_error(GDHCPServerError error)
+{
+       switch (error) {
+       case G_DHCP_SERVER_ERROR_NONE:
+               connman_error("OK");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE:
+               connman_error("Interface unavailable");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_IN_USE:
+               connman_error("Interface in use");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_DOWN:
+               connman_error("Interface down");
+               break;
+       case G_DHCP_SERVER_ERROR_NOMEM:
+               connman_error("No memory");
+               break;
+       case G_DHCP_SERVER_ERROR_INVALID_INDEX:
+               connman_error("Invalid index");
+               break;
+       case G_DHCP_SERVER_ERROR_INVALID_OPTION:
+               connman_error("Invalid option");
+               break;
+       case G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID:
+               connman_error("Invalid address");
+               break;
+       }
+}
+
+static GDHCPServer *dhcp_server_start(const char *bridge,
+                               const char *router, const char* subnet,
+                               const char *start_ip, const char *end_ip,
+                               unsigned int lease_time, const char *dns)
+{
+       GDHCPServerError error;
+       GDHCPServer *dhcp_server;
+       int index;
+
+       DBG("");
+
+       index = connman_inet_ifindex(bridge);
+       if (index < 0)
+               return NULL;
+
+       dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error);
+       if (dhcp_server == NULL) {
+               dhcp_server_error(error);
+               return NULL;
+       }
+
+       g_dhcp_server_set_debug(dhcp_server, dhcp_server_debug, "DHCP server");
+
+       g_dhcp_server_set_lease_time(dhcp_server, lease_time);
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET, subnet);
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, router);
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns);
+       g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip);
+
+       g_dhcp_server_start(dhcp_server);
+
+       return dhcp_server;
+}
+
+static void dhcp_server_stop(GDHCPServer *server)
+{
+       if (server == NULL)
+               return;
+
+       g_dhcp_server_unref(server);
+}
+
+static void tethering_restart(struct connman_ippool *pool, void *user_data)
+{
+       __connman_tethering_set_disabled();
+       __connman_tethering_set_enabled();
+}
+
+void __connman_tethering_set_enabled(void)
+{
+       int index;
+       int err;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       const char *dns;
+       unsigned char prefixlen;
+
+       DBG("enabled %d", tethering_enabled + 1);
+
+       if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
+               return;
+
+       err = __connman_bridge_create(BRIDGE_NAME);
+       if (err < 0)
+               return;
+
+       index = connman_inet_ifindex(BRIDGE_NAME);
+       dhcp_ippool = __connman_ippool_create(index, 2, 252,
+                                               tethering_restart, NULL);
+       if (dhcp_ippool == NULL) {
+               connman_error("Fail to create IP pool");
+               return;
+       }
+
+       gateway = __connman_ippool_get_gateway(dhcp_ippool);
+       broadcast = __connman_ippool_get_broadcast(dhcp_ippool);
+       subnet_mask = __connman_ippool_get_subnet_mask(dhcp_ippool);
+       start_ip = __connman_ippool_get_start_ip(dhcp_ippool);
+       end_ip = __connman_ippool_get_end_ip(dhcp_ippool);
+
+       err = __connman_bridge_enable(BRIDGE_NAME, gateway, broadcast);
+       if (err < 0 && err != -EALREADY) {
+               __connman_bridge_remove(BRIDGE_NAME);
+               return;
+       }
+
+       dns = gateway;
+       if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
+               connman_error("Can't add listener %s to DNS proxy",
+                                                               BRIDGE_NAME);
+               dns = BRIDGE_DNS;
+       }
+
+       tethering_dhcp_server = dhcp_server_start(BRIDGE_NAME,
+                                               gateway, subnet_mask,
+                                               start_ip, end_ip,
+                                               24 * 3600, dns);
+       if (tethering_dhcp_server == NULL) {
+               __connman_bridge_disable(BRIDGE_NAME);
+               __connman_bridge_remove(BRIDGE_NAME);
+               return;
+       }
+
+       prefixlen =
+               __connman_ipconfig_netmask_prefix_len(subnet_mask);
+       __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen);
+
+       DBG("tethering started");
+}
+
+void __connman_tethering_set_disabled(void)
+{
+       DBG("enabled %d", tethering_enabled - 1);
+
+       __connman_dnsproxy_remove_listener(BRIDGE_NAME);
+
+       if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
+               return;
+
+       __connman_nat_disable(BRIDGE_NAME);
+
+       dhcp_server_stop(tethering_dhcp_server);
+
+       tethering_dhcp_server = NULL;
+
+       __connman_bridge_disable(BRIDGE_NAME);
+
+       __connman_ippool_unref(dhcp_ippool);
+
+       __connman_bridge_remove(BRIDGE_NAME);
+
+       DBG("tethering stopped");
+}
+
+static void setup_tun_interface(unsigned int flags, unsigned change,
+               void *data)
+{
+       struct connman_private_network *pn = data;
+       unsigned char prefixlen;
+       DBusMessageIter array, dict;
+       const char *server_ip;
+       const char *peer_ip;
+       const char *subnet_mask;
+       int err;
+
+       DBG("index %d flags %d change %d", pn->index,  flags, change);
+
+       if (flags & IFF_UP)
+               return;
+
+       subnet_mask = __connman_ippool_get_subnet_mask(pn->pool);
+       server_ip = __connman_ippool_get_start_ip(pn->pool);
+       peer_ip = __connman_ippool_get_end_ip(pn->pool);
+       prefixlen =
+               __connman_ipconfig_netmask_prefix_len(subnet_mask);
+
+       if ((__connman_inet_modify_address(RTM_NEWADDR,
+                               NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
+                               server_ip, peer_ip, prefixlen, NULL)) < 0) {
+               DBG("address setting failed");
+               return;
+       }
+
+       connman_inet_ifup(pn->index);
+
+       err = __connman_nat_enable(BRIDGE_NAME, server_ip, prefixlen);
+       if (err < 0) {
+               connman_error("failed to enable NAT");
+               goto error;
+       }
+
+       dbus_message_iter_init_append(pn->reply, &array);
+
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
+                                               &pn->path);
+
+       connman_dbus_dict_open(&array, &dict);
+
+       connman_dbus_dict_append_basic(&dict, "ServerIPv4",
+                                       DBUS_TYPE_STRING, &server_ip);
+       connman_dbus_dict_append_basic(&dict, "PeerIPv4",
+                                       DBUS_TYPE_STRING, &peer_ip);
+       connman_dbus_dict_append_basic(&dict, "PrimaryDNS",
+                                       DBUS_TYPE_STRING, &pn->primary_dns);
+       connman_dbus_dict_append_basic(&dict, "SecondaryDNS",
+                                       DBUS_TYPE_STRING, &pn->secondary_dns);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_UNIX_FD, &pn->fd);
+
+       g_dbus_send_message(connection, pn->reply);
+
+       return;
+
+error:
+       pn->reply = __connman_error_failed(pn->msg, -err);
+       g_dbus_send_message(connection, pn->reply);
+
+       g_hash_table_remove(pn_hash, pn->path);
+}
+
+static void remove_private_network(gpointer user_data)
+{
+       struct connman_private_network *pn = user_data;
+
+       __connman_nat_disable(BRIDGE_NAME);
+       connman_rtnl_remove_watch(pn->iface_watch);
+       __connman_ippool_unref(pn->pool);
+
+       if (pn->watch > 0) {
+               g_dbus_remove_watch(connection, pn->watch);
+               pn->watch = 0;
+       }
+
+       close(pn->fd);
+
+       g_free(pn->interface);
+       g_free(pn->owner);
+       g_free(pn->path);
+       g_free(pn);
+}
+
+static void owner_disconnect(DBusConnection *connection, void *user_data)
+{
+       struct connman_private_network *pn = user_data;
+
+       DBG("%s died", pn->owner);
+
+       pn->watch = 0;
+
+       g_hash_table_remove(pn_hash, pn->path);
+}
+
+static void ippool_disconnect(struct connman_ippool *pool, void *user_data)
+{
+       struct connman_private_network *pn = user_data;
+
+       DBG("block used externally");
+
+       g_hash_table_remove(pn_hash, pn->path);
+}
+
+int __connman_private_network_request(DBusMessage *msg, const char *owner)
+{
+       struct connman_private_network *pn;
+       char *iface = NULL;
+       char *path = NULL;
+       int index, fd, err;
+
+       if (DBUS_TYPE_UNIX_FD < 0)
+               return -EINVAL;
+
+       fd = connman_inet_create_tunnel(&iface);
+       if (fd < 0)
+               return fd;
+
+       path = g_strdup_printf("/tethering/%s", iface);
+
+       pn = g_hash_table_lookup(pn_hash, path);
+       if (pn) {
+               g_free(path);
+               g_free(iface);
+               close(fd);
+               return -EEXIST;
+       }
+
+       index = connman_inet_ifindex(iface);
+       if (index < 0) {
+               err = -ENODEV;
+               goto error;
+       }
+       DBG("interface %s", iface);
+
+       err = connman_inet_set_mtu(index, DEFAULT_MTU);
+
+       pn = g_try_new0(struct connman_private_network, 1);
+       if (pn == NULL) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       pn->owner = g_strdup(owner);
+       pn->path = path;
+       pn->watch = g_dbus_add_disconnect_watch(connection, pn->owner,
+                                       owner_disconnect, pn, NULL);
+       pn->msg = msg;
+       pn->reply = dbus_message_new_method_return(pn->msg);
+       if (pn->reply == NULL)
+               goto error;
+
+       pn->fd = fd;
+       pn->interface = iface;
+       pn->index = index;
+       pn->pool = __connman_ippool_create(pn->index, 1, 1, ippool_disconnect, pn);
+       if (pn->pool == NULL) {
+               errno = -ENOMEM;
+               goto error;
+       }
+
+       pn->primary_dns = PRIVATE_NETWORK_PRIMARY_DNS;
+       pn->secondary_dns = PRIVATE_NETWORK_SECONDARY_DNS;
+
+       pn->iface_watch = connman_rtnl_add_newlink_watch(index,
+                                               setup_tun_interface, pn);
+
+       g_hash_table_insert(pn_hash, pn->path, pn);
+
+       return 0;
+
+error:
+       close(fd);
+       g_free(iface);
+       g_free(path);
+       g_free(pn);
+       return err;
+}
+
+int __connman_private_network_release(const char *path)
+{
+       struct connman_private_network *pn;
+
+       pn = g_hash_table_lookup(pn_hash, path);
+       if (pn == NULL)
+               return -EACCES;
+
+       g_hash_table_remove(pn_hash, path);
+       return 0;
+}
+
+int __connman_tethering_init(void)
+{
+       DBG("");
+
+       tethering_enabled = 0;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EFAULT;
+
+       pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, remove_private_network);
+
+       return 0;
+}
+
+void __connman_tethering_cleanup(void)
+{
+       DBG("");
+
+       __sync_synchronize();
+       if (tethering_enabled == 0) {
+               if (tethering_dhcp_server)
+                       dhcp_server_stop(tethering_dhcp_server);
+               __connman_bridge_disable(BRIDGE_NAME);
+               __connman_bridge_remove(BRIDGE_NAME);
+               __connman_nat_disable(BRIDGE_NAME);
+       }
+
+       if (connection == NULL)
+               return;
+
+       g_hash_table_destroy(pn_hash);
+       dbus_connection_unref(connection);
+}
diff --git a/src/timeserver.c b/src/timeserver.c
new file mode 100644 (file)
index 0000000..467da52
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ *
+ *  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 <errno.h>
+
+#include <glib.h>
+#include <stdlib.h>
+#include <gweb/gresolv.h>
+#include <netdb.h>
+
+#include "connman.h"
+
+static GSList *ts_list = NULL;
+
+static GResolv *resolv = NULL;
+static int resolv_id = 0;
+
+static void resolv_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+static void save_timeservers(char **servers)
+{
+       GKeyFile *keyfile;
+       int cnt;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       for (cnt = 0; servers != NULL && servers[cnt] != NULL; cnt++);
+
+       g_key_file_set_string_list(keyfile, "global", "Timeservers",
+                          (const gchar **)servers, cnt);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+static char **load_timeservers()
+{
+       GKeyFile *keyfile;
+       char **servers = NULL;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               return NULL;
+
+       servers = g_key_file_get_string_list(keyfile, "global",
+                                               "Timeservers", NULL, NULL);
+
+       g_key_file_free(keyfile);
+
+       return servers;
+}
+
+static void resolv_result(GResolvResultStatus status, char **results, gpointer user_data)
+{
+       int i;
+
+       DBG("status %d", status);
+
+       if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
+               if (results != NULL) {
+                       for (i = 0; results[i]; i++)
+                               DBG("result: %s", results[i]);
+
+                       __connman_ntp_start(results[0]);
+
+                       return;
+               }
+       }
+
+       /* If resolving fails, move to the next server */
+       __connman_timeserver_sync_next();
+}
+
+/*
+ * Once the timeserver list (ts_list) is created, we start querying the
+ * servers one by one. If resolving fails on one of them, we move to the
+ * next one. The user can enter either an IP address or a URL for the
+ * timeserver. We only resolve the urls. Once we have a IP for the NTP
+ * server, we start querying it for time corrections.
+ */
+void __connman_timeserver_sync_next()
+{
+       char *server;
+
+       __connman_ntp_stop();
+
+       /* Get the 1st server in the list */
+       if (ts_list == NULL)
+               return;
+
+       server = ts_list->data;
+
+       ts_list = g_slist_delete_link(ts_list, ts_list);
+
+       /* if its a IP , directly query it. */
+       if (connman_inet_check_ipaddress(server) > 0) {
+               DBG("Using timeservers %s", server);
+
+               __connman_ntp_start(server);
+
+               g_free(server);
+               return;
+       }
+
+       DBG("Resolving server %s", server);
+
+       resolv_id = g_resolv_lookup_hostname(resolv, server,
+                                               resolv_result, NULL);
+
+       g_free(server);
+
+       return;
+}
+
+GSList *__connman_timeserver_add_list(GSList *server_list,
+               const char *timeserver)
+{
+       GSList *list = server_list;
+
+       if (timeserver == NULL)
+               return server_list;
+
+       while (list != NULL) {
+               char *existing_server = list->data;
+               if (strcmp(timeserver, existing_server) == 0)
+                       return server_list;
+               list = g_slist_next(list);
+       }
+       return g_slist_prepend(server_list, g_strdup(timeserver));
+}
+
+/*
+ * __connman_timeserver_get_all function creates the timeserver
+ * list which will be used to determine NTP server for time corrections.
+ * The service settings take priority over the global timeservers.
+ */
+GSList *__connman_timeserver_get_all(struct connman_service *service)
+{
+       GSList *list = NULL;
+       struct connman_network *network;
+       char **timeservers;
+       char **service_ts;
+       char **service_ts_config;
+       const char *service_gw;
+       char **fallback_ts;
+       int index, i;
+
+       service_ts_config = connman_service_get_timeservers_config(service);
+
+       /* First add Service Timeservers.Configuration to the list */
+       for (i = 0; service_ts_config != NULL && service_ts_config[i] != NULL;
+                       i++)
+               list = __connman_timeserver_add_list(list,
+                               service_ts_config[i]);
+
+       service_ts = connman_service_get_timeservers(service);
+
+       /* First add Service Timeservers via DHCP to the list */
+       for (i = 0; service_ts != NULL && service_ts[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, service_ts[i]);
+
+       network = __connman_service_get_network(service);
+       if (network != NULL) {
+               index = connman_network_get_index(network);
+               service_gw = __connman_ipconfig_get_gateway_from_index(index,
+                       CONNMAN_IPCONFIG_TYPE_ALL);
+
+               /* Then add Service Gateway to the list */
+               if (service_gw != NULL)
+                       list = __connman_timeserver_add_list(list, service_gw);
+       }
+
+       /* Then add Global Timeservers to the list */
+       timeservers = load_timeservers();
+
+       for (i = 0; timeservers != NULL && timeservers[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, timeservers[i]);
+
+       g_strfreev(timeservers);
+
+       fallback_ts = connman_setting_get_string_list("FallbackTimeservers");
+
+       /* Lastly add the fallback servers */
+       for (i = 0; fallback_ts != NULL && fallback_ts[i] != NULL; i++)
+               list = __connman_timeserver_add_list(list, fallback_ts[i]);
+
+       return g_slist_reverse(list);
+}
+
+/*
+ * This function must be called everytime the default service changes, the
+ * service timeserver(s) or gatway changes or the global timeserver(s) changes.
+ */
+int __connman_timeserver_sync(struct connman_service *default_service)
+{
+       struct connman_service *service;
+
+       if (default_service != NULL)
+               service = default_service;
+       else
+               service = __connman_service_get_default();
+
+       if (service == NULL)
+               return -EINVAL;
+
+       if (resolv == NULL)
+               return 0;
+       /*
+        * Before we start creating the new timeserver list we must stop
+        * any ongoing ntp query and server resolution.
+        */
+
+       __connman_ntp_stop();
+
+       if (resolv_id > 0)
+               g_resolv_cancel_lookup(resolv, resolv_id);
+
+       g_slist_free_full(ts_list, g_free);
+
+       ts_list = __connman_timeserver_get_all(service);
+
+       __connman_service_timeserver_changed(service, ts_list);
+
+       if (ts_list == NULL) {
+               DBG("No timeservers set.");
+               return 0;
+       }
+
+        __connman_timeserver_sync_next();
+
+       return 0;
+}
+
+static int timeserver_start(struct connman_service *service)
+{
+       char **nameservers;
+       int i;
+
+       DBG("service %p", service);
+
+       i = __connman_service_get_index(service);
+       if (i < 0)
+               return -EINVAL;
+
+       nameservers = connman_service_get_nameservers(service);
+       if (nameservers == NULL)
+               return -EINVAL;
+
+       /* Stop an already ongoing resolution, if there is one */
+       if (resolv != NULL && resolv_id > 0)
+               g_resolv_cancel_lookup(resolv, resolv_id);
+
+       /* get rid of the old resolver */
+       if (resolv != NULL) {
+               g_resolv_unref(resolv);
+               resolv = NULL;
+       }
+
+       resolv = g_resolv_new(i);
+       if (resolv == NULL) {
+               g_strfreev(nameservers);
+               return -ENOMEM;
+       }
+
+       if (getenv("CONNMAN_RESOLV_DEBUG"))
+               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+       for (i = 0; nameservers[i] != NULL; i++)
+               g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+
+       g_strfreev(nameservers);
+
+       return __connman_timeserver_sync(service);
+}
+
+static void timeserver_stop()
+{
+       DBG(" ");
+
+       if (resolv != NULL) {
+               g_resolv_unref(resolv);
+               resolv = NULL;
+       }
+
+       g_slist_free_full(ts_list, g_free);
+
+       ts_list = NULL;
+
+       __connman_ntp_stop();
+}
+
+int __connman_timeserver_system_set(char **servers)
+{
+       save_timeservers(servers);
+
+       __connman_timeserver_sync(NULL);
+
+       return 0;
+}
+
+char **__connman_timeserver_system_get()
+{
+       char **servers;
+
+       servers = load_timeservers();
+       return servers;
+}
+
+static void default_changed(struct connman_service *default_service)
+{
+       if (default_service != NULL)
+               timeserver_start(default_service);
+       else
+               timeserver_stop();
+}
+
+static struct connman_notifier timeserver_notifier = {
+       .name                   = "timeserver",
+       .default_changed        = default_changed,
+};
+
+int __connman_timeserver_init(void)
+{
+       DBG("");
+
+       connman_notifier_register(&timeserver_notifier);
+
+       return 0;
+}
+
+void __connman_timeserver_cleanup(void)
+{
+       DBG("");
+
+       connman_notifier_unregister(&timeserver_notifier);
+}
diff --git a/src/timezone.c b/src/timezone.c
new file mode 100644 (file)
index 0000000..173d658
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/inotify.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#define ETC_LOCALTIME          "/etc/localtime"
+#define ETC_SYSCONFIG_CLOCK    "/etc/sysconfig/clock"
+#define USR_SHARE_ZONEINFO     "/usr/share/zoneinfo"
+
+static char *read_key_file(const char *pathname, const char *key)
+{
+       struct stat st;
+       char *map, *ptr, *str;
+       off_t ptrlen, keylen;
+       int fd;
+
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return NULL;
+
+       if (fstat(fd, &st) < 0) {
+               close(fd);
+               return NULL;
+       }
+
+       map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (map == NULL || map == MAP_FAILED) {
+               close(fd);
+               return NULL;
+       }
+
+       ptr = map;
+       ptrlen = st.st_size;
+       keylen = strlen(key);
+
+       while (ptrlen > keylen + 1) {
+               int cmp = strncmp(ptr, key, keylen);
+
+               if (cmp == 0) {
+                       if (ptr == map)
+                               break;
+
+                       if (*(ptr - 1) == '\n' && *(ptr + keylen) == '=')
+                               break;
+               }
+
+               ptr = memchr(ptr + 1, key[0], ptrlen - 1);
+               if (ptr == NULL)
+                       break;
+
+               ptrlen = st.st_size - (ptr - map);
+       }
+
+       if (ptr != NULL) {
+               char *end, *val;
+
+               ptrlen = st.st_size - (ptr - map);
+
+               end = memchr(ptr, '\n', ptrlen);
+               if (end != NULL)
+                       ptrlen = end - ptr;
+
+               val = memchr(ptr, '"', ptrlen);
+               if (val != NULL) {
+                       end = memchr(val + 1, '"', end - val - 1);
+                       if (end != NULL)
+                               str = g_strndup(val + 1, end - val - 1);
+                       else
+                               str = NULL;
+               } else
+                       str = g_strndup(ptr + keylen + 1, ptrlen - keylen - 1);
+       } else
+               str = NULL;
+
+       munmap(map, st.st_size);
+
+       close(fd);
+
+       return str;
+}
+
+static int compare_file(void *src_map, struct stat *src_st,
+                                               const char *pathname)
+{
+       struct stat dst_st;
+       void *dst_map;
+       int fd, result;
+
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -1;
+
+       if (fstat(fd, &dst_st) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       if (src_st->st_size != dst_st.st_size) {
+               close(fd);
+               return -1;
+       }
+
+       dst_map = mmap(0, dst_st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (dst_map == NULL || dst_map == MAP_FAILED) {
+               close(fd);
+               return -1;
+        }
+
+       result = memcmp(src_map, dst_map, src_st->st_size);
+
+       munmap(dst_map, dst_st.st_size);
+
+       close(fd);
+
+       return result;
+}
+
+static char *find_origin(void *src_map, struct stat *src_st,
+                               const char *basepath, const char *subpath)
+{
+       DIR *dir;
+       struct dirent *d;
+       char *str, pathname[PATH_MAX];
+
+       if (subpath == NULL)
+               strncpy(pathname, basepath, sizeof(pathname));
+       else
+               snprintf(pathname, sizeof(pathname),
+                                       "%s/%s", basepath, subpath);
+
+       dir = opendir(pathname);
+       if (dir == NULL)
+               return NULL;
+
+       while ((d = readdir(dir))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                               strcmp(d->d_name, "..") == 0 ||
+                               strcmp(d->d_name, "posix") == 0 ||
+                               strcmp(d->d_name, "right") == 0)
+                       continue;
+
+               switch (d->d_type) {
+               case DT_REG:
+                       if (subpath == NULL)
+                               snprintf(pathname, PATH_MAX,
+                                               "%s/%s", basepath, d->d_name);
+                       else
+                               snprintf(pathname, PATH_MAX,
+                                               "%s/%s/%s", basepath,
+                                                       subpath, d->d_name);
+
+                       if (compare_file(src_map, src_st, pathname) == 0) {
+                               str = g_strdup_printf("%s/%s",
+                                                       subpath, d->d_name);
+                               closedir(dir);
+                               return str;
+                       }
+                       break;
+               case DT_DIR:
+                       if (subpath == NULL)
+                               strncpy(pathname, d->d_name, sizeof(pathname));
+                       else
+                               snprintf(pathname, sizeof(pathname),
+                                               "%s/%s", subpath, d->d_name);
+
+                       str = find_origin(src_map, src_st, basepath, pathname);
+                       if (str != NULL) {
+                               closedir(dir);
+                               return str;
+                       }
+                       break;
+               }
+       }
+
+       closedir(dir);
+
+       return NULL;
+}
+
+char *__connman_timezone_lookup(void)
+{
+       struct stat st;
+       void *map;
+       int fd;
+       char *zone;
+
+       zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE");
+
+       DBG("sysconfig zone %s", zone);
+
+       fd = open(ETC_LOCALTIME, O_RDONLY | O_CLOEXEC);
+       if (fd < 0) {
+               g_free(zone);
+               return NULL;
+       }
+
+       if (fstat(fd, &st) < 0)
+               goto done;
+
+       if (S_ISREG(st.st_mode)) {
+               map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+               if (map == NULL || map == MAP_FAILED) {
+                       g_free(zone);
+                       zone = NULL;
+
+                       goto done;
+               }
+
+               if (zone != NULL) {
+                       char pathname[PATH_MAX];
+
+                       snprintf(pathname, PATH_MAX, "%s/%s",
+                                               USR_SHARE_ZONEINFO, zone);
+
+                       if (compare_file(map, &st, pathname) != 0) {
+                               g_free(zone);
+                               zone = NULL;
+                       }
+               }
+
+               if (zone == NULL)
+                       zone = find_origin(map, &st, USR_SHARE_ZONEINFO, NULL);
+
+               munmap(map, st.st_size);
+       } else {
+               g_free(zone);
+               zone = NULL;
+       }
+
+done:
+       close(fd);
+
+       DBG("localtime zone %s", zone);
+
+       return zone;
+}
+
+static int write_file(void *src_map, struct stat *src_st, const char *pathname)
+{
+       struct stat st;
+       int fd;
+       ssize_t written;
+
+       DBG("pathname %s", pathname);
+
+       if (lstat(pathname, &st) == 0) {
+               if (S_ISLNK(st.st_mode))
+                       unlink(pathname);
+       }
+
+       fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
+       if (fd < 0)
+               return -EIO;
+
+       written = write(fd, src_map, src_st->st_size);
+
+       close(fd);
+
+       if (written < 0)
+               return -EIO;
+
+       return 0;
+}
+
+int __connman_timezone_change(const char *zone)
+{
+       struct stat st;
+       char *map, pathname[PATH_MAX];
+       int fd, err;
+
+       DBG("zone %s", zone);
+
+       snprintf(pathname, PATH_MAX, "%s/%s", USR_SHARE_ZONEINFO, zone);
+
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -EINVAL;
+
+       if (fstat(fd, &st) < 0) {
+               close(fd);
+               return -EIO;
+       }
+
+       map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (map == NULL || map == MAP_FAILED) {
+               close(fd);
+               return -EIO;
+       }
+
+       err = write_file(map, &st, ETC_LOCALTIME);
+
+       munmap(map, st.st_size);
+
+       close(fd);
+
+       return err;
+}
+
+static guint inotify_watch = 0;
+
+static gboolean inotify_data(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       char buffer[256];
+       void *ptr = buffer;
+       GIOStatus status;
+       gsize bytes_read;
+
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               inotify_watch = 0;
+               return FALSE;
+       }
+
+       status = g_io_channel_read_chars(channel, buffer, sizeof(buffer),
+                                                       &bytes_read, NULL);
+
+       switch (status) {
+       case G_IO_STATUS_NORMAL:
+               break;
+       case G_IO_STATUS_AGAIN:
+               return TRUE;
+       default:
+               inotify_watch = 0;
+               return FALSE;
+       }
+
+       DBG("bytes read %zd", bytes_read);
+
+       while (bytes_read > 0) {
+               struct inotify_event *event = ptr;
+
+               if (bytes_read < sizeof(*event))
+                       break;
+
+               ptr += sizeof(*event);
+               bytes_read -= sizeof(*event);
+
+               if (event->len == 0)
+                       continue;
+
+               if (bytes_read < event->len)
+                       break;
+
+               ptr += event->len;
+               bytes_read -= event->len;
+
+               if (g_strcmp0(event->name, "localtime") == 0)
+                       __connman_clock_update_timezone();
+       }
+
+       return TRUE;
+}
+
+int __connman_timezone_init(void)
+{
+       GIOChannel *channel;
+       char *dirname;
+       int fd, wd;
+
+       DBG("");
+
+       fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+       if (fd < 0)
+               return -EIO;
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               close(fd);
+               return -EIO;
+       }
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       inotify_watch = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+                               inotify_data, NULL);
+
+       g_io_channel_unref(channel);
+
+       dirname = g_path_get_dirname(ETC_LOCALTIME);
+
+       wd = inotify_add_watch(fd, dirname, IN_DONT_FOLLOW |
+                                               IN_CLOSE_WRITE | IN_MOVED_TO);
+
+       g_free(dirname);
+
+       if (wd < 0)
+               return -EIO;
+
+       return 0;
+}
+
+void __connman_timezone_cleanup(void)
+{
+       DBG("");
+
+       if (inotify_watch > 0) {
+               g_source_remove(inotify_watch);
+               inotify_watch = 0;
+       }
+}
diff --git a/src/utsname.c b/src/utsname.c
new file mode 100644 (file)
index 0000000..1451c74
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "connman.h"
+
+static GSList *driver_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_utsname_driver *driver1 = a;
+       const struct connman_utsname_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_utsname_driver_register:
+ * @driver: utsname driver definition
+ *
+ * Register a new utsname driver
+ *
+ * Returns: %0 on success
+ */
+int connman_utsname_driver_register(struct connman_utsname_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_utsname_driver_unregister:
+ * @driver: utsname driver definition
+ *
+ * Remove a previously registered utsname driver
+ */
+void connman_utsname_driver_unregister(struct connman_utsname_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+/**
+ * connman_utsname_get_hostname:
+ *
+ * Returns current hostname
+ */
+const char *connman_utsname_get_hostname(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_utsname_driver *driver = list->data;
+               const char *hostname;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->get_hostname == NULL)
+                       continue;
+
+               hostname = driver->get_hostname();
+               if (hostname != NULL)
+                       return hostname;
+       }
+
+       return NULL;
+}
+
+int __connman_utsname_set_hostname(const char *hostname)
+{
+       GSList *list;
+
+       DBG("hostname %s", hostname);
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_utsname_driver *driver = list->data;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->set_hostname == NULL)
+                       continue;
+
+               if (driver->set_hostname(hostname) == 0)
+                       break;
+       }
+
+       return 0;
+}
+
+int __connman_utsname_set_domainname(const char *domainname)
+{
+       GSList *list;
+
+       DBG("domainname %s", domainname);
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_utsname_driver *driver = list->data;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->set_domainname == NULL)
+                       continue;
+
+               if (driver->set_domainname(domainname) == 0)
+                       break;
+       }
+
+       return 0;
+}
diff --git a/src/wispr.c b/src/wispr.c
new file mode 100644 (file)
index 0000000..9571fd8
--- /dev/null
@@ -0,0 +1,1055 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+
+#include <gweb/gweb.h>
+
+#include "connman.h"
+
+#define STATUS_URL_IPV4  "http://ipv4.connman.net/online/status.html"
+#define STATUS_URL_IPV6  "http://ipv6.connman.net/online/status.html"
+
+#define wispr_portal_context_ref(_wp_ctxt) \
+       wispr_portal_context_ref_debug(_wp_ctxt, __FILE__, __LINE__, __func__)
+
+#define wispr_portal_context_unref(_wp_ctxt) \
+       wispr_portal_context_unref_debug(_wp_ctxt, __FILE__, __LINE__, __func__)
+
+struct connman_wispr_message {
+       gboolean has_error;
+       const char *current_element;
+       int message_type;
+       int response_code;
+       char *login_url;
+       char *abort_login_url;
+       char *logoff_url;
+       char *access_procedure;
+       char *access_location;
+       char *location_name;
+};
+
+enum connman_wispr_result {
+       CONNMAN_WISPR_RESULT_UNKNOWN = 0,
+       CONNMAN_WISPR_RESULT_LOGIN   = 1,
+       CONNMAN_WISPR_RESULT_ONLINE  = 2,
+       CONNMAN_WISPR_RESULT_FAILED  = 3,
+};
+
+struct wispr_route {
+       char *address;
+       int if_index;
+};
+
+struct connman_wispr_portal_context {
+       int refcount;
+
+       struct connman_service *service;
+       enum connman_ipconfig_type type;
+
+       /* Portal/WISPr common */
+       GWeb *web;
+       unsigned int token;
+       guint request_id;
+
+       const char *status_url;
+
+       char *redirect_url;
+
+       /* WISPr specific */
+       GWebParser *wispr_parser;
+       struct connman_wispr_message wispr_msg;
+
+       char *wispr_username;
+       char *wispr_password;
+       char *wispr_formdata;
+
+       enum connman_wispr_result wispr_result;
+
+       GSList *route_list;
+};
+
+struct connman_wispr_portal {
+       struct connman_wispr_portal_context *ipv4_context;
+       struct connman_wispr_portal_context *ipv6_context;
+};
+
+static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data);
+
+static GHashTable *wispr_portal_list = NULL;
+
+static void connman_wispr_message_init(struct connman_wispr_message *msg)
+{
+       DBG("");
+
+       msg->has_error = FALSE;
+       msg->current_element = NULL;
+
+       msg->message_type = -1;
+       msg->response_code = -1;
+
+       g_free(msg->login_url);
+       msg->login_url = NULL;
+
+       g_free(msg->abort_login_url);
+       msg->abort_login_url = NULL;
+
+       g_free(msg->logoff_url);
+       msg->logoff_url = NULL;
+
+       g_free(msg->access_procedure);
+       msg->access_procedure = NULL;
+
+       g_free(msg->access_location);
+       msg->access_location = NULL;
+
+       g_free(msg->location_name);
+       msg->location_name = NULL;
+}
+
+static void free_wispr_routes(struct connman_wispr_portal_context *wp_context)
+{
+       while (wp_context->route_list != NULL) {
+               struct wispr_route *route = wp_context->route_list->data;
+
+               DBG("free route to %s if %d type %d", route->address,
+                               route->if_index, wp_context->type);
+
+               switch(wp_context->type) {
+               case CONNMAN_IPCONFIG_TYPE_IPV4:
+                       connman_inet_del_host_route(route->if_index,
+                                       route->address);
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_IPV6:
+                       connman_inet_del_ipv6_host_route(route->if_index,
+                                       route->address);
+                       break;
+               case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+                       break;
+               }
+
+               g_free(route->address);
+               g_free(route);
+
+               wp_context->route_list =
+                       g_slist_delete_link(wp_context->route_list,
+                                       wp_context->route_list);
+       }
+}
+
+static void free_connman_wispr_portal_context(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("context %p", wp_context);
+
+       if (wp_context == NULL)
+               return;
+
+       if (wp_context->service != NULL)
+               connman_service_unref(wp_context->service);
+
+       if (wp_context->token > 0)
+               connman_proxy_lookup_cancel(wp_context->token);
+
+       if (wp_context->request_id > 0)
+               g_web_cancel_request(wp_context->web, wp_context->request_id);
+
+       if (wp_context->web != NULL)
+               g_web_unref(wp_context->web);
+
+       g_free(wp_context->redirect_url);
+
+       if (wp_context->wispr_parser != NULL)
+               g_web_parser_unref(wp_context->wispr_parser);
+
+       connman_wispr_message_init(&wp_context->wispr_msg);
+
+       g_free(wp_context->wispr_username);
+       g_free(wp_context->wispr_password);
+       g_free(wp_context->wispr_formdata);
+
+       free_wispr_routes(wp_context);
+
+       g_free(wp_context);
+}
+
+static struct connman_wispr_portal_context *
+wispr_portal_context_ref_debug(struct connman_wispr_portal_context *wp_context,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", wp_context, wp_context->refcount + 1,
+                                                       file, line, caller);
+
+       __sync_fetch_and_add(&wp_context->refcount, 1);
+
+       return wp_context;
+}
+
+static struct connman_wispr_portal_context *
+wispr_portal_context_unref_debug(struct connman_wispr_portal_context *wp_context,
+                               const char *file, int line, const char *caller)
+{
+       DBG("%p ref %d by %s:%d:%s()", wp_context, wp_context->refcount - 1,
+                                                       file, line, caller);
+
+       if (__sync_fetch_and_sub(&wp_context->refcount, 1) != 1)
+               return wp_context;
+
+       free_connman_wispr_portal_context(wp_context);
+
+       return NULL;
+}
+
+static struct connman_wispr_portal_context *create_wispr_portal_context(void)
+{
+       struct connman_wispr_portal_context *wp_context = NULL;
+
+        wp_context = g_try_new0(struct connman_wispr_portal_context, 1);
+        if (wp_context == NULL)
+               return NULL;
+
+        wp_context->refcount = 1;
+
+        return wp_context;
+}
+
+/**
+ * This function is usued only by free_connman_wispr_portal, context pointer
+ * might still be owned by a third party (gweb, agent dbus call...)
+ * so we don't want service to be referenced in any context when it is not
+ * valid anymore.
+ */
+static void
+reset_service_usage(struct connman_wispr_portal_context *wp_context)
+{
+       if (wp_context->service == NULL)
+               return;
+
+       connman_service_unref(wp_context->service);
+       wp_context->service = NULL;
+}
+
+static void free_connman_wispr_portal(gpointer data)
+{
+       struct connman_wispr_portal *wispr_portal = data;
+
+       DBG("");
+
+       if (wispr_portal == NULL)
+               return;
+
+       if (wispr_portal->ipv4_context != NULL) {
+               reset_service_usage(wispr_portal->ipv4_context);
+               wispr_portal_context_unref(wispr_portal->ipv4_context);
+       }
+
+       if (wispr_portal->ipv6_context != NULL) {
+               reset_service_usage(wispr_portal->ipv6_context);
+               wispr_portal_context_unref(wispr_portal->ipv6_context);
+       }
+
+       g_free(wispr_portal);
+}
+
+static const char *message_type_to_string(int message_type)
+{
+       switch (message_type) {
+       case 100:
+               return "Initial redirect message";
+       case 110:
+               return "Proxy notification";
+       case 120:
+               return "Authentication notification";
+       case 130:
+               return "Logoff notification";
+       case 140:
+               return "Response to Authentication Poll";
+       case 150:
+               return "Response to Abort Login";
+       }
+
+       return NULL;
+}
+
+static const char *response_code_to_string(int response_code)
+{
+       switch (response_code) {
+       case 0:
+               return "No error";
+       case 50:
+               return "Login succeeded";
+       case 100:
+               return "Login failed";
+       case 102:
+               return "RADIUS server error/timeout";
+       case 105:
+               return "RADIUS server not enabled";
+       case 150:
+               return "Logoff succeeded";
+       case 151:
+               return "Login aborted";
+       case 200:
+               return "Proxy detection/repeat operation";
+       case 201:
+               return "Authentication pending";
+       case 255:
+               return "Access gateway internal error";
+       }
+
+       return NULL;
+}
+
+static struct {
+       const char *str;
+       enum {
+               WISPR_ELEMENT_NONE              = 0,
+               WISPR_ELEMENT_ACCESS_PROCEDURE  = 1,
+               WISPR_ELEMENT_ACCESS_LOCATION   = 2,
+               WISPR_ELEMENT_LOCATION_NAME     = 3,
+               WISPR_ELEMENT_LOGIN_URL         = 4,
+               WISPR_ELEMENT_ABORT_LOGIN_URL   = 5,
+               WISPR_ELEMENT_MESSAGE_TYPE      = 6,
+               WISPR_ELEMENT_RESPONSE_CODE     = 7,
+               WISPR_ELEMENT_NEXT_URL          = 8,
+               WISPR_ELEMENT_DELAY             = 9,
+               WISPR_ELEMENT_REPLY_MESSAGE     = 10,
+               WISPR_ELEMENT_LOGIN_RESULTS_URL = 11,
+               WISPR_ELEMENT_LOGOFF_URL        = 12,
+       } element;
+} wispr_element_map[] = {
+       { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
+       { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
+       { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
+       { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
+       { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
+       { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
+       { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
+       { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
+       { "Delay",              WISPR_ELEMENT_DELAY             },
+       { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
+       { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
+       { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
+       { NULL,                 WISPR_ELEMENT_NONE              },
+};
+
+static void xml_wispr_start_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       const gchar **attribute_names,
+                                       const gchar **attribute_values,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->current_element = element_name;
+}
+
+static void xml_wispr_end_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->current_element = NULL;
+}
+
+static void xml_wispr_text_handler(GMarkupParseContext *context,
+                                       const gchar *text, gsize text_len,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+       int i;
+
+       if (msg->current_element == NULL)
+               return;
+
+       for (i = 0; wispr_element_map[i].str; i++) {
+               if (g_str_equal(wispr_element_map[i].str,
+                                       msg->current_element) == FALSE)
+                       continue;
+
+               switch (wispr_element_map[i].element) {
+               case WISPR_ELEMENT_NONE:
+               case WISPR_ELEMENT_ACCESS_PROCEDURE:
+                       g_free(msg->access_procedure);
+                       msg->access_procedure = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ACCESS_LOCATION:
+                       g_free(msg->access_location);
+                       msg->access_location = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOCATION_NAME:
+                       g_free(msg->location_name);
+                       msg->location_name = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOGIN_URL:
+                       g_free(msg->login_url);
+                       msg->login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ABORT_LOGIN_URL:
+                       g_free(msg->abort_login_url);
+                       msg->abort_login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_MESSAGE_TYPE:
+                       msg->message_type = atoi(text);
+                       break;
+               case WISPR_ELEMENT_RESPONSE_CODE:
+                       msg->response_code = atoi(text);
+                       break;
+               case WISPR_ELEMENT_NEXT_URL:
+               case WISPR_ELEMENT_DELAY:
+               case WISPR_ELEMENT_REPLY_MESSAGE:
+               case WISPR_ELEMENT_LOGIN_RESULTS_URL:
+                       break;
+               case WISPR_ELEMENT_LOGOFF_URL:
+                       g_free(msg->logoff_url);
+                       msg->logoff_url = g_strdup(text);
+                       break;
+               }
+       }
+}
+
+static void xml_wispr_error_handler(GMarkupParseContext *context,
+                                       GError *error, gpointer user_data)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->has_error = TRUE;
+}
+
+static const GMarkupParser xml_wispr_parser_handlers = {
+       xml_wispr_start_element_handler,
+       xml_wispr_end_element_handler,
+       xml_wispr_text_handler,
+       NULL,
+       xml_wispr_error_handler,
+};
+
+static void xml_wispr_parser_callback(const char *str, gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       GMarkupParseContext *parser_context = NULL;
+       gboolean result;
+
+       DBG("");
+
+       parser_context = g_markup_parse_context_new(&xml_wispr_parser_handlers,
+                                       G_MARKUP_TREAT_CDATA_AS_TEXT,
+                                       &(wp_context->wispr_msg), NULL);
+
+       result = g_markup_parse_context_parse(parser_context,
+                                       str, strlen(str), NULL);
+       if (result == TRUE)
+               g_markup_parse_context_end_parse(parser_context, NULL);
+
+       g_markup_parse_context_free(parser_context);
+}
+
+static void web_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static void wispr_portal_error(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("Failed to proceed wispr/portal web request");
+
+       wp_context->wispr_result = CONNMAN_WISPR_RESULT_FAILED;
+}
+
+static void portal_manage_status(GWebResult *result,
+                       struct connman_wispr_portal_context *wp_context)
+{
+       const char *str = NULL;
+
+       DBG("");
+
+       /* We currently don't do anything with this info */
+       if (g_web_result_get_header(result, "X-ConnMan-Client-IP",
+                               &str) == TRUE)
+               connman_info("Client-IP: %s", str);
+
+       if (g_web_result_get_header(result, "X-ConnMan-Client-Country",
+                               &str) == TRUE)
+               connman_info("Client-Country: %s", str);
+
+       if (g_web_result_get_header(result, "X-ConnMan-Client-Region",
+                               &str) == TRUE)
+               connman_info("Client-Region: %s", str);
+
+       __connman_service_ipconfig_indicate_state(wp_context->service,
+                                               CONNMAN_SERVICE_STATE_ONLINE,
+                                               wp_context->type);
+}
+
+static gboolean wispr_route_request(const char *address, int ai_family,
+               int if_index, gpointer user_data)
+{
+       int result = -1;
+       struct connman_wispr_portal_context *wp_context = user_data;
+       const char *gateway;
+       struct wispr_route *route;
+
+       gateway = __connman_ipconfig_get_gateway_from_index(if_index,
+               wp_context->type);
+
+       DBG("address %s if %d gw %s", address, if_index, gateway);
+
+       if (gateway == NULL)
+               return FALSE;
+
+       route = g_try_new0(struct wispr_route, 1);
+       if (route == 0) {
+               DBG("could not create struct");
+               return FALSE;
+       }
+
+       switch(wp_context->type) {
+       case CONNMAN_IPCONFIG_TYPE_IPV4:
+               result = connman_inet_add_host_route(if_index, address,
+                               gateway);
+               break;
+       case CONNMAN_IPCONFIG_TYPE_IPV6:
+               result = connman_inet_add_ipv6_host_route(if_index, address,
+                               gateway);
+               break;
+       case CONNMAN_IPCONFIG_TYPE_UNKNOWN:
+               break;
+       }
+
+       if (result < 0) {
+               g_free(route);
+               return FALSE;
+       }
+
+       route->address = g_strdup(address);
+       route->if_index = if_index;
+       wp_context->route_list = g_slist_prepend(wp_context->route_list, route);
+
+       return TRUE;
+}
+
+static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("");
+
+       wp_context->request_id = g_web_request_get(wp_context->web,
+                                       wp_context->status_url,
+                                       wispr_portal_web_result,
+                                       wispr_route_request,
+                                       wp_context);
+
+       if (wp_context->request_id == 0)
+               wispr_portal_error(wp_context);
+       else
+               wispr_portal_context_ref(wp_context);
+}
+
+static gboolean wispr_input(const guint8 **data, gsize *length,
+                                               gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       GString *buf;
+       gsize count;
+
+       DBG("");
+
+       buf = g_string_sized_new(100);
+
+       g_string_append(buf, "button=Login&UserName=");
+       g_string_append_uri_escaped(buf, wp_context->wispr_username,
+                                                               NULL, FALSE);
+       g_string_append(buf, "&Password=");
+       g_string_append_uri_escaped(buf, wp_context->wispr_password,
+                                                               NULL, FALSE);
+       g_string_append(buf, "&FNAME=0&OriginatingServer=");
+       g_string_append_uri_escaped(buf, wp_context->status_url, NULL, FALSE);
+
+       count = buf->len;
+
+       g_free(wp_context->wispr_formdata);
+       wp_context->wispr_formdata = g_string_free(buf, FALSE);
+
+       *data = (guint8 *) wp_context->wispr_formdata;
+       *length = count;
+
+       return FALSE;
+}
+
+static void wispr_portal_browser_reply_cb(struct connman_service *service,
+                                       connman_bool_t authentication_done,
+                                       const char *error, void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("");
+
+       wp_context = wispr_portal_context_unref(wp_context);
+       if (service == NULL || wp_context == NULL)
+               return;
+
+       if (authentication_done == FALSE) {
+               wispr_portal_error(wp_context);
+               free_wispr_routes(wp_context);
+               return;
+       }
+
+       /* Restarting the test */
+       __connman_wispr_start(service, wp_context->type);
+}
+
+static void wispr_portal_request_wispr_login(struct connman_service *service,
+                               connman_bool_t success,
+                               const char *ssid, int ssid_len,
+                               const char *username, const char *password,
+                               gboolean wps, const char *wpspin,
+                               const char *error, void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("");
+
+       wp_context = wispr_portal_context_unref(wp_context);
+       if (wp_context == NULL)
+               return;
+
+       if (error != NULL && g_strcmp0(error,
+                       "net.connman.Agent.Error.LaunchBrowser") == 0) {
+               wispr_portal_context_ref(wp_context);
+
+               __connman_agent_request_browser(service,
+                               wispr_portal_browser_reply_cb,
+                               wp_context->redirect_url, wp_context);
+               return;
+       }
+
+       g_free(wp_context->wispr_username);
+       wp_context->wispr_username = g_strdup(username);
+
+       g_free(wp_context->wispr_password);
+       wp_context->wispr_password = g_strdup(password);
+
+       wp_context->request_id = g_web_request_post(wp_context->web,
+                                       wp_context->wispr_msg.login_url,
+                                       "application/x-www-form-urlencoded",
+                                       wispr_input, wispr_portal_web_result,
+                                       wp_context);
+       if (wp_context->request_id != 0)
+               wispr_portal_context_ref(wp_context);
+
+       connman_wispr_message_init(&wp_context->wispr_msg);
+}
+
+static gboolean wispr_manage_message(GWebResult *result,
+                       struct connman_wispr_portal_context *wp_context)
+{
+       DBG("Message type: %s (%d)",
+               message_type_to_string(wp_context->wispr_msg.message_type),
+                                       wp_context->wispr_msg.message_type);
+       DBG("Response code: %s (%d)",
+               response_code_to_string(wp_context->wispr_msg.response_code),
+                                       wp_context->wispr_msg.response_code);
+
+       if (wp_context->wispr_msg.access_procedure != NULL)
+               DBG("Access procedure: %s",
+                       wp_context->wispr_msg.access_procedure);
+       if (wp_context->wispr_msg.access_location != NULL)
+               DBG("Access location: %s",
+                       wp_context->wispr_msg.access_location);
+       if (wp_context->wispr_msg.location_name != NULL)
+               DBG("Location name: %s",
+                       wp_context->wispr_msg.location_name);
+       if (wp_context->wispr_msg.login_url != NULL)
+               DBG("Login URL: %s", wp_context->wispr_msg.login_url);
+       if (wp_context->wispr_msg.abort_login_url != NULL)
+               DBG("Abort login URL: %s",
+                       wp_context->wispr_msg.abort_login_url);
+       if (wp_context->wispr_msg.logoff_url != NULL)
+               DBG("Logoff URL: %s", wp_context->wispr_msg.logoff_url);
+
+       switch (wp_context->wispr_msg.message_type) {
+       case 100:
+               DBG("Login required");
+
+               wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
+
+               wispr_portal_context_ref(wp_context);
+
+               if (__connman_agent_request_login_input(wp_context->service,
+                                       wispr_portal_request_wispr_login,
+                                       wp_context) != -EINPROGRESS) {
+                       wispr_portal_context_unref(wp_context);
+                       wispr_portal_error(wp_context);
+               }
+
+               break;
+       case 120: /* Falling down */
+       case 140:
+               if (wp_context->wispr_msg.response_code == 50) {
+                       wp_context->wispr_result = CONNMAN_WISPR_RESULT_ONLINE;
+
+                       g_free(wp_context->wispr_username);
+                       wp_context->wispr_username = NULL;
+
+                       g_free(wp_context->wispr_password);
+                       wp_context->wispr_password = NULL;
+
+                       g_free(wp_context->wispr_formdata);
+                       wp_context->wispr_formdata = NULL;
+
+                       wispr_portal_request_portal(wp_context);
+
+                       return TRUE;
+               } else
+                       wispr_portal_error(wp_context);
+
+               break;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       const char *redirect = NULL;
+       const guint8 *chunk = NULL;
+       const char *str = NULL;
+       guint16 status;
+       gsize length;
+
+       DBG("");
+
+       wp_context = wispr_portal_context_unref(wp_context);
+       if (wp_context == NULL)
+               return FALSE;
+
+       if (wp_context->request_id == 0)
+               return FALSE;
+
+       if (wp_context->wispr_result != CONNMAN_WISPR_RESULT_ONLINE) {
+               g_web_result_get_chunk(result, &chunk, &length);
+
+               if (length > 0) {
+                       wispr_portal_context_ref(wp_context);
+
+                       g_web_parser_feed_data(wp_context->wispr_parser,
+                                                               chunk, length);
+                       return TRUE;
+               }
+
+               g_web_parser_end_data(wp_context->wispr_parser);
+
+               if (wp_context->wispr_msg.message_type >= 0) {
+                       if (wispr_manage_message(result, wp_context) == TRUE)
+                               goto done;
+               }
+       }
+
+       status = g_web_result_get_status(result);
+
+       DBG("status: %03u", status);
+
+       switch (status) {
+       case 200:
+               if (wp_context->wispr_msg.message_type >= 0)
+                       break;
+
+               if (g_web_result_get_header(result, "X-ConnMan-Status",
+                                                               &str) == TRUE)
+                       portal_manage_status(result, wp_context);
+               else {
+                       wispr_portal_context_ref(wp_context);
+
+                       __connman_agent_request_browser(wp_context->service,
+                                       wispr_portal_browser_reply_cb,
+                                       wp_context->redirect_url, wp_context);
+               }
+
+               break;
+       case 302:
+               if (g_web_supports_tls() == FALSE ||
+                               g_web_result_get_header(result, "Location",
+                                                       &redirect) == FALSE) {
+                       wispr_portal_context_ref(wp_context);
+
+                       __connman_agent_request_browser(wp_context->service,
+                                       wispr_portal_browser_reply_cb,
+                                       wp_context->status_url, wp_context);
+                       break;
+               }
+
+               DBG("Redirect URL: %s", redirect);
+
+               wp_context->redirect_url = g_strdup(redirect);
+
+               wp_context->request_id = g_web_request_get(wp_context->web,
+                               redirect, wispr_portal_web_result,
+                               wispr_route_request, wp_context);
+               if (wp_context->request_id != 0)
+                       wispr_portal_context_ref(wp_context);
+
+               goto done;
+       case 400:
+       case 404:
+               if (__connman_service_online_check_failed(wp_context->service,
+                                                       wp_context->type) == 0)
+                       wispr_portal_error(wp_context);
+
+               break;
+       default:
+               break;
+       }
+
+       free_wispr_routes(wp_context);
+       wp_context->request_id = 0;
+done:
+       wp_context->wispr_msg.message_type = -1;
+       return FALSE;
+}
+
+static void proxy_callback(const char *proxy, void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("proxy %s", proxy);
+
+       wp_context = wispr_portal_context_unref(wp_context);
+       if (wp_context == NULL)
+               return;
+
+       wp_context->token = 0;
+
+       if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
+               g_web_set_proxy(wp_context->web, proxy);
+
+       g_web_set_accept(wp_context->web, NULL);
+       g_web_set_user_agent(wp_context->web, "ConnMan/%s wispr", VERSION);
+       g_web_set_close_connection(wp_context->web, TRUE);
+
+       connman_wispr_message_init(&wp_context->wispr_msg);
+
+       wp_context->wispr_parser = g_web_parser_new(
+                                       "<WISPAccessGatewayParam",
+                                       "WISPAccessGatewayParam>",
+                                       xml_wispr_parser_callback, wp_context);
+
+       wispr_portal_request_portal(wp_context);
+}
+
+static gboolean no_proxy_callback(gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       proxy_callback("DIRECT", wp_context);
+
+       return FALSE;
+}
+
+static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
+{
+       enum connman_service_proxy_method proxy_method;
+       enum connman_service_type service_type;
+       char *interface = NULL;
+       char **nameservers = NULL;
+       int if_index;
+       int err = 0;
+       int i;
+
+       DBG("wispr/portal context %p", wp_context);
+       DBG("service %p", wp_context->service);
+
+       service_type = connman_service_get_type(wp_context->service);
+
+       switch (service_type) {
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               break;
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EOPNOTSUPP;
+       }
+
+       interface = connman_service_get_interface(wp_context->service);
+       if (interface == NULL)
+               return -EINVAL;
+
+       DBG("interface %s", interface);
+
+       if_index = connman_inet_ifindex(interface);
+       if (if_index < 0) {
+               DBG("Could not get ifindex");
+               err = -EINVAL;
+               goto done;
+       }
+
+       nameservers = connman_service_get_nameservers(wp_context->service);
+       if (nameservers == NULL) {
+               DBG("Could not get nameservers");
+               err = -EINVAL;
+               goto done;
+       }
+
+       wp_context->web = g_web_new(if_index);
+       if (wp_context->web == NULL) {
+               DBG("Could not set up GWeb");
+               err = -ENOMEM;
+               goto done;
+       }
+
+       if (getenv("CONNMAN_WEB_DEBUG"))
+               g_web_set_debug(wp_context->web, web_debug, "WEB");
+
+       if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               g_web_set_address_family(wp_context->web, AF_INET);
+               wp_context->status_url = STATUS_URL_IPV4;
+       } else {
+               g_web_set_address_family(wp_context->web, AF_INET6);
+               wp_context->status_url = STATUS_URL_IPV6;
+       }
+
+       for (i = 0; nameservers[i] != NULL; i++)
+               g_web_add_nameserver(wp_context->web, nameservers[i]);
+
+       proxy_method = connman_service_get_proxy_method(wp_context->service);
+
+       if (proxy_method != CONNMAN_SERVICE_PROXY_METHOD_DIRECT) {
+               wp_context->token = connman_proxy_lookup(interface,
+                                               wp_context->status_url,
+                                               wp_context->service,
+                                               proxy_callback, wp_context);
+
+               if (wp_context->token == 0) {
+                       err = -EINVAL;
+                       goto done;
+               }
+       } else {
+               g_timeout_add_seconds(0, no_proxy_callback, wp_context);
+       }
+
+       wispr_portal_context_ref(wp_context);
+done:
+       g_strfreev(nameservers);
+
+       g_free(interface);
+       return err;
+}
+
+int __connman_wispr_start(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct connman_wispr_portal_context *wp_context = NULL;
+       struct connman_wispr_portal *wispr_portal = NULL;
+       int index;
+
+       DBG("service %p", service);
+
+       if (wispr_portal_list == NULL)
+               return -EINVAL;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return -EINVAL;
+
+       wispr_portal = g_hash_table_lookup(wispr_portal_list,
+                                       GINT_TO_POINTER(index));
+       if (wispr_portal == NULL) {
+               wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
+               if (wispr_portal == NULL)
+                       return -ENOMEM;
+
+               g_hash_table_replace(wispr_portal_list,
+                                       GINT_TO_POINTER(index), wispr_portal);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               wp_context = wispr_portal->ipv4_context;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               wp_context = wispr_portal->ipv6_context;
+       else
+               return -EINVAL;
+
+       /* If there is already an existing context, we wipe it */
+       if (wp_context != NULL)
+               wispr_portal_context_unref(wp_context);
+
+       wp_context = create_wispr_portal_context();
+       if (wp_context == NULL)
+               return -ENOMEM;
+
+       connman_service_ref(service);
+
+       wp_context->service = service;
+       wp_context->type = type;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               wispr_portal->ipv4_context = wp_context;
+       else
+               wispr_portal->ipv6_context = wp_context;
+
+       return wispr_portal_detect(wp_context);
+}
+
+void __connman_wispr_stop(struct connman_service *service)
+{
+       int index;
+
+       DBG("service %p", service);
+
+       if (wispr_portal_list == NULL)
+               return;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return;
+
+       g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
+}
+
+int __connman_wispr_init(void)
+{
+       DBG("");
+
+       wispr_portal_list = g_hash_table_new_full(g_direct_hash,
+                                               g_direct_equal, NULL,
+                                               free_connman_wispr_portal);
+
+       return 0;
+}
+
+void __connman_wispr_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(wispr_portal_list);
+       wispr_portal_list = NULL;
+}
diff --git a/src/wpad.c b/src/wpad.c
new file mode 100644 (file)
index 0000000..4e5834e
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gweb/gresolv.h>
+
+#include "connman.h"
+
+struct connman_wpad {
+       struct connman_service *service;
+       GResolv *resolv;
+       char *hostname;
+       char **addrlist;
+};
+
+static GHashTable *wpad_list = NULL;
+
+static void resolv_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static void free_wpad(gpointer data)
+{
+        struct connman_wpad *wpad = data;
+
+       g_resolv_unref(wpad->resolv);
+
+       g_strfreev(wpad->addrlist);
+       g_free(wpad->hostname);
+        g_free(wpad);
+}
+
+static void download_pac(struct connman_wpad *wpad, const char *target)
+{
+}
+
+static void wpad_result(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+       struct connman_wpad *wpad = user_data;
+       const char *ptr;
+       char *hostname;
+
+       DBG("status %d", status);
+
+       if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
+               char *url;
+
+               if (results == NULL || g_strv_length(results) == 0)
+                       goto failed;
+
+               url = g_strdup_printf("http://%s/wpad.dat", wpad->hostname);
+
+               __connman_service_set_proxy_autoconfig(wpad->service, url);
+
+               wpad->addrlist = g_strdupv(results);
+               if (wpad->addrlist != NULL)
+                       download_pac(wpad, "wpad.dat");
+
+               g_free(url);
+
+               __connman_wispr_start(wpad->service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
+               return;
+       }
+
+       hostname = wpad->hostname;
+
+       if (strlen(hostname) < 6)
+               goto failed;
+
+       ptr = strchr(hostname + 5, '.');
+       if (ptr == NULL || strlen(ptr) < 2)
+               goto failed;
+
+       if (strchr(ptr + 1, '.') == NULL)
+               goto failed;
+
+       wpad->hostname = g_strdup_printf("wpad.%s", ptr + 1);
+       g_free(hostname);
+
+       DBG("hostname %s", wpad->hostname);
+
+       g_resolv_lookup_hostname(wpad->resolv, wpad->hostname,
+                                                       wpad_result, wpad);
+
+       return;
+
+failed:
+       connman_service_set_proxy_method(wpad->service,
+                               CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
+
+       __connman_wispr_start(wpad->service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+}
+
+int __connman_wpad_start(struct connman_service *service)
+{
+       struct connman_wpad *wpad;
+       const char *domainname;
+       char **nameservers;
+       int index;
+       int i;
+
+       DBG("service %p", service);
+
+       if (wpad_list == NULL)
+               return -EINVAL;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return -EINVAL;
+
+       domainname = connman_service_get_domainname(service);
+       if (domainname == NULL)
+               return -EINVAL;
+
+       nameservers = connman_service_get_nameservers(service);
+       if (nameservers == NULL)
+               return -EINVAL;
+
+       wpad = g_try_new0(struct connman_wpad, 1);
+       if (wpad == NULL) {
+               g_strfreev(nameservers);
+               return -ENOMEM;
+       }
+
+       wpad->service = service;
+       wpad->resolv = g_resolv_new(index);
+       if (wpad->resolv == NULL) {
+               g_strfreev(nameservers);
+               g_free(wpad);
+               return -ENOMEM;
+       }
+
+       if (getenv("CONNMAN_RESOLV_DEBUG"))
+               g_resolv_set_debug(wpad->resolv, resolv_debug, "RESOLV");
+
+       for (i = 0; nameservers[i] != NULL; i++)
+               g_resolv_add_nameserver(wpad->resolv, nameservers[i], 53, 0);
+
+       g_strfreev(nameservers);
+
+       wpad->hostname = g_strdup_printf("wpad.%s", domainname);
+
+       DBG("hostname %s", wpad->hostname);
+
+       g_resolv_lookup_hostname(wpad->resolv, wpad->hostname,
+                                                       wpad_result, wpad);
+
+       connman_service_ref(service);
+       g_hash_table_replace(wpad_list, GINT_TO_POINTER(index), wpad);
+
+       return 0;
+}
+
+void __connman_wpad_stop(struct connman_service *service)
+{
+       int index;
+
+       DBG("service %p", service);
+
+       if (wpad_list == NULL)
+               return;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return;
+
+       if (g_hash_table_remove(wpad_list, GINT_TO_POINTER(index)) == TRUE)
+               connman_service_unref(service);
+}
+
+int __connman_wpad_init(void)
+{
+       DBG("");
+
+       wpad_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, free_wpad);
+
+       return 0;
+}
+
+void __connman_wpad_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_destroy(wpad_list);
+       wpad_list = NULL;
+}
diff --git a/test/backtrace b/test/backtrace
new file mode 100755 (executable)
index 0000000..c906f36
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import os
+import re
+import sys
+import subprocess
+
+if (len(sys.argv) < 3):
+       print "Usage: %s [binary] [log]" % (sys.argv[0])
+       sys.exit(1)
+
+binary = sys.argv[1]
+count = 0
+frames = []
+addrs = []
+
+log_file = open(sys.argv[2], 'r')
+
+# Extract addresses
+for line in log_file:
+       matchobj = re.compile(r'\[(0x[0-9a-f]+)\]$').search(line)
+       if matchobj:
+               addrs.append(matchobj.group(1))
+
+log_file.close()
+
+# Feed into addr2line
+command = ['addr2line', '--demangle', '--functions', '--basename',
+                                                       '-e', binary]
+command.extend(addrs)
+
+p = subprocess.Popen(command, shell=False, bufsize=0,
+               stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+child_stdin.close()
+
+# Backtrace display
+for line in child_stdout:
+
+       if line.startswith("??"):
+               continue
+
+       line = line.strip()
+
+       frames.append(line)
+
+child_stdout.close()
+
+frame_count = len(frames);
+
+count = 0
+print "-------- backtrace --------"
+while count < frame_count:
+       print "[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1])
+       count = count + 2
+print "---------------------------"
diff --git a/test/connect-vpn b/test/connect-vpn
new file mode 100755 (executable)
index 0000000..15128c8
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+import sys
+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>"
+       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])
+
+if sys.argv[1] == "openconnect":
+       if (len(sys.argv) > 6):
+               path = manager.ConnectProvider(({ "Type": "openconnect",
+                                       "Name": sys.argv[2],
+                                       "Host": sys.argv[3],
+                                       "VPN.Domain": sys.argv[4],
+                                       "OpenConnect.Cookie": sys.argv[5],
+                                       "OpenConnect.ServerCert": sys.argv[6]}))
+       else:
+               path = manager.ConnectProvider(({ "Type": "openconnect",
+                                       "Name": sys.argv[2],
+                                       "Host": sys.argv[3],
+                                       "VPN.Domain": sys.argv[4],
+                                       "OpenConnect.Cookie": sys.argv[5]}))
+elif sys.argv[1] == "openvpn":
+       if (len(sys.argv) < 6):
+               path = manager.ConnectProvider(({ "Type": "openvpn",
+                                                 "Name": sys.argv[2],
+                                                 "Host": sys.argv[3],
+                                                 "VPN.Domain": sys.argv[4] }))
+       else:
+               path = manager.ConnectProvider(({ "Type": "openvpn",
+                                                 "Name": sys.argv[2],
+                                                 "Host": sys.argv[3],
+                                                 "VPN.Domain": sys.argv[4],
+                                                 "OpenVPN.CACert": sys.argv[5],
+                                                 "OpenVPN.Cert": sys.argv[6],
+                                                 "OpenVPN.Key": sys.argv[7]}))
+
+elif sys.argv[1] == "pptp":
+       path = manager.ConnectProvider(({ "Type": "pptp",
+                                       "Name": sys.argv[2],
+                                       "Host": sys.argv[3],
+                                       "VPN.Domain": sys.argv[4],
+                                       "PPTP.User": sys.argv[5],
+                                       "PPTP.Password": sys.argv[6]}))
+elif sys.argv[1] == "l2tp":
+       path = manager.ConnectProvider(({ "Type": "l2tp",
+                                       "Name": sys.argv[2],
+                                       "Host": sys.argv[3],
+                                       "VPN.Domain": sys.argv[4],
+                                       "L2TP.User": sys.argv[5],
+                                       "L2TP.Password": sys.argv[6]}))
+
+else:
+       print "Unknown VPN type"
+       sys.exit(1)
+
+print "VPN service path is %s" %(path)
diff --git a/test/disable-tethering b/test/disable-tethering
new file mode 100755 (executable)
index 0000000..d9c56a0
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s type" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('net.connman', "/"),
+                                       'net.connman.Manager')
+
+def technology_disable_tethering(path, tech_type):
+       tech = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Technology")
+
+       properties = tech.GetProperties()
+
+       for key in properties.keys():
+               if key in ["Type"]:
+                       if properties[key] == tech_type:
+                               print "Disabling %s tethering" % tech_type
+                               tech.SetProperty("Tethering", dbus.Boolean(0))
+
+                               return tech_type
+                       else:
+                               return None
+
+technologies = manager.GetTechnologies()
+tech = None
+
+for path,_ in technologies:
+       tech = technology_disable_tethering(path, sys.argv[1])
+       if tech != None:
+               break;
+
+if tech == None:
+       print "Failed to disable %s tethering" % (sys.argv[1])
diff --git a/test/disconnect-vpn b/test/disconnect-vpn
new file mode 100755 (executable)
index 0000000..9a6620c
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <VPN service path> " % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "net.connman.Manager")
+
+path = "" + sys.argv[1]
+
+print "remove path is %s" %(path)
+
+manager.RemoveProvider(sys.argv[1])
+
+
diff --git a/test/enable-tethering b/test/enable-tethering
new file mode 100755 (executable)
index 0000000..c9668a1
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) >= 2 and len(sys.argv) < 4 and sys.argv[1] == "wifi"):
+       print "Usage: %s wifi [SSID] [passphrase]" % (sys.argv[0])
+       sys.exit(1)
+elif (len(sys.argv) < 2):
+       print "Usage: %s type" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('net.connman', "/"),
+                                       'net.connman.Manager')
+
+def technology_enable_tethering(path, tech_type, ssid, psk):
+       tech = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Technology")
+
+       properties = tech.GetProperties()
+
+       for key in properties.keys():
+               if key in ["Type"]:
+                       if properties[key] == tech_type:
+                               if len(ssid) > 0:
+                                       tech.SetProperty("TetheringIdentifier",
+                                                               ssid)
+                               if len(psk) > 0:
+                                       tech.SetProperty("TetheringPassphrase",
+                                                               psk)
+                               print "Enabling %s tethering" % tech_type
+                               tech.SetProperty("Tethering", dbus.Boolean(1))
+
+                               return tech_type
+                       else:
+                               return None
+
+technologies = manager.GetTechnologies()
+tech = None
+
+for path,_ in technologies:
+       if (len(sys.argv) == 4):
+               tech = technology_enable_tethering(path,
+                                       sys.argv[1], sys.argv[2], sys.argv[3])
+       else:
+               tech = technology_enable_tethering(path, sys.argv[1], "", "")
+
+       if tech != None:
+               break;
+
+if tech == None:
+       print "Failed to enable %s tethering" % (sys.argv[1])
diff --git a/test/get-global-timeservers b/test/get-global-timeservers
new file mode 100755 (executable)
index 0000000..adcf175
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+clock = dbus.Interface(bus.get_object('net.connman', '/'),
+                                       'net.connman.Clock')
+
+properties = clock.GetProperties()
+
+print "Timeserver is %s" % (properties["Timeservers"])
diff --git a/test/get-proxy-autoconfig b/test/get-proxy-autoconfig
new file mode 100755 (executable)
index 0000000..6709a9e
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import dbus
+import urllib
+
+def get_pac(url):
+       conn = urllib.urlopen(url, proxies={})
+       data = conn.read()
+       print data
+       conn.close()
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('net.connman', '/'),
+                                       'net.connman.Manager')
+
+services = manager.GetServices()
+
+for entry in services:
+       path = entry[0]
+       properties = entry[1]
+
+       proxy = properties["Proxy"]
+
+       if "Method" in proxy:
+               print "[ %s ]" % (path)
+
+               method = proxy["Method"]
+               print "Method = %s" % (method)
+
+               if method in ["auto"]:
+                       url = proxy["URL"]
+                       print "URL = %s" % (url)
+                       print
+                       get_pac(url)
+               else:
+                       print
diff --git a/test/get-services b/test/get-services
new file mode 100755 (executable)
index 0000000..0956480
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+
+import dbus
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["Servers", "Excludes"]:
+                       val += extract_list(values[key])
+               else:
+                       val += str(values[key])
+       val += " }"
+       return val
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('net.connman', '/'),
+                                       'net.connman.Manager')
+
+services = manager.GetServices()
+
+for entry in services:
+       path = entry[0]
+       properties = entry[1]
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["IPv4", "IPv4.Configuration",
+                                       "IPv6", "IPv6.Configuration",
+                                       "Proxy", "Proxy.Configuration",
+                                               "Ethernet", "Provider"]:
+                       val = extract_values(properties[key])
+               elif key in ["Nameservers", "Nameservers.Configuration",
+                                       "Domains", "Domains.Configuration",
+                                               "Security"]:
+                       val = extract_list(properties[key])
+               elif key in ["Favorite", "Immutable", "AutoConnect",
+                               "PassphraseRequired"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+               elif key in ["Strength"]:
+                       val = int(properties[key])
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       print
diff --git a/test/get-state b/test/get-state
new file mode 100755 (executable)
index 0000000..75d5a16
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('net.connman', "/"),
+                                       'net.connman.Manager')
+
+properties = manager.GetProperties()
+
+print "System is %s" % (properties["State"])
diff --git a/test/list-services b/test/list-services
new file mode 100755 (executable)
index 0000000..6be4945
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+import dbus
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["PrefixLength"]:
+                       val += "%s" % (int(values[key]))
+               else:
+                       if key in ["Servers", "Excludes"]:
+                               val += extract_list(values[key])
+                       else:
+                               val += str(values[key])
+       val += " }"
+       return val
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "net.connman.Manager")
+
+for path, properties in manager.GetServices():
+       service = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Service")
+       identifier = path[path.rfind("/") + 1:]
+       print "[ %s ]" % (identifier)
+
+       for key in properties.keys():
+               if key in ["IPv4", "IPv4.Configuration",
+                               "IPv6", "IPv6.Configuration",
+                                       "Proxy", "Proxy.Configuration",
+                                               "Ethernet", "Provider"]:
+                       val = extract_values(properties[key])
+               elif key in ["Nameservers", "Nameservers.Configuration",
+                            "Domains", "Domains.Configuration",
+                            "Timeservers", "Timeservers.Configuration",
+                            "Security"]:
+                       val = extract_list(properties[key])
+               elif key in ["Favorite", "Immutable", "AutoConnect",
+                                       "LoginRequired", "PassphraseRequired"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+               elif key in ["Strength"]:
+                       val = int(properties[key])
+               else:
+                       val = properties[key]
+               print "    %s = %s" % (key, val)
+
+       print
diff --git a/test/monitor-connman b/test/monitor-connman
new file mode 100755 (executable)
index 0000000..1b3b84c
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+from dbus.lowlevel import MethodCallMessage, HANDLER_RESULT_NOT_YET_HANDLED
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               if type(i).__name__ == 'Dictionary':
+                       val = extract_values(i)
+               else:
+                       val += " " + str(i)
+       val += " ]"
+       return val
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["PrefixLength"]:
+                       val += "%s" % (int(values[key]))
+               elif key in ["IPv4", "IPv6", "Ethernet" ]:
+                       val += extract_values(values[key])
+               elif key in [ "AllowedBearers" ]:
+                       val += extract_list(values[key])
+               else:
+                       if key in ["Servers", "Excludes"]:
+                               val += extract_list(values[key])
+                       else:
+                               val += str(values[key])
+       val += " }"
+       return val
+
+def extract(name, value):
+       val = None
+
+       if name in ["Strength", "Priority"]:
+               val = int(value)
+       elif name in ["IPv4", "IPv6", "Ethernet", "Proxy" ]:
+               val = extract_values(value)
+       elif name in ["Services", "Technologies",
+                       "Nameservers", "Domains", "Timeservers"]:
+               val = extract_list(value)
+       else:
+               val = str(value)
+
+       return val
+
+def property_changed(name, value, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       val = extract(name, value)
+
+       print "{%s} [%s] %s = %s" % (iface, path, name, val)
+
+def message_filter(connection, message):
+       if not isinstance(message, MethodCallMessage):
+               return HANDLER_RESULT_NOT_YET_HANDLED
+
+       interface = message.get_interface()
+       path = message.get_path()
+       name = message.get_member()
+       args = message.get_args_list()
+
+       property_changed(name, args, path, interface)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed,
+                                       bus_name="net.connman",
+                                       signal_name = "PropertyChanged",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_match_string("member=Change,interface=net.connman.Session")
+       bus.add_match_string("member=Update,interface=net.connman.Notification")
+       bus.add_message_filter(message_filter)
+
+       mainloop = gobject.MainLoop()
+
+       mainloop.run()
diff --git a/test/monitor-services b/test/monitor-services
new file mode 100755 (executable)
index 0000000..9476bf8
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["Servers", "Excludes"]:
+                       val += extract_list(values[key])
+               else:
+                       val += str(values[key])
+       val += " }"
+       return val
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+def property_changed(name, value, path):
+       service = path[path.rfind("/") + 1:]
+       if name in ["Services"]:
+               val = "["
+               for i in value:
+                       val = val + " " + i[i.rfind("/") + 1:]
+               val = val + " ]"
+       elif name in ["IPv4", "IPv4.Configuration",
+                       "IPv6", "IPv6.Configuration",
+                       "Proxy", "Proxy.Configuration", "Ethernet", "Provider"]:
+               val = extract_values(value)
+       elif name in ["Nameservers", "Nameservers.Configuration",
+                       "Domains", "Domains.Configuration",
+                       "Timeservers", "Timeservers.Configuration", "Security"]:
+               val = extract_list(value)
+       elif name in ["Strength", "Priority"]:
+               val = int(value)
+       else:
+               val = str(value)
+       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():
+                       property_changed(n, i[1][n], i[0])
+       for i in removed:
+               service = i[i.rfind("/") + 1:]
+               print "[%s] removed" % (service)
+
+def technology_added(path, properties):
+       technology = path[path.rfind("/") + 1:]
+       print "[%s] added" % (technology)
+       for n in properties.keys():
+               property_changed(n, properties[n], technology)
+
+def technology_removed(path):
+       technology = path[path.rfind("/") + 1:]
+       print "[%s] removed" % (technology)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="PropertyChanged",
+                               path_keyword="path")
+
+       bus.add_signal_receiver(services_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="ServicesChanged")
+
+       bus.add_signal_receiver(property_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Service",
+                               signal_name="PropertyChanged",
+                               path_keyword="path")
+
+       bus.add_signal_receiver(technology_added,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="TechnologyAdded")
+
+       bus.add_signal_receiver(technology_removed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Manager",
+                               signal_name="TechnologyRemoved")
+
+       bus.add_signal_receiver(property_changed,
+                               bus_name="net.connman",
+                               dbus_interface="net.connman.Technology",
+                               signal_name="PropertyChanged",
+                               path_keyword="path")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/service-move-before b/test/service-move-before
new file mode 100755 (executable)
index 0000000..d912c88
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def print_usage():
+       print "Usage: %s <service> <target service>" % (sys.argv[0])
+
+
+if (len(sys.argv) < 2):
+       print_usage()
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+path2 = "/net/connman/service/" + 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])
+
+service.MoveBefore(service2)
+
+print
diff --git a/test/set-domains b/test/set-domains
new file mode 100755 (executable)
index 0000000..87e563e
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <service> [domain*]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+print "Setting domains to %s" % (sys.argv[2:])
+
+service.SetProperty("Domains.Configuration", 
+       dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
diff --git a/test/set-global-timeservers b/test/set-global-timeservers
new file mode 100755 (executable)
index 0000000..d7551a1
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 1):
+       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:])
+
+clock.SetProperty("Timeservers", dbus.Array(sys.argv[1:],
+                                       signature=dbus.Signature('s')))
diff --git a/test/set-ipv4-method b/test/set-ipv4-method
new file mode 100755 (executable)
index 0000000..235113f
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def make_variant(string):
+       return dbus.String(string, variant_level=1)
+
+def print_usage():
+       print "Usage: %s <service> [off|dhcp|manual <address> [netmask] [gateway]]" % (sys.argv[0])
+
+
+if (len(sys.argv) < 3):
+       print_usage()
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+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["Address"] = make_variant(sys.argv[3])
+if (len(sys.argv) > 4):
+       ipv4_configuration["Netmask"] = make_variant(sys.argv[4])
+if (len(sys.argv) > 5):
+        ipv4_configuration["Gateway"] = make_variant(sys.argv[5])
+
+service.SetProperty("IPv4.Configuration", ipv4_configuration)
+print "New IPv4.Configuration: ", ipv4_configuration
+
+print
diff --git a/test/set-ipv6-method b/test/set-ipv6-method
new file mode 100755 (executable)
index 0000000..4f2de09
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+def make_variant(string):
+       return dbus.String(string, variant_level=1)
+
+def print_usage():
+       print "Usage: %s <service> off|manual|auto [<address> [prefixlen] [gateway]] [<privacy>]" % (sys.argv[0])
+
+if (len(sys.argv) < 3):
+       print_usage()
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+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":
+       if (len(sys.argv) > 3):
+               ipv6_configuration["Privacy"] = make_variant(sys.argv[3])
+else:
+       if (len(sys.argv) > 3):
+               ipv6_configuration["Address"] = make_variant(sys.argv[3])
+       if (len(sys.argv) > 4):
+               ipv6_configuration["PrefixLength"] = make_variant(sys.argv[4])
+       if (len(sys.argv) > 5):
+               ipv6_configuration["Gateway"] = make_variant(sys.argv[5])
+
+service.SetProperty("IPv6.Configuration", ipv6_configuration)
+print "New IPv6.Configuration: ", ipv6_configuration
+
+print
diff --git a/test/set-nameservers b/test/set-nameservers
new file mode 100755 (executable)
index 0000000..ece69b8
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <service> [nameserver*]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+print "Setting nameserver to %s" % (sys.argv[2:])
+
+service.SetProperty("Nameservers.Configuration", 
+       dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
diff --git a/test/set-proxy b/test/set-proxy
new file mode 100755 (executable)
index 0000000..b9da7b0
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+import sys
+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"
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+values = { "Method" : sys.argv[2] }
+
+properties = service.GetProperties()
+
+for arg in sys.argv[3:]:
+       if arg.startswith("url="):
+               url = arg.replace("url=", "", 1)
+               values["URL"] = url
+       if arg.startswith("servers="):
+               try:
+                       servers_uris = arg.replace("servers=","",1).split(",")
+               except:
+                       servers_uris = []
+               values["Servers"] = servers_uris
+       if arg.startswith("excludes="):
+               try:
+                       excludes_uris = arg.replace("excludes=","",1).split(",")
+               except:
+                       excludes_uris = []
+               values["Excludes"] = excludes_uris
+
+try:
+       service.SetProperty("Proxy.Configuration", dbus.Dictionary(values, signature='sv'))
+except dbus.exceptions.DBusException, e_msg:
+       print e_msg
diff --git a/test/set-timeservers b/test/set-timeservers
new file mode 100755 (executable)
index 0000000..19cc938
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <service> [timeserver*]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+path = "/net/connman/service/" + sys.argv[1]
+service = dbus.Interface(bus.get_object('net.connman', path),
+                                       'net.connman.Service')
+
+properties = service.GetProperties()
+
+print "Setting timeserver to %s" % (sys.argv[2:])
+
+service.SetProperty("Timeservers.Configuration",
+       dbus.Array(sys.argv[2:], signature=dbus.Signature('s')))
diff --git a/test/show-introspection b/test/show-introspection
new file mode 100755 (executable)
index 0000000..4b6450f
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+object = dbus.Interface(bus.get_object("net.connman", '/'),
+                               "org.freedesktop.DBus.Introspectable")
+
+print object.Introspect()
+
+manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "net.connman.Manager")
+
+technologies = manager.GetTechnologies()
+
+for path, properties in technologies:
+       object = dbus.Interface(bus.get_object("net.connman", path),
+                                       "org.freedesktop.DBus.Introspectable")
+
+       print object.Introspect()
diff --git a/test/simple-agent b/test/simple-agent
new file mode 100755 (executable)
index 0000000..45437df
--- /dev/null
@@ -0,0 +1,220 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import sys
+
+class Canceled(dbus.DBusException):
+       _dbus_error_name = "net.connman.Error.Canceled"
+
+class LaunchBrowser(dbus.DBusException):
+       _dbus_error_name = "net.connman.Agent.Error.LaunchBrowser"
+
+class Agent(dbus.service.Object):
+       name = None
+       ssid = None
+       identity = None
+       passphrase = None
+       wpspin = None
+       username = None
+       password = None
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='', out_signature='')
+       def Release(self):
+               print("Release")
+               mainloop.quit()
+
+       def input_passphrase(self):
+               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: ')
+
+                       for arg in args.split():
+                               if arg.startswith("cancel"):
+                                       response["Error"] = arg
+                               if arg.startswith("Identity="):
+                                       identity = arg.replace("Identity=", "", 1)
+                                       response["Identity"] = identity
+                               if arg.startswith("Passphrase="):
+                                       passphrase = arg.replace("Passphrase=", "", 1)
+                                       response["Passphrase"] = passphrase
+                               if arg.startswith("WPS="):
+                                       wpspin = arg.replace("WPS=", "", 1)
+                                       response["WPS"] = wpspin
+                                       break
+               else:
+                       if self.identity:
+                               response["Identity"] = self.identity
+                       if self.passphrase:
+                               response["Passphrase"] = self.passphrase
+                       if self.wpspin:
+                               response["WPS"] = self.wpspin
+
+               return response
+
+       def input_username(self):
+               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: ')
+
+                       for arg in args.split():
+                               if arg.startswith("cancel") or arg.startswith("browser"):
+                                       response["Error"] = arg
+                               if arg.startswith("Username="):
+                                       username = arg.replace("Username=", "", 1)
+                                       response["Username"] = username
+                               if arg.startswith("Password="):
+                                       password = arg.replace("Password=", "", 1)
+                                       response["Password"] = password
+               else:
+                       if self.username:
+                               response["Username"] = self.username
+                       if self.password:
+                               response["Password"] = self.password
+
+               return response
+
+       def input_hidden(self):
+               response = {}
+
+               if not self.name and not self.ssid:
+                       args = raw_input('Answer ')
+
+                       for arg in args.split():
+                               if arg.startswith("Name="):
+                                       name = arg.replace("Name=", "", 1)
+                                       response["Name"] = name
+                                       break
+                               if arg.startswith("SSID="):
+                                       ssid = arg.replace("SSID", "", 1)
+                                       response["SSID"] = ssid
+                                       break
+               else:
+                       if self.name:
+                               response["Name"] = self.name
+                       if self.ssid:
+                               response["SSID"] = self.ssid
+
+               return response
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='oa{sv}',
+                                       out_signature='a{sv}')
+       def RequestInput(self, path, fields):
+               print "RequestInput (%s,%s)" % (path, fields)
+
+               response = {}
+
+               if fields.has_key("Name"):
+                       response.update(self.input_hidden())
+               if fields.has_key("Passphrase"):
+                       response.update(self.input_passphrase())
+               if fields.has_key("Username"):
+                       response.update(self.input_username())
+
+               if response.has_key("Error"):
+                       if response["Error"] == "cancel":
+                               raise Canceled("canceled")
+                               return
+                       if response["Error"] == "browser":
+                               raise LaunchBrowser("launch browser")
+                               return
+
+               print "returning (%s)" % (response)
+
+               return response
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='os',
+                                       out_signature='')
+       def RequestBrowser(self, 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"
+
+               args = raw_input('> ')
+
+               if len(args) > 0:
+                       raise Canceled("canceled")
+
+               return
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='os',
+                                       out_signature='')
+       def ReportError(self, path, error):
+               print "ReportError %s, %s" % (path, error)
+               retry = raw_input("Retry service (yes/no): ")
+               if (retry == "yes"):
+                       class Retry(dbus.DBusException):
+                               _dbus_error_name = "net.connman.Agent.Error.Retry"
+
+                       raise Retry("retry service")
+               else:
+                       return
+
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='', out_signature='')
+       def Cancel(self):
+               print "Cancel"
+
+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 input:"
+       print "%s Username=<username> Password=<password>" % (sys.argv[0])
+       print "Help: %s help" % (sys.argv[0])
+       sys.exit(1)
+
+if __name__ == '__main__':
+       if len(sys.argv) == 2 and sys.argv[1] == "help":
+               print_usage()
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('net.connman', "/"),
+                                       'net.connman.Manager')
+
+       path = "/test/agent"
+       object = Agent(bus, path)
+
+       if len(sys.argv) >= 2:
+               for arg in sys.argv[1:]:
+                       if arg.startswith("Name="):
+                               object.name = arg.replace("Name=", "", 1)
+                       elif arg.startswith("SSID="):
+                               object.ssid = arg.replace("SSID=", "", 1)
+                       elif arg.startswith("Identity="):
+                               object.identity = arg.replace("Identity=", "", 1)
+                       elif arg.startswith("Passphrase="):
+                               object.passphrase = arg.replace("Passphrase=", "", 1)
+                       elif arg.startswith("WPS="):
+                               object.wpspin = arg.replace("WPS=", "", 1)
+                       elif arg.startswith("Username="):
+                               object.username = arg.replace("Username=", "", 1)
+                       elif arg.startswith("Password="):
+                               object.password = arg.replace("Password=", "", 1)
+                       else:
+                               print_usage()
+
+       manager.RegisterAgent(path)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
+
+       #manager.UnregisterAgent(path)
diff --git a/test/test-clock b/test/test-clock
new file mode 100755 (executable)
index 0000000..e9b76fc
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+clock = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "net.connman.Clock")
+
+properties = clock.GetProperties()
+
+for key in properties.keys():
+       if key in ["Timeservers"]:
+               list = ""
+               for val in properties[key]:
+                       list = list + val + " "
+               print "%s = [ %s]" % (key, list)
+       else:
+               print "%s = %s" % (key, properties[key])
diff --git a/test/test-compat b/test/test-compat
new file mode 100755 (executable)
index 0000000..cd1ca7a
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.freedesktop.NetworkManager',
+                               '/org/freedesktop/NetworkManager'),
+                                       'org.freedesktop.NetworkManager')
+
+states = [ "unknown", "asleep", "connecting", "connected", "disconnected" ]
+
+state = manager.state()
+
+print "System is %s" % (states[state])
diff --git a/test/test-connman b/test/test-connman
new file mode 100755 (executable)
index 0000000..67b0c85
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "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]"
+       sys.exit(1)
+
+def print_services(services):
+       for path, properties in services:
+               identifier = path[path.rfind("/") + 1:]
+               state = " "
+               autoconnect = "  "
+
+               if properties["Favorite"] == dbus.Boolean(1):
+                       favorite = "*"
+
+                       if properties["AutoConnect"] == dbus.Boolean(1):
+                               autoconnect = " A"
+                       else:
+                               autoconnect = "  "
+
+                       if properties["State"] == "ready":
+                               state = "R"
+                       elif properties["State"] == "online":
+                               state = "O"
+               else:
+                       favorite = " "
+
+               if "Name" in properties.keys():
+                       name = properties["Name"]
+               else:
+                       name = "{" + properties["Type"] + "}"
+
+               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"])
+
+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"
+               sys.exit(1)
+
+       path = "/net/connman/service/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Service")
+
+       if (len(sys.argv) > 3):
+               flag = sys.argv[3].strip().lower()
+               autoconnect = dbus.Boolean(flag not in ['false', 'f', 'n', '0'])
+
+               service.SetProperty("AutoConnect", autoconnect);
+
+               print "Auto connect %s for %s" % (autoconnect, sys.argv[2])
+       else:
+               properties = service.GetProperties()
+
+               if "Name" in properties.keys():
+                       name = properties["Name"]
+               else:
+                       name = "{" + properties["Type"] + "}"
+
+               if "AutoConnect" in properties.keys():
+                       autoconnect = properties["AutoConnect"]
+               else:
+                       autoconnect = dbus.Boolean(0)
+
+               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"
+               sys.exit(1)
+
+       path = "/net/connman/service/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Service")
+
+       try:
+               service.Connect(timeout=60000)
+       except dbus.DBusException, 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"
+               sys.exit(1)
+
+       path = "/net/connman/service/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Service")
+
+       try:
+               service.Disconnect()
+       except dbus.DBusException, 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"
+               sys.exit(1)
+
+       path = "/net/connman/service/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Service")
+
+       properties = service.GetProperties()
+
+       if properties["Favorite"] == dbus.Boolean(0):
+               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)
+
+elif sys.argv[1] == "scan":
+       if len(sys.argv) > 2:
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                               "net.connman.Technology")
+               technology.Scan()
+
+elif sys.argv[1] == "enable":
+       if len(sys.argv) > 2:
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                           "net.connman.Technology")
+               technology.SetProperty("Powered", True)
+
+elif sys.argv[1] == "disable":
+       if len(sys.argv) > 2:
+               path = "/net/connman/technology/" + sys.argv[2]
+               technology = dbus.Interface(bus.get_object("net.connman", path),
+                                           "net.connman.Technology")
+               technology.SetProperty("Powered", False)
+
+elif sys.argv[1] in ["offlinemode", "flightmode"]:
+       if len(sys.argv) > 2:
+               if sys.argv[2] == "on":
+                       active = dbus.Boolean(1)
+               elif sys.argv[2] == "off":
+                       active = dbus.Boolean(0)
+               else:
+                       active = dbus.Boolean(sys.argv[2])
+               manager.SetProperty("OfflineMode", active)
+       else:
+               properties = manager.GetProperties()
+               print "Offline mode is %s" % (properties["OfflineMode"])
+
+else:
+       print "Unknown command"
diff --git a/test/test-counter b/test/test-counter
new file mode 100755 (executable)
index 0000000..ce83580
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+
+import sys
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+def make_bytes_readable(bytes):
+       SUFFIXES = [ 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ]
+       size = 1024
+
+       if bytes < size:
+               return ''
+
+       for suffix in SUFFIXES:
+               if bytes > size * 1024:
+                       size = size * 1024
+                       continue
+
+               return '%.1f %s' % (bytes / float(size), suffix)
+
+       return ''
+
+def print_stats(stats):
+       keys = stats.keys()
+       keys.sort()
+
+       for key in keys:
+               val = int(stats[key])
+               str = "    %s = %s" % (key, val)
+
+               if key in ["RX.Bytes", "TX.Bytes"]:
+                       hstr = make_bytes_readable(val)
+                       if hstr:
+                               str = "%s (%s)" % (str, hstr)
+
+               print str
+
+class Counter(dbus.service.Object):
+       @dbus.service.method("net.connman.Counter",
+                               in_signature='', out_signature='')
+       def Release(self):
+               print("Release")
+               mainloop.quit()
+
+       @dbus.service.method("net.connman.Counter",
+                               in_signature='oa{sv}a{sv}', out_signature='')
+       def Usage(self, path, home, roaming):
+               print "%s" % (path)
+
+               if len(home) > 0:
+                       print "  Home"
+                       print_stats(home)
+               if len(roaming) > 0:
+                       print "  Roaming"
+                       print_stats(roaming)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('net.connman', "/"),
+                                       'net.connman.Manager')
+
+       period = 2
+       if len(sys.argv) > 1:
+               period = sys.argv[1]
+
+       path = "/test/counter%s" % period
+       object = Counter(bus, path)
+
+       manager.RegisterCounter(path, dbus.UInt32(10), dbus.UInt32(period))
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
+
+       #manager.UnregisterCounter(path)
diff --git a/test/test-manager b/test/test-manager
new file mode 100755 (executable)
index 0000000..2b4493c
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/python
+
+import dbus
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["PrefixLength"]:
+                       val += "%s" % (int(values[key]))
+               else:
+                       if key in ["Servers", "Excludes"]:
+                               val += extract_list(values[key])
+                       else:
+                               val += str(values[key])
+       val += " }"
+       return val
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                       "net.connman.Manager")
+
+properties = manager.GetProperties()
+
+for key in properties.keys():
+       if key in ["OfflineMode", "SessionMode"]:
+               print "%s" % (key)
+               if properties[key] == dbus.Boolean(1):
+                       print "    true"
+               else:
+                       print "    false"
+       else:
+               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():
+               if key in ["Available", "Remember", "Default",
+                               "Favorite", "Immutable", "AutoConnect",
+                                       "LoginRequired",
+                                       "PassphraseRequired"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+
+               elif key in ["IPv4", "IPv4.Configuration",
+                               "IPv6", "IPv6.Configuration",
+                                       "Proxy", "Proxy.Configuration",
+                                               "Ethernet", "Provider"]:
+                       val = extract_values(properties[key])
+
+               elif key in ["Nameservers", "Nameservers.Configuration",
+                               "Domains", "Domains.Configuration",
+                                       "Security"]:
+                       val = extract_list(properties[key])
+
+               elif key in ["Strength", "Priority"]:
+                       val = int(properties[key])
+
+               else:
+                       val = str(properties[key])
+
+               print "        %s = %s" % (key, val)
+
+print ("Technologies")
+technologies = manager.GetTechnologies()
+
+for (path, properties) in technologies:
+       print "    %s" % (path)
+       for key in properties.keys():
+
+               if key in ["Connected", "Powered", "Tethering"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+               else:
+                       val = properties[key]
+
+               print "        %s = %s" % (key, val)
diff --git a/test/test-new-supplicant b/test/test-new-supplicant
new file mode 100755 (executable)
index 0000000..be230dc
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+import dbus
+
+WPA_NAME='fi.w1.wpa_supplicant1'
+WPA_INTF='fi.w1.wpa_supplicant1'
+WPA_PATH='/fi/w1/wpa_supplicant1'
+
+bus = dbus.SystemBus()
+
+dummy = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH),
+                               'org.freedesktop.DBus.Introspectable')
+
+print dummy.Introspect()
diff --git a/test/test-session b/test/test-session
new file mode 100755 (executable)
index 0000000..2d82fb6
--- /dev/null
@@ -0,0 +1,341 @@
+#!/usr/bin/python
+
+import sys
+import gobject
+import string
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+import glib
+
+import traceback
+
+def extract_list(list):
+       val = "["
+       for i in list:
+               val += " " + str(i)
+       val += " ]"
+       return val
+
+def extract_values(values):
+       val = "{"
+       for key in values.keys():
+               val += " " + key + "="
+               if key in ["PrefixLength"]:
+                       val += "%s" % (int(values[key]))
+               else:
+                       if key in ["Servers", "Excludes"]:
+                               val += extract_list(values[key])
+                       else:
+                               val += str(values[key])
+       val += " }"
+       return val
+
+class Notification(dbus.service.Object):
+       def __init__(self, bus, app, notify_path):
+               dbus.service.Object.__init__(self)
+               self.app = app
+
+       @dbus.service.method("net.connman.Notification",
+                               in_signature='', out_signature='')
+       def Release(self):
+               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)
+
+               try:
+                       for key in 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)
+               except:
+                       print "Exception:"
+                       traceback.print_exc()
+
+class SessionApplication(dbus.service.Object):
+       def __init__(self, bus, object_path, mainloop):
+               dbus.service.Object.__init__(self, bus, object_path)
+
+               self.manager = None
+               self.mainloop = mainloop
+               self.sessions = {}
+
+               try:
+                       bus = dbus.SystemBus()
+                       bus.watch_name_owner('net.connman', self.connman_name_owner_changed)
+               except dbus.DBusException:
+                       traceback.print_exc()
+
+       def connman_name_owner_changed(self, proxy):
+               try:
+                       if 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"
+                               self.manager = None
+                               for s in self.sessions.keys():
+                                       self.sessions[s]['notify'].remove_from_connection()
+                                       self.sessions[s]['notify'] = None
+
+                               self.sessions = {}
+
+               except dbus.DBusException:
+                       traceback.print_exc()
+
+       def release(self, session_name):
+               s = self.find_session(session_name)
+               if not s:
+                       return
+               if s['session']:
+                       s['session'].Destroy()
+                       s['session'] = None
+               if s['notify']:
+                       s['notify'].remove_from_connection()
+                       s['notify'] = None
+               del self.sessions[session_name]
+
+       def type_convert(self, key, value):
+               if key in [ "AllowedBearers" ]:
+                       return value
+               elif key in [ "RoamingPolicy", "ConnectionType" ]:
+                       if len(value) > 0:
+                               return value[0]
+               elif key in [ "Priority", "AvoidHandover",
+                             "StayConnected", "EmergencyCall" ]:
+                       flag = str(value[0]).strip().lower()
+                       val = flag not in ['false', 'f', 'n', '0']
+                       return dbus.Boolean(val)
+               elif key in [ "PeriodicConnect", "IdleTimeout" ]:
+                       val = value[0]
+                       return dbus.UInt32(val)
+
+               return value
+
+       def find_session(self, session_name):
+               if not session_name in 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"
+
+               s = self.find_session(session_name)
+               if s and s['session'] :
+                       print "Session %s already created-> drop reqest" % (session_name)
+                       return
+
+               try:
+                       bus = dbus.SystemBus()
+
+                       if s == None:
+                               s = {}
+                       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():
+                               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'])
+                       s['session'] = dbus.Interface(bus.get_object("net.connman", s['session_path']),
+                                                     "net.connman.Session")
+                       self.sessions[session_name] = s
+
+               except dbus.DBusException, e:
+                       if e.get_dbus_name() in ['net.connman.Error.Failed']:
+                               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"
+
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
+                       return
+
+               try:
+                       self.release(session_name)
+               except dbus.DBusException:
+                       traceback.print_exc()
+
+       @dbus.service.method("com.example.TestSession",
+                               in_signature='', out_signature='')
+       def Connect(self, session_name):
+               print "Connect session"
+
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
+                       return
+
+               try:
+                       s['session'].Connect()
+               except dbus.DBusException, e:
+                       if e.get_dbus_name() in ['net.connman.Error.Failed']:
+                               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"
+
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
+                       return
+
+               try:
+                       s['session'].Disconnect()
+               except dbus.DBusException, e:
+                       if e.get_dbus_name() in ['net.connman.Error.Failed']:
+                               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"
+
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       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:
+                       if e.get_dbus_name() in ['net.connman.Error.Failed']:
+                               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"
+               s = self.find_session(session_name)
+               if s == None:
+                       s = {}
+                       s['notify_path'] = None
+                       s['notify'] = None
+                       if not 'settings' in 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"
+                       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>"
+               sys.exit(1)
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       if sys.argv[1] == "enable":
+               bus = dbus.SystemBus()
+               manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                        "net.connman.Manager")
+               manager.SetProperty("SessionMode", True)
+               return
+
+       elif sys.argv[1] == "disable":
+               bus = dbus.SystemBus()
+               manager = dbus.Interface(bus.get_object("net.connman", "/"),
+                                        "net.connman.Manager")
+               manager.SetProperty("SessionMode", False)
+               return
+
+       if (len(sys.argv) < 3):
+               print "Need test application path"
+               sys.exit(1)
+
+       app_path = sys.argv[2]
+       bus = dbus.SessionBus()
+
+       app_name = "com.example.SessionApplication.%s" % (string.strip(app_path, "/"))
+
+       if sys.argv[1] == "run":
+               name = dbus.service.BusName(app_name, bus)
+               mainloop = gobject.MainLoop()
+
+               app = SessionApplication(bus, app_path, mainloop)
+
+               mainloop.run()
+               return
+
+       app = dbus.Interface(bus.get_object(app_name, app_path),
+                            "com.example.TestSession")
+
+       if sys.argv[1] == "create":
+               app.CreateSession(sys.argv[3])
+
+       elif sys.argv[1] == "destroy":
+               app.DestroySession(sys.argv[3])
+
+       elif sys.argv[1] == "connect":
+               app.Connect(sys.argv[3])
+
+       elif sys.argv[1] == "disconnect":
+               app.Disconnect(sys.argv[3])
+
+       elif sys.argv[1] == "change":
+               if len(sys.argv) < 5:
+                       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"
+                       sys.exit(1)
+
+               app.Configure(sys.argv[3], sys.argv[4], sys.argv[5:])
+
+       else:
+               print "Unknown command '%s'" % sys.argv[1]
+               sys.exit(1)
+
+if __name__ == '__main__':
+       main()
diff --git a/test/test-supplicant b/test/test-supplicant
new file mode 100755 (executable)
index 0000000..68ac663
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+import dbus
+import time
+
+WPA_NAME='fi.epitest.hostap.WPASupplicant'
+WPA_INTF='fi.epitest.hostap.WPASupplicant'
+WPA_PATH='/fi/epitest/hostap/WPASupplicant'
+
+bus = dbus.SystemBus()
+
+dummy = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH),
+                               'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+manager = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH), WPA_INTF)
+
+try:
+       path = manager.getInterface("wlan0")
+except:
+       path = manager.addInterface("wlan0")
+
+interface = dbus.Interface(bus.get_object(WPA_NAME, path),
+                                       WPA_INTF + ".Interface")
+
+print "state = %s" % (interface.state())
+
+try:
+       print "scanning = %s" % (interface.scanning())
+except:
+       pass
+
+print "[ %s ]" % (path)
+
+capabilities = interface.capabilities()
+
+for key in capabilities.keys():
+       list = ""
+       for value in capabilities[key]:
+               list += " " + value
+       print "    %s =%s" % (key, list)
+
+interface.scan()
+
+time.sleep(1)
+
+try:
+       print "scanning = %s" % (interface.scanning())
+except:
+       pass
+
+time.sleep(1)
+
+print "state = %s" % (interface.state())
+
+results = interface.scanResults()
+
+print results
+
+path = results[0]
+
+print "[ %s ]" % (path)
+
+bssid = dbus.Interface(bus.get_object(WPA_NAME, path),
+                                       WPA_INTF + ".BSSID")
+
+properties = bssid.properties()
+
+for key in properties.keys():
+       print "    %s = %s" % (key, properties[key])
diff --git a/tools/addr-test.c b/tools/addr-test.c
new file mode 100644 (file)
index 0000000..273ab02
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *
+ *  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 <arpa/inet.h>
+
+static unsigned char netmask2prefixlen(const char *netmask)
+{
+       unsigned char bits = 0;
+       in_addr_t mask = inet_network(netmask);
+       in_addr_t host = ~mask;
+
+       /* a valid netmask must be 2^n - 1 */
+       if ((host & (host + 1)) != 0)
+               return -1;
+
+       for (; mask; mask <<= 1)
+               ++bits;
+
+       return bits;
+}
+
+int main(int argc, char *argv[])
+{
+       const char *masks[] = { "255.255.255.255", "255.255.255.0",
+                                               "255.255.0.0", "255.0.0.0" };
+       struct in_addr addr;
+       int i, len;
+
+       for (i = 0; i < 4; i++) {
+               printf("subnet %-16s  prefixlen %u\n", masks[i],
+                                               netmask2prefixlen(masks[i]));
+       }
+
+       printf("\n");
+
+       for (i = 0; i < 4; i++) {
+               len = (i + 1) * 8;
+               addr.s_addr = htonl(~(0xfffffffflu >> len));
+               printf("prefixlen %-2u  netmask %s\n", len, inet_ntoa(addr));
+       }
+
+       return 0;
+}
diff --git a/tools/dbus-test.c b/tools/dbus-test.c
new file mode 100644 (file)
index 0000000..94d94f0
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ *  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 <signal.h>
+
+#include <gdbus.h>
+
+#define DBG(fmt, arg...) do { \
+       printf("%s() " fmt "\n", __FUNCTION__ , ## arg); \
+} while (0)
+
+static GMainLoop *main_loop = NULL;
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       printf("D-Bus disconnect\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       DBusError err;
+       struct sigaction sa;
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/dhcp-server-test.c b/tools/dhcp-server-test.c
new file mode 100644 (file)
index 0000000..4252e62
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *
+ *  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 <gdhcp/gdhcp.h>
+
+static GMainLoop *main_loop;
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void handle_error(GDHCPServerError error)
+{
+       switch (error) {
+       case G_DHCP_SERVER_ERROR_NONE:
+               printf("dhcp server ok\n");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE:
+               printf("Interface unavailable\n");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_IN_USE:
+               printf("Interface in use\n");
+               break;
+       case G_DHCP_SERVER_ERROR_INTERFACE_DOWN:
+               printf("Interface down\n");
+               break;
+       case G_DHCP_SERVER_ERROR_NOMEM:
+               printf("No memory\n");
+               break;
+       case G_DHCP_SERVER_ERROR_INVALID_INDEX:
+               printf("Invalid index\n");
+               break;
+       case G_DHCP_SERVER_ERROR_INVALID_OPTION:
+               printf("Invalid option\n");
+               break;
+       case G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID:
+               printf("Invalid address\n");
+               break;
+       }
+}
+
+static void dhcp_debug(const char *str, void *data)
+{
+       printf("%s: %s\n", (const char *) data, str);
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       GDHCPServerError error;
+       GDHCPServer *dhcp_server;
+       int index;
+
+       if (argc < 2) {
+               printf("Usage: dhcp-server-test <interface index>\n");
+               exit(0);
+       }
+
+       index = atoi(argv[1]);
+
+       printf("Create DHCP server for interface %d\n", index);
+
+       dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error);
+       if (dhcp_server == NULL) {
+               handle_error(error);
+               exit(0);
+       }
+
+       g_dhcp_server_set_debug(dhcp_server, dhcp_debug, "DHCP");
+
+       g_dhcp_server_set_lease_time(dhcp_server, 3600);
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET, "255.255.0.0");
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, "192.168.0.2");
+       g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, "192.168.0.3");
+       g_dhcp_server_set_ip_range(dhcp_server, "192.168.0.101",
+                                                       "192.168.0.102");
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       printf("Start DHCP Server operation\n");
+
+       g_dhcp_server_start(dhcp_server);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_dhcp_server_unref(dhcp_server);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/dhcp-test.c b/tools/dhcp-test.c
new file mode 100644 (file)
index 0000000..284656b
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ *
+ *  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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <net/ethernet.h>
+#include <linux/if_arp.h>
+
+#include <gdhcp/gdhcp.h>
+
+static GTimer *timer;
+
+static GMainLoop *main_loop;
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void print_elapsed(void)
+{
+       gdouble elapsed;
+
+       elapsed = g_timer_elapsed(timer, NULL);
+
+       printf("elapsed: %f seconds\n", elapsed);
+}
+
+static void handle_error(GDHCPClientError error)
+{
+       switch (error) {
+       case G_DHCP_CLIENT_ERROR_NONE:
+               printf("dhcp client ok\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE:
+               printf("Interface unavailable\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_INTERFACE_IN_USE:
+               printf("Interface in use\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_INTERFACE_DOWN:
+               printf("Interface down\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_NOMEM:
+               printf("No memory\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_INVALID_INDEX:
+               printf("Invalid index\n");
+               break;
+       case G_DHCP_CLIENT_ERROR_INVALID_OPTION:
+               printf("Invalid option\n");
+               break;
+       }
+}
+
+static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       print_elapsed();
+
+       printf("No lease available\n");
+
+       g_main_loop_quit(main_loop);
+}
+
+static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
+{
+       GList *list, *option_value = NULL;
+       char *address;
+
+       print_elapsed();
+
+       printf("Lease available\n");
+
+       address = g_dhcp_client_get_address(dhcp_client);
+       printf("address %s\n", address);
+       if (address == NULL)
+               return;
+
+       option_value = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
+       for (list = option_value; list; list = list->next)
+               printf("sub-mask %s\n", (char *) list->data);
+
+       option_value = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
+       for (list = option_value; list; list = list->next)
+               printf("domain-name-servers %s\n", (char *) list->data);
+
+       option_value = g_dhcp_client_get_option(dhcp_client, G_DHCP_DOMAIN_NAME);
+       for (list = option_value; list; list = list->next)
+               printf("domain-name %s\n", (char *) list->data);
+
+       option_value = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
+       for (list = option_value; list; list = list->next)
+               printf("routers %s\n", (char *) list->data);
+
+       option_value = g_dhcp_client_get_option(dhcp_client, G_DHCP_HOST_NAME);
+       for (list = option_value; list; list = list->next)
+               printf("hostname %s\n", (char *) list->data);
+}
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       GDHCPClientError error;
+       GDHCPClient *dhcp_client;
+       int index;
+
+       if (argc < 2) {
+               printf("Usage: dhcp-test <interface index>\n");
+               exit(0);
+       }
+
+       index = atoi(argv[1]);
+
+       printf("Create DHCP client for interface %d\n", index);
+
+       dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error);
+       if (dhcp_client == NULL) {
+               handle_error(error);
+               exit(0);
+       }
+
+       g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, "<hostname>");
+
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
+       g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
+                                               lease_available_cb, NULL);
+
+       g_dhcp_client_register_event(dhcp_client,
+                       G_DHCP_CLIENT_EVENT_NO_LEASE, no_lease_cb, NULL);
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       printf("Start DHCP operation\n");
+
+       timer = g_timer_new();
+
+       g_dhcp_client_start(dhcp_client, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_timer_destroy(timer);
+
+       g_dhcp_client_unref(dhcp_client);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/iptables-test.c b/tools/iptables-test.c
new file mode 100644 (file)
index 0000000..881fa0b
--- /dev/null
@@ -0,0 +1,1863 @@
+/*
+ *  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
+ *
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <xtables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#include <glib.h>
+
+static const char *hooknames[] = {
+       [NF_IP_PRE_ROUTING]     = "PREROUTING",
+       [NF_IP_LOCAL_IN]        = "INPUT",
+       [NF_IP_FORWARD]         = "FORWARD",
+       [NF_IP_LOCAL_OUT]       = "OUTPUT",
+       [NF_IP_POST_ROUTING]    = "POSTROUTING",
+};
+
+#define LABEL_ACCEPT  "ACCEPT"
+#define LABEL_DROP    "DROP"
+#define LABEL_QUEUE   "QUEUE"
+#define LABEL_RETURN  "RETURN"
+
+#define XT_OPTION_OFFSET_SCALE 256
+
+/* fn returns 0 to continue iteration */
+#define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
+({                                                             \
+       unsigned int __i;                                       \
+       int __n;                                                \
+       int __ret = 0;                                          \
+       type *__entry;                                          \
+                                                               \
+       for (__i = 0, __n = 0; __i < (size);                    \
+            __i += __entry->next_offset, __n++) {              \
+               __entry = (void *)(entries) + __i;              \
+               if (__n < n)                                    \
+                       continue;                               \
+                                                               \
+               __ret = fn(__entry,  ## args);                  \
+               if (__ret != 0)                                 \
+                       break;                                  \
+       }                                                       \
+       __ret;                                                  \
+})
+
+/* fn returns 0 to continue iteration */
+#define _XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
+       _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
+
+#define ENTRY_ITERATE(entries, size, fn, args...) \
+       _XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
+
+#define MIN_ALIGN (__alignof__(struct ipt_entry))
+
+#define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
+
+struct error_target {
+       struct xt_entry_target t;
+       char error[IPT_TABLE_MAXNAMELEN];
+};
+
+struct connman_iptables_entry {
+       int offset;
+       int builtin;
+
+       struct ipt_entry *entry;
+};
+
+struct connman_iptables {
+       int ipt_sock;
+
+       struct ipt_getinfo *info;
+       struct ipt_get_entries *blob_entries;
+
+       unsigned int num_entries;
+       unsigned int old_entries;
+       unsigned int size;
+
+       unsigned int underflow[NF_INET_NUMHOOKS];
+       unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+       GList *entries;
+};
+
+
+static struct ipt_entry *get_entry(struct connman_iptables *table,
+                                       unsigned int offset)
+{
+       return (struct ipt_entry *)((char *)table->blob_entries->entrytable +
+                                                                       offset);
+}
+
+static int is_hook_entry(struct connman_iptables *table,
+                               struct ipt_entry *entry)
+{
+       unsigned int i;
+
+       for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+               if ((table->info->valid_hooks & (1 << i))
+               && get_entry(table, table->info->hook_entry[i]) == entry)
+                       return i;
+       }
+
+       return -1;
+}
+
+static unsigned long entry_to_offset(struct connman_iptables *table,
+                                       struct ipt_entry *entry)
+{
+       return (void *)entry - (void *)table->blob_entries->entrytable;
+}
+
+static int target_to_verdict(char *target_name)
+{
+       if (!strcmp(target_name, LABEL_ACCEPT))
+               return -NF_ACCEPT - 1;
+
+       if (!strcmp(target_name, LABEL_DROP))
+               return -NF_DROP - 1;
+
+       if (!strcmp(target_name, LABEL_QUEUE))
+               return -NF_QUEUE - 1;
+
+       if (!strcmp(target_name, LABEL_RETURN))
+               return XT_RETURN;
+
+       return 0;
+}
+
+static gboolean is_builtin_target(char *target_name)
+{
+       if (!strcmp(target_name, LABEL_ACCEPT) ||
+               !strcmp(target_name, LABEL_DROP) ||
+               !strcmp(target_name, LABEL_QUEUE) ||
+               !strcmp(target_name, LABEL_RETURN))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean is_jump(struct connman_iptables_entry *e)
+{
+       struct xt_entry_target *target;
+
+       target = ipt_get_target(e->entry);
+
+       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+               struct xt_standard_target *t;
+
+               t = (struct xt_standard_target *)target;
+
+               switch (t->verdict) {
+               case XT_RETURN:
+               case -NF_ACCEPT - 1:
+               case -NF_DROP - 1:
+               case -NF_QUEUE - 1:
+               case -NF_STOP - 1:
+                       return false;
+
+               default:
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static gboolean is_chain(struct connman_iptables *table,
+                               struct connman_iptables_entry *e)
+{
+       struct ipt_entry *entry;
+       struct xt_entry_target *target;
+
+       entry = e->entry;
+       if (e->builtin >= 0)
+               return TRUE;
+
+       target = ipt_get_target(entry);
+       if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
+               return TRUE;
+
+       return FALSE;
+}
+
+static GList *find_chain_head(struct connman_iptables *table,
+                               char *chain_name)
+{
+       GList *list;
+       struct connman_iptables_entry *head;
+       struct ipt_entry *entry;
+       struct xt_entry_target *target;
+       int builtin;
+
+       for (list = table->entries; list; list = list->next) {
+               head = list->data;
+               entry = head->entry;
+
+               /* Buit-in chain */
+               builtin = head->builtin;
+               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
+                       break;
+
+               /* User defined chain */
+               target = ipt_get_target(entry);
+               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
+                   !strcmp((char *)target->data, chain_name))
+                       break;
+       }
+
+       return list;
+}
+
+static GList *find_chain_tail(struct connman_iptables *table,
+                               char *chain_name)
+{
+       GList *chain_head, *list;
+       struct connman_iptables_entry *tail;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       /* Then we look for the next chain */
+       for (list = chain_head->next; list; list = list->next) {
+               tail = list->data;
+
+               if (is_chain(table, tail))
+                       return list;
+       }
+
+       /* Nothing found, we return the table end */
+       return g_list_last(table->entries);
+}
+
+static void update_offsets(struct connman_iptables *table)
+{
+       GList *list, *prev;
+       struct connman_iptables_entry *entry, *prev_entry;
+
+       for (list = table->entries; list; list = list->next) {
+               entry = list->data;
+
+               if (list == table->entries) {
+                       entry->offset = 0;
+
+                       continue;
+               }
+
+               prev = list->prev;
+               prev_entry = prev->data;
+
+               entry->offset = prev_entry->offset +
+                                       prev_entry->entry->next_offset;
+       }
+}
+
+static void update_targets_reference(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry_before,
+                               struct connman_iptables_entry *modified_entry,
+                               gboolean is_removing)
+{
+       struct connman_iptables_entry *tmp;
+       struct xt_standard_target *t;
+       GList *list;
+       int offset;
+
+       offset = modified_entry->entry->next_offset;
+
+       for (list = table->entries; list; list = list->next) {
+               tmp = list->data;
+
+               if (!is_jump(tmp))
+                       continue;
+
+               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+
+               if (is_removing == TRUE) {
+                       if (t->verdict >= entry_before->offset)
+                               t->verdict -= offset;
+               } else {
+                       if (t->verdict > entry_before->offset)
+                               t->verdict += offset;
+               }
+       }
+}
+
+static int connman_add_entry(struct connman_iptables *table,
+                               struct ipt_entry *entry, GList *before,
+                                       int builtin)
+{
+       struct connman_iptables_entry *e, *entry_before;
+
+       if (table == NULL)
+               return -1;
+
+       e = g_try_malloc0(sizeof(struct connman_iptables_entry));
+       if (e == NULL)
+               return -1;
+
+       e->entry = entry;
+       e->builtin = builtin;
+
+       table->entries = g_list_insert_before(table->entries, before, e);
+       table->num_entries++;
+       table->size += entry->next_offset;
+
+       if (before == NULL) {
+               e->offset = table->size - entry->next_offset;
+
+               return 0;
+       }
+
+       entry_before = before->data;
+
+       /*
+        * We've just appended/insterted a new entry. All references
+        * should be bumped accordingly.
+        */
+       update_targets_reference(table, entry_before, e, FALSE);
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int remove_table_entry(struct connman_iptables *table,
+                                       struct connman_iptables_entry *entry)
+{
+       int removed = 0;
+
+       table->num_entries--;
+       table->size -= entry->entry->next_offset;
+       removed = entry->entry->next_offset;
+
+       g_free(entry->entry);
+
+       table->entries = g_list_remove(table->entries, entry);
+
+       return removed;
+}
+
+static int connman_iptables_flush_chain(struct connman_iptables *table,
+                                               char *name)
+{
+       GList *chain_head, *chain_tail, *list, *next;
+       struct connman_iptables_entry *entry;
+       int builtin, removed = 0;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       if (list == chain_tail->prev)
+               return 0;
+
+       while (list != chain_tail->prev) {
+               entry = list->data;
+               next = g_list_next(list);
+
+               removed += remove_table_entry(table, entry);
+
+               list = next;
+       }
+
+       if (builtin >= 0) {
+               struct connman_iptables_entry *e;
+
+               entry = list->data;
+
+               entry->builtin = builtin;
+
+               table->underflow[builtin] -= removed;
+
+               for (list = chain_tail; list; list = list->next) {
+                       e = list->data;
+
+                       builtin = e->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int connman_iptables_delete_chain(struct connman_iptables *table,
+                                               char *name)
+{
+       GList *chain_head, *chain_tail;
+       struct connman_iptables_entry *entry;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+
+       /* We cannot remove builtin chain */
+       if (entry->builtin >= 0)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       /* Chain must be flushed */
+       if (chain_head->next != chain_tail->prev)
+               return -EINVAL;
+
+       remove_table_entry(table, entry);
+
+       entry = chain_tail->prev->data;
+       remove_table_entry(table, entry);
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int connman_iptables_add_chain(struct connman_iptables *table,
+                                       char *name)
+{
+       GList *last;
+       struct ipt_entry *entry_head;
+       struct ipt_entry *entry_return;
+       struct error_target *error;
+       struct ipt_standard_target *standard;
+       u_int16_t entry_head_size, entry_return_size;
+
+       last = g_list_last(table->entries);
+
+       /*
+        * An empty chain is composed of:
+        * - A head entry, with no match and an error target.
+        *   The error target data is the chain name.
+        * - A tail entry, with no match and a standard target.
+        *   The standard target verdict is XT_RETURN (return to the
+        *   caller).
+        */
+
+       /* head entry */
+       entry_head_size = sizeof(struct ipt_entry) +
+                               sizeof(struct error_target);
+       entry_head = g_try_malloc0(entry_head_size);
+       if (entry_head == NULL)
+               goto err_head;
+
+       memset(entry_head, 0, entry_head_size);
+
+       entry_head->target_offset = sizeof(struct ipt_entry);
+       entry_head->next_offset = entry_head_size;
+
+       error = (struct error_target *) entry_head->elems;
+       strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
+       error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
+       strcpy(error->error, name);
+
+       if (connman_add_entry(table, entry_head, last, -1) < 0)
+               goto err_head;
+
+       /* tail entry */
+       entry_return_size = sizeof(struct ipt_entry) +
+                               sizeof(struct ipt_standard_target);
+       entry_return = g_try_malloc0(entry_return_size);
+       if (entry_return == NULL)
+               goto err;
+
+       memset(entry_return, 0, entry_return_size);
+
+       entry_return->target_offset = sizeof(struct ipt_entry);
+       entry_return->next_offset = entry_return_size;
+
+       standard = (struct ipt_standard_target *) entry_return->elems;
+       standard->target.u.user.target_size =
+                               ALIGN(sizeof(struct ipt_standard_target));
+       standard->verdict = XT_RETURN;
+
+       if (connman_add_entry(table, entry_return, last, -1) < 0)
+               goto err;
+
+       return 0;
+
+err:
+       g_free(entry_return);
+err_head:
+       g_free(entry_head);
+
+       return -ENOMEM;
+}
+
+static struct ipt_entry *new_rule(struct ipt_ip *ip,
+                       char *target_name, struct xtables_target *xt_t,
+                       struct xtables_rule_match *xt_rm)
+{
+       struct xtables_rule_match *tmp_xt_rm;
+       struct ipt_entry *new_entry;
+       size_t match_size, target_size;
+
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
+               match_size += tmp_xt_rm->match->m->u.match_size;
+
+       if (xt_t)
+               target_size = ALIGN(xt_t->t->u.target_size);
+       else
+               target_size = ALIGN(sizeof(struct xt_standard_target));
+
+       new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
+                                                               match_size);
+       if (new_entry == NULL)
+               return NULL;
+
+       memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
+
+       new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
+       new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
+                                                               match_size;
+
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next) {
+               memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+                                       tmp_xt_rm->match->m->u.match_size);
+               match_size += tmp_xt_rm->match->m->u.match_size;
+       }
+
+       if (xt_t) {
+               struct xt_entry_target *entry_target;
+
+               entry_target = ipt_get_target(new_entry);
+               memcpy(entry_target, xt_t->t, target_size);
+       }
+
+       return new_entry;
+}
+
+static void update_hooks(struct connman_iptables *table, GList *chain_head,
+                               struct ipt_entry *entry)
+{
+       GList *list;
+       struct connman_iptables_entry *head, *e;
+       int builtin;
+
+       if (chain_head == NULL)
+               return;
+
+       head = chain_head->data;
+
+       builtin = head->builtin;
+       if (builtin < 0)
+               return;
+
+       table->underflow[builtin] += entry->next_offset;
+
+       for (list = chain_head->next; list; list = list->next) {
+               e = list->data;
+
+               builtin = e->builtin;
+               if (builtin < 0)
+                       continue;
+
+               table->hook_entry[builtin] += entry->next_offset;
+               table->underflow[builtin] += entry->next_offset;
+       }
+}
+
+static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               int *builtin, struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head;
+       struct ipt_entry *new_entry;
+       struct connman_iptables_entry *head;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return NULL;
+
+       new_entry = new_rule(ip, target_name, xt_t, xt_rm);
+       if (new_entry == NULL)
+               return NULL;
+
+       update_hooks(table, chain_head, new_entry);
+
+       /*
+        * If the chain is builtin, and does not have any rule,
+        * then the one that we're inserting is becoming the head
+        * and thus needs the builtin flag.
+        */
+       head = chain_head->data;
+       if (head->builtin < 0)
+               *builtin = -1;
+       else if (chain_head == chain_tail->prev) {
+               *builtin = head->builtin;
+               head->builtin = -1;
+       }
+
+       return new_entry;
+}
+
+static int connman_iptables_append_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = connman_add_entry(table, new_entry, chain_tail->prev, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static int connman_iptables_insert_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_head;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       if (builtin == -1)
+               chain_head = chain_head->next;
+
+       ret = connman_add_entry(table, new_entry, chain_head, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
+                                       struct ipt_entry *i_e2)
+{
+       if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
+               return FALSE;
+
+       if (i_e1->target_offset != i_e2->target_offset)
+               return FALSE;
+
+       if (i_e1->next_offset != i_e2->next_offset)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
+                                       struct xt_entry_target *xt_e_t2)
+{
+       if (xt_e_t1 == NULL || xt_e_t2 == NULL)
+               return FALSE;
+
+       if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+               struct xt_standard_target *xt_s_t1;
+               struct xt_standard_target *xt_s_t2;
+
+               xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
+               xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
+
+               if (xt_s_t1->verdict != xt_s_t2->verdict)
+                       return FALSE;
+       } else {
+               if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
+                       return FALSE;
+
+               if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
+                               struct xt_entry_match *xt_e_m2)
+{
+       if (xt_e_m1 == NULL || xt_e_m2 == NULL)
+               return FALSE;
+
+       if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
+               return FALSE;
+
+       if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
+               return FALSE;
+
+       if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static GList *find_existing_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head, *list;
+       struct xt_entry_target *xt_e_t = NULL;
+       struct xt_entry_match *xt_e_m = NULL;
+       struct connman_iptables_entry *entry;
+       struct ipt_entry *entry_test;
+       int builtin;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return NULL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return NULL;
+
+       if (!xt_t && !xt_m)
+               return NULL;
+
+       entry_test = new_rule(ip, target_name, xt_t, xt_rm);
+       if (entry_test == NULL)
+               return NULL;
+
+       if (xt_t != NULL)
+               xt_e_t = ipt_get_target(entry_test);
+       if (xt_m != NULL)
+               xt_e_m = (struct xt_entry_match *)entry_test->elems;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       for (; list != chain_tail->prev; list = list->next) {
+               struct connman_iptables_entry *tmp;
+               struct ipt_entry *tmp_e;
+
+               tmp = list->data;
+               tmp_e = tmp->entry;
+
+               if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
+                       continue;
+
+               if (xt_t != NULL) {
+                       struct xt_entry_target *tmp_xt_e_t;
+
+                       tmp_xt_e_t = ipt_get_target(tmp_e);
+
+                       if (!is_same_target(tmp_xt_e_t, xt_e_t))
+                               continue;
+               }
+
+               if (xt_m != NULL) {
+                       struct xt_entry_match *tmp_xt_e_m;
+
+                       tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+
+                       if (!is_same_match(tmp_xt_e_m, xt_e_m))
+                               continue;
+               }
+
+               break;
+       }
+
+       g_free(entry_test);
+
+       if (list != chain_tail->prev)
+               return list;
+
+       return NULL;
+}
+
+static int connman_iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_tail, *list;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       list = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (list == NULL)
+               return -EINVAL;
+
+       entry = list->data;
+
+       if (entry == NULL)
+               return -EINVAL;
+
+       builtin = entry->builtin;
+
+       /* We have deleted a rule,
+        * all references should be bumped accordingly */
+       if (list->next != NULL)
+               update_targets_reference(table, list->next->data,
+                                               list->data, TRUE);
+
+       removed += remove_table_entry(table, entry);
+
+       if (builtin >= 0) {
+               list = list->next;
+               if (list) {
+                       entry = list->data;
+                       entry->builtin = builtin;
+               }
+
+               table->underflow[builtin] -= removed;
+               for (list = chain_tail; list; list = list->next) {
+                       entry = list->data;
+
+                       builtin = entry->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int connman_iptables_compare_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct connman_iptables_entry *entry;
+       GList *found;
+
+       found = find_existing_rule(table, ip, chain_name, target_name,
+                                                       xt_t, xt_m, xt_rm);
+       if (found == NULL)
+               return -EINVAL;
+
+       entry = found->data;
+       if (entry == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int connman_iptables_change_policy(struct connman_iptables *table,
+                                               char *chain_name, char *policy)
+{
+       GList *chain_head;
+       struct connman_iptables_entry *entry;
+       struct xt_entry_target *target;
+       struct xt_standard_target *t;
+       int verdict;
+
+       verdict = target_to_verdict(policy);
+       if (verdict == 0)
+               return -EINVAL;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       if (entry->builtin < 0)
+               return -EINVAL;
+
+       target = ipt_get_target(entry->entry);
+
+       t = (struct xt_standard_target *)target;
+       t->verdict = verdict;
+
+       return 0;
+}
+
+static struct ipt_replace *connman_iptables_blob(struct connman_iptables *table)
+{
+       struct ipt_replace *r;
+       GList *list;
+       struct connman_iptables_entry *e;
+       unsigned char *entry_index;
+
+       r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
+       if (r == NULL)
+               return NULL;
+
+       memset(r, 0, sizeof(*r) + table->size);
+
+       r->counters = g_try_malloc0(sizeof(struct xt_counters)
+                               * table->old_entries);
+       if (r->counters == NULL) {
+               g_free(r);
+               return NULL;
+       }
+
+       strcpy(r->name, table->info->name);
+       r->num_entries = table->num_entries;
+       r->size = table->size;
+
+       r->num_counters = table->old_entries;
+       r->valid_hooks  = table->info->valid_hooks;
+
+       memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
+       memcpy(r->underflow, table->underflow, sizeof(table->underflow));
+
+       entry_index = (unsigned char *)r->entries;
+       for (list = table->entries; list; list = list->next) {
+               e = list->data;
+
+               memcpy(entry_index, e->entry, e->entry->next_offset);
+               entry_index += e->entry->next_offset;
+       }
+
+       return r;
+}
+
+static void dump_target(struct connman_iptables *table,
+                               struct ipt_entry *entry)
+
+{
+       struct xtables_target *xt_t;
+       struct xt_entry_target *target;
+
+       target = ipt_get_target(entry);
+
+       if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+               struct xt_standard_target *t;
+
+               t = (struct xt_standard_target *)target;
+
+               switch (t->verdict) {
+               case XT_RETURN:
+                       printf("\ttarget RETURN\n");
+                       break;
+
+               case -NF_ACCEPT - 1:
+                       printf("\ttarget ACCEPT\n");
+                       break;
+
+               case -NF_DROP - 1:
+                       printf("\ttarget DROP\n");
+                       break;
+
+               case -NF_QUEUE - 1:
+                       printf("\ttarget QUEUE\n");
+                       break;
+
+               case -NF_STOP - 1:
+                       printf("\ttarget STOP\n");
+                       break;
+
+               default:
+                       printf("\tJUMP @%p (0x%x)\n",
+                               (char*)table->blob_entries->entrytable +
+                               t->verdict, t->verdict);
+                       break;
+               }
+
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+
+               if(xt_t->print != NULL)
+                       xt_t->print(NULL, target, 1);
+       } else {
+               xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
+               if (xt_t == NULL) {
+                       printf("\ttarget %s\n", target->u.user.name);
+                       return;
+               }
+
+               if(xt_t->print != NULL) {
+                       printf("\ttarget ");
+                       xt_t->print(NULL, target, 1);
+                       printf("\n");
+               }
+       }
+}
+
+static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
+{
+       struct xtables_match *xt_m;
+       struct xt_entry_match *match;
+
+       if (entry->elems == (unsigned char *)entry + entry->target_offset)
+               return;
+
+       match = (struct xt_entry_match *) entry->elems;
+
+       if (!strlen(match->u.user.name))
+               return;
+
+       xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
+       if (xt_m == NULL)
+               goto out;
+
+       if(xt_m->print != NULL) {
+               printf("\tmatch ");
+               xt_m->print(NULL, match, 1);
+               printf("\n");
+
+               return;
+       }
+
+out:
+       printf("\tmatch %s\n", match->u.user.name);
+
+}
+
+static int connman_iptables_dump_entry(struct ipt_entry *entry,
+                                       struct connman_iptables *table)
+{
+       struct xt_entry_target *target;
+       unsigned int offset;
+       int builtin;
+
+       offset = (char *)entry - (char *)table->blob_entries->entrytable;
+       target = ipt_get_target(entry);
+       builtin = is_hook_entry(table, entry);
+
+       if (entry_to_offset(table, entry) + entry->next_offset ==
+                                       table->blob_entries->size) {
+               printf("End of CHAIN 0x%x\n", offset);
+               return 0;
+       }
+
+       if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
+               printf("USER CHAIN (%s) %p  match %p  target %p  size %d\n",
+                       target->data, entry, entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+
+               return 0;
+       } else if (builtin >= 0) {
+               printf("CHAIN (%s) %p  match %p  target %p  size %d\n",
+                       hooknames[builtin], entry, entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+       } else {
+               printf("RULE %p  match %p  target %p  size %d\n", entry,
+                       entry->elems,
+                       (char *)entry + entry->target_offset,
+                               entry->next_offset);
+       }
+
+       dump_match(table, entry);
+       dump_target(table, entry);
+
+       return 0;
+}
+
+static void connman_iptables_dump_hook(struct connman_iptables *table)
+{
+       int i;
+       printf("hooks: \n");
+       for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+               if ((table->info->valid_hooks & (1 << i)))
+                       printf("%s entry %p underflow %p (%#x)\n",
+                               hooknames[i],
+                               table->blob_entries->entrytable +
+                                               table->info->hook_entry[i],
+                               table->blob_entries->entrytable +
+                                               table->info->underflow[i],
+                                       table->info->underflow[i]);
+       }
+}
+
+static void connman_iptables_dump(struct connman_iptables *table)
+{
+       printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+               table->info->name,
+               table->info->valid_hooks, table->info->num_entries,
+               table->info->size);
+
+       connman_iptables_dump_hook(table);
+
+       ENTRY_ITERATE(table->blob_entries->entrytable,
+                       table->blob_entries->size,
+                       connman_iptables_dump_entry, table);
+
+}
+
+static int connman_iptables_get_entries(struct connman_iptables *table)
+{
+       socklen_t entry_size;
+
+       entry_size = sizeof(struct ipt_get_entries) + table->info->size;
+
+       return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
+                               table->blob_entries, &entry_size);
+}
+
+static int connman_iptables_replace(struct connman_iptables *table,
+                                       struct ipt_replace *r)
+{
+       return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
+                        sizeof(*r) + r->size);
+}
+
+static void connman_iptables_cleanup(struct connman_iptables *table)
+{
+       GList *list;
+       struct connman_iptables_entry *entry;
+
+       close(table->ipt_sock);
+
+       for (list = table->entries; list; list = list->next) {
+               entry = list->data;
+
+               g_free(entry->entry);
+       }
+
+       g_free(table->info);
+       g_free(table->blob_entries);
+       g_free(table);
+
+       xtables_free_opts(1);
+}
+
+static int connman_iptables_commit(struct connman_iptables *table)
+{
+       struct ipt_replace *repl;
+
+       repl = connman_iptables_blob(table);
+
+       return connman_iptables_replace(table, repl);
+}
+
+static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
+{
+       struct ipt_entry *new_entry;
+       int builtin;
+
+       new_entry = g_try_malloc0(entry->next_offset);
+       if (new_entry == NULL)
+               return -ENOMEM;
+
+       memcpy(new_entry, entry, entry->next_offset);
+
+       builtin = is_hook_entry(table, entry);
+
+       return connman_add_entry(table, new_entry, NULL, builtin);
+}
+
+static struct connman_iptables *connman_iptables_init(const char *table_name)
+{
+       struct connman_iptables *table = NULL;
+       char *module = NULL;
+       socklen_t s;
+
+       if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
+               goto err;
+
+       module = g_strconcat("iptable_", table_name, NULL);
+       if (module == NULL)
+               goto err;
+
+       if (xtables_insmod(module, NULL, TRUE) != 0)
+               goto err;
+
+       g_free(module);
+       module = NULL;
+
+       table =  g_try_new0(struct connman_iptables, 1);
+       if (table == NULL)
+               return NULL;
+
+       table->info =  g_try_new0(struct ipt_getinfo, 1);
+       if (table->info == NULL)
+               goto err;
+
+       table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+       if (table->ipt_sock < 0)
+               goto err;
+
+       s = sizeof(*table->info);
+       strcpy(table->info->name, table_name);
+       if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
+                                               table->info, &s) < 0)
+               goto err;
+
+       table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
+                                               table->info->size);
+       if (table->blob_entries == NULL)
+               goto err;
+
+       strcpy(table->blob_entries->name, table_name);
+       table->blob_entries->size = table->info->size;
+
+       if (connman_iptables_get_entries(table) < 0)
+               goto err;
+
+       table->num_entries = 0;
+       table->old_entries = table->info->num_entries;
+       table->size = 0;
+
+       memcpy(table->underflow, table->info->underflow,
+                               sizeof(table->info->underflow));
+       memcpy(table->hook_entry, table->info->hook_entry,
+                               sizeof(table->info->hook_entry));
+
+       ENTRY_ITERATE(table->blob_entries->entrytable,
+                       table->blob_entries->size,
+                               add_entry, table);
+
+       return table;
+
+err:
+       g_free(module);
+
+       connman_iptables_cleanup(table);
+
+       return NULL;
+}
+
+static struct option connman_iptables_opts[] = {
+       {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "compare",       .has_arg = 1, .val = 'C'},
+       {.name = "delete",        .has_arg = 1, .val = 'D'},
+       {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
+       {.name = "insert",        .has_arg = 1, .val = 'I'},
+       {.name = "list",          .has_arg = 2, .val = 'L'},
+       {.name = "new-chain",     .has_arg = 1, .val = 'N'},
+       {.name = "policy",        .has_arg = 1, .val = 'P'},
+       {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
+       {.name = "destination",   .has_arg = 1, .val = 'd'},
+       {.name = "in-interface",  .has_arg = 1, .val = 'i'},
+       {.name = "jump",          .has_arg = 1, .val = 'j'},
+       {.name = "match",         .has_arg = 1, .val = 'm'},
+       {.name = "out-interface", .has_arg = 1, .val = 'o'},
+       {.name = "source",        .has_arg = 1, .val = 's'},
+       {.name = "table",         .has_arg = 1, .val = 't'},
+       {NULL},
+};
+
+struct xtables_globals connman_iptables_globals = {
+       .option_offset = 0,
+       .opts = connman_iptables_opts,
+       .orig_opts = connman_iptables_opts,
+};
+
+static struct xtables_target *prepare_target(struct connman_iptables *table,
+                                                       char *target_name)
+{
+       struct xtables_target *xt_t = NULL;
+       gboolean is_builtin, is_user_defined;
+       GList *chain_head = NULL;
+       size_t target_size;
+
+       is_builtin = FALSE;
+       is_user_defined = FALSE;
+
+       if (is_builtin_target(target_name))
+               is_builtin = TRUE;
+       else {
+               chain_head = find_chain_head(table, target_name);
+               if (chain_head != NULL && chain_head->next != NULL)
+                       is_user_defined = TRUE;
+       }
+
+       if (is_builtin || is_user_defined)
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+       else
+               xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+       if (xt_t == NULL)
+               return NULL;
+
+       target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+       xt_t->t = g_try_malloc0(target_size);
+       if (xt_t->t == NULL)
+               return NULL;
+
+       xt_t->t->u.target_size = target_size;
+
+       if (is_builtin || is_user_defined) {
+               struct xt_standard_target *target;
+
+               target = (struct xt_standard_target *)(xt_t->t);
+               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+
+               if (is_builtin == TRUE)
+                       target->verdict = target_to_verdict(target_name);
+               else if (is_user_defined == TRUE) {
+                       struct connman_iptables_entry *target_rule;
+
+                       if (chain_head == NULL) {
+                               g_free(xt_t->t);
+                               return NULL;
+                       }
+
+                       target_rule = chain_head->next->data;
+                       target->verdict = target_rule->offset;
+               }
+       } else {
+               strcpy(xt_t->t->u.user.name, target_name);
+               xt_t->t->u.user.revision = xt_t->revision;
+               if (xt_t->init != NULL)
+                       xt_t->init(xt_t->t);
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_t->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+                               connman_iptables_globals.orig_opts,
+
+                               connman_iptables_globals.opts,
+                               xt_t->x6_options,
+                               &xt_t->option_offset);
+       else
+#endif
+               connman_iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_t->extra_opts,
+                               &xt_t->option_offset);
+
+       if (connman_iptables_globals.opts == NULL) {
+               g_free(xt_t->t);
+               xt_t = NULL;
+       }
+
+       return xt_t;
+}
+
+static struct xtables_match *prepare_matches(struct connman_iptables *table,
+                       struct xtables_rule_match **xt_rm, char *match_name)
+{
+       struct xtables_match *xt_m;
+       size_t match_size;
+
+       if (match_name == NULL)
+               return NULL;
+
+       xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
+       match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+       xt_m->m = g_try_malloc0(match_size);
+       if (xt_m->m == NULL)
+               return NULL;
+
+       xt_m->m->u.match_size = match_size;
+       strcpy(xt_m->m->u.user.name, xt_m->name);
+       xt_m->m->u.user.revision = xt_m->revision;
+
+       if (xt_m->init != NULL)
+               xt_m->init(xt_m->m);
+
+       if (xt_m == xt_m->next)
+               goto done;
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_m->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+                               connman_iptables_globals.orig_opts,
+                               connman_iptables_globals.opts,
+                               xt_m->x6_options,
+                               &xt_m->option_offset);
+       else
+#endif
+               connman_iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_m->extra_opts,
+                               &xt_m->option_offset);
+
+       if (connman_iptables_globals.opts == NULL) {
+               g_free(xt_m->m);
+               xt_m = NULL;
+       }
+
+done:
+       return xt_m;
+}
+
+static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
+{
+       char **tokens;
+       uint32_t prefixlength;
+       uint32_t tmp;
+       int err;
+
+       tokens = g_strsplit(str, "/", 2);
+       if (tokens == NULL)
+               return -1;
+
+       if (!inet_pton(AF_INET, tokens[0], ip)) {
+               err = -1;
+               goto out;
+       }
+
+       if (tokens[1] != NULL) {
+               prefixlength = strtol(tokens[1], NULL, 10);
+               if (prefixlength > 31) {
+                       err = -1;
+                       goto out;
+               }
+
+               tmp = ~(0xffffffff >> prefixlength);
+       } else {
+               tmp = 0xffffffff;
+       }
+
+       mask->s_addr = htonl(tmp);
+       ip->s_addr = ip->s_addr & mask->s_addr;
+       err = 0;
+out:
+       g_strfreev(tokens);
+
+       return err;
+}
+
+int main(int argc, char *argv[])
+{
+       struct connman_iptables *table;
+       struct xtables_rule_match *xt_rm, *tmp_xt_rm;
+       struct xtables_match *xt_m, *xt_m_t;
+       struct xtables_target *xt_t;
+       struct ipt_ip ip;
+       char *table_name, *chain, *new_chain, *match_name, *target_name;
+       char *delete_chain, *flush_chain, *policy;
+       int c, in_len, out_len;
+       gboolean dump, invert, delete, insert, delete_rule, compare_rule;
+
+       xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
+
+       dump = FALSE;
+       invert = FALSE;
+       delete = FALSE;
+       insert = FALSE;
+       delete_rule = FALSE;
+       compare_rule = FALSE;
+       chain = new_chain = match_name = target_name = NULL;
+       delete_chain = flush_chain = policy = NULL;
+       memset(&ip, 0, sizeof(struct ipt_ip));
+       table = NULL;
+       xt_rm = NULL;
+       xt_m = NULL;
+       xt_t = NULL;
+
+       /* extension's options will generate false-positives errors */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+                               "-A:C:D:F:I:L::N:P:X:d:i:j:m:o:s:t:",
+                               connman_iptables_globals.opts, NULL)) != -1) {
+               switch (c) {
+               case 'A':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       break;
+
+               case 'C':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       compare_rule = TRUE;
+                       break;
+
+               case 'D':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       delete_rule = TRUE;
+                       break;
+
+               case 'F':
+                       flush_chain = optarg;
+                       break;
+
+               case 'I':
+                       /* It is either -A, -C, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       insert = TRUE;
+                       break;
+
+               case 'L':
+                       dump = true;
+                       break;
+
+               case 'N':
+                       new_chain = optarg;
+                       break;
+
+               case 'P':
+                       chain = optarg;
+                       if (optind < argc)
+                               policy = argv[optind++];
+                       else
+                               goto out;
+
+                       break;
+
+               case 'X':
+                       delete = true;
+                       delete_chain = optarg;
+                       break;
+
+               case 'd':
+                       if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
+                               break;
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_DSTIP;
+
+
+                       break;
+
+               case 'i':
+                       in_len = strlen(optarg);
+
+                       if (in_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.iniface, optarg);
+                       memset(ip.iniface_mask, 0xff, in_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_IN;
+
+                       break;
+
+               case 'j':
+                       target_name = optarg;
+                       xt_t = prepare_target(table, target_name);
+                       if (xt_t == NULL)
+                               goto out;
+
+                       break;
+
+               case 'm':
+                       match_name = optarg;
+                       xt_m = prepare_matches(table, &xt_rm, match_name);
+                       if (xt_m == NULL)
+                               goto out;
+
+                       break;
+
+               case 'o':
+                       out_len = strlen(optarg);
+
+                       if (out_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.outiface, optarg);
+                       memset(ip.outiface_mask, 0xff, out_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_OUT;
+
+                       break;
+
+               case 's':
+                       if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
+                               break;
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_SRCIP;
+
+                       break;
+
+               case 't':
+                       table_name = optarg;
+
+                       table = connman_iptables_init(table_name);
+                       if (table == NULL)
+                               return -1;
+
+                       break;
+
+               case 1:
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       printf("Consecutive ! not allowed\n");
+
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+
+                       printf("Invalid option\n");
+
+                       return -1;
+
+               default:
+#if XTABLES_VERSION_CODE > 5
+                       if (xt_t != NULL && (xt_t->x6_parse != NULL ||
+                                               xt_t->parse != NULL) &&
+                                       (c >= (int) xt_t->option_offset &&
+                                       c < (int) xt_t->option_offset +
+                                       XT_OPTION_OFFSET_SCALE)) {
+                               xtables_option_tpcall(c, argv,
+                                                       invert, xt_t, NULL);
+
+                               break;
+                       }
+
+                       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                               xt_m_t = tmp_xt_rm->match;
+
+                               if (tmp_xt_rm->completed ||
+                                               (xt_m_t->x6_parse == NULL &&
+                                                xt_m_t->parse == NULL))
+                                       continue;
+
+                               if (c < (int) xt_m_t->option_offset ||
+                                       c >= (int) xt_m_t->option_offset
+                                       + XT_OPTION_OFFSET_SCALE)
+                                       continue;
+
+                               xtables_option_mpcall(c, argv,
+                                                       invert, xt_m_t, NULL);
+
+                               break;
+                       }
+#else
+                       if (xt_t == NULL || xt_t->parse == NULL ||
+                               !xt_t->parse(c - xt_t->option_offset,
+                               argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
+
+                               for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                                       xt_m_t = tmp_xt_rm->match;
+
+                                       if (tmp_xt_rm->completed ||
+                                                       xt_m_t->parse == NULL)
+                                               continue;
+
+                                       if (xt_m->parse(c - xt_m->option_offset,
+                                               argv, invert, &xt_m->mflags,
+                                               NULL, &xt_m->m))
+                                               break;
+                               }
+                       }
+#endif
+                       break;
+               }
+
+               invert = FALSE;
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               xtables_option_mfcall(tmp_xt_rm->match);
+
+       if (xt_t != NULL)
+               xtables_option_tfcall(xt_t);
+#else
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               if (tmp_xt_rm->match->final_check != NULL)
+                       tmp_xt_rm->match->final_check(
+                                       tmp_xt_rm->match->mflags);
+
+       if (xt_t != NULL && xt_t->final_check != NULL)
+               xt_t->final_check(xt_t->tflags);
+#endif
+
+       if (table == NULL) {
+               table_name = "filter";
+
+               table = connman_iptables_init(table_name);
+               if (table == NULL)
+                       return -1;
+       }
+
+       if (delete) {
+               if (delete_chain == NULL)
+                       goto out;
+
+               printf("Delete chain %s\n", delete_chain);
+
+               connman_iptables_delete_chain(table, delete_chain);
+
+               goto commit;
+       }
+
+       if (flush_chain) {
+               printf("Flush chain %s\n", flush_chain);
+
+               connman_iptables_flush_chain(table, flush_chain);
+
+               goto commit;
+       }
+
+       if (dump) {
+               connman_iptables_dump(table);
+
+               return 0;
+       }
+
+       if (chain && new_chain)
+               return -1;
+
+       if (new_chain) {
+               printf("New chain %s\n", new_chain);
+
+               connman_iptables_add_chain(table, new_chain);
+
+               goto commit;
+       }
+
+       if (chain) {
+               if (policy != NULL) {
+                       printf("Changing policy of %s to %s\n", chain, policy);
+
+                       connman_iptables_change_policy(table, chain, policy);
+
+                       goto commit;
+               }
+
+               if (compare_rule == TRUE) {
+                       int ret;
+
+                       ret = connman_iptables_compare_rule(table, &ip,
+                               chain, target_name, xt_t, xt_m, xt_rm);
+
+                       if (ret == 0)
+                               printf("Rule exists.\n");
+                       else
+                               printf("Rule does not exist.\n");
+
+                       goto out;
+               }
+
+               if (delete_rule == TRUE) {
+                       printf("Deleting %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_delete_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+
+                       goto commit;
+               }
+
+               if (insert == TRUE) {
+                       printf("Inserting %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_insert_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+               } else {
+                       printf("Appending %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_append_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+               }
+       }
+
+commit:
+
+       connman_iptables_commit(table);
+
+out:
+       connman_iptables_cleanup(table);
+
+       if (xt_t)
+               g_free(xt_t->t);
+
+       if (xt_m)
+               g_free(xt_m->m);
+
+       return 0;
+}
diff --git a/tools/polkit-test.c b/tools/polkit-test.c
new file mode 100644 (file)
index 0000000..45e6eb8
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *
+ *  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 <errno.h>
+
+#include <string.h>
+#include <signal.h>
+
+#include <dbus/dbus.h>
+
+static volatile sig_atomic_t __io_terminate = 0;
+
+static void sig_term(int sig)
+{
+       __io_terminate = 1;
+}
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+                                       const char *key, const char *str)
+{
+       DBusMessageIter dict, entry, value;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       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_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_STRING_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       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);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter)
+{
+       const char *busname = dbus_bus_get_unique_name(conn);
+       const char *kind = "system-bus-name";
+       const char *action = "org.freedesktop.policykit.exec";
+       const char *cancel = "";
+       dbus_uint32_t flags = 0x00000001;
+       DBusMessageIter subject;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                                       NULL, &subject);
+       dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+       add_dict_with_string_value(&subject, "name", busname);
+       dbus_message_iter_close_container(iter, &subject);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+       add_empty_string_dict(iter);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static void print_arguments(DBusMessageIter *iter)
+{
+       DBusMessageIter result;
+       dbus_bool_t authorized, challenge;
+
+       dbus_message_iter_recurse(iter, &result);
+
+       dbus_message_iter_get_basic(&result, &authorized);
+       dbus_message_iter_get_basic(&result, &challenge);
+
+       printf("Authorized %d (Challenge %d)\n", authorized, challenge);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+static int check_authorization(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusMessageIter iter;
+       DBusError err;
+
+       msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+                               AUTHORITY_INTF, "CheckAuthorization");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return -ENOMEM;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+       add_arguments(conn, &iter);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't check authorization\n");
+               return -EIO;
+       }
+
+       if (dbus_message_has_signature(reply, "(bba{ss})") == TRUE) {
+               dbus_message_iter_init(reply, &iter);
+               print_arguments(&iter);
+       }
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       struct sigaction sa;
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus");
+               return 1;
+       }
+
+       check_authorization(conn);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+#if 0
+       while (!__io_terminate) {
+               if (dbus_connection_read_write_dispatch(conn, 500) == FALSE)
+                       break;
+       }
+#endif
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/tools/private-network-test.c b/tools/private-network-test.c
new file mode 100644 (file)
index 0000000..da0c88a
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011 ProFUSION embedded systems
+ *
+ *  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
+ *
+ */
+
+#define CONNMAN_SERVICE "net.connman"
+
+#define MANAGER_PATH   "/"
+#define MANAGER_INTERFACE CONNMAN_SERVICE ".Manager"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <gdbus.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+static int release_private_network(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusError error;
+
+       msg = dbus_message_new_method_call(CONNMAN_SERVICE, MANAGER_PATH,
+                               MANAGER_INTERFACE, "ReleasePrivateNetwork");
+
+       dbus_error_init(&error);
+
+       printf("Releasing private-network...\n");
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1,
+                                                                       &error);
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error)) {
+                       fprintf(stderr, "1. %s\n", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       fprintf(stderr, "Release() failed");
+               }
+
+               return -1;
+       }
+
+       return 0;
+}
+
+static void request_private_network(DBusConnection *conn, int *out_fd,
+                                       char **out_server_ip,
+                                       char **out_peer_ip,
+                                       char **out_primary_dns,
+                                       char **out_secondary_dns)
+{
+       DBusMessageIter array, dict, entry;
+       DBusMessage *msg, *reply;
+       DBusError error;
+
+       msg = dbus_message_new_method_call(CONNMAN_SERVICE, MANAGER_PATH,
+                               MANAGER_INTERFACE, "RequestPrivateNetwork");
+
+       dbus_error_init(&error);
+
+       printf("Requesting private-network...\n");
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1,
+                                                                       &error);
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error)) {
+                       fprintf(stderr, "1. %s\n", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       fprintf(stderr, "Request() failed");
+               }
+               return;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_UNIX_FD)
+               goto done;
+
+       dbus_message_iter_get_basic(&array, out_fd);
+       printf("Fildescriptor = %d\n", *out_fd);
+
+       dbus_message_iter_next(&array);
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter iter;
+               const char *key;
+               int type;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               dbus_message_iter_get_basic(&entry, &key);
+
+               printf("key %s", key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &iter);
+
+               type = dbus_message_iter_get_arg_type(&iter);
+               if (type != DBUS_TYPE_STRING)
+                       break;
+
+               if (g_str_equal(key, "ServerIPv4")
+                               && type == DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&iter, out_server_ip);
+                       printf(" = %s\n", *out_server_ip);
+
+               } else if (g_str_equal(key, "PeerIPv4")
+                               && type == DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&iter, out_peer_ip);
+                       printf(" = %s\n", *out_peer_ip);
+
+               } else if (g_str_equal(key, "PrimaryDNS")
+                               && type == DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&iter, out_primary_dns);
+                       printf(" = %s\n", *out_primary_dns);
+
+               } else if (g_str_equal(key, "SecondaryDNS")
+                               && type == DBUS_TYPE_STRING) {
+                       dbus_message_iter_get_basic(&iter, out_secondary_dns);
+                       printf(" = %s\n", *out_secondary_dns);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       int fd = -1;
+       char *server_ip;
+       char *peer_ip;
+       char *primary_dns;
+       char *secondary_dns;
+
+       /*
+        * IP packet: src: 192.168.219.2 dst www.connman.net
+        * HTTP GET / request
+        */
+       int buf[81] = { 0x45, 0x00, 0x00, 0x51, 0x5a, 0xbe, 0x00, 0x00, 0x40,
+                       0x06, 0x50, 0x73, 0xc0, 0xa8, 0xdb, 0x01, 0x3e, 0x4b,
+                       0xf5, 0x80, 0x30, 0x3b, 0x00, 0x50, 0x00, 0x00, 0x00,
+                       0x28, 0x04, 0xfd, 0xac, 0x9b, 0x50, 0x18, 0x02, 0x00,
+                       0xa1, 0xb3, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2f,
+                       0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+                       0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77,
+                       0x77, 0x77, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x6d, 0x61,
+                       0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x0d, 0x0a, 0x0d, 0x0a};
+
+
+       int buf2[81] = { 0x45, 0x00, 0x00, 0x51, 0x57, 0x9d, 0x00, 0x00, 0x40,
+                        0x06, 0x53, 0x93, 0xc0, 0xa8, 0xdb, 0x02, 0x3e, 0x4b,
+                        0xf5, 0x80, 0x30, 0x3b, 0x00, 0x50, 0x00, 0x00, 0x00,
+                        0x28, 0x17, 0xdb, 0x2e, 0x6d, 0x50, 0x18, 0x02, 0x00,
+                        0x0d, 0x03, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2f,
+                        0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31,
+                        0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x77,
+                        0x77, 0x77, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x6d, 0x61,
+                        0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x0d, 0x0a, 0x0d, 0x0a};
+
+       if (DBUS_TYPE_UNIX_FD < 0) {
+               fprintf(stderr, "File-descriptor passing not supported\n");
+               exit(1);
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus\n");
+               exit(1);
+       }
+
+       request_private_network(conn, &fd, &server_ip, &peer_ip,
+                                       &primary_dns, &secondary_dns);
+       if (fd < 0)
+               return -1;
+
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+
+       printf("Press ENTER to write data to the network.\n");
+       getchar();
+
+
+       if (!fork()) {
+               if (write(fd, buf, 81) < 0) {
+                       fprintf(stderr, "err on write() buf\n");
+                       return -1;
+               }
+
+               if (write(fd, buf2, 81) < 0) {
+                       fprintf(stderr, "err on write() buf2\n");
+                       return -1;
+               }
+
+               printf("Press ENTER to release private network.\n");
+               getchar();
+
+               if (release_private_network(conn) < 0)
+                       return -1;
+
+               close(fd);
+
+               dbus_connection_unref(conn);
+
+       } else {
+               struct pollfd p;
+               char buf[1500];
+               int len;
+
+               p.fd = fd;
+               p.events = POLLIN | POLLERR | POLLHUP;
+
+               while (1) {
+                       p.revents = 0;
+                       if (poll(&p, 1, -1) <= 0)
+                               return -1;
+
+                       if (p.revents & (POLLERR | POLLHUP))
+                               return -1;
+
+                       len = read(fd, buf, sizeof(buf));
+                       if (len < 0)
+                               return -1;
+
+                       printf("%d bytes received\n", len);
+               }
+       }
+
+       return 0;
+}
diff --git a/tools/resolv-test.c b/tools/resolv-test.c
new file mode 100644 (file)
index 0000000..9867a4b
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *
+ *  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 <signal.h>
+
+#include <gweb/gresolv.h>
+
+static GTimer *timer;
+
+static GMainLoop *main_loop;
+
+static void resolv_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static const char *status2str(GResolvResultStatus status)
+{
+       switch (status) {
+       case G_RESOLV_RESULT_STATUS_SUCCESS:
+               return "success";
+       case G_RESOLV_RESULT_STATUS_ERROR:
+               return "error";
+       case G_RESOLV_RESULT_STATUS_NO_RESPONSE:
+               return "no response";
+       case G_RESOLV_RESULT_STATUS_FORMAT_ERROR:
+               return "format error";
+       case G_RESOLV_RESULT_STATUS_SERVER_FAILURE:
+               return "server failure";
+       case G_RESOLV_RESULT_STATUS_NAME_ERROR:
+               return "name error";
+       case G_RESOLV_RESULT_STATUS_NOT_IMPLEMENTED:
+               return "not implemented";
+       case G_RESOLV_RESULT_STATUS_REFUSED:
+               return "refused";
+       }
+
+       return NULL;
+}
+
+static void resolv_result(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+       gdouble elapsed;
+       int i;
+
+       elapsed = g_timer_elapsed(timer, NULL);
+
+       g_print("elapse: %f seconds\n", elapsed);
+
+       g_print("status: %s\n", status2str(status));
+
+       if (results != NULL) {
+               for (i = 0; results[i]; i++)
+                       g_print("result: %s\n", results[i]);
+       }
+
+       g_main_loop_quit(main_loop);
+}
+
+static gboolean option_debug = FALSE;
+
+static GOptionEntry options[] = {
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                                       "Enable debug output" },
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       struct sigaction sa;
+       GResolv *resolv;
+       int index = 0;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (argc < 2) {
+               printf("missing argument\n");
+               return 1;
+       }
+
+       resolv = g_resolv_new(index);
+       if (resolv == NULL) {
+               printf("failed to create resolver\n");
+               return 1;
+       }
+
+       if (option_debug == TRUE)
+               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       if (argc > 2) {
+               int i;
+
+               for (i = 2; i < argc; i++)
+                       g_resolv_add_nameserver(resolv, argv[i], 53, 0);
+       }
+
+       timer = g_timer_new();
+
+       if (g_resolv_lookup_hostname(resolv, argv[1],
+                                       resolv_result, NULL) == 0) {
+               printf("failed to start lookup\n");
+               return 1;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_timer_destroy(timer);
+
+       g_resolv_unref(resolv);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/stats-tool.c b/tools/stats-tool.c
new file mode 100644 (file)
index 0000000..0f06d8e
--- /dev/null
@@ -0,0 +1,911 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. 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
+
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#ifdef TEMP_FAILURE_RETRY
+#define TFR TEMP_FAILURE_RETRY
+#else
+#define TFR
+#endif
+
+#define MAGIC 0xFA00B916
+
+struct connman_stats_data {
+       unsigned int rx_packets;
+       unsigned int tx_packets;
+       unsigned int rx_bytes;
+       unsigned int tx_bytes;
+       unsigned int rx_errors;
+       unsigned int tx_errors;
+       unsigned int rx_dropped;
+       unsigned int tx_dropped;
+       unsigned int time;
+};
+
+struct stats_file_header {
+       unsigned int magic;
+       unsigned int begin;
+       unsigned int end;
+       unsigned int home;
+       unsigned int roaming;
+};
+
+struct stats_record {
+       time_t ts;
+       unsigned int roaming;
+       struct connman_stats_data data;
+};
+
+struct stats_file {
+       int fd;
+       char *name;
+       char *addr;
+       size_t len;
+       size_t max_len;
+
+       /* cached values */
+       int max_nr;
+       int nr;
+       struct stats_record *first;
+       struct stats_record *last;
+       struct stats_record *home_first;
+       struct stats_record *roaming_first;
+};
+
+struct stats_iter {
+       struct stats_file *file;
+       struct stats_record *begin;
+       struct stats_record *end;
+       struct stats_record *it;
+};
+
+static gint option_create = 0;
+static gint option_interval = 3;
+static gboolean option_dump = FALSE;
+static gboolean option_summary = FALSE;
+static char *option_info_file_name = NULL;
+static time_t option_start_ts = -1;
+static char *option_last_file_name = NULL;
+
+static gboolean parse_start_ts(const char *key, const char *value,
+                                       gpointer user_data, GError **error)
+{
+       GTimeVal time_val;
+
+       if (g_time_val_from_iso8601(value, &time_val) == FALSE)
+               return FALSE;
+
+       option_start_ts = time_val.tv_sec;
+
+       return TRUE;
+}
+
+static GOptionEntry options[] = {
+       { "create", 'c', 0, G_OPTION_ARG_INT, &option_create,
+                       "Create a .data file with NR faked entries", "NR" },
+       { "interval", 'i', 0, G_OPTION_ARG_INT, &option_interval,
+                       "Interval in seconds (used with create)", "INTERVAL" },
+       { "dump", 'd', 0, G_OPTION_ARG_NONE, &option_dump,
+                       "Dump contents of .data file" },
+       { "summary", 's', 0, G_OPTION_ARG_NONE, &option_summary,
+                       "Summary of .data file" },
+       { "info", 'f', 0, G_OPTION_ARG_FILENAME, &option_info_file_name,
+                       ".info file name" },
+       { "startts", 't', 0, G_OPTION_ARG_CALLBACK, parse_start_ts,
+                       "Set start time for creating .data file "
+                       "(example 2010-11-05T23:00:12Z)", "TS"},
+       { "last", 'l', 0, G_OPTION_ARG_FILENAME, &option_last_file_name,
+                         "Start values from last .data file" },
+       { NULL },
+};
+
+static struct stats_file_header *get_hdr(struct stats_file *file)
+{
+       return (struct stats_file_header *)file->addr;
+}
+
+static struct stats_record *get_begin(struct stats_file *file)
+{
+       unsigned int off = get_hdr(file)->begin;
+
+       return (struct stats_record *)(file->addr + off);
+}
+
+static struct stats_record *get_end(struct stats_file *file)
+{
+       unsigned int off = get_hdr(file)->end;
+
+       return (struct stats_record *)(file->addr + off);
+}
+
+static struct stats_record *get_home(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+
+       if (hdr->home == UINT_MAX)
+               return NULL;
+
+       return (struct stats_record *)(file->addr + hdr->home);
+}
+
+static struct stats_record *get_roaming(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+
+       if (hdr->roaming == UINT_MAX)
+               return NULL;
+
+       return (struct stats_record *)(file->addr + hdr->roaming);
+}
+
+static void set_end(struct stats_file *file, struct stats_record *end)
+{
+       struct stats_file_header *hdr;
+
+       hdr = get_hdr(file);
+       hdr->end = (char *)end - file->addr;
+}
+
+static int get_index(struct stats_file *file, struct stats_record *rec)
+{
+       return rec - file->first;
+}
+
+static struct stats_record *get_next(struct stats_file *file,
+                                       struct stats_record *cur)
+{
+       cur++;
+
+       if (cur > file->last)
+               cur = file->first;
+
+       return cur;
+}
+
+static struct stats_record *get_iterator_begin(struct stats_file *file)
+{
+       return get_next(file, get_begin(file));
+}
+
+static struct stats_record *get_iterator_end(struct stats_file *file)
+{
+       return get_next(file, get_end(file));
+}
+
+static void stats_print_record(struct stats_record *rec)
+{
+       char buffer[30];
+
+       strftime(buffer, 30, "%d-%m-%Y %T", localtime(&rec->ts));
+       printf("%p %lld %s %01d %d %d %d %d %d %d %d %d %d\n",
+               rec, (long long int)rec->ts, buffer,
+               rec->roaming,
+               rec->data.rx_packets,
+               rec->data.tx_packets,
+               rec->data.rx_bytes,
+               rec->data.tx_bytes,
+               rec->data.rx_errors,
+               rec->data.tx_errors,
+               rec->data.rx_dropped,
+               rec->data.tx_dropped,
+               rec->data.time);
+}
+
+static void stats_hdr_info(struct stats_file *file)
+{
+       struct stats_file_header *hdr;
+       struct stats_record *begin, *end, *home, *roaming;
+       unsigned int home_idx, roaming_idx;
+
+       hdr = get_hdr(file);
+       begin = get_begin(file);
+       end = get_end(file);
+
+       home = get_home(file);
+       if (home == NULL)
+               home_idx = UINT_MAX;
+       else
+               home_idx = get_index(file, home);
+
+       roaming = get_roaming(file);
+       if (roaming == NULL)
+               roaming_idx = UINT_MAX;
+       else
+               roaming_idx = get_index(file, roaming);
+
+       printf("Data Structure Sizes\n");
+       printf("  sizeof header   %zd/0x%02zx\n",
+               sizeof(struct stats_file_header),
+               sizeof(struct stats_file_header));
+       printf("  sizeof entry    %zd/0%02zx\n\n",
+               sizeof(struct stats_record),
+               sizeof(struct stats_record));
+
+       printf("File\n");
+       printf("  addr            %p\n",  file->addr);
+       printf("  len             %zd\n", file->len);
+
+       printf("  max nr entries  %d\n", file->max_nr);
+       printf("  nr entries      %d\n\n", file->nr);
+
+       printf("Header\n");
+       printf("  magic           0x%08x\n", hdr->magic);
+       printf("  begin           [%d] 0x%08x\n",
+               get_index(file, begin), hdr->begin);
+       printf("  end             [%d] 0x%08x\n",
+               get_index(file, end), hdr->end);
+       printf("  home            [%d] 0x%08x\n",
+               home_idx, hdr->home);
+       printf("  roaming         [%d] 0x%08x\n\n",
+               roaming_idx, hdr->roaming);
+
+
+       printf("Pointers\n");
+       printf("  hdr             %p\n", hdr);
+       printf("  begin           %p\n", begin);
+       printf("  end             %p\n", end);
+       printf("  home            %p\n", home);
+       printf("  romaing         %p\n", roaming);
+       printf("  first           %p\n", file->first);
+       printf("  last            %p\n\n", file->last);
+}
+
+static void stats_print_entries(struct stats_file *file)
+{
+       struct stats_record *it;
+       int i;
+
+       printf("[ idx] ptr ts ts rx_packets tx_packets rx_bytes "
+               "tx_bytes rx_errors tx_errors rx_dropped tx_dropped time\n\n");
+
+       for (i = 0, it = file->first; it <= file->last; it++, i++) {
+               printf("[%04d] ", i);
+               stats_print_record(it);
+       }
+}
+
+static void stats_print_rec_diff(struct stats_record *begin,
+                                       struct stats_record *end)
+{
+       printf("\trx_packets: %d\n",
+               end->data.rx_packets - begin->data.rx_packets);
+       printf("\ttx_packets: %d\n",
+               end->data.tx_packets - begin->data.tx_packets);
+       printf("\trx_bytes:   %d\n",
+               end->data.rx_bytes - begin->data.rx_bytes);
+       printf("\ttx_bytes:   %d\n",
+               end->data.tx_bytes - begin->data.tx_bytes);
+       printf("\trx_errors:  %d\n",
+               end->data.rx_errors - begin->data.rx_errors);
+       printf("\ttx_errors:  %d\n",
+               end->data.tx_errors - begin->data.tx_errors);
+       printf("\trx_dropped: %d\n",
+               end->data.rx_dropped - begin->data.rx_dropped);
+       printf("\ttx_dropped: %d\n",
+               end->data.tx_dropped - begin->data.tx_dropped);
+       printf("\ttime:       %d\n",
+               end->data.time - begin->data.time);
+}
+
+static void stats_print_diff(struct stats_file *file)
+{
+       struct stats_record *begin, *end;
+
+       begin = get_begin(file);
+       begin = get_next(file, begin);
+       end = get_end(file);
+
+       printf("\n(begin + 1)\n");
+       printf("\t[%04d] ", get_index(file, begin));
+       stats_print_record(begin);
+       printf("end\n");
+       printf("\t[%04d] ", get_index(file, end));
+       stats_print_record(end);
+
+       if (file->home_first != NULL && get_home(file) != NULL) {
+               printf("\nhome\n");
+               stats_print_rec_diff(file->home_first, get_home(file));
+       }
+
+       if (file->roaming_first != NULL && get_roaming(file) != NULL) {
+               printf("\roaming\n");
+               stats_print_rec_diff(file->roaming_first, get_roaming(file));
+       }
+}
+
+static void update_max_nr_entries(struct stats_file *file)
+{
+       file->max_nr = (file->len - sizeof(struct stats_file_header)) /
+               sizeof(struct stats_record);
+}
+
+static void update_nr_entries(struct stats_file *file)
+{
+       struct stats_record *begin, *end;
+       int nr;
+
+       begin = get_begin(file);
+       end = get_end(file);
+
+       nr = get_index(file, end) - get_index(file, begin);
+
+       if (nr < 0)
+               nr += file->max_nr;
+
+       file->nr = nr;
+}
+
+static void update_first(struct stats_file *file)
+{
+       file->first = (struct stats_record *)(file->addr +
+                                       sizeof(struct stats_file_header));
+}
+
+static void update_last(struct stats_file *file)
+{
+       struct stats_record *last;
+
+       last = file->first;
+       last += file->max_nr - 1;
+
+       file->last = last;
+}
+
+static int stats_file_update_cache(struct stats_file *file)
+{
+       struct stats_record *it, *end;
+
+       update_max_nr_entries(file);
+       update_nr_entries(file);
+       update_first(file);
+       update_last(file);
+       file->home_first = NULL;
+       file->roaming_first = NULL;
+
+       end = get_iterator_end(file);
+       for (it = get_iterator_begin(file);
+                       it != end;
+                       it = get_next(file, it)) {
+
+               if (file->home_first == NULL && it->roaming == 0)
+                       file->home_first = it;
+
+               if (file->roaming_first == NULL && it->roaming == 1)
+                       file->roaming_first = it;
+
+               if (file->home_first != NULL && file->roaming_first != NULL)
+                       break;
+       }
+
+       return 0;
+}
+
+static int stats_file_remap(struct stats_file *file, size_t size)
+{
+       size_t page_size, new_size;
+       void *addr;
+       int err;
+
+       page_size = sysconf(_SC_PAGESIZE);
+       new_size = (size + page_size - 1) & ~(page_size - 1);
+
+       err = ftruncate(file->fd, new_size);
+       if (err < 0) {
+               fprintf(stderr, "ftrunctate error %s for %s",
+                               strerror(errno), file->name);
+               return -errno;
+       }
+
+       if (file->addr == NULL) {
+               addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
+                               MAP_SHARED, file->fd, 0);
+       } else {
+               addr = mremap(file->addr, file->len, new_size, MREMAP_MAYMOVE);
+       }
+
+       if (addr == MAP_FAILED) {
+               fprintf(stderr, "mmap error %s for %s\n",
+                       strerror(errno), file->name);
+               return -errno;
+       }
+
+       file->addr = addr;
+       file->len = new_size;
+       file->max_len = new_size;
+
+       return 0;
+}
+
+static int stats_open(struct stats_file *file, const char *name)
+{
+       struct stats_file_header *hdr;
+       struct stat tm;
+       int err;
+       size_t size = 0;
+
+       bzero(file, sizeof(struct stats_file));
+
+       if (name != NULL) {
+               file->name = g_strdup(name);
+
+               file->fd = TFR(open(file->name,
+                                       O_RDWR | O_CREAT | O_CLOEXEC, 0644));
+               if (file->fd == -1) {
+                       fprintf(stderr, "open error %s for %s\n",
+                               strerror(errno), file->name);
+                       return -errno;
+               }
+
+               err = fstat(file->fd, &tm);
+               if (err < 0) {
+                       fprintf(stderr, "fstat error %s for %s\n",
+                               strerror(errno), file->name);
+                       return err;
+               }
+
+               size = (size_t)tm.st_size;
+       } else {
+               file->name = g_strdup("stats.XXXXXX.tmp");
+               file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644);
+               if (file->fd == -1) {
+                       fprintf(stderr, "creating tmp failed\n");
+                       return -1;
+               }
+       }
+
+       if (size == 0)
+               size = sysconf(_SC_PAGESIZE);
+
+       err = stats_file_remap(file, size);
+       if (err < 0) {
+               fprintf(stderr, "remap failed\n");
+               return err;
+       }
+
+       /* Initialize new file */
+       hdr = get_hdr(file);
+       if (hdr->magic != MAGIC ||
+                       hdr->begin < sizeof(struct stats_file_header) ||
+                       hdr->end < sizeof(struct stats_file_header) ||
+                       hdr->home < sizeof(struct stats_file_header) ||
+                       hdr->roaming < sizeof(struct stats_file_header) ||
+                       hdr->begin > file->len ||
+                       hdr->end > file->len) {
+               hdr->magic = MAGIC;
+               hdr->begin = sizeof(struct stats_file_header);
+               hdr->end = sizeof(struct stats_file_header);
+               hdr->home = UINT_MAX;
+               hdr->roaming = UINT_MAX;
+
+       }
+       stats_file_update_cache(file);
+
+       return 0;
+}
+
+static void stats_close(struct stats_file *file)
+{
+       munmap(file->addr, file->len);
+       close(file->fd);
+       g_free(file->name);
+}
+
+static int stats_create(struct stats_file *file, unsigned int nr,
+                       unsigned int interval, time_t start_ts,
+                       struct stats_record *start)
+{
+       unsigned int i;
+       int err;
+       struct stats_record *cur, *next;
+       struct stats_file_header *hdr;
+       unsigned int pkt;
+       unsigned int step_ts;
+       unsigned int roaming = FALSE;
+
+       hdr = get_hdr(file);
+
+       hdr->magic = MAGIC;
+       hdr->begin = sizeof(struct stats_file_header);
+       hdr->end = sizeof(struct stats_file_header);
+       hdr->home = UINT_MAX;
+       hdr->roaming = UINT_MAX;
+
+       stats_file_update_cache(file);
+
+       if (start != NULL) {
+               struct stats_record *rec;
+
+               rec = get_end(file);
+               memcpy(rec, start, sizeof(struct stats_record));
+       } else {
+               get_end(file)->ts = start_ts;
+       }
+
+       for (i = 0; i < nr; i++) {
+               if (file->last == get_end(file)) {
+                       err = stats_file_remap(file, file->len +
+                                               sysconf(_SC_PAGESIZE));
+                       if (err < 0)
+                               return err;
+
+                       stats_file_update_cache(file);
+               }
+               cur = get_end(file);
+               next = get_next(file, cur);
+
+               step_ts = (rand() % interval);
+               if (step_ts == 0)
+                       step_ts = 1;
+
+               next->ts = cur->ts + step_ts;
+               next->roaming = roaming;
+               next->data.time = cur->data.time + step_ts;
+
+               next->data.rx_packets = cur->data.rx_packets;
+               next->data.rx_bytes = cur->data.rx_bytes;
+
+               if (rand() % 3 == 0) {
+                       pkt = rand() % 5;
+                       next->data.rx_packets += pkt;
+                       next->data.rx_bytes += pkt * (rand() % 1500);
+               }
+
+               next->data.tx_packets = cur->data.tx_packets;
+               next->data.tx_bytes = cur->data.tx_bytes;
+
+               if (rand() % 3 == 0) {
+                       pkt = rand() % 5;
+                       next->data.tx_packets += pkt;
+                       next->data.tx_bytes += pkt * (rand() % 1500);
+               }
+
+               set_end(file, next);
+
+               if ((rand() % 50) == 0)
+                       roaming = roaming == TRUE? FALSE : TRUE;
+
+       }
+
+       return 0;
+}
+
+static struct stats_record *get_next_record(struct stats_iter *iter)
+{
+       if (iter->it != iter->end) {
+               struct stats_record *tmp;
+
+               tmp = iter->it;
+               iter->it = get_next(iter->file, iter->it);
+
+               return tmp;
+       }
+
+       return NULL;
+}
+
+static int append_record(struct stats_file *file,
+                               struct stats_record *rec)
+{
+       struct stats_record *cur, *next;
+       int err;
+
+       if (file->last == get_end(file)) {
+               err = stats_file_remap(file, file->len +
+                                       sysconf(_SC_PAGESIZE));
+               if (err < 0)
+                       return err;
+
+               stats_file_update_cache(file);
+       }
+
+       cur = get_end(file);
+       next = get_next(file, cur);
+
+       memcpy(next, rec, sizeof(struct stats_record));
+
+       set_end(file, next);
+
+       return 0;
+}
+
+static struct stats_record *process_file(struct stats_iter *iter,
+                                       struct stats_file *temp_file,
+                                       struct stats_record *cur,
+                                       GDate *date_change_step_size,
+                                       int account_period_offset)
+{
+       struct stats_record *home, *roaming;
+       struct stats_record *next;
+
+       home = NULL;
+       roaming = NULL;
+
+       if (cur == NULL)
+               cur = get_next_record(iter);
+       next = get_next_record(iter);
+
+       while (next != NULL) {
+               GDate date_cur;
+               GDate date_next;
+               int append;
+
+               append = FALSE;
+
+               if (cur->roaming == TRUE)
+                       roaming = cur;
+               else
+                       home = cur;
+
+               g_date_set_time_t(&date_cur, cur->ts);
+               g_date_set_time_t(&date_next, next->ts);
+
+               if (g_date_compare(&date_cur, date_change_step_size) < 0) {
+                       /* month period size */
+                       GDateDay day_cur, day_next;
+                       GDateMonth month_cur, month_next;
+
+                       month_cur = g_date_get_month(&date_cur);
+                       month_next = g_date_get_month(&date_next);
+
+                       day_cur = g_date_get_day(&date_cur);
+                       day_next = g_date_get_day(&date_next);
+
+                       if (day_cur == day_next && month_cur != month_next)
+                               append = TRUE;
+                       else if (day_cur < account_period_offset && day_next >= account_period_offset)
+                               append = TRUE;
+               } else {
+                       /* day period size */
+                       if (g_date_days_between(&date_cur, &date_next) > 0)
+                               append = TRUE;
+               }
+
+               if (append == TRUE) {
+                       if (home != NULL) {
+                               append_record(temp_file, home);
+                               home = NULL;
+                       }
+
+                       if (roaming != NULL) {
+                               append_record(temp_file, roaming);
+                               roaming = NULL;
+                       }
+               }
+
+               cur = next;
+               next = get_next_record(iter);
+       }
+
+       return cur;
+}
+
+static int summarize(struct stats_file *data_file,
+                       struct stats_file *history_file,
+                       struct stats_file *temp_file,
+                       int account_period_offset)
+{
+       struct stats_iter data_iter;
+       struct stats_iter history_iter;
+       struct stats_record *cur, *next;
+
+       GDate today, date_change_step_size;
+
+       /*
+        * First calculate the date when switch from monthly
+        * accounting period size to daily size
+        */
+       g_date_set_time_t(&today, time(NULL));
+
+       date_change_step_size = today;
+       if (g_date_get_day(&today) - account_period_offset >= 0)
+               g_date_subtract_months(&date_change_step_size, 2);
+       else
+               g_date_subtract_months(&date_change_step_size, 3);
+
+       g_date_set_day(&date_change_step_size, account_period_offset);
+
+
+       /* Now process history file */
+       cur = NULL;
+
+       if (history_file != NULL) {
+               history_iter.file = history_file;
+               history_iter.begin = get_iterator_begin(history_iter.file);
+               history_iter.end = get_iterator_end(history_iter.file);
+               history_iter.it = history_iter.begin;
+
+               cur = process_file(&history_iter, temp_file, NULL,
+                                       &date_change_step_size, account_period_offset);
+       }
+
+       data_iter.file = data_file;
+       data_iter.begin = get_iterator_begin(data_iter.file);
+       data_iter.end = get_iterator_end(data_iter.file);
+       data_iter.it = data_iter.begin;
+
+       /*
+        * Ensure date_file records are newer than the history_file
+        * record
+        */
+       if (cur != NULL) {
+               next = get_next_record(&data_iter);
+               while(next != NULL && cur->ts > next->ts)
+                       next = get_next_record(&data_iter);
+       }
+
+       /* And finally process the new data records */
+       cur = process_file(&data_iter, temp_file, cur,
+                               &date_change_step_size, account_period_offset);
+
+       if (cur != NULL)
+               append_record(temp_file, cur);
+
+       return 0;
+}
+
+static void swap_and_close_files(struct stats_file *history_file,
+                       struct stats_file *temp_file)
+{
+       munmap(history_file->addr, history_file->len);
+       munmap(temp_file->addr, temp_file->len);
+
+       TFR(close(temp_file->fd));
+
+       unlink(history_file->name);
+
+       if (link(temp_file->name, history_file->name) < 0)
+               return;
+
+       unlink(temp_file->name);
+       TFR(close(history_file->fd));
+}
+
+static void history_file_update(struct stats_file *data_file,
+                               const char *history_file_name)
+{
+       struct stats_file _history_file;
+       struct stats_file tempory_file;
+
+       struct stats_file *history_file = NULL;
+
+       if (stats_open(&_history_file, history_file_name) == 0)
+               history_file = &_history_file;
+
+       if (stats_open(&tempory_file, NULL) < 0) {
+               if (history_file != NULL)
+                       stats_close(history_file);
+               return;
+       }
+
+       summarize(data_file, history_file, &tempory_file, 13);
+
+       swap_and_close_files(history_file, &tempory_file);
+}
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+
+       struct stats_file_header *hdr;
+       struct stats_file data, *data_file;
+       struct stats_record *rec;
+       time_t start_ts;
+       int err;
+
+       rec = NULL;
+
+       data_file = &data;
+
+       putenv("TZ=GMT0");
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (argc < 2) {
+               printf("Usage: %s [FILENAME]\n", argv[0]);
+               exit(0);
+       }
+
+       err = stats_open(data_file, argv[1]);
+       if (err < 0) {
+               fprintf(stderr, "failed open file %s\n", argv[1]);
+               exit(1);
+       }
+
+       if (option_last_file_name != NULL) {
+               struct stats_file last;
+               if (stats_open(&last, option_last_file_name) < 0) {
+                       fprintf(stderr, "failed open file %s\n",
+                               option_last_file_name);
+                       exit(1);
+               }
+
+               rec = get_end(&last);
+       }
+
+       if (option_start_ts == -1)
+               start_ts = time(NULL);
+       else
+               start_ts = option_start_ts;
+
+       if (option_create > 0)
+               stats_create(data_file, option_create, option_interval, start_ts, rec);
+
+       hdr = get_hdr(data_file);
+       if (hdr->magic != MAGIC) {
+               fprintf(stderr, "header file magic test failed\n");
+               goto err;
+       }
+
+       stats_file_update_cache(data_file);
+
+       stats_hdr_info(data_file);
+
+       if (option_dump == TRUE)
+               stats_print_entries(data_file);
+
+       if (option_summary == TRUE)
+               stats_print_diff(data_file);
+
+       if (option_info_file_name != NULL)
+               history_file_update(data_file, option_info_file_name);
+
+err:
+       stats_close(data_file);
+
+       return 0;
+}
diff --git a/tools/supplicant-dbus.c b/tools/supplicant-dbus.c
new file mode 100644 (file)
index 0000000..028a275
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ *
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "supplicant-dbus.h"
+
+#define TIMEOUT 5000
+
+static DBusConnection *connection = NULL;
+
+void supplicant_dbus_setup(DBusConnection *conn)
+{
+       connection = conn;
+}
+
+void supplicant_dbus_array_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_array_function function,
+                                                       void *user_data)
+{
+       DBusMessageIter entry;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) {
+               if (function != NULL)
+                       function(&entry, user_data);
+
+               dbus_message_iter_next(&entry);
+       }
+}
+
+void supplicant_dbus_property_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data)
+{
+       DBusMessageIter dict;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       return;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (key != NULL) {
+                       if (strcmp(key, "Properties") == 0)
+                               supplicant_dbus_property_foreach(&value,
+                                                       function, user_data);
+                       else if (function != NULL)
+                               function(key, &value, user_data);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+struct property_get_data {
+       supplicant_dbus_property_function function;
+       void *user_data;
+};
+
+static void property_get_all_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_get_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       supplicant_dbus_property_foreach(&iter, data->function,
+                                                       data->user_data);
+
+       if (data->function != NULL)
+               data->function(NULL, NULL, data->user_data);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_property_get_all(const char *path, const char *interface,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data)
+{
+       struct property_get_data *data;
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                       DBUS_INTERFACE_PROPERTIES, "GetAll");
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &interface, NULL);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, property_get_all_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+struct property_set_data {
+       supplicant_dbus_result_function function;
+       void *user_data;
+};
+
+static void property_set_reply(DBusPendingCall *call, void *user_data)
+{
+       struct property_set_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       const char *error;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               error = dbus_message_get_error_name(reply);
+       else
+               error = NULL;
+
+       if (dbus_message_iter_init(reply, &iter) == FALSE)
+               goto done;
+
+       if (data->function != NULL)
+               data->function(error, &iter, data->user_data);
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_property_set(const char *path, const char *interface,
+                               const char *key, const char *signature,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data)
+{
+       struct property_set_data *data;
+       DBusMessage *message;
+       DBusMessageIter iter, value;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL)
+               return -EINVAL;
+
+       if (key == NULL || signature == NULL || setup == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                       DBUS_INTERFACE_PROPERTIES, "Set");
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       setup(&value, user_data);
+       dbus_message_iter_close_container(&iter, &value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, property_set_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+struct method_call_data {
+       supplicant_dbus_result_function function;
+       void *user_data;
+};
+
+static void method_call_reply(DBusPendingCall *call, void *user_data)
+{
+       struct method_call_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       const char *error;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               error = dbus_message_get_error_name(reply);
+       else
+               error = NULL;
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (data->function != NULL)
+               data->function(error, &iter, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+int supplicant_dbus_method_call(const char *path,
+                               const char *interface, const char *method,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data)
+{
+       struct method_call_data *data;
+       DBusMessage *message;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       if (connection == NULL)
+               return -EINVAL;
+
+       if (path == NULL || interface == NULL || method == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call(SUPPLICANT_SERVICE, path,
+                                                       interface, method);
+       if (message == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       dbus_message_set_auto_start(message, FALSE);
+
+       dbus_message_iter_init_append(message, &iter);
+       if (setup != NULL)
+               setup(&iter, user_data);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, method_call_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter value;
+       const char *signature;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       case DBUS_TYPE_STRING:
+               signature = DBUS_TYPE_STRING_AS_STRING;
+               break;
+       case DBUS_TYPE_BYTE:
+               signature = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT16:
+               signature = DBUS_TYPE_UINT16_AS_STRING;
+               break;
+       case DBUS_TYPE_INT16:
+               signature = DBUS_TYPE_INT16_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT32:
+               signature = DBUS_TYPE_UINT32_AS_STRING;
+               break;
+       case DBUS_TYPE_INT32:
+               signature = DBUS_TYPE_INT32_AS_STRING;
+               break;
+       case DBUS_TYPE_OBJECT_PATH:
+               signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+               break;
+       default:
+               signature = DBUS_TYPE_VARIANT_AS_STRING;
+               break;
+       }
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(iter, &value);
+}
diff --git a/tools/supplicant-dbus.h b/tools/supplicant-dbus.h
new file mode 100644 (file)
index 0000000..4452894
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define SUPPLICANT_SERVICE     "fi.w1.wpa_supplicant1"
+#define SUPPLICANT_INTERFACE   "fi.w1.wpa_supplicant1"
+#define SUPPLICANT_PATH                "/fi/w1/wpa_supplicant1"
+
+typedef void (* supplicant_dbus_array_function) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+typedef void (* supplicant_dbus_property_function) (const char *key,
+                               DBusMessageIter *iter, void *user_data);
+
+typedef void (* supplicant_dbus_setup_function) (DBusMessageIter *iter,
+                                                       void *user_data);
+
+typedef void (* supplicant_dbus_result_function) (const char *error,
+                               DBusMessageIter *iter, void *user_data);
+
+void supplicant_dbus_setup(DBusConnection *conn);
+
+void supplicant_dbus_array_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_array_function function,
+                                                       void *user_data);
+
+void supplicant_dbus_property_foreach(DBusMessageIter *iter,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_property_get_all(const char *path, const char *interface,
+                               supplicant_dbus_property_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_property_set(const char *path, const char *interface,
+                               const char *key, const char *signature,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data);
+
+int supplicant_dbus_method_call(const char *path,
+                               const char *interface, const char *method,
+                               supplicant_dbus_setup_function setup,
+                               supplicant_dbus_result_function function,
+                                                       void *user_data);
+
+void supplicant_dbus_property_append_basic(DBusMessageIter *iter,
+                                       const char *key, int type, void *val);
+
+static inline void supplicant_dbus_dict_open(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void supplicant_dbus_dict_close(DBusMessageIter *iter,
+                                                       DBusMessageIter *dict)
+{
+       dbus_message_iter_close_container(iter, dict);
+}
+
+static inline void supplicant_dbus_dict_append_basic(DBusMessageIter *dict,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       supplicant_dbus_property_append_basic(&entry, key, type, val);
+       dbus_message_iter_close_container(dict, &entry);
+}
diff --git a/tools/supplicant-test.c b/tools/supplicant-test.c
new file mode 100644 (file)
index 0000000..9b33275
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *
+ *  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 <signal.h>
+#include <syslog.h>
+
+#include <gdbus.h>
+
+#include "supplicant.h"
+
+#define DBG(fmt, arg...) do { \
+       syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \
+} while (0)
+
+static void create_callback(int result, struct supplicant_interface *interface,
+                                                       void *user_data)
+{
+       DBG("* result %d ifname %s", result,
+                               supplicant_interface_get_ifname(interface));
+
+       if (result < 0)
+               return;
+
+       //supplicant_set_debug_level(1);
+}
+
+static void system_ready(void)
+{
+       DBG("*");
+
+       supplicant_interface_create("wlan0", "nl80211,wext",
+                                               create_callback, NULL);
+}
+
+static void system_killed(void)
+{
+       DBG("*");
+}
+
+static void scan_callback(int result, void *user_data)
+{
+       DBG("* result %d", result);
+
+       if (result < 0)
+               return;
+}
+
+static void interface_added(struct supplicant_interface *interface)
+{
+       const char *ifname = supplicant_interface_get_ifname(interface);
+       const char *driver = supplicant_interface_get_driver(interface);
+
+       DBG("* ifname %s driver %s", ifname, driver);
+
+       if (supplicant_interface_scan(interface, scan_callback, NULL) < 0)
+               DBG("scan failed");
+}
+
+static void interface_removed(struct supplicant_interface *interface)
+{
+       const char *ifname = supplicant_interface_get_ifname(interface);
+
+       DBG("* ifname %s", ifname);
+}
+
+static void scan_started(struct supplicant_interface *interface)
+{
+       const char *ifname = supplicant_interface_get_ifname(interface);
+
+       DBG("* ifname %s", ifname);
+}
+
+static void scan_finished(struct supplicant_interface *interface)
+{
+       const char *ifname = supplicant_interface_get_ifname(interface);
+
+       DBG("* ifname %s", ifname);
+}
+
+static void network_added(struct supplicant_network *network)
+{
+       const char *name = supplicant_network_get_name(network);
+
+       DBG("* name %s", name);
+
+       DBG("* %s", supplicant_network_get_identifier(network));
+}
+
+static void network_removed(struct supplicant_network *network)
+{
+       const char *name = supplicant_network_get_name(network);
+
+       DBG("* name %s", name);
+}
+
+static const struct supplicant_callbacks callbacks = {
+       .system_ready           = system_ready,
+       .system_killed          = system_killed,
+       .interface_added        = interface_added,
+       .interface_removed      = interface_removed,
+       .scan_started           = scan_started,
+       .scan_finished          = scan_finished,
+       .network_added          = network_added,
+       .network_removed        = network_removed,
+};
+
+static GMainLoop *main_loop = NULL;
+
+static void sig_term(int sig)
+{
+       syslog(LOG_INFO, "Terminating");
+
+       g_main_loop_quit(main_loop);
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       syslog(LOG_ERR, "D-Bus disconnect");
+
+       g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       DBusError err;
+       struct sigaction sa;
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       openlog("supplicant", LOG_NDELAY | LOG_PERROR, LOG_USER);
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       syslog(LOG_INFO, "Startup");
+
+       if (supplicant_register(&callbacks) < 0) {
+               syslog(LOG_ERR, "Failed to init supplicant");
+               goto done;
+       }
+
+       g_main_loop_run(main_loop);
+
+       supplicant_unregister(&callbacks);
+
+done:
+       syslog(LOG_INFO, "Exit");
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       closelog();
+
+       return 0;
+}
diff --git a/tools/supplicant.c b/tools/supplicant.c
new file mode 100644 (file)
index 0000000..1c96823
--- /dev/null
@@ -0,0 +1,1942 @@
+/*
+ *
+ *  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 <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <syslog.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "supplicant-dbus.h"
+#include "supplicant.h"
+
+#define DBG(fmt, arg...) do { \
+       syslog(LOG_DEBUG, "%s() " fmt, __FUNCTION__ , ## arg); \
+} while (0)
+
+#define TIMEOUT 5000
+
+#define IEEE80211_CAP_ESS      0x0001
+#define IEEE80211_CAP_IBSS     0x0002
+#define IEEE80211_CAP_PRIVACY  0x0010
+
+static DBusConnection *connection;
+
+static const struct supplicant_callbacks *callbacks_pointer;
+
+static dbus_bool_t system_available = FALSE;
+static dbus_bool_t system_ready = FALSE;
+
+static dbus_int32_t debug_level = 0;
+static dbus_bool_t debug_timestamp = FALSE;
+static dbus_bool_t debug_showkeys = FALSE;
+
+static const char *debug_strings[] = {
+       "msgdump", "debug", "info", "warning", "error", NULL
+};
+
+static unsigned int eap_methods;
+
+struct strvalmap {
+       const char *str;
+       unsigned int val;
+};
+
+static struct strvalmap eap_method_map[] = {
+       { "MD5",        SUPPLICANT_EAP_METHOD_MD5       },
+       { "TLS",        SUPPLICANT_EAP_METHOD_TLS       },
+       { "MSCHAPV2",   SUPPLICANT_EAP_METHOD_MSCHAPV2  },
+       { "PEAP",       SUPPLICANT_EAP_METHOD_PEAP      },
+       { "TTLS",       SUPPLICANT_EAP_METHOD_TTLS      },
+       { "GTC",        SUPPLICANT_EAP_METHOD_GTC       },
+       { "OTP",        SUPPLICANT_EAP_METHOD_OTP       },
+       { "LEAP",       SUPPLICANT_EAP_METHOD_LEAP      },
+       { "WSC",        SUPPLICANT_EAP_METHOD_WSC       },
+       { }
+};
+
+static struct strvalmap keymgmt_capa_map[] = {
+       { "none",       SUPPLICANT_CAPABILITY_KEYMGMT_NONE      },
+       { "ieee8021x",  SUPPLICANT_CAPABILITY_KEYMGMT_IEEE8021X },
+       { "wpa-none",   SUPPLICANT_CAPABILITY_KEYMGMT_WPA_NONE  },
+       { "wpa-psk",    SUPPLICANT_CAPABILITY_KEYMGMT_WPA_PSK   },
+       { "wpa-eap",    SUPPLICANT_CAPABILITY_KEYMGMT_WPA_EAP   },
+       { "wps",        SUPPLICANT_CAPABILITY_KEYMGMT_WPS       },
+       { }
+};
+
+static struct strvalmap authalg_capa_map[] = {
+       { "open",       SUPPLICANT_CAPABILITY_AUTHALG_OPEN      },
+       { "shared",     SUPPLICANT_CAPABILITY_AUTHALG_SHARED    },
+       { "leap",       SUPPLICANT_CAPABILITY_AUTHALG_LEAP      },
+       { }
+};
+
+static struct strvalmap proto_capa_map[] = {
+       { "wpa",        SUPPLICANT_CAPABILITY_PROTO_WPA         },
+       { "rsn",        SUPPLICANT_CAPABILITY_PROTO_RSN         },
+       { }
+};
+
+static struct strvalmap group_capa_map[] = {
+       { "wep40",      SUPPLICANT_CAPABILITY_GROUP_WEP40       },
+       { "wep104",     SUPPLICANT_CAPABILITY_GROUP_WEP104      },
+       { "tkip",       SUPPLICANT_CAPABILITY_GROUP_TKIP        },
+       { "ccmp",       SUPPLICANT_CAPABILITY_GROUP_CCMP        },
+       { }
+};
+
+static struct strvalmap pairwise_capa_map[] = {
+       { "none",       SUPPLICANT_CAPABILITY_PAIRWISE_NONE     },
+       { "tkip",       SUPPLICANT_CAPABILITY_PAIRWISE_TKIP     },
+       { "ccmp",       SUPPLICANT_CAPABILITY_PAIRWISE_CCMP     },
+       { }
+};
+
+static struct strvalmap scan_capa_map[] = {
+       { "active",     SUPPLICANT_CAPABILITY_SCAN_ACTIVE       },
+       { "passive",    SUPPLICANT_CAPABILITY_SCAN_PASSIVE      },
+       { "ssid",       SUPPLICANT_CAPABILITY_SCAN_SSID         },
+       { }
+};
+
+static struct strvalmap mode_capa_map[] = {
+       { "infrastructure",     SUPPLICANT_CAPABILITY_MODE_INFRA        },
+       { "ad-hoc",             SUPPLICANT_CAPABILITY_MODE_IBSS         },
+       { "ap",                 SUPPLICANT_CAPABILITY_MODE_AP           },
+       { }
+};
+
+static GHashTable *interface_table;
+static GHashTable *bss_mapping;
+
+struct supplicant_interface {
+       char *path;
+       unsigned int keymgmt_capa;
+       unsigned int authalg_capa;
+       unsigned int proto_capa;
+       unsigned int group_capa;
+       unsigned int pairwise_capa;
+       unsigned int scan_capa;
+       unsigned int mode_capa;
+       dbus_bool_t ready;
+       enum supplicant_state state;
+       dbus_bool_t scanning;
+       supplicant_interface_scan_callback scan_callback;
+       void *scan_data;
+       int apscan;
+       char *ifname;
+       char *driver;
+       char *bridge;
+       GHashTable *network_table;
+       GHashTable *net_mapping;
+       GHashTable *bss_mapping;
+};
+
+struct supplicant_network {
+       struct supplicant_interface *interface;
+       char *path;
+       char *group;
+       char *name;
+       enum supplicant_mode mode;
+       GHashTable *bss_table;
+       GHashTable *config_table;
+};
+
+struct supplicant_bss {
+       struct supplicant_interface *interface;
+       char *path;
+       unsigned char bssid[6];
+       unsigned char ssid[32];
+       unsigned int ssid_len;
+       dbus_uint16_t frequency;
+       dbus_uint32_t maxrate;
+       enum supplicant_mode mode;
+       enum supplicant_security security;
+       dbus_bool_t privacy;
+       dbus_bool_t psk;
+       dbus_bool_t ieee8021x;
+};
+
+static enum supplicant_mode string2mode(const char *mode)
+{
+       if (mode == NULL)
+               return SUPPLICANT_MODE_UNKNOWN;
+
+       if (g_str_equal(mode, "infrastructure") == TRUE)
+               return SUPPLICANT_MODE_INFRA;
+       else if (g_str_equal(mode, "ad-hoc") == TRUE)
+               return SUPPLICANT_MODE_IBSS;
+
+       return SUPPLICANT_MODE_UNKNOWN;
+}
+
+static const char *mode2string(enum supplicant_mode mode)
+{
+       switch (mode) {
+       case SUPPLICANT_MODE_UNKNOWN:
+               break;
+       case SUPPLICANT_MODE_INFRA:
+               return "infra";
+       case SUPPLICANT_MODE_IBSS:
+               return "adhoc";
+       }
+
+       return NULL;
+}
+
+static const char *security2string(enum supplicant_security security)
+{
+       switch (security) {
+       case SUPPLICANT_SECURITY_UNKNOWN:
+               break;
+       case SUPPLICANT_SECURITY_NONE:
+               return "none";
+       case SUPPLICANT_SECURITY_WEP:
+               return "wep";
+       case SUPPLICANT_SECURITY_PSK:
+               return "psk";
+       case SUPPLICANT_SECURITY_IEEE8021X:
+               return "ieee8021x";
+       }
+
+       return NULL;
+}
+
+static enum supplicant_state string2state(const char *state)
+{
+       if (state == NULL)
+               return SUPPLICANT_STATE_UNKNOWN;
+
+       if (g_str_equal(state, "unknown") == TRUE)
+               return SUPPLICANT_STATE_UNKNOWN;
+       else if (g_str_equal(state, "disconnected") == TRUE)
+               return SUPPLICANT_STATE_DISCONNECTED;
+       else if (g_str_equal(state, "inactive") == TRUE)
+               return SUPPLICANT_STATE_INACTIVE;
+       else if (g_str_equal(state, "scanning") == TRUE)
+               return SUPPLICANT_STATE_SCANNING;
+       else if (g_str_equal(state, "authenticating") == TRUE)
+               return SUPPLICANT_STATE_AUTHENTICATING;
+       else if (g_str_equal(state, "associating") == TRUE)
+               return SUPPLICANT_STATE_ASSOCIATING;
+       else if (g_str_equal(state, "associated") == TRUE)
+               return SUPPLICANT_STATE_ASSOCIATED;
+       else if (g_str_equal(state, "group_handshake") == TRUE)
+               return SUPPLICANT_STATE_GROUP_HANDSHAKE;
+       else if (g_str_equal(state, "4way_handshake") == TRUE)
+               return SUPPLICANT_STATE_4WAY_HANDSHAKE;
+       else if (g_str_equal(state, "completed") == TRUE)
+               return SUPPLICANT_STATE_COMPLETED;
+
+       return SUPPLICANT_STATE_UNKNOWN;
+}
+
+static void callback_system_ready(void)
+{
+       if (system_ready == TRUE)
+               return;
+
+       system_ready = TRUE;
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->system_ready == NULL)
+               return;
+
+       callbacks_pointer->system_ready();
+}
+
+static void callback_system_killed(void)
+{
+       system_ready = FALSE;
+
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->system_killed == NULL)
+               return;
+
+       callbacks_pointer->system_killed();
+}
+
+static void callback_interface_added(struct supplicant_interface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->interface_added == NULL)
+               return;
+
+       callbacks_pointer->interface_added(interface);
+}
+
+static void callback_interface_removed(struct supplicant_interface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->interface_removed == NULL)
+               return;
+
+       callbacks_pointer->interface_removed(interface);
+}
+
+static void callback_scan_started(struct supplicant_interface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->scan_started == NULL)
+               return;
+
+       callbacks_pointer->scan_started(interface);
+}
+
+static void callback_scan_finished(struct supplicant_interface *interface)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->scan_finished == NULL)
+               return;
+
+       callbacks_pointer->scan_finished(interface);
+}
+
+static void callback_network_added(struct supplicant_network *network)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_added == NULL)
+               return;
+
+       callbacks_pointer->network_added(network);
+}
+
+static void callback_network_removed(struct supplicant_network *network)
+{
+       if (callbacks_pointer == NULL)
+               return;
+
+       if (callbacks_pointer->network_removed == NULL)
+               return;
+
+       callbacks_pointer->network_removed(network);
+}
+
+static void remove_interface(gpointer data)
+{
+       struct supplicant_interface *interface = data;
+
+       g_hash_table_destroy(interface->bss_mapping);
+       g_hash_table_destroy(interface->net_mapping);
+       g_hash_table_destroy(interface->network_table);
+
+       callback_interface_removed(interface);
+
+       g_free(interface->path);
+       g_free(interface->ifname);
+       g_free(interface->driver);
+       g_free(interface->bridge);
+       g_free(interface);
+}
+
+static void remove_network(gpointer data)
+{
+       struct supplicant_network *network = data;
+
+       g_hash_table_destroy(network->bss_table);
+
+       callback_network_removed(network);
+
+       g_hash_table_destroy(network->config_table);
+
+       g_free(network->group);
+       g_free(network->name);
+       g_free(network);
+}
+
+static void remove_bss(gpointer data)
+{
+       struct supplicant_bss *bss = data;
+
+       g_free(bss->path);
+       g_free(bss);
+}
+
+static void debug_strvalmap(const char *label, struct strvalmap *map,
+                                                       unsigned int val)
+{
+       int i;
+
+       for (i = 0; map[i].str != NULL; i++) {
+               if (val & map[i].val)
+                       DBG("%s: %s", label, map[i].str);
+       }
+}
+
+static void interface_capability_keymgmt(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; keymgmt_capa_map[i].str != NULL; i++)
+               if (strcmp(str, keymgmt_capa_map[i].str) == 0) {
+                       interface->keymgmt_capa |= keymgmt_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_authalg(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; authalg_capa_map[i].str != NULL; i++)
+               if (strcmp(str, authalg_capa_map[i].str) == 0) {
+                       interface->authalg_capa |= authalg_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_proto(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; proto_capa_map[i].str != NULL; i++)
+               if (strcmp(str, proto_capa_map[i].str) == 0) {
+                       interface->proto_capa |= proto_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_pairwise(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; pairwise_capa_map[i].str != NULL; i++)
+               if (strcmp(str, pairwise_capa_map[i].str) == 0) {
+                       interface->pairwise_capa |= pairwise_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_group(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; group_capa_map[i].str != NULL; i++)
+               if (strcmp(str, group_capa_map[i].str) == 0) {
+                       interface->group_capa |= group_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_scan(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; scan_capa_map[i].str != NULL; i++)
+               if (strcmp(str, scan_capa_map[i].str) == 0) {
+                       interface->scan_capa |= scan_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability_mode(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; mode_capa_map[i].str != NULL; i++)
+               if (strcmp(str, mode_capa_map[i].str) == 0) {
+                       interface->mode_capa |= mode_capa_map[i].val;
+                       break;
+               }
+}
+
+static void interface_capability(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+
+       if (key == NULL)
+               return;
+
+       if (g_strcmp0(key, "KeyMgmt") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_keymgmt, interface);
+       else if (g_strcmp0(key, "AuthAlg") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_authalg, interface);
+       else if (g_strcmp0(key, "Protocol") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_proto, interface);
+       else if (g_strcmp0(key, "Pairwise") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_pairwise, interface);
+       else if (g_strcmp0(key, "Group") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_group, interface);
+       else if (g_strcmp0(key, "Scan") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_scan, interface);
+       else if (g_strcmp0(key, "Modes") == 0)
+               supplicant_dbus_array_foreach(iter,
+                               interface_capability_mode, interface);
+       else
+               DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+const char *supplicant_interface_get_ifname(struct supplicant_interface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return interface->ifname;
+}
+
+const char *supplicant_interface_get_driver(struct supplicant_interface *interface)
+{
+       if (interface == NULL)
+               return NULL;
+
+       return interface->driver;
+}
+
+struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network)
+{
+       if (network == NULL)
+               return NULL;
+
+       return network->interface;
+}
+
+const char *supplicant_network_get_name(struct supplicant_network *network)
+{
+       if (network == NULL || network->name == NULL)
+               return "";
+
+       return network->name;
+}
+
+const char *supplicant_network_get_identifier(struct supplicant_network *network)
+{
+       if (network == NULL || network->group == NULL)
+               return "";
+
+       return network->group;
+}
+
+enum supplicant_mode supplicant_network_get_mode(struct supplicant_network *network)
+{
+       if (network == NULL)
+               return SUPPLICANT_MODE_UNKNOWN;
+
+       return network->mode;
+}
+
+static void merge_network(struct supplicant_network *network)
+{
+       GString *str;
+       const char *ssid, *mode, *key_mgmt;
+       unsigned int i, ssid_len;
+       char *group;
+
+       ssid = g_hash_table_lookup(network->config_table, "ssid");
+       mode = g_hash_table_lookup(network->config_table, "mode");
+       key_mgmt = g_hash_table_lookup(network->config_table, "key_mgmt");
+
+       DBG("ssid %s mode %s", ssid, mode);
+
+       if (ssid != NULL)
+               ssid_len = strlen(ssid);
+       else
+               ssid_len = 0;
+
+       str = g_string_sized_new((ssid_len * 2) + 24);
+       if (str == NULL)
+               return;
+
+       for (i = 0; i < ssid_len; i++)
+               g_string_append_printf(str, "%02x", ssid[i]);
+
+       if (g_strcmp0(mode, "0") == 0)
+               g_string_append_printf(str, "_infra");
+       else if (g_strcmp0(mode, "1") == 0)
+               g_string_append_printf(str, "_adhoc");
+
+       if (g_strcmp0(key_mgmt, "WPA-PSK") == 0)
+               g_string_append_printf(str, "_psk");
+
+       group = g_string_free(str, FALSE);
+
+       DBG("%s", group);
+
+       g_free(group);
+
+       g_hash_table_destroy(network->config_table);
+
+       g_free(network->path);
+       g_free(network);
+}
+
+static void network_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct supplicant_network *network = user_data;
+
+       if (network->interface == NULL)
+               return;
+
+       if (key == NULL) {
+               merge_network(network);
+               return;
+       }
+
+       if (g_strcmp0(key, "Enabled") == 0) {
+               dbus_bool_t enabled = FALSE;
+
+               dbus_message_iter_get_basic(iter, &enabled);
+       } else if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       g_hash_table_replace(network->config_table,
+                                               g_strdup(key), g_strdup(str));
+       } else
+               DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void interface_network_added(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       struct supplicant_network *network;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       network = g_hash_table_lookup(interface->net_mapping, path);
+       if (network != NULL)
+               return;
+
+       network = g_try_new0(struct supplicant_network, 1);
+       if (network == NULL)
+               return;
+
+       network->interface = interface;
+       network->path = g_strdup(path);
+
+       network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               supplicant_dbus_property_foreach(iter, network_property,
+                                                               network);
+               network_property(NULL, NULL, network);
+               return;
+       }
+
+       supplicant_dbus_property_get_all(path,
+                               SUPPLICANT_INTERFACE ".Interface.Network",
+                                               network_property, network);
+}
+
+static void interface_network_removed(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       struct supplicant_network *network;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->net_mapping, path);
+       if (network == NULL)
+               return;
+
+       g_hash_table_remove(interface->net_mapping, path);
+}
+
+static char *create_name(unsigned char *ssid, int ssid_len)
+{
+       char *name;
+       int i;
+
+       if (ssid_len < 1 || ssid[0] == '\0')
+               name = NULL;
+       else
+               name = g_try_malloc0(ssid_len + 1);
+
+       if (name == NULL)
+               return g_strdup("");
+
+       for (i = 0; i < ssid_len; i++) {
+               if (g_ascii_isprint(ssid[i]))
+                       name[i] = ssid[i];
+               else
+                       name[i] = ' ';
+       }
+
+       return name;
+}
+
+static char *create_group(struct supplicant_bss *bss)
+{
+       GString *str;
+       unsigned int i;
+       const char *mode, *security;
+
+       str = g_string_sized_new((bss->ssid_len * 2) + 24);
+       if (str == NULL)
+               return NULL;
+
+       if (bss->ssid_len > 0 && bss->ssid[0] != '\0') {
+               for (i = 0; i < bss->ssid_len; i++)
+                       g_string_append_printf(str, "%02x", bss->ssid[i]);
+       } else
+               g_string_append_printf(str, "hidden");
+
+       mode = mode2string(bss->mode);
+       if (mode != NULL)
+               g_string_append_printf(str, "_%s", mode);
+
+       security = security2string(bss->security);
+       if (security != NULL)
+               g_string_append_printf(str, "_%s", security);
+
+       return g_string_free(str, FALSE);
+}
+
+static void add_bss_to_network(struct supplicant_bss *bss)
+{
+       struct supplicant_interface *interface = bss->interface;
+       struct supplicant_network *network;
+       char *group;
+
+       group = create_group(bss);
+       if (group == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->network_table, group);
+       if (network != NULL) {
+               g_free(group);
+               goto done;
+       }
+
+       network = g_try_new0(struct supplicant_network, 1);
+       if (network == NULL) {
+               g_free(group);
+               return;
+       }
+
+       network->group = group;
+       network->name = create_name(bss->ssid, bss->ssid_len);
+       network->mode = bss->mode;
+
+       network->bss_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, remove_bss);
+
+       network->config_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       g_free, g_free);
+
+       g_hash_table_replace(interface->network_table,
+                                               network->group, network);
+
+       callback_network_added(network);
+
+done:
+       g_hash_table_replace(interface->bss_mapping, bss->path, network);
+       g_hash_table_replace(network->bss_table, bss->path, bss);
+
+       g_hash_table_replace(bss_mapping, bss->path, interface);
+}
+
+static unsigned char wifi_oui[3]      = { 0x00, 0x50, 0xf2 };
+static unsigned char ieee80211_oui[3] = { 0x00, 0x0f, 0xac };
+
+static void extract_rsn(struct supplicant_bss *bss,
+                                       const unsigned char *buf, int len)
+{
+       uint16_t count;
+       int i;
+
+       /* Version */
+       if (len < 2)
+               return;
+
+       buf += 2;
+       len -= 2;
+
+       /* Group cipher */
+       if (len < 4)
+               return;
+
+       buf += 4;
+       len -= 4;
+
+       /* Pairwise cipher */
+       if (len < 2)
+               return;
+
+       count = buf[0] | (buf[1] << 8);
+       if (2 + (count * 4) > len)
+               return;
+
+       buf += 2 + (count * 4);
+       len -= 2 + (count * 4);
+
+       /* Authentication */
+       if (len < 2)
+               return;
+
+       count = buf[0] | (buf[1] << 8);
+       if (2 + (count * 4) > len)
+               return;
+
+       for (i = 0; i < count; i++) {
+               const unsigned char *ptr = buf + 2 + (i * 4);
+
+               if (memcmp(ptr, wifi_oui, 3) == 0) {
+                       switch (ptr[3]) {
+                       case 1:
+                               bss->ieee8021x = TRUE;
+                               break;
+                       case 2:
+                               bss->psk = TRUE;
+                               break;
+                       }
+               } else if (memcmp(ptr, ieee80211_oui, 3) == 0) {
+                       switch (ptr[3]) {
+                       case 1:
+                               bss->ieee8021x = TRUE;
+                               break;
+                       case 2:
+                               bss->psk = TRUE;
+                               break;
+                       }
+               }
+       }
+
+}
+
+static void bss_rates(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_bss *bss = user_data;
+       dbus_uint32_t rate = 0;
+
+       dbus_message_iter_get_basic(iter, &rate);
+       if (rate == 0)
+               return;
+
+       if (rate > bss->maxrate)
+               bss->maxrate = rate;
+}
+
+static void bss_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct supplicant_bss *bss = user_data;
+
+       if (bss->interface == NULL)
+               return;
+
+       if (key == NULL) {
+               if (bss->ieee8021x == TRUE)
+                       bss->security = SUPPLICANT_SECURITY_IEEE8021X;
+               else if (bss->psk == TRUE)
+                       bss->security = SUPPLICANT_SECURITY_PSK;
+               else if (bss->privacy == TRUE)
+                       bss->security = SUPPLICANT_SECURITY_WEP;
+               else
+                       bss->security = SUPPLICANT_SECURITY_NONE;
+
+               add_bss_to_network(bss);
+               return;
+       }
+
+       if (g_strcmp0(key, "BSSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *addr;
+               int addr_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
+
+               if (addr_len == 6)
+                       memcpy(bss->bssid, addr, addr_len);
+       } else if (g_strcmp0(key, "SSID") == 0) {
+               DBusMessageIter array;
+               unsigned char *ssid;
+               int ssid_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+               if (ssid_len > 0 && ssid_len < 33) {
+                       memcpy(bss->ssid, ssid, ssid_len);
+                       bss->ssid_len = ssid_len;
+               } else {
+                       memset(bss->ssid, 0, sizeof(bss->ssid));
+                       bss->ssid_len = 0;
+               }
+       } else if (g_strcmp0(key, "Capabilities") == 0) {
+               dbus_uint16_t capabilities = 0x0000;
+
+               dbus_message_iter_get_basic(iter, &capabilities);
+
+               if (capabilities & IEEE80211_CAP_ESS)
+                       bss->mode = SUPPLICANT_MODE_INFRA;
+               else if (capabilities & IEEE80211_CAP_IBSS)
+                       bss->mode = SUPPLICANT_MODE_IBSS;
+
+               if (capabilities & IEEE80211_CAP_PRIVACY)
+                       bss->privacy = TRUE;
+       } else if (g_strcmp0(key, "Mode") == 0) {
+               const char *mode = NULL;
+
+               dbus_message_iter_get_basic(iter, &mode);
+               bss->mode = string2mode(mode);
+       } else if (g_strcmp0(key, "Frequency") == 0) {
+               dbus_uint16_t frequency = 0;
+
+               dbus_message_iter_get_basic(iter, &frequency);
+               bss->frequency = frequency;
+       } else if (g_strcmp0(key, "Signal") == 0) {
+               dbus_int16_t signal = 0;
+
+               dbus_message_iter_get_basic(iter, &signal);
+       } else if (g_strcmp0(key, "Level") == 0) {
+               dbus_int32_t level = 0;
+
+               dbus_message_iter_get_basic(iter, &level);
+       } else if (g_strcmp0(key, "Rates") == 0) {
+               supplicant_dbus_array_foreach(iter, bss_rates, bss);
+       } else if (g_strcmp0(key, "MaxRate") == 0) {
+               dbus_uint32_t maxrate = 0;
+
+               dbus_message_iter_get_basic(iter, &maxrate);
+               if (maxrate != 0)
+                       bss->maxrate =maxrate;
+       } else if (g_strcmp0(key, "Privacy") == 0) {
+               dbus_bool_t privacy = FALSE;
+
+               dbus_message_iter_get_basic(iter, &privacy);
+               bss->privacy = privacy;
+       } else if (g_strcmp0(key, "RSNIE") == 0) {
+               DBusMessageIter array;
+               unsigned char *ie;
+               int ie_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+               if (ie_len > 2)
+                       extract_rsn(bss, ie + 2, ie_len - 2);
+       } else if (g_strcmp0(key, "WPAIE") == 0) {
+               DBusMessageIter array;
+               unsigned char *ie;
+               int ie_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+               if (ie_len > 6)
+                       extract_rsn(bss, ie + 6, ie_len - 6);
+       } else if (g_strcmp0(key, "WPSIE") == 0) {
+               DBusMessageIter array;
+               unsigned char *ie;
+               int ie_len;
+
+               dbus_message_iter_recurse(iter, &array);
+               dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+       } else
+               DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void interface_bss_added(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       struct supplicant_network *network;
+       struct supplicant_bss *bss;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network != NULL) {
+               bss = g_hash_table_lookup(network->bss_table, path);
+               if (bss != NULL)
+                       return;
+       }
+
+       bss = g_try_new0(struct supplicant_bss, 1);
+       if (bss == NULL)
+               return;
+
+       bss->interface = interface;
+       bss->path = g_strdup(path);
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               supplicant_dbus_property_foreach(iter, bss_property, bss);
+               bss_property(NULL, NULL, bss);
+               return;
+       }
+
+       supplicant_dbus_property_get_all(path,
+                                       SUPPLICANT_INTERFACE ".Interface.BSS",
+                                                       bss_property, bss);
+}
+
+static void interface_bss_removed(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+       struct supplicant_network *network;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network == NULL)
+               return;
+
+       g_hash_table_remove(bss_mapping, path);
+
+       g_hash_table_remove(interface->bss_mapping, path);
+       g_hash_table_remove(network->bss_table, path);
+
+       if (g_hash_table_size(network->bss_table) == 0)
+               g_hash_table_remove(interface->network_table, network->group);
+}
+
+static void interface_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct supplicant_interface *interface = user_data;
+
+       if (interface == NULL)
+               return;
+
+       if (key == NULL) {
+               debug_strvalmap("KeyMgmt capability", keymgmt_capa_map,
+                                               interface->keymgmt_capa);
+               debug_strvalmap("AuthAlg capability", authalg_capa_map,
+                                               interface->authalg_capa);
+               debug_strvalmap("Protocol capability", proto_capa_map,
+                                               interface->proto_capa);
+               debug_strvalmap("Pairwise capability", pairwise_capa_map,
+                                               interface->pairwise_capa);
+               debug_strvalmap("Group capability", group_capa_map,
+                                               interface->group_capa);
+               debug_strvalmap("Scan capability", scan_capa_map,
+                                               interface->scan_capa);
+               debug_strvalmap("Mode capability", mode_capa_map,
+                                               interface->mode_capa);
+
+               interface->ready = TRUE;
+               callback_interface_added(interface);
+               return;
+       }
+
+       if (g_strcmp0(key, "Capabilities") == 0) {
+               supplicant_dbus_property_foreach(iter, interface_capability,
+                                                               interface);
+       } else if (g_strcmp0(key, "State") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       interface->state = string2state(str);
+
+               DBG("state %s (%d)", str, interface->state);
+       } else if (g_strcmp0(key, "Scanning") == 0) {
+               dbus_bool_t scanning = FALSE;
+
+               dbus_message_iter_get_basic(iter, &scanning);
+               interface->scanning = scanning;
+
+               if (interface->ready == TRUE) {
+                       if (interface->scanning == TRUE)
+                               callback_scan_started(interface);
+                       else
+                               callback_scan_finished(interface);
+               }
+       } else if (g_strcmp0(key, "ApScan") == 0) {
+               int apscan = 1;
+
+               dbus_message_iter_get_basic(iter, &apscan);
+               interface->apscan = apscan;
+       } else if (g_strcmp0(key, "Ifname") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       interface->ifname = g_strdup(str);
+       } else if (g_strcmp0(key, "Driver") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       interface->driver = g_strdup(str);
+       } else if (g_strcmp0(key, "BridgeIfname") == 0) {
+               const char *str = NULL;
+
+               dbus_message_iter_get_basic(iter, &str);
+               if (str != NULL)
+                       interface->bridge = g_strdup(str);
+       } else if (g_strcmp0(key, "CurrentBSS") == 0) {
+               interface_bss_added(iter, interface);
+       } else if (g_strcmp0(key, "CurrentNetwork") == 0) {
+               interface_network_added(iter, interface);
+       } else if (g_strcmp0(key, "BSSs") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_bss_added,
+                                                               interface);
+       } else if (g_strcmp0(key, "Blobs") == 0) {
+       } else if (g_strcmp0(key, "Networks") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_network_added,
+                                                               interface);
+       } else
+               DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static struct supplicant_interface *interface_alloc(const char *path)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_try_new0(struct supplicant_interface, 1);
+       if (interface == NULL)
+               return NULL;
+
+       interface->path = g_strdup(path);
+
+       interface->network_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                       NULL, remove_network);
+
+       interface->net_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+       interface->bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       g_hash_table_replace(interface_table, interface->path, interface);
+
+       return interface;
+}
+
+static void interface_added(DBusMessageIter *iter, void *user_data)
+{
+       struct supplicant_interface *interface;
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       if (g_strcmp0(path, "/") == 0)
+               return;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface != NULL)
+               return;
+
+       interface = interface_alloc(path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_next(iter);
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID) {
+               supplicant_dbus_property_foreach(iter, interface_property,
+                                                               interface);
+               interface_property(NULL, NULL, interface);
+               return;
+       }
+
+       supplicant_dbus_property_get_all(path,
+                                       SUPPLICANT_INTERFACE ".Interface",
+                                               interface_property, interface);
+}
+
+static void interface_removed(DBusMessageIter *iter, void *user_data)
+{
+       const char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL)
+               return;
+
+       g_hash_table_remove(interface_table, path);
+}
+
+static void eap_method(DBusMessageIter *iter, void *user_data)
+{
+       const char *str = NULL;
+       int i;
+
+       dbus_message_iter_get_basic(iter, &str);
+       if (str == NULL)
+               return;
+
+       for (i = 0; eap_method_map[i].str != NULL; i++)
+               if (strcmp(str, eap_method_map[i].str) == 0) {
+                       eap_methods |= eap_method_map[i].val;
+                       break;
+               }
+}
+
+static void service_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       if (key == NULL) {
+               callback_system_ready();
+               return;
+       }
+
+       if (g_strcmp0(key, "DebugLevel") == 0) {
+               const char *str = NULL;
+               int i;
+
+               dbus_message_iter_get_basic(iter, &str);
+               for (i = 0; debug_strings[i] != NULL; i++)
+                       if (g_strcmp0(debug_strings[i], str) == 0) {
+                               debug_level = i;
+                               break;
+                       }
+               DBG("Debug level %d", debug_level);
+       } else if (g_strcmp0(key, "DebugTimestamp") == 0) {
+               dbus_message_iter_get_basic(iter, &debug_timestamp);
+               DBG("Debug timestamp %u", debug_timestamp);
+       } else if (g_strcmp0(key, "DebugShowKeys") == 0) {
+               dbus_message_iter_get_basic(iter, &debug_showkeys);
+               DBG("Debug show keys %u", debug_showkeys);
+       } else if (g_strcmp0(key, "Interfaces") == 0) {
+               supplicant_dbus_array_foreach(iter, interface_added, NULL);
+       } else if (g_strcmp0(key, "EapMethods") == 0) {
+               supplicant_dbus_array_foreach(iter, eap_method, NULL);
+               debug_strvalmap("EAP method", eap_method_map, eap_methods);
+       } else
+               DBG("key %s type %c",
+                               key, dbus_message_iter_get_arg_type(iter));
+}
+
+static void supplicant_bootstrap(void)
+{
+       supplicant_dbus_property_get_all(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               service_property, NULL);
+}
+
+static void signal_name_owner_changed(const char *path, DBusMessageIter *iter)
+{
+       const char *name = NULL, *old = NULL, *new = NULL;
+
+       if (g_strcmp0(path, DBUS_PATH_DBUS) != 0)
+               return;
+
+       dbus_message_iter_get_basic(iter, &name);
+       if (name == NULL)
+               return;
+
+       if (g_strcmp0(name, SUPPLICANT_SERVICE) != 0)
+               return;
+
+       dbus_message_iter_next(iter);
+       dbus_message_iter_get_basic(iter, &old);
+       dbus_message_iter_next(iter);
+       dbus_message_iter_get_basic(iter, &new);
+
+       if (old == NULL || new == NULL)
+               return;
+
+       if (strlen(old) > 0 && strlen(new) == 0) {
+               system_available = FALSE;
+               g_hash_table_remove_all(bss_mapping);
+               g_hash_table_remove_all(interface_table);
+               callback_system_killed();
+       }
+
+       if (strlen(new) > 0 && strlen(old) == 0) {
+               system_available = TRUE;
+               supplicant_bootstrap();
+       }
+}
+
+static void signal_properties_changed(const char *path, DBusMessageIter *iter)
+{
+       if (g_strcmp0(path, SUPPLICANT_PATH) != 0)
+               return;
+
+       supplicant_dbus_property_foreach(iter, service_property, NULL);
+}
+
+static void signal_interface_added(const char *path, DBusMessageIter *iter)
+{
+       if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+               interface_added(iter, NULL);
+}
+
+static void signal_interface_removed(const char *path, DBusMessageIter *iter)
+{
+       if (g_strcmp0(path, SUPPLICANT_PATH) == 0)
+               interface_removed(iter, NULL);
+}
+
+static void signal_interface_changed(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, interface_property, interface);
+}
+
+static void signal_scan_done(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+       dbus_bool_t success = FALSE;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       dbus_message_iter_get_basic(iter, &success);
+
+       if (interface->scan_callback != NULL) {
+               int result = 0;
+
+               if (success == FALSE)
+                       result = -EIO;
+
+               interface->scan_callback(result, interface->scan_data);
+       }
+
+       interface->scan_callback = NULL;
+       interface->scan_data = NULL;
+}
+
+static void signal_bss_added(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_bss_added(iter, interface);
+}
+
+static void signal_bss_removed(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_bss_removed(iter, interface);
+}
+
+static void signal_network_added(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_network_added(iter, interface);
+}
+
+static void signal_network_removed(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL)
+               return;
+
+       interface_network_removed(iter, interface);
+}
+
+static void signal_bss_changed(const char *path, DBusMessageIter *iter)
+{
+       struct supplicant_interface *interface;
+       struct supplicant_network *network;
+       struct supplicant_bss *bss;
+
+       interface = g_hash_table_lookup(bss_mapping, path);
+       if (interface == NULL)
+               return;
+
+       network = g_hash_table_lookup(interface->bss_mapping, path);
+       if (network == NULL)
+               return;
+
+       bss = g_hash_table_lookup(network->bss_table, path);
+       if (bss == NULL)
+               return;
+
+       supplicant_dbus_property_foreach(iter, bss_property, bss);
+}
+
+static struct {
+       const char *interface;
+       const char *member;
+       void (*function) (const char *path, DBusMessageIter *iter);
+} signal_map[] = {
+       { DBUS_INTERFACE_DBUS,  "NameOwnerChanged",  signal_name_owner_changed },
+
+       { SUPPLICANT_INTERFACE, "PropertiesChanged", signal_properties_changed },
+       { SUPPLICANT_INTERFACE, "InterfaceAdded",    signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceCreated",  signal_interface_added    },
+       { SUPPLICANT_INTERFACE, "InterfaceRemoved",  signal_interface_removed  },
+
+       { SUPPLICANT_INTERFACE ".Interface", "PropertiesChanged", signal_interface_changed },
+       { SUPPLICANT_INTERFACE ".Interface", "ScanDone",          signal_scan_done         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSAdded",          signal_bss_added         },
+       { SUPPLICANT_INTERFACE ".Interface", "BSSRemoved",        signal_bss_removed       },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkAdded",      signal_network_added     },
+       { SUPPLICANT_INTERFACE ".Interface", "NetworkRemoved",    signal_network_removed   },
+
+       { SUPPLICANT_INTERFACE ".Interface.BSS", "PropertiesChanged", signal_bss_changed   },
+
+       { }
+};
+
+static DBusHandlerResult supplicant_filter(DBusConnection *conn,
+                                       DBusMessage *message, void *data)
+{
+       DBusMessageIter iter;
+       const char *path;
+       int i;
+
+       path = dbus_message_get_path(message);
+       if (path == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (i = 0; signal_map[i].interface != NULL; i++) {
+               if (dbus_message_has_interface(message,
+                                       signal_map[i].interface) == FALSE)
+                       continue;
+
+               if (dbus_message_has_member(message,
+                                       signal_map[i].member) == FALSE)
+                       continue;
+
+               signal_map[i].function(path, &iter);
+               break;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const char *supplicant_rule0 = "type=signal,"
+                                       "path=" DBUS_PATH_DBUS ","
+                                       "sender=" DBUS_SERVICE_DBUS ","
+                                       "interface=" DBUS_INTERFACE_DBUS ","
+                                       "member=NameOwnerChanged,"
+                                       "arg0=" SUPPLICANT_SERVICE;
+static const char *supplicant_rule1 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE;
+static const char *supplicant_rule2 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface";
+static const char *supplicant_rule3 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface.WPS";
+static const char *supplicant_rule4 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface.BSS";
+static const char *supplicant_rule5 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface.Network";
+static const char *supplicant_rule6 = "type=signal,"
+                       "interface=" SUPPLICANT_INTERFACE ".Interface.Blob";
+
+int supplicant_register(const struct supplicant_callbacks *callbacks)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (dbus_connection_add_filter(connection,
+                               supplicant_filter, NULL, NULL) == FALSE) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+               return -EIO;
+       }
+
+       callbacks_pointer = callbacks;
+       eap_methods = 0;
+
+       interface_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               NULL, remove_interface);
+
+       bss_mapping = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       supplicant_dbus_setup(connection);
+
+       dbus_bus_add_match(connection, supplicant_rule0, NULL);
+       dbus_bus_add_match(connection, supplicant_rule1, NULL);
+       dbus_bus_add_match(connection, supplicant_rule2, NULL);
+       dbus_bus_add_match(connection, supplicant_rule3, NULL);
+       dbus_bus_add_match(connection, supplicant_rule4, NULL);
+       dbus_bus_add_match(connection, supplicant_rule5, NULL);
+       dbus_bus_add_match(connection, supplicant_rule6, NULL);
+       dbus_connection_flush(connection);
+
+       if (dbus_bus_name_has_owner(connection,
+                                       SUPPLICANT_SERVICE, NULL) == TRUE) {
+               system_available = TRUE;
+               supplicant_bootstrap();
+       }
+
+       return 0;
+}
+
+void supplicant_unregister(const struct supplicant_callbacks *callbacks)
+{
+       if (connection != NULL) {
+               dbus_bus_remove_match(connection, supplicant_rule6, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule5, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule4, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule3, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule2, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule1, NULL);
+               dbus_bus_remove_match(connection, supplicant_rule0, NULL);
+               dbus_connection_flush(connection);
+
+               dbus_connection_remove_filter(connection,
+                                               supplicant_filter, NULL);
+       }
+
+       if (bss_mapping != NULL) {
+               g_hash_table_destroy(bss_mapping);
+               bss_mapping = NULL;
+       }
+
+       if (interface_table != NULL) {
+               g_hash_table_destroy(interface_table);
+               interface_table = NULL;
+       }
+
+       if (system_available == TRUE)
+               callback_system_killed();
+
+       if (connection != NULL) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+       }
+
+       callbacks_pointer = NULL;
+       eap_methods = 0;
+}
+
+static void debug_level_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       if (error != NULL)
+               DBG("debug level failure: %s", error);
+}
+
+static void debug_level_params(DBusMessageIter *iter, void *user_data)
+{
+       guint level = GPOINTER_TO_UINT(user_data);
+       const char *str;
+
+       if (level > 4)
+               level = 4;
+
+       str = debug_strings[level];
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+}
+
+void supplicant_set_debug_level(unsigned int level)
+{
+       if (system_available == FALSE)
+               return;
+
+       supplicant_dbus_property_set(SUPPLICANT_PATH, SUPPLICANT_INTERFACE,
+                               "DebugLevel", DBUS_TYPE_STRING_AS_STRING,
+                               debug_level_params, debug_level_result,
+                                               GUINT_TO_POINTER(level));
+}
+
+struct interface_create_data {
+       const char *ifname;
+       const char *driver;
+       struct supplicant_interface *interface;
+       supplicant_interface_create_callback callback;
+       void *user_data;
+};
+
+static void interface_create_property(const char *key, DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       struct supplicant_interface *interface = data->interface;
+
+       if (key == NULL) {
+               if (data->callback != NULL)
+                       data->callback(0, data->interface, data->user_data);
+
+               dbus_free(data);
+       }
+
+       interface_property(key, iter, interface);
+}
+
+static void interface_create_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       const char *path = NULL;
+       int err;
+
+       if (error != NULL) {
+               err = -EIO;
+               goto done;
+       }
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (system_available == FALSE) {
+               err = -EFAULT;
+               goto done;
+       }
+
+       data->interface = g_hash_table_lookup(interface_table, path);
+       if (data->interface == NULL) {
+               data->interface = interface_alloc(path);
+               if (data->interface == NULL) {
+                       err = -ENOMEM;
+                       goto done;
+               }
+       }
+
+       err = supplicant_dbus_property_get_all(path,
+                                       SUPPLICANT_INTERFACE ".Interface",
+                                       interface_create_property, data);
+       if (err == 0)
+               return;
+
+done:
+       if (data->callback != NULL)
+               data->callback(err, NULL, data->user_data);
+
+       dbus_free(data);
+}
+
+static void interface_create_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       DBusMessageIter dict;
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       supplicant_dbus_dict_append_basic(&dict, "Ifname",
+                                       DBUS_TYPE_STRING, &data->ifname);
+
+       if (data->driver != NULL)
+               supplicant_dbus_dict_append_basic(&dict, "Driver",
+                                       DBUS_TYPE_STRING, &data->driver);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+static void interface_get_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+       struct supplicant_interface *interface;
+       const char *path = NULL;
+       int err;
+
+       if (error != NULL)
+               goto create;
+
+       dbus_message_iter_get_basic(iter, &path);
+       if (path == NULL) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       interface = g_hash_table_lookup(interface_table, path);
+       if (interface == NULL) {
+               err = -ENOENT;
+               goto done;
+       }
+
+       if (data->callback != NULL)
+               data->callback(0, interface, data->user_data);
+
+       dbus_free(data);
+
+       return;
+
+create:
+       if (system_available == FALSE) {
+               err = -EFAULT;
+               goto done;
+       }
+
+       err = supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "CreateInterface",
+                                               interface_create_params,
+                                               interface_create_result, data);
+       if (err == 0)
+               return;
+
+done:
+       if (data->callback != NULL)
+               data->callback(err, NULL, data->user_data);
+
+       dbus_free(data);
+}
+
+static void interface_get_params(DBusMessageIter *iter, void *user_data)
+{
+       struct interface_create_data *data = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->ifname);
+}
+
+int supplicant_interface_create(const char *ifname, const char *driver,
+                       supplicant_interface_create_callback callback,
+                                                       void *user_data)
+{
+       struct interface_create_data *data;
+
+       if (ifname == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->ifname = ifname;
+       data->driver = driver;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(SUPPLICANT_PATH,
+                                               SUPPLICANT_INTERFACE,
+                                               "GetInterface",
+                                               interface_get_params,
+                                               interface_get_result, data);
+}
+
+int supplicant_interface_remove(struct supplicant_interface *interface,
+                       supplicant_interface_remove_callback callback,
+                                                       void *user_data)
+{
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       return 0;
+}
+
+struct interface_scan_data {
+       struct supplicant_interface *interface;
+       supplicant_interface_scan_callback callback;
+       void *user_data;
+};
+
+static void interface_scan_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_scan_data *data = user_data;
+
+       if (error != NULL) {
+               if (data->callback != NULL)
+                       data->callback(-EIO, data->user_data);
+       } else {
+               data->interface->scan_callback = data->callback;
+               data->interface->scan_data = data->user_data;
+       }
+
+       dbus_free(data);
+}
+
+static void interface_scan_params(DBusMessageIter *iter, void *user_data)
+{
+       DBusMessageIter dict;
+       const char *type = "passive";
+
+       supplicant_dbus_dict_open(iter, &dict);
+
+       supplicant_dbus_dict_append_basic(&dict, "Type",
+                                               DBUS_TYPE_STRING, &type);
+
+       supplicant_dbus_dict_close(iter, &dict);
+}
+
+int supplicant_interface_scan(struct supplicant_interface *interface,
+                       supplicant_interface_scan_callback callback,
+                                                       void *user_data)
+{
+       struct interface_scan_data *data;
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       if (interface->scanning == TRUE)
+               return -EALREADY;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->interface = interface;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "Scan",
+                       interface_scan_params, interface_scan_result, data);
+}
+
+struct interface_disconnect_data {
+       supplicant_interface_disconnect_callback callback;
+       void *user_data;
+};
+
+static void interface_disconnect_result(const char *error,
+                               DBusMessageIter *iter, void *user_data)
+{
+       struct interface_disconnect_data *data = user_data;
+       int result = 0;
+
+       if (error != NULL)
+               result = -EIO;
+
+       if (data->callback != NULL)
+               data->callback(result, data->user_data);
+
+       dbus_free(data);
+}
+
+int supplicant_interface_disconnect(struct supplicant_interface *interface,
+                       supplicant_interface_disconnect_callback callback,
+                                                       void *user_data)
+{
+       struct interface_disconnect_data *data;
+
+       if (interface == NULL)
+               return -EINVAL;
+
+       if (system_available == FALSE)
+               return -EFAULT;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return supplicant_dbus_method_call(interface->path,
+                       SUPPLICANT_INTERFACE ".Interface", "Disconnect",
+                               NULL, interface_disconnect_result, data);
+}
diff --git a/tools/supplicant.h b/tools/supplicant.h
new file mode 100644 (file)
index 0000000..556dc7d
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#define SUPPLICANT_EAP_METHOD_MD5      (1 << 0)
+#define SUPPLICANT_EAP_METHOD_TLS      (1 << 1)
+#define SUPPLICANT_EAP_METHOD_MSCHAPV2 (1 << 2)
+#define SUPPLICANT_EAP_METHOD_PEAP     (1 << 3)
+#define SUPPLICANT_EAP_METHOD_TTLS     (1 << 4)
+#define SUPPLICANT_EAP_METHOD_GTC      (1 << 5)
+#define SUPPLICANT_EAP_METHOD_OTP      (1 << 6)
+#define SUPPLICANT_EAP_METHOD_LEAP     (1 << 7)
+#define SUPPLICANT_EAP_METHOD_WSC      (1 << 8)
+
+#define SUPPLICANT_CAPABILITY_KEYMGMT_NONE     (1 << 0)
+#define SUPPLICANT_CAPABILITY_KEYMGMT_IEEE8021X        (1 << 1)
+#define SUPPLICANT_CAPABILITY_KEYMGMT_WPA_NONE (1 << 2)
+#define SUPPLICANT_CAPABILITY_KEYMGMT_WPA_PSK  (1 << 3)
+#define SUPPLICANT_CAPABILITY_KEYMGMT_WPA_EAP  (1 << 4)
+#define SUPPLICANT_CAPABILITY_KEYMGMT_WPS      (1 << 5)
+
+#define SUPPLICANT_CAPABILITY_AUTHALG_OPEN     (1 << 0)
+#define SUPPLICANT_CAPABILITY_AUTHALG_SHARED   (1 << 1)
+#define SUPPLICANT_CAPABILITY_AUTHALG_LEAP     (1 << 2)
+
+#define SUPPLICANT_CAPABILITY_PROTO_WPA                (1 << 0)
+#define SUPPLICANT_CAPABILITY_PROTO_RSN                (1 << 1)
+
+#define SUPPLICANT_CAPABILITY_GROUP_WEP40      (1 << 0)
+#define SUPPLICANT_CAPABILITY_GROUP_WEP104     (1 << 1)
+#define SUPPLICANT_CAPABILITY_GROUP_TKIP       (1 << 2)
+#define SUPPLICANT_CAPABILITY_GROUP_CCMP       (1 << 3)
+
+#define SUPPLICANT_CAPABILITY_PAIRWISE_NONE    (1 << 0)
+#define SUPPLICANT_CAPABILITY_PAIRWISE_TKIP    (1 << 1)
+#define SUPPLICANT_CAPABILITY_PAIRWISE_CCMP    (1 << 2)
+
+#define SUPPLICANT_CAPABILITY_SCAN_ACTIVE      (1 << 0)
+#define SUPPLICANT_CAPABILITY_SCAN_PASSIVE     (1 << 1)
+#define SUPPLICANT_CAPABILITY_SCAN_SSID                (1 << 2)
+
+#define SUPPLICANT_CAPABILITY_MODE_INFRA       (1 << 0)
+#define SUPPLICANT_CAPABILITY_MODE_IBSS                (1 << 1)
+#define SUPPLICANT_CAPABILITY_MODE_AP          (1 << 2)
+
+enum supplicant_mode {
+       SUPPLICANT_MODE_UNKNOWN,
+       SUPPLICANT_MODE_INFRA,
+       SUPPLICANT_MODE_IBSS,
+};
+
+enum supplicant_security {
+       SUPPLICANT_SECURITY_UNKNOWN,
+       SUPPLICANT_SECURITY_NONE,
+       SUPPLICANT_SECURITY_WEP,
+       SUPPLICANT_SECURITY_PSK,
+       SUPPLICANT_SECURITY_IEEE8021X,
+};
+
+enum supplicant_state {
+       SUPPLICANT_STATE_UNKNOWN,
+       SUPPLICANT_STATE_DISCONNECTED,
+       SUPPLICANT_STATE_INACTIVE,
+       SUPPLICANT_STATE_SCANNING,
+       SUPPLICANT_STATE_AUTHENTICATING,
+       SUPPLICANT_STATE_ASSOCIATING,
+       SUPPLICANT_STATE_ASSOCIATED,
+       SUPPLICANT_STATE_4WAY_HANDSHAKE,
+       SUPPLICANT_STATE_GROUP_HANDSHAKE,
+       SUPPLICANT_STATE_COMPLETED,
+};
+
+struct supplicant_interface;
+
+typedef void (* supplicant_interface_create_callback) (int result,
+                               struct supplicant_interface *interface,
+                                                       void *user_data);
+typedef void (* supplicant_interface_remove_callback) (int result,
+                                                       void *user_data);
+typedef void (* supplicant_interface_scan_callback) (int result,
+                                                       void *user_data);
+typedef void (* supplicant_interface_disconnect_callback) (int result,
+                                                       void *user_data);
+
+int supplicant_interface_create(const char *ifname, const char *driver,
+                       supplicant_interface_create_callback callback,
+                                                       void *user_data);
+int supplicant_interface_remove(struct supplicant_interface *interface,
+                       supplicant_interface_remove_callback callback,
+                                                       void *user_data);
+int supplicant_interface_scan(struct supplicant_interface *interface,
+                       supplicant_interface_scan_callback callback,
+                                                       void *user_data);
+int supplicant_interface_disconnect(struct supplicant_interface *interface,
+                       supplicant_interface_disconnect_callback callback,
+                                                       void *user_data);
+
+const char *supplicant_interface_get_ifname(struct supplicant_interface *interface);
+const char *supplicant_interface_get_driver(struct supplicant_interface *interface);
+
+struct supplicant_network;
+
+struct supplicant_interface *supplicant_network_get_interface(struct supplicant_network *network);
+const char *supplicant_network_get_name(struct supplicant_network *network);
+const char *supplicant_network_get_identifier(struct supplicant_network *network);
+enum supplicant_mode supplicant_network_get_mode(struct supplicant_network *network);
+
+struct supplicant_callbacks {
+       void (*system_ready) (void);
+       void (*system_killed) (void);
+       void (*interface_added) (struct supplicant_interface *interface);
+       void (*interface_removed) (struct supplicant_interface *interface);
+       void (*scan_started) (struct supplicant_interface *interface);
+       void (*scan_finished) (struct supplicant_interface *interface);
+       void (*network_added) (struct supplicant_network *network);
+       void (*network_removed) (struct supplicant_network *network);
+};
+
+int supplicant_register(const struct supplicant_callbacks *callbacks);
+void supplicant_unregister(const struct supplicant_callbacks *callbacks);
+
+void supplicant_set_debug_level(unsigned int level);
diff --git a/tools/tap-test.c b/tools/tap-test.c
new file mode 100644 (file)
index 0000000..1074c9c
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/if_tun.h>
+
+static int inet_ifup(const char *ifname)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       ifr.ifr_flags |= IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+static int create_tap(const char *ifname)
+{
+       struct ifreq ifr;
+       int fd, val;
+
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               perror("Failed to open TUN/TAP device");
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+       if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
+               perror("Failed to set TUN/TAP interface");
+               close(fd);
+               return -1;
+       }
+
+       val = ARPHRD_ETHER;
+
+       if (ioctl(fd, TUNSETLINK, (unsigned long) val) < 0)
+               perror("Failed to set TUN/TAP link type");
+
+       return fd;
+}
+
+static void dump_packet(unsigned char *buf, int len)
+{
+       int i;
+
+       printf("[");
+       for (i = 0; i < len; i++) {
+               printf(" %02x", buf[i]);
+               if ((i + 1) % 16 == 0)
+                       printf("\n ");
+               else if ((i + 1) % 8 == 0)
+                       printf(" ");
+       }
+       printf(" ]\n");
+}
+
+static void decode_ip(unsigned char *buf, int len)
+{
+       struct iphdr *ip = (void *) buf;
+
+       printf("    IP proto %d saddr 0x%08x daddr 0x%08x\n",
+                                       ip->protocol, ip->saddr, ip->daddr);
+}
+
+static void decode_packet(unsigned char *buf, int len)
+{
+       struct ether_header *eh = (void *) buf;
+       char src[18], dst[18];
+       uint16_t type;
+
+       snprintf(dst, sizeof(dst), "%02x:%02x:%02x:%02x:%02x:%02x",
+                               eh->ether_dhost[0], eh->ether_dhost[1],
+                               eh->ether_dhost[2], eh->ether_dhost[3],
+                               eh->ether_dhost[4], eh->ether_dhost[5]);
+
+       snprintf(src, sizeof(src), "%02x:%02x:%02x:%02x:%02x:%02x",
+                               eh->ether_shost[0], eh->ether_shost[1],
+                               eh->ether_shost[2], eh->ether_shost[3],
+                               eh->ether_shost[4], eh->ether_shost[5]);
+
+       type = ntohs(eh->ether_type);
+
+       printf("> type 0x%04x src %s dst %s <\n", type, src, dst);
+
+       switch (type) {
+       case ETHERTYPE_IP:
+               decode_ip(buf + 14, len - 14);
+               break;
+       case ETHERTYPE_LOOPBACK:
+               dump_packet(buf, len);
+               break;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       const char *ifname = "xxx";
+       struct pollfd p;
+       int fd;
+
+       fd = create_tap(ifname);
+       if (fd < 0)
+               return 1;
+
+       if (inet_ifup(ifname) < 0) {
+               close(fd);
+               return 1;
+       }
+
+       memset(&p, 0, sizeof(p));
+       p.fd = fd;
+       p.events = POLLHUP | POLLIN;
+
+       while (1) {
+               unsigned char buf[2048];
+               int len;
+
+               len = poll(&p, 1, -1);
+               if (len < 0)
+                       break;
+               if (len == 0)
+                       continue;
+
+               len = read(fd, buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               decode_packet(buf, len);
+       }
+
+       return 0;
+}
diff --git a/tools/web-test.c b/tools/web-test.c
new file mode 100644 (file)
index 0000000..a507397
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *
+ *  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 <string.h>
+#include <signal.h>
+
+#include <gweb/gweb.h>
+
+static GTimer *timer;
+
+static GMainLoop *main_loop;
+
+static void web_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static gboolean web_result(GWebResult *result, gpointer user_data)
+{
+       const guint8 *chunk;
+       gsize length;
+       guint16 status;
+       gdouble elapsed;
+
+       g_web_result_get_chunk(result, &chunk, &length);
+
+       if (length > 0) {
+               printf("%s\n", (char *) chunk);
+               return TRUE;
+       }
+
+       status = g_web_result_get_status(result);
+
+       g_print("status: %03u\n", status);
+
+       elapsed = g_timer_elapsed(timer, NULL);
+
+       g_print("elapse: %f seconds\n", elapsed);
+
+       g_main_loop_quit(main_loop);
+
+       return FALSE;
+}
+
+static gboolean option_debug = FALSE;
+static gchar *option_proxy = NULL;
+static gchar *option_nameserver = NULL;
+static gchar *option_user_agent = NULL;
+static gchar *option_http_version = NULL;
+
+static GOptionEntry options[] = {
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                                       "Enable debug output" },
+       { "proxy", 'p', 0, G_OPTION_ARG_STRING, &option_proxy,
+                                       "Specify proxy", "ADDRESS" },
+       { "nameserver", 'n', 0, G_OPTION_ARG_STRING, &option_nameserver,
+                                       "Specify nameserver", "ADDRESS" },
+       { "user-agent", 'A', 0, G_OPTION_ARG_STRING, &option_user_agent,
+                                       "Specific user agent", "STRING" },
+       { "http-version", 'H', 0, G_OPTION_ARG_STRING, &option_http_version,
+                                       "Specific HTTP version", "STRING" },
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       struct sigaction sa;
+       GWeb *web;
+       int index = 0;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               return 1;
+       }
+
+       g_option_context_free(context);
+
+       if (argc < 2) {
+               fprintf(stderr, "Missing argument\n");
+               return 1;
+       }
+
+       web = g_web_new(index);
+       if (web == NULL) {
+               fprintf(stderr, "Failed to create web service\n");
+               return 1;
+       }
+
+       if (option_debug == TRUE)
+               g_web_set_debug(web, web_debug, "WEB");
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       if (option_proxy != NULL) {
+               g_web_set_proxy(web, option_proxy);
+               g_free(option_proxy);
+       }
+
+       if (option_nameserver != NULL) {
+               g_web_add_nameserver(web, option_nameserver);
+               g_free(option_nameserver);
+       }
+
+       if (option_user_agent != NULL) {
+               g_web_set_user_agent(web, "%s", option_user_agent);
+               g_free(option_user_agent);
+       }
+
+       if (option_http_version != NULL) {
+               g_web_set_http_version(web, option_http_version);
+               g_free(option_http_version);
+       }
+
+       timer = g_timer_new();
+
+       if (g_web_request_get(web, argv[1], web_result, NULL,  NULL) == 0) {
+               fprintf(stderr, "Failed to start request\n");
+               return 1;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_timer_destroy(timer);
+
+       g_web_unref(web);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/tools/wispr.c b/tools/wispr.c
new file mode 100644 (file)
index 0000000..122bea5
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <netdb.h>
+
+#include <gweb/gweb.h>
+
+#define DEFAULT_URL  "http://www.connman.net/online/status.html"
+
+static GTimer *timer;
+
+static GMainLoop *main_loop;
+
+static void web_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static const char *message_type_to_string(int message_type)
+{
+       switch (message_type) {
+       case 100:
+               return "Initial redirect message";
+       case 110:
+               return "Proxy notification";
+       case 120:
+               return "Authentication notification";
+       case 130:
+               return "Logoff notification";
+       case 140:
+               return "Response to Authentication Poll";
+       case 150:
+               return "Response to Abort Login";
+       }
+
+       return NULL;
+}
+
+static const char *response_code_to_string(int response_code)
+{
+       switch (response_code) {
+       case 0:
+               return "No error";
+       case 50:
+               return "Login succeeded";
+       case 100:
+               return "Login failed";
+       case 102:
+               return "RADIUS server error/timeout";
+       case 105:
+               return "RADIUS server not enabled";
+       case 150:
+               return "Logoff succeeded";
+       case 151:
+               return "Login aborted";
+       case 200:
+               return "Proxy detection/repeat operation";
+       case 201:
+               return "Authentication pending";
+       case 255:
+               return "Access gateway internal error";
+       }
+
+       return NULL;
+}
+
+struct wispr_msg {
+       gboolean has_error;
+       const char *current_element;
+       int message_type;
+       int response_code;
+       char *login_url;
+       char *abort_login_url;
+       char *logoff_url;
+       char *access_procedure;
+       char *access_location;
+       char *location_name;
+};
+
+static inline void wispr_msg_init(struct wispr_msg *msg)
+{
+       msg->has_error = FALSE;
+       msg->current_element = NULL;
+
+       msg->message_type = -1;
+       msg->response_code = -1;
+
+       g_free(msg->login_url);
+       msg->login_url = NULL;
+
+       g_free(msg->abort_login_url);
+       msg->abort_login_url = NULL;
+
+       g_free(msg->logoff_url);
+       msg->logoff_url = NULL;
+
+       g_free(msg->access_procedure);
+       msg->access_procedure = NULL;
+
+       g_free(msg->access_location);
+       msg->access_location = NULL;
+
+       g_free(msg->location_name);
+       msg->location_name = NULL;
+}
+
+struct wispr_session {
+       GWeb *web;
+       GWebParser *parser;
+       guint request;
+       struct wispr_msg msg;
+       char *username;
+       char *password;
+       char *originurl;
+       char *formdata;
+};
+
+static gboolean execute_login(gpointer user_data);
+
+static struct {
+       const char *str;
+       enum {
+               WISPR_ELEMENT_NONE,
+               WISPR_ELEMENT_ACCESS_PROCEDURE,
+               WISPR_ELEMENT_ACCESS_LOCATION,
+               WISPR_ELEMENT_LOCATION_NAME,
+               WISPR_ELEMENT_LOGIN_URL,
+               WISPR_ELEMENT_ABORT_LOGIN_URL,
+               WISPR_ELEMENT_MESSAGE_TYPE,
+               WISPR_ELEMENT_RESPONSE_CODE,
+               WISPR_ELEMENT_NEXT_URL,
+               WISPR_ELEMENT_DELAY,
+               WISPR_ELEMENT_REPLY_MESSAGE,
+               WISPR_ELEMENT_LOGIN_RESULTS_URL,
+               WISPR_ELEMENT_LOGOFF_URL,
+       } element;
+} wispr_element_map[] = {
+       { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
+       { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
+       { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
+       { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
+       { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
+       { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
+       { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
+       { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
+       { "Delay",              WISPR_ELEMENT_DELAY             },
+       { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
+       { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
+       { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
+       { NULL,                 WISPR_ELEMENT_NONE              },
+};
+
+static void start_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       const gchar **attribute_names,
+                                       const gchar **attribute_values,
+                                       gpointer user_data, GError **error)
+{
+       struct wispr_msg *msg = user_data;
+
+       msg->current_element = element_name;
+}
+
+static void end_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer user_data, GError **error)
+{
+       struct wispr_msg *msg = user_data;
+
+       msg->current_element = NULL;
+}
+
+static void text_handler(GMarkupParseContext *context,
+                                       const gchar *text, gsize text_len,
+                                       gpointer user_data, GError **error)
+{
+       struct wispr_msg *msg = user_data;
+       int i;
+
+       if (msg->current_element == NULL)
+               return;
+
+       for (i = 0; wispr_element_map[i].str; i++) {
+               if (g_str_equal(wispr_element_map[i].str,
+                                       msg->current_element) == FALSE)
+                       continue;
+
+               switch (wispr_element_map[i].element) {
+               case WISPR_ELEMENT_NONE:
+               case WISPR_ELEMENT_ACCESS_PROCEDURE:
+                       g_free(msg->access_procedure);
+                       msg->access_procedure = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ACCESS_LOCATION:
+                       g_free(msg->access_location);
+                       msg->access_location = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOCATION_NAME:
+                       g_free(msg->location_name);
+                       msg->location_name = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOGIN_URL:
+                       g_free(msg->login_url);
+                       msg->login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ABORT_LOGIN_URL:
+                       g_free(msg->abort_login_url);
+                       msg->abort_login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_MESSAGE_TYPE:
+                       msg->message_type = atoi(text);
+                       break;
+               case WISPR_ELEMENT_RESPONSE_CODE:
+                       msg->response_code = atoi(text);
+                       break;
+               case WISPR_ELEMENT_NEXT_URL:
+               case WISPR_ELEMENT_DELAY:
+               case WISPR_ELEMENT_REPLY_MESSAGE:
+               case WISPR_ELEMENT_LOGIN_RESULTS_URL:
+                       break;
+               case WISPR_ELEMENT_LOGOFF_URL:
+                       g_free(msg->logoff_url);
+                       msg->logoff_url = g_strdup(text);
+                       break;
+               }
+       }
+}
+
+static void error_handler(GMarkupParseContext *context,
+                                       GError *error, gpointer user_data)
+{
+       struct wispr_msg *msg = user_data;
+
+       msg->has_error = TRUE;
+}
+
+static const GMarkupParser wispr_parser = {
+       start_element_handler,
+       end_element_handler,
+       text_handler,
+       NULL,
+       error_handler,
+};
+
+static void parser_callback(const char *str, gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+       GMarkupParseContext *context;
+       gboolean result;
+
+       //printf("%s\n", str);
+
+       context = g_markup_parse_context_new(&wispr_parser,
+                       G_MARKUP_TREAT_CDATA_AS_TEXT, &wispr->msg, NULL);
+
+       result = g_markup_parse_context_parse(context, str, strlen(str), NULL);
+       if (result == TRUE)
+               g_markup_parse_context_end_parse(context, NULL);
+
+       g_markup_parse_context_free(context);
+}
+
+typedef void (*user_input_cb)(const char *value, gpointer user_data);
+
+struct user_input_data {
+       GString *str;
+       user_input_cb cb;
+       gpointer user_data;
+       gboolean hidden;
+       int fd;
+       struct termios saved_termios;
+};
+
+static void user_callback(struct user_input_data *data)
+{
+       char *value;
+
+       if (data->hidden == TRUE) {
+               ssize_t len;
+
+               len = write(data->fd, "\n", 1);
+               if (len < 0)
+                       return;
+       }
+
+       tcsetattr(data->fd, TCSADRAIN, &data->saved_termios);
+
+       close(data->fd);
+
+       value = g_string_free(data->str, FALSE);
+
+       if (data->cb)
+               data->cb(value, data->user_data);
+
+       g_free(value);
+
+       g_free(data);
+}
+
+static gboolean keyboard_input(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct user_input_data *data = user_data;
+       char buf[1];
+       int len;
+
+       len = read(data->fd, buf, 1);
+
+       if (len != 1)
+               return TRUE;
+
+       if (buf[0] == '\n') {
+               user_callback(data);
+               return FALSE;
+       }
+
+       g_string_append_c(data->str, buf[0]);
+
+       if (data->hidden == TRUE)
+               len = write(data->fd, "*", 1);
+
+       return TRUE;
+}
+
+static gboolean user_input(const char *label, gboolean hidden,
+                               user_input_cb func, gpointer user_data)
+{
+       struct user_input_data *data;
+       struct termios new_termios;
+       GIOChannel *channel;
+       guint watch;
+       ssize_t len;
+
+       data = g_try_new0(struct user_input_data, 1);
+       if (data == NULL)
+               return FALSE;
+
+       data->str = g_string_sized_new(32);
+       data->cb = func;
+       data->user_data = user_data;
+       data->hidden = hidden;
+
+       data->fd = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
+       if (data->fd < 0)
+               goto error;
+
+       if (tcgetattr(data->fd, &data->saved_termios) < 0) {
+               close(data->fd);
+               goto error;
+       }
+
+       new_termios = data->saved_termios;
+       if (data->hidden == TRUE)
+               new_termios.c_lflag &= ~(ICANON|ECHO);
+       else
+               new_termios.c_lflag &= ~ICANON;
+       new_termios.c_cc[VMIN] = 1;
+       new_termios.c_cc[VTIME] = 0;
+
+       tcsetattr(data->fd, TCSADRAIN, &new_termios);
+
+       channel = g_io_channel_unix_new(data->fd);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+       watch = g_io_add_watch(channel, G_IO_IN, keyboard_input, data);
+       g_io_channel_unref(channel);
+
+       if (watch == 0)
+               goto error;
+
+       len = write(data->fd, label, strlen(label));
+       if (len < 0)
+               goto error;
+
+       len = write(data->fd, ": ", 2);
+       if (len < 0)
+               goto error;
+
+       return TRUE;
+
+error:
+       g_string_free(data->str, TRUE);
+       g_free(data);
+
+       return FALSE;
+}
+
+static void password_callback(const char *value, gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+
+       g_free(wispr->password);
+       wispr->password = g_strdup(value);
+
+       printf("\n");
+
+       execute_login(wispr);
+}
+
+static void username_callback(const char *value, gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+
+       g_free(wispr->username);
+       wispr->username = g_strdup(value);
+
+       if (wispr->password == NULL) {
+               user_input("Password", TRUE, password_callback, wispr);
+               return;
+       }
+
+       printf("\n");
+
+       execute_login(wispr);
+}
+
+static gboolean wispr_input(const guint8 **data, gsize *length,
+                                               gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+       GString *buf;
+       gsize count;
+
+       buf = g_string_sized_new(100);
+
+       g_string_append(buf, "button=Login&UserName=");
+       g_string_append_uri_escaped(buf, wispr->username, NULL, FALSE);
+       g_string_append(buf, "&Password=");
+       g_string_append_uri_escaped(buf, wispr->password, NULL, FALSE);
+       g_string_append(buf, "&FNAME=0&OriginatingServer=");
+       g_string_append_uri_escaped(buf, wispr->originurl, NULL, FALSE);
+
+       count = buf->len;
+
+       g_free(wispr->formdata);
+       wispr->formdata = g_string_free(buf, FALSE);
+
+       *data = (guint8 *) wispr->formdata;
+       *length = count;
+
+       return FALSE;
+}
+
+static gboolean wispr_route(const char *addr, int ai_family, int if_index,
+               gpointer user_data)
+{
+       char *family = "unknown";
+
+       if (ai_family == AF_INET)
+               family = "IPv4";
+       else if (ai_family == AF_INET6)
+               family = "IPv6";
+
+       printf("Route request: %s %s index %d\n", family, addr, if_index);
+
+       if (ai_family != AF_INET && ai_family != AF_INET6)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean wispr_result(GWebResult *result, gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+       const guint8 *chunk;
+       gsize length;
+       guint16 status;
+       gdouble elapsed;
+
+       g_web_result_get_chunk(result, &chunk, &length);
+
+       if (length > 0) {
+               //printf("%s\n", (char *) chunk);
+               g_web_parser_feed_data(wispr->parser, chunk, length);
+               return TRUE;
+       }
+
+       g_web_parser_end_data(wispr->parser);
+
+       status = g_web_result_get_status(result);
+
+       g_print("status: %03u\n", status);
+
+       elapsed = g_timer_elapsed(timer, NULL);
+
+       g_print("elapse: %f seconds\n", elapsed);
+
+       if (wispr->msg.message_type < 0) {
+               const char *redirect;
+
+               if (status != 302)
+                       goto done;
+
+               if (g_web_result_get_header(result, "Location",
+                                                       &redirect) == FALSE)
+                       goto done;
+
+               printf("Redirect URL: %s\n", redirect);
+               printf("\n");
+
+               wispr->request = g_web_request_get(wispr->web, redirect,
+                               wispr_result, wispr_route, wispr);
+
+               return FALSE;
+       }
+
+       printf("Message type: %s (%d)\n",
+                       message_type_to_string(wispr->msg.message_type),
+                                               wispr->msg.message_type);
+       printf("Response code: %s (%d)\n",
+                       response_code_to_string(wispr->msg.response_code),
+                                               wispr->msg.response_code);
+       if (wispr->msg.access_procedure != NULL)
+               printf("Access procedure: %s\n", wispr->msg.access_procedure);
+       if (wispr->msg.access_location != NULL)
+               printf("Access location: %s\n", wispr->msg.access_location);
+       if (wispr->msg.location_name != NULL)
+               printf("Location name: %s\n", wispr->msg.location_name);
+       if (wispr->msg.login_url != NULL)
+               printf("Login URL: %s\n", wispr->msg.login_url);
+       if (wispr->msg.abort_login_url != NULL)
+               printf("Abort login URL: %s\n", wispr->msg.abort_login_url);
+       if (wispr->msg.logoff_url != NULL)
+               printf("Logoff URL: %s\n", wispr->msg.logoff_url);
+       printf("\n");
+
+       if (status != 200 && status != 302 && status != 404)
+               goto done;
+
+       if (wispr->msg.message_type == 100) {
+               if (wispr->username == NULL) {
+                       user_input("Username", FALSE, username_callback, wispr);
+                       return FALSE;
+               }
+
+               if (wispr->password == NULL) {
+                       user_input("Password", TRUE, password_callback, wispr);
+                       return FALSE;
+               }
+
+               g_idle_add(execute_login, wispr);
+               return FALSE;
+       } else if (wispr->msg.message_type == 120 ||
+                                       wispr->msg.message_type == 140) {
+               int code = wispr->msg.response_code;
+               printf("Login process: %s\n",
+                                       code == 50 ? "SUCCESS" : "FAILURE");
+       }
+
+       if (status == 302) {
+               const char *redirect;
+
+               if (g_web_result_get_header(result, "Location",
+                                                       &redirect) == FALSE)
+                       goto done;
+
+               printf("\n");
+               printf("Redirect URL: %s\n", redirect);
+               printf("\n");
+
+               wispr->request = g_web_request_get(wispr->web, redirect,
+                               wispr_result, NULL, wispr);
+
+               return FALSE;
+       }
+
+done:
+       g_main_loop_quit(main_loop);
+
+       return FALSE;
+}
+
+static gboolean execute_login(gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+
+       wispr->request = g_web_request_post(wispr->web, wispr->msg.login_url,
+                                       "application/x-www-form-urlencoded",
+                                       wispr_input, wispr_result, wispr);
+
+       wispr_msg_init(&wispr->msg);
+
+       return FALSE;
+}
+
+static gboolean option_debug = FALSE;
+static gchar *option_nameserver = NULL;
+static gchar *option_username = NULL;
+static gchar *option_password = NULL;
+static gchar *option_url = NULL;
+
+static GOptionEntry options[] = {
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                                       "Enable debug output" },
+       { "nameserver", 'n', 0, G_OPTION_ARG_STRING, &option_nameserver,
+                                       "Specify nameserver", "ADDRESS" },
+       { "username", 'u', 0, G_OPTION_ARG_STRING, &option_username,
+                                       "Specify username", "USERNAME" },
+       { "password", 'p', 0, G_OPTION_ARG_STRING, &option_password,
+                                       "Specify password", "PASSWORD" },
+       { "url", 'U', 0, G_OPTION_ARG_STRING, &option_url,
+                                       "Specify arbitrary request", "URL" },
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       struct sigaction sa;
+       struct wispr_session wispr;
+       int index = 0;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               return 1;
+       }
+
+       g_option_context_free(context);
+
+       memset(&wispr, 0, sizeof(wispr));
+       wispr_msg_init(&wispr.msg);
+
+       wispr.web = g_web_new(index);
+       if (wispr.web == NULL) {
+               fprintf(stderr, "Failed to create web service\n");
+               return 1;
+       }
+
+       if (option_debug == TRUE)
+               g_web_set_debug(wispr.web, web_debug, "WEB");
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       if (option_nameserver != NULL) {
+               g_web_add_nameserver(wispr.web, option_nameserver);
+               g_free(option_nameserver);
+       }
+
+       g_web_set_accept(wispr.web, NULL);
+       g_web_set_user_agent(wispr.web, "SmartClient/%s wispr", VERSION);
+       g_web_set_close_connection(wispr.web, TRUE);
+
+       if (option_url == NULL)
+               option_url = g_strdup(DEFAULT_URL);
+
+       wispr.username = option_username;
+       wispr.password = option_password;
+       wispr.originurl = option_url;
+
+       timer = g_timer_new();
+
+       wispr.parser = g_web_parser_new("<WISPAccessGatewayParam",
+                                               "WISPAccessGatewayParam>",
+                                               parser_callback, &wispr);
+
+       wispr.request = g_web_request_get(wispr.web, option_url,
+                       wispr_result, wispr_route, &wispr);
+
+       if (wispr.request == 0) {
+               fprintf(stderr, "Failed to start request\n");
+               return 1;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_timer_destroy(timer);
+
+       if (wispr.request > 0)
+               g_web_cancel_request(wispr.web, wispr.request);
+
+       g_web_parser_unref(wispr.parser);
+       g_web_unref(wispr.web);
+
+       g_main_loop_unref(main_loop);
+
+       g_free(wispr.username);
+       g_free(wispr.password);
+       g_free(wispr.originurl);
+
+       return 0;
+}
diff --git a/tools/wpad-test.c b/tools/wpad-test.c
new file mode 100644 (file)
index 0000000..a6cc792
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *
+ *  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 <unistd.h>
+#include <string.h>
+#include <signal.h>
+
+#include <gweb/gresolv.h>
+
+static GTimer *timer;
+
+static GMainLoop *main_loop;
+
+static GResolv *resolv;
+
+static void resolv_debug(const char *str, void *data)
+{
+       g_print("%s: %s\n", (const char *) data, str);
+}
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void resolv_result(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+       char *hostname = user_data;
+       char *str, *ptr;
+       gdouble elapsed;
+       int i;
+
+       if (status == G_RESOLV_RESULT_STATUS_SUCCESS) {
+               g_print("Found %s\n", hostname);
+               goto done;
+       }
+
+       g_print("No result for %s\n", hostname);
+
+       ptr = strchr(hostname + 5, '.');
+       if (ptr == NULL || strlen(ptr) < 2) {
+               g_print("No more names\n");
+               goto done;
+       }
+
+       if (strchr(ptr + 1, '.') == NULL) {
+               g_print("Not found\n");
+               goto done;
+       }
+
+       str = g_strdup_printf("wpad.%s", ptr + 1);
+
+       g_resolv_lookup_hostname(resolv, str, resolv_result, str);
+
+       g_free(hostname);
+
+       return;
+
+done:
+       elapsed = g_timer_elapsed(timer, NULL);
+
+       g_print("elapse: %f seconds\n", elapsed);
+
+       if (results != NULL) {
+               for (i = 0; results[i]; i++)
+                       g_print("result: %s\n", results[i]);
+       }
+
+       g_free(hostname);
+
+       g_main_loop_quit(main_loop);
+}
+
+static void start_wpad(const char *search)
+{
+       char domainname[256];
+       char *hostname;
+
+       if (search == NULL) {
+               if (getdomainname(domainname, sizeof(domainname)) < 0) {
+                       g_printerr("Failed to get domain name\n");
+                       goto quit;
+               }
+       } else
+               snprintf(domainname, sizeof(domainname), "%s", search);
+
+       if (strlen(domainname) == 0) {
+               g_printerr("Domain name is not set\n");
+               goto quit;
+       }
+               
+       g_print("domainname: %s\n", domainname);
+
+       hostname = g_strdup_printf("wpad.%s", domainname);
+
+       g_resolv_lookup_hostname(resolv, hostname, resolv_result, hostname);
+
+       return;
+
+quit:
+       g_main_loop_quit(main_loop);
+}
+
+static gboolean option_debug = FALSE;
+static gchar *option_search = NULL;
+
+static GOptionEntry options[] = {
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                                       "Enable debug output" },
+       { "search", 's', 0, G_OPTION_ARG_STRING, &option_search,
+                                       "Specify search domain name" },
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       struct sigaction sa;
+       int index = 0;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       resolv = g_resolv_new(index);
+       if (resolv == NULL) {
+               g_printerr("Failed to create resolver\n");
+               return 1;
+       }
+
+       if (option_debug == TRUE)
+               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       if (argc > 1) {
+               int i;
+
+               for (i = 1; i < argc; i++)
+                       g_resolv_add_nameserver(resolv, argv[i], 53, 0);
+       } else
+               g_resolv_add_nameserver(resolv, "127.0.0.1", 53, 0);
+
+       timer = g_timer_new();
+
+       start_wpad(option_search);
+
+       g_free(option_search);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_timer_destroy(timer);
+
+       g_resolv_unref(resolv);
+
+       g_main_loop_unref(main_loop);
+
+       return 0;
+}
diff --git a/unit/manager-api.c b/unit/manager-api.c
new file mode 100644 (file)
index 0000000..40ad370
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  BWM CarIT GmbH. 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 "test-connman.h"
+
+static DBusMessage *set_property(DBusConnection *connection,
+                               const char *property, int type, void *value)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       DBusMessageIter iter;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                               "SetProperty");
+       if (message == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, property, type, value);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("Failed to get properties");
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *manager_get_services(DBusConnection *connection)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "GetServices");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("Failed to get properties");
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *manager_get_properties(DBusConnection *connection)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "GetProperties");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("%s", error.message);
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *manager_create_session(DBusConnection *connection,
+                                       struct test_session_info *info,
+                                       const char *notifier_path)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "CreateSession");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       dbus_message_iter_init_append(message, &array);
+
+       connman_dbus_dict_open(&array, &dict);
+
+       session_append_settings(&dict, info);
+
+       connman_dbus_dict_close(&array, &dict);
+
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
+                               &notifier_path);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("Failed to get properties");
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *manager_destroy_session(DBusConnection *connection,
+                                       const char *notifier_path)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       DBusMessageIter array;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "DestroySession");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       dbus_message_iter_init_append(message, &array);
+
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
+                               &notifier_path);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("%s", error.message);
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *manager_set_session_mode(DBusConnection *connection,
+                                       connman_bool_t enable)
+{
+       return set_property(connection, "SessionMode",
+                               DBUS_TYPE_BOOLEAN, &enable);
+}
+
+int manager_parse_properties(DBusMessage *msg,
+                               struct test_manager *manager)
+{
+       DBusMessageIter iter, array;
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_STRING:
+                       if (g_str_equal(key, "State") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               if (manager->state != NULL)
+                                       g_free(manager->state);
+
+                               LOG("State %s", val);
+
+                               manager->state = g_strdup(val);
+                       }
+                       break;
+               default:
+                       break;
+               }
+               dbus_message_iter_next(&array);
+       }
+
+       return 0;
+}
diff --git a/unit/session-api.c b/unit/session-api.c
new file mode 100644 (file)
index 0000000..14d48cf
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  BWM CarIT GmbH. 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 <errno.h>
+
+#include <gdbus/gdbus.h>
+
+#include "test-connman.h"
+
+static enum connman_session_state string2state(const char *state)
+{
+       if (g_strcmp0(state, "connected") == 0)
+               return CONNMAN_SESSION_STATE_CONNECTED;
+       if (g_strcmp0(state, "online") == 0)
+               return CONNMAN_SESSION_STATE_ONLINE;
+
+       return CONNMAN_SESSION_STATE_DISCONNECTED;
+}
+
+static enum connman_session_state string2type(const char *type)
+{
+       if (g_strcmp0(type, "local") == 0)
+               return CONNMAN_SESSION_TYPE_LOCAL;
+       if (g_strcmp0(type, "internet") == 0)
+               return CONNMAN_SESSION_TYPE_INTERNET;
+
+       return CONNMAN_SESSION_TYPE_ANY;
+}
+
+static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
+{
+       switch (policy) {
+       case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
+               break;
+       case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
+               return "default";
+       case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
+               return "always";
+       case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
+               return "forbidden";
+       case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
+               return "national";
+       case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
+               return "international";
+       }
+
+       return "";
+}
+
+static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
+{
+       if (g_strcmp0(policy, "default") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
+       else if (g_strcmp0(policy, "always") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
+       else if (g_strcmp0(policy, "forbidden") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
+       else if (g_strcmp0(policy, "national") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
+       else if (g_strcmp0(policy, "international") == 0)
+               return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
+       else
+               return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
+}
+
+void bearer_info_cleanup(gpointer data, gpointer user_data)
+{
+       struct test_bearer_info *info = data;
+
+       g_free(info->name);
+       g_free(info);
+}
+
+static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
+{
+       struct test_bearer_info *info;
+       DBusMessageIter array;
+       GSList *list = NULL;
+
+       dbus_message_iter_recurse(iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+               char *bearer = NULL;
+
+               dbus_message_iter_get_basic(&array, &bearer);
+
+               info = g_try_new0(struct test_bearer_info, 1);
+               if (info == NULL) {
+                       g_slist_foreach(list, bearer_info_cleanup, NULL);
+                       g_slist_free(list);
+
+                       return NULL;
+               }
+
+               info->name = g_strdup(bearer);
+
+               list = g_slist_append(list, info);
+
+               dbus_message_iter_next(&array);
+       }
+
+       return list;
+}
+
+static DBusMessage *notify_release(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct test_session *session = user_data;
+
+       LOG("session %p", session);
+
+       if (session->notify != NULL)
+               session->notify(session);
+
+       return NULL;
+}
+
+static DBusMessage *notify_update(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct test_session *session = user_data;
+       struct test_session_info *info = session->info;
+       DBusMessageIter iter, array;
+       GSList *allowed_bearers;
+
+       LOG("session %p notify %s", session, session->notify_path);
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_ARRAY:
+                       if (g_str_equal(key, "AllowedBearers") == TRUE) {
+                               allowed_bearers = session_parse_allowed_bearers(&value);
+
+                               g_slist_foreach(info->allowed_bearers,
+                                               bearer_info_cleanup, NULL);
+                               g_slist_free(info->allowed_bearers);
+
+                               info->allowed_bearers = allowed_bearers;
+
+                       } else if (g_str_equal(key, "IPv4") == TRUE) {
+                               /* XXX */
+
+                       } else if (g_str_equal(key, "IPv6") == TRUE) {
+                               /* XXX */
+
+                       } else {
+                               g_assert(FALSE);
+                               return __connman_error_invalid_arguments(msg);
+                       }
+                       break;
+               case DBUS_TYPE_BOOLEAN:
+                       if (g_str_equal(key, "Priority") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->priority);
+
+                       } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->avoid_handover);
+
+                       } else if (g_str_equal(key, "StayConnected") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->stay_connected);
+
+                       } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->ecall);
+
+                       } else {
+                               g_assert(FALSE);
+                               return __connman_error_invalid_arguments(msg);
+                       }
+                       break;
+               case DBUS_TYPE_UINT32:
+                       if (g_str_equal(key, "PeriodicConnect") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->periodic_connect);
+
+                       } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->idle_timeout);
+
+                       } else if (g_str_equal(key, "SessionMarker") == TRUE) {
+                               dbus_message_iter_get_basic(&value,
+                                                       &info->marker);
+
+                       } else {
+                               g_assert(FALSE);
+                               return __connman_error_invalid_arguments(msg);
+                       }
+                       break;
+               case DBUS_TYPE_STRING:
+                       if (g_str_equal(key, "State") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               info->state = string2state(val);
+                       } else if (g_str_equal(key, "Bearer") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               if (info->bearer != NULL)
+                                       g_free(info->bearer);
+
+                               info->bearer = g_strdup(val);
+
+                       } else if (g_str_equal(key, "Name") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               if (info->name != NULL)
+                                       g_free(info->name);
+
+                               info->name = g_strdup(val);
+
+                       } else if (g_str_equal(key, "RoamingPolicy") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+                               info->roaming_policy =
+                                       string2roamingpolicy(val);
+
+                       } else if (g_str_equal(key, "Interface") == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               if (info->interface != NULL)
+                                       g_free(info->interface);
+
+                               info->interface = g_strdup(val);
+
+                       } else if (g_str_equal(key, "ConnectionType")
+                                                               == TRUE) {
+                               const char *val;
+                               dbus_message_iter_get_basic(&value, &val);
+
+                               info->type = string2type(val);
+                       } else {
+                               g_assert(FALSE);
+                               return __connman_error_invalid_arguments(msg);
+                       }
+                       break;
+               default:
+                       g_assert(FALSE);
+                       return __connman_error_invalid_arguments(msg);
+               }
+               dbus_message_iter_next(&array);
+       }
+
+       if (session->notify != NULL)
+               session->notify(session);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable notify_methods[] = {
+       { GDBUS_METHOD("Release", NULL, NULL, notify_release) },
+       { GDBUS_METHOD("Update",
+                       GDBUS_ARGS({ "settings", "a{sv}" }), NULL,
+                       notify_update) },
+       { },
+};
+
+int session_notify_register(struct test_session *session,
+                               const char *notify_path)
+{
+       if (g_dbus_register_interface(session->connection, notify_path,
+                       CONNMAN_NOTIFICATION_INTERFACE,
+                       notify_methods, NULL, NULL,
+                       session, NULL) == FALSE) {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int session_notify_unregister(struct test_session *session,
+                               const char *notify_path)
+{
+       if (g_dbus_unregister_interface(session->connection, notify_path,
+                               CONNMAN_NOTIFICATION_INTERFACE) == FALSE) {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
+{
+       struct test_session_info *info = user_data;
+       GSList *list;
+
+       for (list = info->allowed_bearers;
+                       list != NULL; list = list->next) {
+               struct test_bearer_info *info = list->data;
+
+               dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                               &info->name);
+       }
+}
+
+void session_append_settings(DBusMessageIter *dict,
+                               struct test_session_info *info)
+{
+       const char *policy;
+
+       connman_dbus_dict_append_basic(dict, "Priority",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->priority);
+
+       connman_dbus_dict_append_array(dict, "AllowedBearers",
+                                               DBUS_TYPE_STRING,
+                                               append_allowed_bearers,
+                                               info);
+
+       connman_dbus_dict_append_basic(dict, "AvoidHandover",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->avoid_handover);
+
+       connman_dbus_dict_append_basic(dict, "StayConnected",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->stay_connected);
+
+       connman_dbus_dict_append_basic(dict, "PeriodicConnect",
+                                               DBUS_TYPE_UINT32,
+                                               &info->periodic_connect);
+
+       connman_dbus_dict_append_basic(dict, "IdleTimeout",
+                                               DBUS_TYPE_UINT32,
+                                               &info->idle_timeout);
+
+       connman_dbus_dict_append_basic(dict, "EmergencyCall",
+                                               DBUS_TYPE_BOOLEAN,
+                                               &info->ecall);
+
+       policy = roamingpolicy2string(info->roaming_policy);
+       connman_dbus_dict_append_basic(dict, "RoamingPolicy",
+                                               DBUS_TYPE_STRING,
+                                               &policy);
+}
+
+DBusMessage *session_connect(DBusConnection *connection,
+                               struct test_session *session)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               session->session_path,
+                                               CONNMAN_SESSION_INTERFACE,
+                                                       "Connect");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("Failed to get properties");
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+DBusMessage *session_disconnect(DBusConnection *connection,
+                                       struct test_session *session)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               session->session_path,
+                                               CONNMAN_SESSION_INTERFACE,
+                                                       "Disconnect");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       LOG("%s", error.message);
+                       dbus_error_free(&error);
+               } else {
+                       LOG("Failed to get properties");
+               }
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
diff --git a/unit/test-connman.h b/unit/test-connman.h
new file mode 100644 (file)
index 0000000..872aabd
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *
+ *  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
+ *
+ */
+
+#include <glib.h>
+
+#include <connman/dbus.h>
+
+#include "../src/connman.h"
+
+struct test_session;
+
+struct test_manager {
+       char *state;
+};
+
+struct test_fix {
+       gpointer user_data;
+
+       GMainLoop *main_loop;
+       DBusConnection *main_connection;
+       guint watch;
+       guint manager_watch;
+
+       struct test_manager manager;
+       GSourceFunc manager_changed;
+
+       /* session test cases */
+       unsigned int max_sessions;
+       struct test_session *session;
+};
+
+/* utils.c */
+typedef void (* util_test_setup_cb) (struct test_fix *fix,
+                                       gconstpointer data);
+typedef void (* util_test_teardown_cb) (struct test_fix *fix,
+                                       gconstpointer data);
+
+gboolean util_quit_loop(gpointer fix);
+guint util_idle_call(struct test_fix *fix, GSourceFunc func,
+                       GDestroyNotify notify);
+guint util_call(struct test_fix *fix, GSourceFunc func,
+               GDestroyNotify notify);
+void util_test_add(const char *test_name, GSourceFunc test_func,
+                       util_test_setup_cb setup_cb,
+                       util_test_teardown_cb teardown_cb);
+void util_setup(struct test_fix *fix, gconstpointer data);
+void util_teardown(struct test_fix *fix, gconstpointer data);
+
+void util_session_create(struct test_fix *fix, unsigned int max_sessions);
+void util_session_destroy(gpointer fix);
+void util_session_init(struct test_session *session);
+void util_session_cleanup(struct test_session *session);
+
+typedef void (* notify_cb) (struct test_session *session);
+
+enum connman_session_state {
+       CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
+       CONNMAN_SESSION_STATE_CONNECTED      = 1,
+       CONNMAN_SESSION_STATE_ONLINE         = 2,
+};
+
+enum connman_session_type {
+       CONNMAN_SESSION_TYPE_ANY      = 0,
+       CONNMAN_SESSION_TYPE_LOCAL    = 1,
+       CONNMAN_SESSION_TYPE_INTERNET = 2,
+};
+
+enum connman_session_roaming_policy {
+       CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
+       CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
+       CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
+       CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
+       CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
+       CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
+};
+
+struct test_session_info {
+       char *bearer;
+       enum connman_session_state state;
+       enum connman_session_type type;
+       char *name;
+       /* ipv4, ipv6 dicts */
+       GSList *allowed_bearers;
+       connman_bool_t priority;
+       connman_bool_t avoid_handover;
+       connman_bool_t stay_connected;
+       unsigned int periodic_connect;
+       unsigned int idle_timeout;
+       connman_bool_t ecall;
+       enum connman_session_roaming_policy roaming_policy;
+       char *interface;
+       unsigned int marker;
+};
+
+struct test_session {
+       gpointer user_data;
+
+       struct test_fix *fix;
+       DBusConnection *connection;
+
+       char *session_path;
+       char *notify_path;
+       notify_cb notify;
+
+       struct test_session_info *info;
+};
+
+struct test_bearer_info {
+       char *name;
+};
+
+/* session-api.c */
+void bearer_info_cleanup(gpointer bearer_info, gpointer user_data);
+
+void session_append_settings(DBusMessageIter *dict,
+                               struct test_session_info *info);
+int session_notify_register(struct test_session *session,
+                               const char *notify_path);
+int session_notify_unregister(struct test_session *session,
+                               const char *notify_path);
+
+DBusMessage *session_connect(DBusConnection *connection,
+                               struct test_session *session);
+DBusMessage *session_disconnect(DBusConnection *connection,
+                                       struct test_session *session);
+
+/* manager-api.c */
+DBusMessage *manager_get_services(DBusConnection *connection);
+DBusMessage *manager_get_properties(DBusConnection *connection);
+DBusMessage *manager_create_session(DBusConnection *connection,
+                                       struct test_session_info *info,
+                                       const char *notifier_path);
+DBusMessage *manager_destroy_session(DBusConnection *connection,
+                                       const char *notifier_path);
+DBusMessage *manager_set_session_mode(DBusConnection *connection,
+                                       connman_bool_t enable);
+int manager_parse_properties(DBusMessage *msg,
+                               struct test_manager *manager);
+
+/* #define DEBUG */
+#ifdef DEBUG
+#include <stdio.h>
+
+#define LOG(fmt, arg...) do { \
+       fprintf(stdout, "%s:%s() " fmt "\n", \
+                       __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
+#else
+#define LOG(fmt, arg...)
+#endif
diff --git a/unit/test-ippool.c b/unit/test-ippool.c
new file mode 100644 (file)
index 0000000..2df8561
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BWM CarIT GmbH. 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 <glib.h>
+
+#include "../src/connman.h"
+
+/* #define DEBUG */
+#ifdef DEBUG
+#include <stdio.h>
+
+#define LOG(fmt, arg...) do { \
+       fprintf(stdout, "%s:%s() " fmt "\n", \
+                       __FILE__, __func__ , ## arg); \
+} while (0)
+#else
+#define LOG(fmt, arg...)
+#endif
+
+static void test_ippool_basic0(void)
+{
+       struct connman_ippool *pool;
+       int i;
+
+       __connman_ippool_init();
+
+       pool = __connman_ippool_create(23, 1, 500, NULL, NULL);
+       g_assert(pool == NULL);
+
+       for (i = 0; i < 100000; i++) {
+               pool = __connman_ippool_create(23, 1, 20, NULL, NULL);
+               g_assert(pool);
+
+               __connman_ippool_unref(pool);
+       }
+
+       __connman_ippool_cleanup();
+}
+
+static void test_ippool_basic1(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       int i;
+
+       __connman_ippool_init();
+
+       /* Test the IP range */
+       for (i = 1; i < 254; i++) {
+               pool = __connman_ippool_create(23, 1, i, NULL, NULL);
+               g_assert(pool);
+
+               gateway = __connman_ippool_get_gateway(pool);
+               broadcast = __connman_ippool_get_broadcast(pool);
+               subnet_mask = __connman_ippool_get_subnet_mask(pool);
+               start_ip = __connman_ippool_get_start_ip(pool);
+               end_ip = __connman_ippool_get_end_ip(pool);
+
+               g_assert(gateway);
+               g_assert(broadcast);
+               g_assert(subnet_mask);
+               g_assert(start_ip);
+               g_assert(end_ip);
+
+               LOG("\n\tIP range %s --> %s\n"
+                       "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+                       gateway, broadcast, subnet_mask);
+
+               __connman_ippool_unref(pool);
+       }
+
+       __connman_ippool_cleanup();
+}
+
+static void test_ippool_exhaust0(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       GSList *list = NULL, *it;
+       int i = 0;
+
+       __connman_ippool_init();
+
+       /* Allocate all possible pools */
+
+       /*
+        *                                             Number of addresses
+        * 24-bit block         10.0.0.0    â€“ 10.255.255.255    16,777,216
+        * 20-bit block         172.16.0.0  â€“ 172.31.255.255     1,048,576
+        * 16-bit block         192.168.0.0 â€“ 192.168.255.255       65,536
+        *
+        * Total                                                17,891,328
+        *
+        * Total numbers of 256 blocks:                             69,888
+        */
+
+       while (TRUE) {
+               pool = __connman_ippool_create(23, 1, 100, NULL, NULL);
+               if (pool == NULL)
+                       break;
+               i += 1;
+               g_assert(i < 69888);
+
+               list = g_slist_prepend(list, pool);
+
+               gateway = __connman_ippool_get_gateway(pool);
+               broadcast = __connman_ippool_get_broadcast(pool);
+               subnet_mask = __connman_ippool_get_subnet_mask(pool);
+               start_ip = __connman_ippool_get_start_ip(pool);
+               end_ip = __connman_ippool_get_end_ip(pool);
+
+               g_assert(gateway);
+               g_assert(broadcast);
+               g_assert(subnet_mask);
+               g_assert(start_ip);
+               g_assert(end_ip);
+       }
+
+       LOG("Number of blocks %d", i);
+
+       for (it = list; it != NULL; it = it->next) {
+               pool = it->data;
+
+               __connman_ippool_unref(pool);
+       }
+
+       g_slist_free(list);
+
+       __connman_ippool_cleanup();
+}
+
+static void collision_cb(struct connman_ippool *pool, void *user_data)
+{
+       int *flag = user_data;
+
+       LOG("collision detected");
+
+       g_assert(*flag == 0);
+       g_assert(pool);
+
+       *flag = 1;
+}
+
+static void test_ippool_collision0(void)
+{
+       struct connman_ippool *pool;
+       const char *gateway;
+       const char *broadcast;
+       const char *subnet_mask;
+       const char *start_ip;
+       const char *end_ip;
+       int flag;
+
+       __connman_ippool_init();
+
+       /* Test the IP range collision */
+
+       flag = 0;
+       pool = __connman_ippool_create(23, 1, 100, collision_cb, &flag);
+       g_assert(pool);
+
+       gateway = __connman_ippool_get_gateway(pool);
+       broadcast = __connman_ippool_get_broadcast(pool);
+       subnet_mask = __connman_ippool_get_subnet_mask(pool);
+       start_ip = __connman_ippool_get_start_ip(pool);
+       end_ip = __connman_ippool_get_end_ip(pool);
+
+       g_assert(gateway);
+       g_assert(broadcast);
+       g_assert(subnet_mask);
+       g_assert(start_ip);
+       g_assert(end_ip);
+
+       LOG("\n\tIP range %s --> %s\n"
+               "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+               gateway, broadcast, subnet_mask);
+
+       __connman_ippool_newaddr(23, start_ip, 24);
+
+       g_assert(flag == 0);
+
+       __connman_ippool_newaddr(42, start_ip, 16);
+
+       g_assert(flag == 1);
+
+       __connman_ippool_unref(pool);
+
+       flag = 0;
+
+       pool = __connman_ippool_create(23, 1, 100, collision_cb, &flag);
+       g_assert(pool);
+
+       gateway = __connman_ippool_get_gateway(pool);
+       broadcast = __connman_ippool_get_broadcast(pool);
+       subnet_mask = __connman_ippool_get_subnet_mask(pool);
+       start_ip = __connman_ippool_get_start_ip(pool);
+       end_ip = __connman_ippool_get_end_ip(pool);
+
+       g_assert(gateway);
+       g_assert(broadcast);
+       g_assert(subnet_mask);
+       g_assert(start_ip);
+       g_assert(end_ip);
+
+       LOG("\n\tIP range %s --> %s\n"
+               "\tgateway %s broadcast %s mask %s", start_ip, end_ip,
+               gateway, broadcast, subnet_mask);
+
+       __connman_ippool_newaddr(45, start_ip, 22);
+
+       g_assert(flag == 1);
+
+       __connman_ippool_unref(pool);
+
+       __connman_ippool_cleanup();
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/basic0", test_ippool_basic0);
+       g_test_add_func("/basic1", test_ippool_basic1);
+       g_test_add_func("/exhaust0", test_ippool_exhaust0);
+       g_test_add_func("/collision0", test_ippool_collision0);
+
+       return g_test_run();
+}
diff --git a/unit/test-nat.c b/unit/test-nat.c
new file mode 100644 (file)
index 0000000..578ff3e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2012  BWM CarIT GmbH. 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 <glib.h>
+
+#include "../src/connman.h"
+
+/* #define DEBUG */
+#ifdef DEBUG
+#include <stdio.h>
+
+#define LOG(fmt, arg...) do { \
+       fprintf(stdout, "%s:%s() " fmt "\n", \
+                       __FILE__, __func__ , ## arg); \
+} while (0)
+#else
+#define LOG(fmt, arg...)
+#endif
+
+struct connman_notifier *nat_notifier;
+
+struct connman_service {
+       char *dummy;
+};
+
+char *connman_service_get_interface(struct connman_service *service)
+{
+       return "eth0";
+}
+
+int connman_notifier_register(struct connman_notifier *notifier)
+{
+       nat_notifier = notifier;
+
+       return 0;
+}
+
+void connman_notifier_unregister(struct connman_notifier *notifier)
+{
+       nat_notifier = NULL;
+}
+
+
+static void test_iptables_basic0(void)
+{
+       int err;
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-I INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-D INPUT -i session-bridge -j ACCEPT");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+
+       err = __connman_iptables_command("-C INPUT -i session-bridge -j ACCEPT");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("filter");
+       g_assert(err == 0);
+}
+
+static void test_nat_basic0(void)
+{
+       int err;
+
+       err = __connman_nat_enable("bridge", "192.168.2.1", 24);
+       g_assert(err == 0);
+
+       /* test that table is empty */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+
+
+       __connman_nat_disable("bridge");
+}
+
+static void test_nat_basic1(void)
+{
+       struct connman_service *service;
+       int err;
+
+       service = g_try_new0(struct connman_service, 1);
+       g_assert(service);
+
+       nat_notifier->default_changed(service);
+
+       err = __connman_nat_enable("bridge", "192.168.2.1", 24);
+       g_assert(err == 0);
+
+       /* test that table is not empty */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err == 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+
+       __connman_nat_disable("bridge");
+
+       /* test that table is empty again */
+       err = __connman_iptables_command("-t nat -C POSTROUTING "
+                                       "-s 192.168.2.1/24 -o eth0 -j MASQUERADE");
+       g_assert(err != 0);
+       err = __connman_iptables_commit("nat");
+       g_assert(err == 0);
+}
+
+int main(int argc, char *argv[])
+{
+       int err;
+
+       g_test_init(&argc, &argv, NULL);
+
+       __connman_log_init(argv[0], "*", FALSE);
+       __connman_iptables_init();
+       __connman_nat_init();
+
+       g_test_add_func("/iptables/basic0", test_iptables_basic0);
+       g_test_add_func("/nat/basic0", test_nat_basic0);
+       g_test_add_func("/nat/basic1", test_nat_basic1);
+
+       err = g_test_run();
+
+       __connman_nat_cleanup();
+       __connman_iptables_cleanup();
+       __connman_log_cleanup();
+
+       return err;
+}
diff --git a/unit/test-session.c b/unit/test-session.c
new file mode 100644 (file)
index 0000000..38ed2bf
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  BWM CarIT GmbH. 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 "gdbus/gdbus.h"
+
+#include "test-connman.h"
+
+enum test_session_state {
+       TEST_SESSION_STATE_0 = 0,
+       TEST_SESSION_STATE_1 = 1,
+       TEST_SESSION_STATE_2 = 2,
+       TEST_SESSION_STATE_3 = 3,
+};
+
+static enum test_session_state get_session_state(struct test_session *session)
+{
+       return GPOINTER_TO_UINT(session->fix->user_data);
+}
+
+static void set_session_state(struct test_session *session,
+                               enum test_session_state state)
+{
+       session->fix->user_data = GUINT_TO_POINTER(state);
+}
+
+static struct test_session *get_session(struct test_session *session,
+                                       unsigned int index)
+{
+       return &session->fix->session[index];
+}
+
+static gboolean test_session_create_no_notify(gpointer data)
+{
+       struct test_fix *fix = data;
+       DBusMessage *msg;
+
+       util_session_create(fix, 1);
+
+       msg = manager_create_session(fix->session->connection,
+                                       fix->session->info, "/foo");
+       g_assert(msg != NULL);
+       g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
+
+       dbus_message_unref(msg);
+
+       util_idle_call(fix, util_quit_loop, util_session_destroy);
+
+       return FALSE;
+}
+
+static gboolean test_session_destroy_no_notify(gpointer data)
+{
+       struct test_fix *fix = data;
+       DBusMessage *msg;
+
+       util_session_create(fix, 1);
+
+       msg = manager_destroy_session(fix->session->connection, "/foo");
+       g_assert(msg == NULL);
+
+       util_idle_call(fix, util_quit_loop, util_session_destroy);
+
+       return FALSE;
+}
+
+static void test_session_create_notify(struct test_session *session)
+{
+       LOG("session %p", session);
+
+       util_idle_call(session->fix, util_quit_loop, util_session_destroy);
+}
+
+static gboolean test_session_create(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+       DBusMessage *msg;
+       int err;
+
+       util_session_create(fix, 1);
+       session = fix->session;
+
+       session->notify_path = "/foo";
+       session->notify = test_session_create_notify;
+
+       err = session_notify_register(session, session->notify_path);
+       g_assert(err == 0);
+
+       msg = manager_create_session(session->connection,
+                                       session->info,
+                                       session->notify_path);
+       g_assert(msg != NULL);
+       g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
+
+       dbus_message_unref(msg);
+
+       return FALSE;
+}
+
+static gboolean test_session_create_destroy(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+
+       util_session_create(fix, 1);
+       session = fix->session;
+
+       session->notify_path = g_strdup("/foo");
+
+       util_session_init(fix->session);
+       util_session_cleanup(fix->session);
+
+       util_idle_call(fix, util_quit_loop, util_session_destroy);
+
+       return FALSE;
+}
+
+static gboolean test_session_create_already_exists(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session0, *session1;
+       DBusMessage *msg;
+
+       util_session_create(fix, 2);
+       session0 = &fix->session[0];
+       session1 = &fix->session[1];
+
+       session0->notify_path = g_strdup("/foo");
+       session1->notify_path = session0->notify_path;
+
+       util_session_init(session0);
+
+       msg = manager_create_session(session1->connection,
+                                       session1->info,
+                                       session1->notify_path);
+       g_assert(msg == NULL);
+
+       util_session_cleanup(session0);
+
+       util_idle_call(fix, util_quit_loop, util_session_destroy);
+
+       return FALSE;
+}
+
+static void test_session_create_many_notify(struct test_session *session)
+{
+       unsigned int nr;
+
+       LOG("session %p", session);
+
+       nr = GPOINTER_TO_UINT(session->fix->user_data);
+       nr--;
+       session->fix->user_data = GUINT_TO_POINTER(nr);
+
+       if (nr > 0)
+               return;
+
+       util_idle_call(session->fix, util_quit_loop, util_session_destroy);
+}
+
+static gboolean test_session_create_many(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+       unsigned int i, max;
+
+       max = 100;
+
+       fix->user_data = GUINT_TO_POINTER(max);
+
+       util_session_create(fix, max);
+
+       for (i = 0; i < max; i++) {
+               session = &fix->session[i];
+
+               session->notify_path = g_strdup_printf("/foo/%d", i);
+               session->notify = test_session_create_many_notify;
+
+               util_session_init(session);
+       }
+
+       return FALSE;
+}
+
+static void set_session_mode(struct test_fix *fix,
+                                       connman_bool_t enable)
+{
+       DBusMessage *msg;
+
+       msg = manager_set_session_mode(fix->main_connection, enable);
+       g_assert(msg != NULL);
+       g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
+
+       dbus_message_unref(msg);
+}
+
+static void test_session_connect_notify(struct test_session *session)
+{
+       LOG("session %p state %d", session, session->info->state);
+
+       if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
+               return;
+
+       util_session_cleanup(session);
+
+       util_idle_call(session->fix, util_quit_loop, util_session_destroy);
+}
+
+static gboolean test_session_connect(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+       DBusMessage *msg;
+
+       util_session_create(fix, 1);
+       session = fix->session;
+
+       session->notify_path = g_strdup("/foo");
+       session->notify =  test_session_connect_notify;
+       util_session_init(session);
+
+       msg = session_connect(session->connection, session);
+       g_assert(msg != NULL);
+       g_assert(dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR);
+
+       dbus_message_unref(msg);
+
+       return FALSE;
+}
+
+static void test_session_disconnect_notify(struct test_session *session)
+{
+       LOG("session %p state %d", session, session->info->state);
+
+       if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
+               return;
+
+       util_session_cleanup(session);
+
+       util_idle_call(session->fix, util_quit_loop, util_session_destroy);
+}
+
+static gboolean test_session_disconnect(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+       DBusMessage *msg;
+
+       util_session_create(fix, 1);
+       session = fix->session;
+
+       session->notify_path = g_strdup("/foo");
+       session->notify =  test_session_disconnect_notify;
+       util_session_init(session);
+
+       msg = session_disconnect(session->connection, session);
+       g_assert(msg != NULL);
+       dbus_message_unref(msg);
+
+       return FALSE;
+}
+
+static void test_session_connect_disconnect_notify(struct test_session *session)
+{
+       enum test_session_state state = get_session_state(session);
+       enum test_session_state next_state = state;
+       DBusMessage *msg;
+
+       LOG("state %d session %p %s state %d", state, session,
+               session->notify_path, session->info->state);
+
+       switch (state) {
+       case TEST_SESSION_STATE_0:
+               if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
+                       next_state = TEST_SESSION_STATE_1;
+               break;
+       case TEST_SESSION_STATE_1:
+               if (session->info->state >= CONNMAN_SESSION_STATE_CONNECTED)
+                       next_state = TEST_SESSION_STATE_2;
+               break;
+       case TEST_SESSION_STATE_2:
+               if (session->info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
+                       next_state = TEST_SESSION_STATE_3;
+       default:
+               break;
+       }
+
+       if (state == next_state)
+               return;
+
+       set_session_state(session, next_state);
+
+       LOG("next_state %d", next_state);
+
+       switch (next_state) {
+       case TEST_SESSION_STATE_1:
+               msg = session_connect(session->connection, session);
+               g_assert(msg != NULL);
+               dbus_message_unref(msg);
+               return;
+       case TEST_SESSION_STATE_2:
+               msg = session_disconnect(session->connection, session);
+               g_assert(msg != NULL);
+               dbus_message_unref(msg);
+               return;
+       case TEST_SESSION_STATE_3:
+               util_session_cleanup(session);
+               util_idle_call(session->fix, util_quit_loop,
+                               util_session_destroy);
+               return;
+       default:
+               return;
+       }
+}
+
+static gboolean test_session_connect_disconnect(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session;
+
+       /*
+        * +-------------------+
+        * |       START       |
+        * +-------------------+
+        *   |
+        *   | connect foo
+        *   v
+        * +-------------------+
+        * |   FOO-CONNECTED   |
+        * +-------------------+
+        *  |
+        *  | disconnect foo
+        *  v
+        * +-------------------+
+        * |        END        |
+        * +-------------------+
+        */
+
+       util_session_create(fix, 1);
+       session = fix->session;
+
+       session->notify_path = g_strdup("/foo");
+       session->notify =  test_session_connect_disconnect_notify;
+
+       util_session_init(session);
+
+       set_session_state(session, TEST_SESSION_STATE_0);
+
+       return FALSE;
+}
+
+static void test_session_connect_free_ride_notify(struct test_session *session)
+{
+       struct test_session *session0 = get_session(session, 0);
+       struct test_session *session1 = get_session(session, 1);
+       enum test_session_state state = get_session_state(session);
+       enum test_session_state next_state = state;
+       DBusMessage *msg;
+
+       LOG("state %d session %p %s state %d", state, session,
+               session->notify_path, session->info->state);
+
+       switch (state) {
+       case TEST_SESSION_STATE_0:
+               if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
+                               && session1->info->state ==
+                                       CONNMAN_SESSION_STATE_DISCONNECTED) {
+                       next_state = TEST_SESSION_STATE_1;
+               }
+
+               break;
+       case TEST_SESSION_STATE_1:
+               if (session0->info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
+                               session1->info->state >=
+                                       CONNMAN_SESSION_STATE_CONNECTED) {
+                       next_state = TEST_SESSION_STATE_2;
+               }
+
+               break;
+       case TEST_SESSION_STATE_2:
+               if (session0->info->state == CONNMAN_SESSION_STATE_DISCONNECTED
+                               && session1->info->state ==
+                                       CONNMAN_SESSION_STATE_DISCONNECTED) {
+                       next_state = TEST_SESSION_STATE_3;
+               }
+
+               break;
+       case TEST_SESSION_STATE_3:
+
+               return;
+       }
+
+       if (state == next_state)
+               return;
+
+       set_session_state(session, next_state);
+
+       LOG("next_state %d", next_state);
+
+       switch (next_state) {
+       case TEST_SESSION_STATE_0:
+
+               return;
+       case TEST_SESSION_STATE_1:
+               msg = session_connect(session0->connection, session0);
+               g_assert(msg != NULL);
+               dbus_message_unref(msg);
+
+               return;
+
+       case TEST_SESSION_STATE_2:
+               msg = session_disconnect(session0->connection, session0);
+               g_assert(msg != NULL);
+               dbus_message_unref(msg);
+
+               return;
+       case TEST_SESSION_STATE_3:
+               util_session_cleanup(session0);
+               util_session_cleanup(session1);
+
+               util_idle_call(session0->fix, util_quit_loop,
+                               util_session_destroy);
+
+               return;
+       }
+}
+
+static gboolean test_session_connect_free_ride(gpointer data)
+{
+       struct test_fix *fix = data;
+       struct test_session *session0, *session1;
+
+       /*
+        * +-------------------+
+        * |       START       |
+        * +-------------------+
+        *   |
+        *   | connect foo
+        *   v
+        * +-------------------+
+        * |   FOO-CONNECTED   |
+        * +-------------------+
+        *   |
+        *   | free-ride bar
+        *   v
+        * +-------------------+
+        * | FOO-BAR-CONNECTED |
+        * +-------------------+
+        *  |
+        *  | disconnect foo
+        *  v
+        * +-------------------+
+        * |        END        |
+        * +-------------------+
+        */
+
+       util_session_create(fix, 2);
+       session0 = &fix->session[0];
+       session1 = &fix->session[1];
+
+       session0->notify_path = g_strdup("/foo");
+       session1->notify_path = g_strdup("/bar");
+       session0->notify = test_session_connect_free_ride_notify;
+       session1->notify = test_session_connect_free_ride_notify;
+
+       util_session_init(session0);
+       util_session_init(session1);
+
+       set_session_state(session0, TEST_SESSION_STATE_0);
+
+       return FALSE;
+}
+
+static connman_bool_t is_online(struct test_fix *fix)
+{
+       if (g_strcmp0(fix->manager.state, "online") == 0)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean enable_session_mode(gpointer data)
+{
+       struct test_fix *fix = data;
+
+       set_session_mode(fix, TRUE);
+
+       if (is_online(fix) == FALSE)
+               util_idle_call(fix, util_quit_loop, NULL);
+
+       return FALSE;
+}
+
+static gboolean manager_state_changed(gpointer data)
+{
+       struct test_fix *fix = data;
+
+       if (is_online(fix) == FALSE) {
+               fix->manager_changed = NULL;
+               util_idle_call(fix, util_quit_loop, NULL);
+       }
+
+       return FALSE;
+}
+
+static gboolean disable_session_mode(gpointer data)
+{
+       struct test_fix *fix = data;
+
+       set_session_mode(fix, FALSE);
+
+       return FALSE;
+}
+
+static void setup_cb(struct test_fix *fix, gconstpointer data)
+{
+       fix->manager_changed = manager_state_changed;
+
+       util_setup(fix, data);
+       util_call(fix, enable_session_mode, NULL);
+
+       g_main_loop_run(fix->main_loop);
+
+       fix->manager_changed = NULL;
+}
+
+static void teardown_cb(struct test_fix *fix, gconstpointer data)
+{
+       util_call(fix, disable_session_mode, NULL);
+       util_idle_call(fix, util_quit_loop, NULL);
+
+       g_main_loop_run(fix->main_loop);
+
+       util_teardown(fix, data);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       util_test_add("/manager/session create no notify",
+               test_session_create_no_notify, setup_cb, teardown_cb);
+       util_test_add("/manager/session destroy no notify",
+               test_session_destroy_no_notify, setup_cb, teardown_cb);
+       util_test_add("/manager/session create",
+               test_session_create, setup_cb, teardown_cb);
+       util_test_add("/manager/session create destroy",
+               test_session_create_destroy, setup_cb, teardown_cb);
+       util_test_add("/manager/session create already exists",
+               test_session_create_already_exists, setup_cb, teardown_cb);
+       util_test_add("/manager/session create many",
+               test_session_create_many, setup_cb, teardown_cb);
+
+       util_test_add("/session/connect",
+               test_session_connect, setup_cb, teardown_cb);
+       util_test_add("/session/disconnect",
+               test_session_disconnect, setup_cb, teardown_cb);
+       util_test_add("/session/connect disconnect",
+               test_session_connect_disconnect, setup_cb, teardown_cb);
+       util_test_add("/session/connect free-ride",
+               test_session_connect_free_ride, setup_cb, teardown_cb);
+
+       return g_test_run();
+}
diff --git a/unit/utils.c b/unit/utils.c
new file mode 100644 (file)
index 0000000..758528a
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  BWM CarIT GmbH. 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 "gdbus/gdbus.h"
+
+#include "test-connman.h"
+
+#define ENABLE_WRAPPER 1
+#define PROPERTY_CHANGED               "PropertyChanged"
+
+gboolean util_quit_loop(gpointer data)
+{
+       struct test_fix *fix = data;
+
+       g_main_loop_quit(fix->main_loop);
+
+       return FALSE;
+}
+
+guint util_idle_call(struct test_fix *fix, GSourceFunc func,
+                       GDestroyNotify notify)
+{
+       GSource *source;
+       guint id;
+
+       source = g_idle_source_new();
+       g_source_set_callback(source, func, fix, notify);
+       id = g_source_attach(source, g_main_loop_get_context(fix->main_loop));
+       g_source_unref(source);
+
+       return id;
+}
+
+static void connman_died(DBusConnection *connection, void *user_data)
+{
+       g_assert(FALSE);
+}
+
+static void manager_changed(struct test_fix *fix,
+                                       DBusMessageIter *entry)
+{
+       DBusMessageIter iter;
+       const char *key;
+       const char *value;
+       int type;
+
+       dbus_message_iter_get_basic(entry, &key);
+
+       LOG("key %s", key);
+
+       dbus_message_iter_next(entry);
+
+       dbus_message_iter_recurse(entry, &iter);
+
+       type = dbus_message_iter_get_arg_type(&iter);
+
+       if (type != DBUS_TYPE_STRING)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &value);
+
+       if (g_str_equal(key, "State") == TRUE) {
+               LOG("State %s", value);
+
+               if (fix->manager.state != NULL)
+                       g_free(fix->manager.state);
+
+               fix->manager.state = g_strdup(value);
+       }
+
+       if (fix->manager_changed != NULL)
+               fix->manager_changed(fix);
+}
+
+static gboolean handle_manager_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       struct test_fix *fix = user_data;
+
+       DBusMessageIter iter;
+
+       if (dbus_message_iter_init(message, &iter))
+               manager_changed(fix, &iter);
+
+       return TRUE;
+}
+
+guint util_call(struct test_fix *fix, GSourceFunc func,
+               GDestroyNotify notify)
+{
+       GSource *source;
+       guint id;
+
+       source = g_timeout_source_new(0);
+       g_source_set_callback(source, func, fix, notify);
+       id = g_source_attach(source, g_main_loop_get_context(fix->main_loop));
+       g_source_unref(source);
+
+       return id;
+}
+
+void util_setup(struct test_fix *fix, gconstpointer data)
+{
+       DBusMessage *msg;
+
+       fix->main_loop = g_main_loop_new(NULL, FALSE);
+       fix->main_connection = g_dbus_setup_private(DBUS_BUS_SYSTEM,
+                                                       NULL, NULL);
+       fix->watch = g_dbus_add_service_watch(fix->main_connection,
+                                               CONNMAN_SERVICE,
+                                               NULL,
+                                               connman_died,
+                                               NULL, NULL);
+       fix->manager_watch = g_dbus_add_signal_watch(fix->main_connection,
+                                               CONNMAN_SERVICE, NULL,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               handle_manager_changed,
+                                               fix, NULL);
+
+       msg = manager_get_properties(fix->main_connection);
+       manager_parse_properties(msg, &fix->manager);
+       dbus_message_unref(msg);
+}
+
+void util_teardown(struct test_fix *fix, gconstpointer data)
+{
+       g_dbus_remove_watch(fix->main_connection, fix->watch);
+       g_dbus_remove_watch(fix->main_connection, fix->manager_watch);
+       dbus_connection_close(fix->main_connection);
+       dbus_connection_unref(fix->main_connection);
+
+       g_main_loop_unref(fix->main_loop);
+}
+
+static void util_wrapper(struct test_fix *fix, gconstpointer data)
+{
+       GSourceFunc func = data;
+#if ENABLE_WRAPPER
+       if (g_test_trap_fork(60 * 1000 * 1000, 0) == TRUE) {
+               util_call(fix, func, NULL);
+               g_main_loop_run(fix->main_loop);
+               exit(0);
+       }
+
+       g_test_trap_assert_passed();
+#else
+       util_call(fix, func, NULL);
+       g_main_loop_run(fix->main_loop);
+#endif
+}
+
+void util_test_add(const char *test_name, GSourceFunc test_func,
+                       util_test_setup_cb setup_cb,
+                       util_test_teardown_cb teardown_cb)
+{
+       g_test_add(test_name, struct test_fix, test_func,
+               setup_cb, util_wrapper, teardown_cb);
+}
+
+void util_session_create(struct test_fix *fix, unsigned int max_sessions)
+{
+       unsigned int i;
+
+       fix->max_sessions = max_sessions;
+       fix->session = g_try_new0(struct test_session, max_sessions);
+
+       for (i = 0; i < max_sessions; i++) {
+               fix->session[i].fix = fix;
+               fix->session[i].info = g_try_new0(struct test_session_info, 1);
+               fix->session[i].connection = g_dbus_setup_private(
+                                               DBUS_BUS_SYSTEM, NULL, NULL);
+       }
+}
+
+void util_session_destroy(gpointer data)
+{
+       struct test_fix *fix = data;
+
+       unsigned int i;
+
+       for (i = 0; i < fix->max_sessions; i++) {
+               dbus_connection_close(fix->session[i].connection);
+               g_free(fix->session[i].info);
+       }
+
+       g_free(fix->session);
+}
+
+void util_session_init(struct test_session *session)
+{
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       const char *path;
+       int err;
+
+       err = session_notify_register(session, session->notify_path);
+       g_assert(err == 0);
+
+       msg = manager_create_session(session->connection,
+                                       session->info,
+                                       session->notify_path);
+       g_assert(msg != NULL);
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &path);
+       session->session_path = g_strdup(path);
+
+       dbus_message_unref(msg);
+}
+
+void util_session_cleanup(struct test_session *session)
+{
+       DBusMessage *msg;
+       int err;
+
+       msg = manager_destroy_session(session->connection,
+                                       session->session_path);
+       g_assert(msg != NULL);
+       dbus_message_unref(msg);
+
+       err = session_notify_unregister(session,
+                                       session->notify_path);
+       g_assert(err == 0);
+
+       g_free(session->info->bearer);
+       g_free(session->info->name);
+       g_free(session->info->interface);
+       g_slist_foreach(session->info->allowed_bearers,
+                       bearer_info_cleanup, NULL);
+       g_slist_free(session->info->allowed_bearers);
+
+       session->notify = NULL;
+       g_free(session->notify_path);
+       g_free(session->session_path);
+}