1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <errno.h>
29#include <poll.h>
30#include <signal.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <syslog.h>
35#include <unistd.h>
36
37#include <dbus/dbus.h>
38
39#include "../config.h"
40#include "../eloop.h"
41#include "../dhcp.h"
42#ifdef INET6
43#include "../dhcp6.h"
44#endif
45#include "../rpc-interface.h"
46#include "dbus-dict.h"
47
48#define SERVICE_NAME 	"org.chromium.dhcpcd"
49#define SERVICE_PATH    "/org/chromium/dhcpcd"
50#define S_EINVAL	SERVICE_NAME ".InvalidArgument"
51#define S_ARGS		"Not enough arguments"
52
53static DBusConnection *connection;
54static struct dhcpcd_ctx *dhcpcd_ctx;
55
56static const char dhcpcd_introspection_xml[] =
57    "    <method name=\"GetVersion\">\n"
58    "      <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
59    "    </method>\n"
60    "    <method name=\"Rebind\">\n"
61    "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
62    "    </method>\n"
63    "    <method name=\"Release\">\n"
64    "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
65    "    </method>\n"
66    "    <method name=\"Stop\">\n"
67    "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
68    "    </method>\n"
69    "    <signal name=\"Event\">\n"
70    "      <arg name=\"configuration\" type=\"usa{sv}\"/>\n"
71    "    </signal>\n"
72    "    <signal name=\"StatusChanged\">\n"
73    "      <arg name=\"status\" type=\"us\"/>\n"
74    "    </signal>\n";
75
76static const char service_watch_rule[] = "interface=" DBUS_INTERFACE_DBUS
77	",type=signal,member=NameOwnerChanged";
78
79static const char introspection_header_xml[] =
80    "<!DOCTYPE node PUBLIC \"-//freedesktop//"
81    "DTD D-BUS Object Introspection 1.0//EN\"\n"
82    "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
83    "<node name=\"" SERVICE_PATH "\">\n"
84    "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
85    "    <method name=\"Introspect\">\n"
86    "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
87    "    </method>\n"
88    "  </interface>\n"
89    "  <interface name=\"" SERVICE_NAME "\">\n";
90
91static const char introspection_footer_xml[] =
92    "  </interface>\n"
93    "</node>\n";
94
95static const struct o_dbus dhos[] = {
96	{ "ip_address=", DBUS_TYPE_UINT32, 0, "IPAddress" },
97	{ "server_name=", DBUS_TYPE_STRING, 0, "ServerName"},
98	{ "subnet_mask=", DBUS_TYPE_UINT32, 0, "SubnetMask" },
99	{ "subnet_cidr=", DBUS_TYPE_BYTE, 0, "SubnetCIDR" },
100	{ "network_number=", DBUS_TYPE_UINT32, 0, "NetworkNumber" },
101	{ "classless_static_routes=", DBUS_TYPE_STRING, 0,
102	  "ClasslessStaticRoutes" },
103	{ "ms_classless_static_routes=", DBUS_TYPE_STRING, 0,
104	  "MSClasslessStaticRoutes" },
105	{ "static_routes=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
106	  "StaticRoutes"} ,
107	{ "routers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "Routers" },
108	{ "time_offset=", DBUS_TYPE_UINT32, 0, "TimeOffset" },
109	{ "time_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "TimeServers" },
110	{ "ien116_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
111	  "IEN116NameServers" },
112	{ "domain_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
113	  "DomainNameServers" },
114	{ "log_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LogServers" },
115	{ "cookie_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
116	  "CookieServers" },
117	{ "lpr_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LPRServers" },
118	{ "impress_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
119	  "ImpressServers" },
120	{ "resource_location_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
121	  "ResourceLocationServers" },
122	{ "host_name=", DBUS_TYPE_STRING, 0, "Hostname" },
123	{ "boot_size=", DBUS_TYPE_UINT16, 0, "BootSize" },
124	{ "merit_dump=", DBUS_TYPE_STRING, 0, "MeritDump" },
125	{ "domain_name=", DBUS_TYPE_STRING, 0, "DomainName" },
126	{ "swap_server=", DBUS_TYPE_UINT32, 0, "SwapServer" },
127	{ "root_path=", DBUS_TYPE_STRING, 0, "RootPath" },
128	{ "extensions_path=", DBUS_TYPE_STRING, 0, "ExtensionsPath" },
129	{ "ip_forwarding=", DBUS_TYPE_BOOLEAN, 0, "IPForwarding" },
130	{ "non_local_source_routing=", DBUS_TYPE_BOOLEAN, 0,
131	  "NonLocalSourceRouting" },
132	{ "policy_filter=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
133	  "PolicyFilter" },
134	{ "max_dgram_reassembly=", DBUS_TYPE_INT16, 0,
135	  "MaxDatagramReassembly" },
136	{ "default_ip_ttl=", DBUS_TYPE_UINT16, 0, "DefaultIPTTL" },
137	{ "path_mtu_aging_timeout=", DBUS_TYPE_UINT32, 0,
138	  "PathMTUAgingTimeout" },
139	{ "path_mtu_plateau_table=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT16,
140	  "PolicyFilter"} ,
141	{ "interface_mtu=", DBUS_TYPE_UINT16, 0, "InterfaceMTU" },
142	{ "all_subnets_local=", DBUS_TYPE_BOOLEAN, 0, "AllSubnetsLocal" },
143	{ "broadcast_address=", DBUS_TYPE_UINT32, 0, "BroadcastAddress" },
144	{ "perform_mask_discovery=", DBUS_TYPE_BOOLEAN, 0,
145	  "PerformMaskDiscovery" },
146	{ "mask_supplier=", DBUS_TYPE_BOOLEAN, 0, "MaskSupplier" },
147	{ "router_discovery=", DBUS_TYPE_BOOLEAN, 0, "RouterDiscovery" },
148	{ "router_solicitiation_address=", DBUS_TYPE_UINT32, 0,
149	  "RouterSolicationAddress" },
150	{ "trailer_encapsulation=", DBUS_TYPE_BOOLEAN, 0,
151	  "TrailerEncapsulation" },
152	{ "arp_cache_timeout=", DBUS_TYPE_UINT32, 0, "ARPCacheTimeout" },
153	{ "ieee802_3_encapsulation=", DBUS_TYPE_UINT16, 0,
154	  "IEEE8023Encapsulation" },
155	{ "default_tcp_ttl=", DBUS_TYPE_BYTE, 0, "DefaultTCPTTL" },
156	{ "tcp_keepalive_interval=", DBUS_TYPE_UINT32, 0,
157	  "TCPKeepAliveInterval" },
158	{ "tcp_keepalive_garbage=", DBUS_TYPE_BOOLEAN, 0,
159	  "TCPKeepAliveGarbage" },
160	{ "nis_domain=", DBUS_TYPE_STRING, 0, "NISDomain" },
161	{ "nis_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NISServers" },
162	{ "ntp_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NTPServers" },
163	{ "vendor_encapsulated_options=", DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
164	  "VendorEncapsulatedOptions" },
165	{ "netbios_name_servers=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
166	  "NetBIOSNameServers" },
167	{ "netbios_dd_server=", DBUS_TYPE_UINT32, 0, "NetBIOSDDServer" },
168	{ "netbios_node_type=", DBUS_TYPE_BYTE, 0, "NetBIOSNodeType" },
169	{ "netbios_scope=", DBUS_TYPE_STRING, 0, "NetBIOSScope" },
170	{ "font_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "FontServers" },
171	{ "x_display_manager=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
172	  "XDisplayManager" },
173	{ "dhcp_requested_address=", DBUS_TYPE_UINT32, 0,
174	  "DHCPRequestedAddress" },
175	{ "dhcp_lease_time=", DBUS_TYPE_UINT32, 0, "DHCPLeaseTime" },
176	{ "dhcp_option_overload=", DBUS_TYPE_BOOLEAN, 0,
177	  "DHCPOptionOverload" },
178	{ "dhcp_message_type=", DBUS_TYPE_BYTE, 0, "DHCPMessageType" },
179	{ "dhcp_server_identifier=", DBUS_TYPE_UINT32, 0,
180	  "DHCPServerIdentifier" },
181	{ "dhcp_message=", DBUS_TYPE_STRING, 0, "DHCPMessage" },
182	{ "dhcp_max_message_size=", DBUS_TYPE_UINT16, 0,
183	  "DHCPMaxMessageSize" },
184	{ "dhcp_renewal_time=", DBUS_TYPE_UINT32, 0, "DHCPRenewalTime" },
185	{ "dhcp_rebinding_time=", DBUS_TYPE_UINT32, 0, "DHCPRebindingTime" },
186	{ "nisplus_domain=", DBUS_TYPE_STRING, 0, "NISPlusDomain" },
187	{ "nisplus_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
188	  "NISPlusServers" },
189	{ "tftp_server_name=", DBUS_TYPE_STRING, 0, "TFTPServerName" },
190	{ "bootfile_name=", DBUS_TYPE_STRING, 0, "BootFileName" },
191	{ "mobile_ip_home_agent=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
192	  "MobileIPHomeAgent" },
193	{ "smtp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "SMTPServer" },
194	{ "pop_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "POPServer" },
195	{ "nntp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NNTPServer" },
196	{ "www_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "WWWServer" },
197	{ "finger_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
198	  "FingerServer" },
199	{ "irc_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "IRCServer" },
200	{ "streettalk_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
201	  "StreetTalkServer" },
202	{ "streettalk_directory_assistance_server=", DBUS_TYPE_ARRAY,
203	  DBUS_TYPE_UINT32, "StreetTalkDirectoryAssistanceServer" },
204	{ "user_class=", DBUS_TYPE_STRING, 0, "UserClass" },
205	{ "new_fqdn_name=", DBUS_TYPE_STRING, 0, "FQDNName" },
206	{ "nds_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NDSServers" },
207	{ "nds_tree_name=", DBUS_TYPE_STRING, 0, "NDSTreeName" },
208	{ "nds_context=", DBUS_TYPE_STRING, 0, "NDSContext" },
209	{ "bcms_controller_names=", DBUS_TYPE_STRING, 0,
210	  "BCMSControllerNames" },
211	{ "client_last_transaction_time=", DBUS_TYPE_UINT32, 0,
212	  "ClientLastTransactionTime" },
213	{ "associated_ip=", DBUS_TYPE_UINT32, 0, "AssociatedIP" },
214	{ "uap_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "UAPServers" },
215	{ "netinfo_server_address=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
216	  "NetinfoServerAddress" },
217	{ "netinfo_server_tag=", DBUS_TYPE_STRING, 0, "NetinfoServerTag" },
218	{ "default_url=", DBUS_TYPE_STRING, 0, "DefaultURL" },
219	{ "subnet_selection=", DBUS_TYPE_UINT32, 0, "SubnetSelection" },
220	{ "domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
221	  "DomainSearch" },
222	{ "wpad_url=", DBUS_TYPE_STRING, 0, "WebProxyAutoDiscoveryUrl" },
223#ifdef INET6
224	{ "dhcp6_server_id=", DBUS_TYPE_STRING, 0,
225	  "DHCPv6ServerIdentifier" },
226	{ "dhcp6_ia_na1_ia_addr1=", DBUS_TYPE_STRING, 0, "DHCPv6Address" },
227	{ "dhcp6_ia_na1_ia_addr1_vltime=", DBUS_TYPE_UINT32, 0,
228	  "DHCPv6AddressLeaseTime" },
229	{ "dhcp6_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
230	  "DHCPv6NameServers" },
231	{ "dhcp6_domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
232	  "DHCPv6DomainSearch" },
233	{ "dhcp6_ia_pd1_prefix1=", DBUS_TYPE_STRING, 0,
234	  "DHCPv6DelegatedPrefix" },
235	{ "dhcp6_ia_pd1_prefix1_length=", DBUS_TYPE_UINT32, 0,
236	  "DHCPv6DelegatedPrefixLength" },
237	{ "dhcp6_ia_pd1_prefix1_vltime=", DBUS_TYPE_UINT32, 0,
238	  "DHCPv6DelegatedPrefixLeaseTime" },
239#endif
240	{ NULL, 0, 0, NULL }
241};
242
243static int
244append_config(DBusMessageIter *iter,
245    const char *prefix, char **env, ssize_t elen)
246{
247	char **eenv, *p;
248	const struct o_dbus *dhop;
249	size_t l, lp;
250	int retval;
251
252	retval = 0;
253	lp = strlen(prefix);
254	for (eenv = env + elen; env < eenv; env++) {
255		p = env[0];
256		for (dhop = dhos; dhop->var; dhop++) {
257			l = strlen(dhop->var);
258			if (strncmp(p, dhop->var, l) == 0) {
259				retval = dict_append_config_item(iter,
260				    dhop, p + l);
261				break;
262			}
263			if (strncmp(p, prefix, lp) == 0 &&
264			    strncmp(p + lp, dhop->var, l) == 0)
265			{
266				retval = dict_append_config_item(iter,
267				    dhop, p + l + lp);
268				break;
269			}
270		}
271		if (retval == -1)
272			break;
273	}
274	return retval;
275}
276
277static DBusHandlerResult
278get_dbus_error(DBusConnection *con, DBusMessage *msg,
279		  const char *name, const char *fmt, ...)
280{
281	char buffer[1024];
282	DBusMessage *reply;
283	va_list args;
284
285	va_start(args, fmt);
286	vsnprintf(buffer, sizeof(buffer), fmt, args);
287	va_end(args);
288	reply = dbus_message_new_error(msg, name, buffer);
289	dbus_connection_send(con, reply, NULL);
290	dbus_message_unref(reply);
291	return DBUS_HANDLER_RESULT_HANDLED;
292}
293
294static dbus_bool_t
295dbus_send_message(const struct interface *ifp, const char *reason,
296    const char *prefix, struct dhcp_message *message)
297{
298	const struct if_options *ifo = ifp->options;
299	DBusMessage* msg;
300	DBusMessageIter args, dict;
301	int pid = getpid();
302	char **env = NULL;
303	ssize_t e, elen;
304	int retval;
305	int success = FALSE;
306
307	syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name);
308
309	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event");
310	if (msg == NULL) {
311		syslog(LOG_ERR, "failed to make a configure message");
312		return FALSE;
313	}
314	dbus_message_iter_init_append(msg, &args);
315	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
316	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason);
317	dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
318	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
319	    DBUS_TYPE_STRING_AS_STRING
320	    DBUS_TYPE_VARIANT_AS_STRING
321	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
322	    &dict);
323	if (prefix == NULL || message == NULL)
324		retval = 0;
325	else {
326		e = dhcp_env(NULL, NULL, message, ifp);
327		if (e > 0) {
328			char *config_prefix = strdup(prefix);
329			if (config_prefix == NULL) {
330				logger(dhcpcd_ctx, LOG_ERR,
331				       "Memory exhausted (strdup)");
332				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
333			}
334			char *p = config_prefix + strlen(config_prefix) - 1;
335			if (p >= config_prefix && *p == '_')
336				*p = '\0';
337			env = calloc(e + 1, sizeof(char *));
338			if (env == NULL) {
339				logger(dhcpcd_ctx, LOG_ERR,
340				       "Memory exhausted (calloc)");
341				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
342			}
343			elen = dhcp_env(env, config_prefix, message, ifp);
344			free(config_prefix);
345		}
346		retval = append_config(&dict, prefix, env, elen);
347	}
348
349	/* Release memory allocated for env. */
350	if (env) {
351		char **current = env;
352		while (*current)
353			free(*current++);
354		free(env);
355	}
356
357	dbus_message_iter_close_container(&args, &dict);
358	if (retval == 0) {
359		success = dbus_connection_send(connection, msg, NULL);
360		if (!success)
361			syslog(LOG_ERR, "failed to send dhcp to dbus");
362	} else
363		syslog(LOG_ERR, "failed to construct dbus message");
364	dbus_message_unref(msg);
365
366	return success;
367}
368
369#ifdef INET6
370static dbus_bool_t
371dbus_send_dhcpv6_message(const struct interface *ifp, const char *reason,
372    const char *prefix, struct dhcp6_message *message, size_t length)
373{
374	const struct if_options *ifo = ifp->options;
375	DBusMessage* msg;
376	DBusMessageIter args, dict;
377	int pid = getpid();
378	char **env = NULL;
379	ssize_t e, elen;
380	int retval;
381	int success = FALSE;
382
383	syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name);
384
385	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event");
386	if (msg == NULL) {
387		syslog(LOG_ERR, "failed to make a configure message");
388		return FALSE;
389	}
390	dbus_message_iter_init_append(msg, &args);
391	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
392	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason);
393	dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
394	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
395	    DBUS_TYPE_STRING_AS_STRING
396	    DBUS_TYPE_VARIANT_AS_STRING
397	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
398	    &dict);
399	if (prefix == NULL || message == NULL)
400		retval = 0;
401	else {
402		e = dhcp6_env(NULL, NULL, ifp, message, length);
403		if (e > 0) {
404			char *config_prefix = strdup(prefix);
405			if (config_prefix == NULL) {
406				logger(dhcpcd_ctx, LOG_ERR,
407				       "Memory exhausted (strdup)");
408				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
409			}
410			char *p = config_prefix + strlen(config_prefix) - 1;
411			if (p >= config_prefix && *p == '_')
412				*p = '\0';
413			env = calloc(e + 1, sizeof(char *));
414			if (env == NULL) {
415				logger(dhcpcd_ctx, LOG_ERR,
416				       "Memory exhausted (calloc)");
417				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
418			}
419			elen = dhcp6_env(env, "new", ifp, message, length);
420			free(config_prefix);
421		}
422		retval = append_config(&dict, prefix, env, elen);
423	}
424
425	/* Release memory allocated for env. */
426	if (env) {
427		char **current = env;
428		while (*current)
429			free(*current++);
430		free(env);
431	}
432
433	dbus_message_iter_close_container(&args, &dict);
434	if (retval == 0) {
435		success = dbus_connection_send(connection, msg, NULL);
436		if (!success)
437			syslog(LOG_ERR, "failed to send dhcpv6 to dbus");
438	} else
439		syslog(LOG_ERR, "failed to construct dbus message");
440	dbus_message_unref(msg);
441
442	return success;
443}
444#endif
445
446static DBusHandlerResult
447introspect(DBusConnection *con, DBusMessage *msg)
448{
449	DBusMessage *reply;
450	char *xml;
451	size_t len;
452
453	len = sizeof(introspection_header_xml) - 1
454	    + sizeof(dhcpcd_introspection_xml) - 1
455	    + sizeof(introspection_footer_xml) - 1
456	    + 1; /* terminal \0 */
457	xml = malloc(len);
458	if (xml == NULL)
459		return DBUS_HANDLER_RESULT_HANDLED;
460	snprintf(xml, len, "%s%s%s",
461	    introspection_header_xml,
462	    dhcpcd_introspection_xml,
463	    introspection_footer_xml);
464	reply = dbus_message_new_method_return(msg);
465	dbus_message_append_args(reply,
466	    DBUS_TYPE_STRING, &xml,
467	    DBUS_TYPE_INVALID);
468	dbus_connection_send(con, reply, NULL);
469	dbus_message_unref(reply);
470	free(xml);
471	return DBUS_HANDLER_RESULT_HANDLED;
472}
473
474static DBusHandlerResult
475version(DBusConnection *con, DBusMessage *msg, const char *ver)
476{
477	DBusMessage *reply;
478
479	reply = dbus_message_new_method_return(msg);
480	dbus_message_append_args(reply,
481	    DBUS_TYPE_STRING, &ver,
482	    DBUS_TYPE_INVALID);
483	dbus_connection_send(con, reply, NULL);
484	dbus_message_unref(reply);
485	return DBUS_HANDLER_RESULT_HANDLED;
486}
487
488static DBusHandlerResult
489dbus_ack(DBusConnection *con, DBusMessage *msg)
490{
491	DBusMessage *reply;
492
493	reply = dbus_message_new_method_return(msg);
494	dbus_connection_send(con, reply, NULL);
495	dbus_message_unref(reply);
496	return DBUS_HANDLER_RESULT_HANDLED;
497}
498
499static DBusHandlerResult
500msg_handler(DBusConnection *con, DBusMessage *msg, __unused void *data)
501{
502#define	IsMethod(msg, method) \
503	dbus_message_is_method_call(msg, SERVICE_NAME, method)
504
505	if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE,
506					"Introspect")) {
507		return introspect(con, msg);
508	} else if (IsMethod(msg, "GetVersion")) {
509		return version(con, msg, VERSION);
510	} else if (IsMethod(msg, "Rebind")) {
511		const char *iface_name;
512		if (!dbus_message_get_args(msg, NULL,
513				   DBUS_TYPE_STRING, &iface_name,
514				   DBUS_TYPE_INVALID)) {
515			logger(dhcpcd_ctx, LOG_ERR,
516			       "Invalid arguments for Rebind");
517			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
518		}
519		dhcpcd_start_interface(dhcpcd_ctx, iface_name);
520		return dbus_ack(con, msg);
521	} else if (IsMethod(msg, "Release")) {
522		const char *iface_name;
523		if (!dbus_message_get_args(msg, NULL,
524				   DBUS_TYPE_STRING, &iface_name,
525				   DBUS_TYPE_INVALID)) {
526			logger(dhcpcd_ctx, LOG_ERR,
527			       "Invalid arguments for Release");
528			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
529		}
530		dhcpcd_release_ipv4(dhcpcd_ctx, iface_name);
531		return dbus_ack(con, msg);
532	} else if (IsMethod(msg, "Stop")) {
533		const char *iface_name;
534		if (!dbus_message_get_args(msg, NULL,
535				   DBUS_TYPE_STRING, &iface_name,
536				   DBUS_TYPE_INVALID)) {
537			logger(dhcpcd_ctx, LOG_ERR,
538			       "Invalid arguments for Stop");
539			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
540		}
541		dhcpcd_stop_interface(dhcpcd_ctx, iface_name);
542		(void) dbus_ack(con, msg);
543		exit(EXIT_FAILURE);
544	} else if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL,
545					  "Disconnected")) {
546		dhcpcd_stop_interfaces(dhcpcd_ctx);
547		exit(EXIT_FAILURE);
548	}
549	return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
550#undef IsMethod
551}
552
553static void
554dbus_handle_event(DBusWatch *watch, int flags)
555{
556	dbus_watch_handle((DBusWatch *)watch, flags);
557
558	if (connection != NULL) {
559		dbus_connection_ref(connection);
560		while (dbus_connection_dispatch(connection) ==
561				DBUS_DISPATCH_DATA_REMAINS)
562				;
563		dbus_connection_unref(connection);
564	}
565}
566
567static void
568dbus_read_event(void *watch)
569{
570	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_READABLE);
571}
572
573static void
574dbus_write_event(void *watch)
575{
576	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_WRITABLE);
577}
578
579static dbus_bool_t
580add_watch(DBusWatch *watch, __unused void *data)
581{
582	int fd, flags;
583	void (*read_event)(void *) = NULL;
584	void *read_arg = NULL;
585	void (*write_event)(void *) = NULL;
586	void *write_arg = NULL;
587
588	fd = dbus_watch_get_unix_fd(watch);
589	flags = dbus_watch_get_flags(watch);
590	if (flags & DBUS_WATCH_READABLE) {
591		read_event = dbus_read_event;
592		read_arg = watch;
593	}
594	if (flags & DBUS_WATCH_WRITABLE) {
595		write_event = dbus_write_event;
596		write_arg = watch;
597	}
598
599	if (eloop_event_add(dhcpcd_ctx->eloop, fd, read_event, read_arg,
600			    write_event, write_arg) == 0)
601		return TRUE;
602	return FALSE;
603}
604
605static void
606remove_watch(DBusWatch *watch, __unused void *data)
607{
608	int fd, flags;
609	int write_only = 0;
610	fd = dbus_watch_get_unix_fd(watch);
611	flags = dbus_watch_get_flags(watch);
612	if (!(flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE))
613		write_only = 1;
614	eloop_event_delete(dhcpcd_ctx->eloop, fd, write_only);
615}
616
617static DBusHandlerResult
618dhcpcd_dbus_filter(DBusConnection *conn, DBusMessage *msg, void *user_data)
619{
620	const char *service = NULL;
621	const char *old_owner = NULL;
622	const char *new_owner = NULL;
623
624	if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
625				    "NameOwnerChanged"))
626		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
627
628	if (!dbus_message_get_args(msg, NULL,
629				   DBUS_TYPE_STRING, &service,
630				   DBUS_TYPE_STRING, &old_owner,
631				   DBUS_TYPE_STRING, &new_owner,
632				   DBUS_TYPE_INVALID)) {
633		syslog(LOG_ERR,
634		       "Invalid arguments for NameOwnerChanged signal");
635		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
636	}
637	if (strcmp(service, "org.chromium.flimflam") == 0 &&
638	    strlen(new_owner) == 0) {
639		syslog(LOG_INFO, "exiting because flimflamd has died");
640		dhcpcd_stop_interfaces(dhcpcd_ctx);
641		exit(EXIT_FAILURE);
642	}
643	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
644}
645
646int
647rpc_init(struct dhcpcd_ctx *ctx)
648{
649	DBusObjectPathVTable vt = {
650		NULL, &msg_handler, NULL, NULL, NULL, NULL
651	};
652	DBusError err;
653	int ret;
654
655	dhcpcd_ctx = ctx;
656
657	dbus_error_init(&err);
658	connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
659	if (connection == NULL) {
660		if (dbus_error_is_set(&err))
661			syslog(LOG_ERR, "%s", err.message);
662		else
663			syslog(LOG_ERR, "failed to get a dbus connection");
664		return -1;
665	}
666	atexit(rpc_close);
667
668	if (!dbus_connection_set_watch_functions(connection,
669		add_watch, remove_watch, NULL, NULL, NULL))
670	{
671		syslog(LOG_ERR, "dbus: failed to set watch functions");
672		return -1;
673	}
674	if (!dbus_connection_register_object_path(connection,
675		SERVICE_PATH, &vt, NULL))
676	{
677		syslog(LOG_ERR, "dbus: failed to register object path");
678		return -1;
679	}
680	dbus_connection_add_filter(connection, dhcpcd_dbus_filter, NULL, NULL);
681	dbus_bus_add_match(connection, service_watch_rule, &err);
682	if (dbus_error_is_set(&err)) {
683		syslog(LOG_ERR, "Cannot add rule: %s", err.message);
684		return -1;
685	}
686	return 0;
687}
688
689void
690rpc_close(void)
691{
692	if (connection) {
693		dbus_bus_remove_match(connection, service_watch_rule, NULL);
694		dbus_connection_remove_filter(connection,
695					      dhcpcd_dbus_filter,
696					      NULL);
697		dbus_connection_unref(connection);
698		connection = NULL;
699	}
700}
701
702void
703rpc_signal_status(const char *status)
704{
705	DBusMessage *msg;
706	DBusMessageIter args;
707	int pid = getpid();
708
709	syslog(LOG_INFO, "status changed to %s", status);
710
711	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME,
712	    "StatusChanged");
713	if (msg == NULL) {
714		syslog(LOG_ERR, "failed to make a status changed message");
715		return;
716	}
717	dbus_message_iter_init_append(msg, &args);
718	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
719	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &status);
720	if (!dbus_connection_send(connection, msg, NULL))
721		syslog(LOG_ERR, "failed to send status to dbus");
722	dbus_message_unref(msg);
723}
724
725
726int
727rpc_update_ipv4(struct interface *ifp)
728{
729	struct dhcp_state *state = D_STATE(ifp);
730	if (state->new != NULL) {
731		/* push state over d-bus */
732		dbus_send_message(ifp, state->reason, "new_", state->new);
733		rpc_signal_status("Bound");
734	} else {
735		rpc_signal_status("Release");
736	}
737	return 0;
738}
739
740#ifdef INET6
741int
742rpc_update_ipv6(struct interface *ifp)
743{
744	struct dhcp6_state *state = D6_STATE(ifp);
745	if (state->new != NULL) {
746		/* push state over d-bus */
747		dbus_send_dhcpv6_message(ifp, state->reason, "new_",
748					 state->new, state->new_len);
749		rpc_signal_status("Bound6");
750	} else {
751		rpc_signal_status("Release6");
752	}
753	return 0;
754}
755#endif
756
757int
758rpc_notify_unicast_arp(struct interface *ifp) {
759	struct dhcp_state *state = D_STATE(ifp);
760	return dbus_send_message(ifp, "GATEWAY-ARP", "saved_", state->offer);
761}
762