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