• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
53 static DBusConnection *connection;
54 static struct dhcpcd_ctx *dhcpcd_ctx;
55 
56 static 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 
76 static const char service_watch_rule[] = "interface=" DBUS_INTERFACE_DBUS
77 	",type=signal,member=NameOwnerChanged";
78 
79 static 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 
91 static const char introspection_footer_xml[] =
92     "  </interface>\n"
93     "</node>\n";
94 
95 static 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 
243 static int
append_config(DBusMessageIter * iter,const char * prefix,char ** env,ssize_t elen)244 append_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 
277 static DBusHandlerResult
get_dbus_error(DBusConnection * con,DBusMessage * msg,const char * name,const char * fmt,...)278 get_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 
294 static dbus_bool_t
dbus_send_message(const struct interface * ifp,const char * reason,const char * prefix,struct dhcp_message * message)295 dbus_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
370 static dbus_bool_t
dbus_send_dhcpv6_message(const struct interface * ifp,const char * reason,const char * prefix,struct dhcp6_message * message,size_t length)371 dbus_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 
446 static DBusHandlerResult
introspect(DBusConnection * con,DBusMessage * msg)447 introspect(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 
474 static DBusHandlerResult
version(DBusConnection * con,DBusMessage * msg,const char * ver)475 version(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 
488 static DBusHandlerResult
dbus_ack(DBusConnection * con,DBusMessage * msg)489 dbus_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 
499 static DBusHandlerResult
msg_handler(DBusConnection * con,DBusMessage * msg,__unused void * data)500 msg_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 
553 static void
dbus_handle_event(DBusWatch * watch,int flags)554 dbus_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 
567 static void
dbus_read_event(void * watch)568 dbus_read_event(void *watch)
569 {
570 	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_READABLE);
571 }
572 
573 static void
dbus_write_event(void * watch)574 dbus_write_event(void *watch)
575 {
576 	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_WRITABLE);
577 }
578 
579 static dbus_bool_t
add_watch(DBusWatch * watch,__unused void * data)580 add_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 
605 static void
remove_watch(DBusWatch * watch,__unused void * data)606 remove_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 
617 static DBusHandlerResult
dhcpcd_dbus_filter(DBusConnection * conn,DBusMessage * msg,void * user_data)618 dhcpcd_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 
646 int
rpc_init(struct dhcpcd_ctx * ctx)647 rpc_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 
689 void
rpc_close(void)690 rpc_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 
702 void
rpc_signal_status(const char * status)703 rpc_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 
726 int
rpc_update_ipv4(struct interface * ifp)727 rpc_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
741 int
rpc_update_ipv6(struct interface * ifp)742 rpc_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 
757 int
rpc_notify_unicast_arp(struct interface * ifp)758 rpc_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