| /* |
| * dhcpcd - DHCP client daemon |
| * Copyright (c) 2006-2015 Roy Marples <[email protected]> |
| * All rights reserved |
| |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <errno.h> |
| #include <poll.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <syslog.h> |
| #include <unistd.h> |
| |
| #include <dbus/dbus.h> |
| |
| #include "../config.h" |
| #include "../eloop.h" |
| #include "../dhcp.h" |
| #ifdef INET6 |
| #include "../dhcp6.h" |
| #endif |
| #include "../rpc-interface.h" |
| #include "dbus-dict.h" |
| |
| #define SERVICE_NAME "org.chromium.dhcpcd" |
| #define SERVICE_PATH "/org/chromium/dhcpcd" |
| #define S_EINVAL SERVICE_NAME ".InvalidArgument" |
| #define S_ARGS "Not enough arguments" |
| |
| static DBusConnection *connection; |
| static struct dhcpcd_ctx *dhcpcd_ctx; |
| |
| static const char dhcpcd_introspection_xml[] = |
| " <method name=\"GetVersion\">\n" |
| " <arg name=\"version\" direction=\"out\" type=\"s\"/>\n" |
| " </method>\n" |
| " <method name=\"Rebind\">\n" |
| " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" |
| " </method>\n" |
| " <method name=\"Release\">\n" |
| " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" |
| " </method>\n" |
| " <method name=\"Stop\">\n" |
| " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" |
| " </method>\n" |
| " <signal name=\"Event\">\n" |
| " <arg name=\"configuration\" type=\"usa{sv}\"/>\n" |
| " </signal>\n" |
| " <signal name=\"StatusChanged\">\n" |
| " <arg name=\"status\" type=\"us\"/>\n" |
| " </signal>\n"; |
| |
| static const char service_watch_rule[] = "interface=" DBUS_INTERFACE_DBUS |
| ",type=signal,member=NameOwnerChanged"; |
| |
| static const char introspection_header_xml[] = |
| "<!DOCTYPE node PUBLIC \"-//freedesktop//" |
| "DTD D-BUS Object Introspection 1.0//EN\"\n" |
| "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" |
| "<node name=\"" SERVICE_PATH "\">\n" |
| " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" |
| " <method name=\"Introspect\">\n" |
| " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n" |
| " </method>\n" |
| " </interface>\n" |
| " <interface name=\"" SERVICE_NAME "\">\n"; |
| |
| static const char introspection_footer_xml[] = |
| " </interface>\n" |
| "</node>\n"; |
| |
| static const struct o_dbus dhos[] = { |
| { "ip_address=", DBUS_TYPE_UINT32, 0, "IPAddress" }, |
| { "server_name=", DBUS_TYPE_STRING, 0, "ServerName"}, |
| { "subnet_mask=", DBUS_TYPE_UINT32, 0, "SubnetMask" }, |
| { "subnet_cidr=", DBUS_TYPE_BYTE, 0, "SubnetCIDR" }, |
| { "network_number=", DBUS_TYPE_UINT32, 0, "NetworkNumber" }, |
| { "classless_static_routes=", DBUS_TYPE_STRING, 0, |
| "ClasslessStaticRoutes" }, |
| { "ms_classless_static_routes=", DBUS_TYPE_STRING, 0, |
| "MSClasslessStaticRoutes" }, |
| { "static_routes=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "StaticRoutes"} , |
| { "routers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "Routers" }, |
| { "time_offset=", DBUS_TYPE_UINT32, 0, "TimeOffset" }, |
| { "time_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "TimeServers" }, |
| { "ien116_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "IEN116NameServers" }, |
| { "domain_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "DomainNameServers" }, |
| { "log_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LogServers" }, |
| { "cookie_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "CookieServers" }, |
| { "lpr_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LPRServers" }, |
| { "impress_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "ImpressServers" }, |
| { "resource_location_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "ResourceLocationServers" }, |
| { "host_name=", DBUS_TYPE_STRING, 0, "Hostname" }, |
| { "boot_size=", DBUS_TYPE_UINT16, 0, "BootSize" }, |
| { "merit_dump=", DBUS_TYPE_STRING, 0, "MeritDump" }, |
| { "domain_name=", DBUS_TYPE_STRING, 0, "DomainName" }, |
| { "swap_server=", DBUS_TYPE_UINT32, 0, "SwapServer" }, |
| { "root_path=", DBUS_TYPE_STRING, 0, "RootPath" }, |
| { "extensions_path=", DBUS_TYPE_STRING, 0, "ExtensionsPath" }, |
| { "ip_forwarding=", DBUS_TYPE_BOOLEAN, 0, "IPForwarding" }, |
| { "non_local_source_routing=", DBUS_TYPE_BOOLEAN, 0, |
| "NonLocalSourceRouting" }, |
| { "policy_filter=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "PolicyFilter" }, |
| { "max_dgram_reassembly=", DBUS_TYPE_INT16, 0, |
| "MaxDatagramReassembly" }, |
| { "default_ip_ttl=", DBUS_TYPE_UINT16, 0, "DefaultIPTTL" }, |
| { "path_mtu_aging_timeout=", DBUS_TYPE_UINT32, 0, |
| "PathMTUAgingTimeout" }, |
| { "path_mtu_plateau_table=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT16, |
| "PolicyFilter"} , |
| { "interface_mtu=", DBUS_TYPE_UINT16, 0, "InterfaceMTU" }, |
| { "all_subnets_local=", DBUS_TYPE_BOOLEAN, 0, "AllSubnetsLocal" }, |
| { "broadcast_address=", DBUS_TYPE_UINT32, 0, "BroadcastAddress" }, |
| { "perform_mask_discovery=", DBUS_TYPE_BOOLEAN, 0, |
| "PerformMaskDiscovery" }, |
| { "mask_supplier=", DBUS_TYPE_BOOLEAN, 0, "MaskSupplier" }, |
| { "router_discovery=", DBUS_TYPE_BOOLEAN, 0, "RouterDiscovery" }, |
| { "router_solicitiation_address=", DBUS_TYPE_UINT32, 0, |
| "RouterSolicationAddress" }, |
| { "trailer_encapsulation=", DBUS_TYPE_BOOLEAN, 0, |
| "TrailerEncapsulation" }, |
| { "arp_cache_timeout=", DBUS_TYPE_UINT32, 0, "ARPCacheTimeout" }, |
| { "ieee802_3_encapsulation=", DBUS_TYPE_UINT16, 0, |
| "IEEE8023Encapsulation" }, |
| { "default_tcp_ttl=", DBUS_TYPE_BYTE, 0, "DefaultTCPTTL" }, |
| { "tcp_keepalive_interval=", DBUS_TYPE_UINT32, 0, |
| "TCPKeepAliveInterval" }, |
| { "tcp_keepalive_garbage=", DBUS_TYPE_BOOLEAN, 0, |
| "TCPKeepAliveGarbage" }, |
| { "nis_domain=", DBUS_TYPE_STRING, 0, "NISDomain" }, |
| { "nis_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NISServers" }, |
| { "ntp_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NTPServers" }, |
| { "vendor_encapsulated_options=", DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, |
| "VendorEncapsulatedOptions" }, |
| { "netbios_name_servers=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "NetBIOSNameServers" }, |
| { "netbios_dd_server=", DBUS_TYPE_UINT32, 0, "NetBIOSDDServer" }, |
| { "netbios_node_type=", DBUS_TYPE_BYTE, 0, "NetBIOSNodeType" }, |
| { "netbios_scope=", DBUS_TYPE_STRING, 0, "NetBIOSScope" }, |
| { "font_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "FontServers" }, |
| { "x_display_manager=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "XDisplayManager" }, |
| { "dhcp_requested_address=", DBUS_TYPE_UINT32, 0, |
| "DHCPRequestedAddress" }, |
| { "dhcp_lease_time=", DBUS_TYPE_UINT32, 0, "DHCPLeaseTime" }, |
| { "dhcp_option_overload=", DBUS_TYPE_BOOLEAN, 0, |
| "DHCPOptionOverload" }, |
| { "dhcp_message_type=", DBUS_TYPE_BYTE, 0, "DHCPMessageType" }, |
| { "dhcp_server_identifier=", DBUS_TYPE_UINT32, 0, |
| "DHCPServerIdentifier" }, |
| { "dhcp_message=", DBUS_TYPE_STRING, 0, "DHCPMessage" }, |
| { "dhcp_max_message_size=", DBUS_TYPE_UINT16, 0, |
| "DHCPMaxMessageSize" }, |
| { "dhcp_renewal_time=", DBUS_TYPE_UINT32, 0, "DHCPRenewalTime" }, |
| { "dhcp_rebinding_time=", DBUS_TYPE_UINT32, 0, "DHCPRebindingTime" }, |
| { "nisplus_domain=", DBUS_TYPE_STRING, 0, "NISPlusDomain" }, |
| { "nisplus_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "NISPlusServers" }, |
| { "tftp_server_name=", DBUS_TYPE_STRING, 0, "TFTPServerName" }, |
| { "bootfile_name=", DBUS_TYPE_STRING, 0, "BootFileName" }, |
| { "mobile_ip_home_agent=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "MobileIPHomeAgent" }, |
| { "smtp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "SMTPServer" }, |
| { "pop_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "POPServer" }, |
| { "nntp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NNTPServer" }, |
| { "www_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "WWWServer" }, |
| { "finger_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "FingerServer" }, |
| { "irc_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "IRCServer" }, |
| { "streettalk_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "StreetTalkServer" }, |
| { "streettalk_directory_assistance_server=", DBUS_TYPE_ARRAY, |
| DBUS_TYPE_UINT32, "StreetTalkDirectoryAssistanceServer" }, |
| { "user_class=", DBUS_TYPE_STRING, 0, "UserClass" }, |
| { "new_fqdn_name=", DBUS_TYPE_STRING, 0, "FQDNName" }, |
| { "nds_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NDSServers" }, |
| { "nds_tree_name=", DBUS_TYPE_STRING, 0, "NDSTreeName" }, |
| { "nds_context=", DBUS_TYPE_STRING, 0, "NDSContext" }, |
| { "bcms_controller_names=", DBUS_TYPE_STRING, 0, |
| "BCMSControllerNames" }, |
| { "client_last_transaction_time=", DBUS_TYPE_UINT32, 0, |
| "ClientLastTransactionTime" }, |
| { "associated_ip=", DBUS_TYPE_UINT32, 0, "AssociatedIP" }, |
| { "uap_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "UAPServers" }, |
| { "netinfo_server_address=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| "NetinfoServerAddress" }, |
| { "netinfo_server_tag=", DBUS_TYPE_STRING, 0, "NetinfoServerTag" }, |
| { "default_url=", DBUS_TYPE_STRING, 0, "DefaultURL" }, |
| { "subnet_selection=", DBUS_TYPE_UINT32, 0, "SubnetSelection" }, |
| { "domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| "DomainSearch" }, |
| { "wpad_url=", DBUS_TYPE_STRING, 0, "WebProxyAutoDiscoveryUrl" }, |
| #ifdef INET6 |
| { "dhcp6_server_id=", DBUS_TYPE_STRING, 0, |
| "DHCPv6ServerIdentifier" }, |
| { "dhcp6_ia_na1_ia_addr1=", DBUS_TYPE_STRING, 0, "DHCPv6Address" }, |
| { "dhcp6_ia_na1_ia_addr1_vltime=", DBUS_TYPE_UINT32, 0, |
| "DHCPv6AddressLeaseTime" }, |
| { "dhcp6_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| "DHCPv6NameServers" }, |
| { "dhcp6_domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| "DHCPv6DomainSearch" }, |
| { "dhcp6_ia_pd1_prefix1=", DBUS_TYPE_STRING, 0, |
| "DHCPv6DelegatedPrefix" }, |
| { "dhcp6_ia_pd1_prefix1_length=", DBUS_TYPE_UINT32, 0, |
| "DHCPv6DelegatedPrefixLength" }, |
| { "dhcp6_ia_pd1_prefix1_vltime=", DBUS_TYPE_UINT32, 0, |
| "DHCPv6DelegatedPrefixLeaseTime" }, |
| #endif |
| { NULL, 0, 0, NULL } |
| }; |
| |
| static int |
| append_config(DBusMessageIter *iter, |
| const char *prefix, char **env, ssize_t elen) |
| { |
| char **eenv, *p; |
| const struct o_dbus *dhop; |
| size_t l, lp; |
| int retval; |
| |
| retval = 0; |
| lp = strlen(prefix); |
| for (eenv = env + elen; env < eenv; env++) { |
| p = env[0]; |
| for (dhop = dhos; dhop->var; dhop++) { |
| l = strlen(dhop->var); |
| if (strncmp(p, dhop->var, l) == 0) { |
| retval = dict_append_config_item(iter, |
| dhop, p + l); |
| break; |
| } |
| if (strncmp(p, prefix, lp) == 0 && |
| strncmp(p + lp, dhop->var, l) == 0) |
| { |
| retval = dict_append_config_item(iter, |
| dhop, p + l + lp); |
| break; |
| } |
| } |
| if (retval == -1) |
| break; |
| } |
| return retval; |
| } |
| |
| static DBusHandlerResult |
| get_dbus_error(DBusConnection *con, DBusMessage *msg, |
| const char *name, const char *fmt, ...) |
| { |
| char buffer[1024]; |
| DBusMessage *reply; |
| va_list args; |
| |
| va_start(args, fmt); |
| vsnprintf(buffer, sizeof(buffer), fmt, args); |
| va_end(args); |
| reply = dbus_message_new_error(msg, name, buffer); |
| dbus_connection_send(con, reply, NULL); |
| dbus_message_unref(reply); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static dbus_bool_t |
| dbus_send_message(const struct interface *ifp, const char *reason, |
| const char *prefix, struct dhcp_message *message) |
| { |
| const struct if_options *ifo = ifp->options; |
| DBusMessage* msg; |
| DBusMessageIter args, dict; |
| int pid = getpid(); |
| char **env = NULL; |
| ssize_t e, elen; |
| int retval; |
| int success = FALSE; |
| |
| syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name); |
| |
| msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event"); |
| if (msg == NULL) { |
| syslog(LOG_ERR, "failed to make a configure message"); |
| return FALSE; |
| } |
| dbus_message_iter_init_append(msg, &args); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason); |
| dbus_message_iter_open_container(&args, 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); |
| if (prefix == NULL || message == NULL) |
| retval = 0; |
| else { |
| e = dhcp_env(NULL, NULL, message, ifp); |
| if (e > 0) { |
| char *config_prefix = strdup(prefix); |
| if (config_prefix == NULL) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Memory exhausted (strdup)"); |
| eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| } |
| char *p = config_prefix + strlen(config_prefix) - 1; |
| if (p >= config_prefix && *p == '_') |
| *p = '\0'; |
| env = calloc(e + 1, sizeof(char *)); |
| if (env == NULL) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Memory exhausted (calloc)"); |
| eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| } |
| elen = dhcp_env(env, config_prefix, message, ifp); |
| free(config_prefix); |
| } |
| retval = append_config(&dict, prefix, env, elen); |
| } |
| |
| /* Release memory allocated for env. */ |
| if (env) { |
| char **current = env; |
| while (*current) |
| free(*current++); |
| free(env); |
| } |
| |
| dbus_message_iter_close_container(&args, &dict); |
| if (retval == 0) { |
| success = dbus_connection_send(connection, msg, NULL); |
| if (!success) |
| syslog(LOG_ERR, "failed to send dhcp to dbus"); |
| } else |
| syslog(LOG_ERR, "failed to construct dbus message"); |
| dbus_message_unref(msg); |
| |
| return success; |
| } |
| |
| #ifdef INET6 |
| 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) |
| { |
| const struct if_options *ifo = ifp->options; |
| DBusMessage* msg; |
| DBusMessageIter args, dict; |
| int pid = getpid(); |
| char **env = NULL; |
| ssize_t e, elen; |
| int retval; |
| int success = FALSE; |
| |
| syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name); |
| |
| msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event"); |
| if (msg == NULL) { |
| syslog(LOG_ERR, "failed to make a configure message"); |
| return FALSE; |
| } |
| dbus_message_iter_init_append(msg, &args); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason); |
| dbus_message_iter_open_container(&args, 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); |
| if (prefix == NULL || message == NULL) |
| retval = 0; |
| else { |
| e = dhcp6_env(NULL, NULL, ifp, message, length); |
| if (e > 0) { |
| char *config_prefix = strdup(prefix); |
| if (config_prefix == NULL) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Memory exhausted (strdup)"); |
| eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| } |
| char *p = config_prefix + strlen(config_prefix) - 1; |
| if (p >= config_prefix && *p == '_') |
| *p = '\0'; |
| env = calloc(e + 1, sizeof(char *)); |
| if (env == NULL) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Memory exhausted (calloc)"); |
| eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| } |
| elen = dhcp6_env(env, "new", ifp, message, length); |
| free(config_prefix); |
| } |
| retval = append_config(&dict, prefix, env, elen); |
| } |
| |
| /* Release memory allocated for env. */ |
| if (env) { |
| char **current = env; |
| while (*current) |
| free(*current++); |
| free(env); |
| } |
| |
| dbus_message_iter_close_container(&args, &dict); |
| if (retval == 0) { |
| success = dbus_connection_send(connection, msg, NULL); |
| if (!success) |
| syslog(LOG_ERR, "failed to send dhcpv6 to dbus"); |
| } else |
| syslog(LOG_ERR, "failed to construct dbus message"); |
| dbus_message_unref(msg); |
| |
| return success; |
| } |
| #endif |
| |
| static DBusHandlerResult |
| introspect(DBusConnection *con, DBusMessage *msg) |
| { |
| DBusMessage *reply; |
| char *xml; |
| size_t len; |
| |
| len = sizeof(introspection_header_xml) - 1 |
| + sizeof(dhcpcd_introspection_xml) - 1 |
| + sizeof(introspection_footer_xml) - 1 |
| + 1; /* terminal \0 */ |
| xml = malloc(len); |
| if (xml == NULL) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| snprintf(xml, len, "%s%s%s", |
| introspection_header_xml, |
| dhcpcd_introspection_xml, |
| introspection_footer_xml); |
| reply = dbus_message_new_method_return(msg); |
| dbus_message_append_args(reply, |
| DBUS_TYPE_STRING, &xml, |
| DBUS_TYPE_INVALID); |
| dbus_connection_send(con, reply, NULL); |
| dbus_message_unref(reply); |
| free(xml); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static DBusHandlerResult |
| version(DBusConnection *con, DBusMessage *msg, const char *ver) |
| { |
| DBusMessage *reply; |
| |
| reply = dbus_message_new_method_return(msg); |
| dbus_message_append_args(reply, |
| DBUS_TYPE_STRING, &ver, |
| DBUS_TYPE_INVALID); |
| dbus_connection_send(con, reply, NULL); |
| dbus_message_unref(reply); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static DBusHandlerResult |
| dbus_ack(DBusConnection *con, DBusMessage *msg) |
| { |
| DBusMessage *reply; |
| |
| reply = dbus_message_new_method_return(msg); |
| dbus_connection_send(con, reply, NULL); |
| dbus_message_unref(reply); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static DBusHandlerResult |
| msg_handler(DBusConnection *con, DBusMessage *msg, __unused void *data) |
| { |
| #define IsMethod(msg, method) \ |
| dbus_message_is_method_call(msg, SERVICE_NAME, method) |
| |
| if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, |
| "Introspect")) { |
| return introspect(con, msg); |
| } else if (IsMethod(msg, "GetVersion")) { |
| return version(con, msg, VERSION); |
| } else if (IsMethod(msg, "Rebind")) { |
| const char *iface_name; |
| if (!dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &iface_name, |
| DBUS_TYPE_INVALID)) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Invalid arguments for Rebind"); |
| return get_dbus_error(con, msg, S_EINVAL, S_ARGS); |
| } |
| dhcpcd_start_interface(dhcpcd_ctx, iface_name); |
| return dbus_ack(con, msg); |
| } else if (IsMethod(msg, "Release")) { |
| const char *iface_name; |
| if (!dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &iface_name, |
| DBUS_TYPE_INVALID)) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Invalid arguments for Release"); |
| return get_dbus_error(con, msg, S_EINVAL, S_ARGS); |
| } |
| dhcpcd_release_ipv4(dhcpcd_ctx, iface_name); |
| return dbus_ack(con, msg); |
| } else if (IsMethod(msg, "Stop")) { |
| const char *iface_name; |
| if (!dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &iface_name, |
| DBUS_TYPE_INVALID)) { |
| logger(dhcpcd_ctx, LOG_ERR, |
| "Invalid arguments for Stop"); |
| return get_dbus_error(con, msg, S_EINVAL, S_ARGS); |
| } |
| dhcpcd_stop_interface(dhcpcd_ctx, iface_name); |
| (void) dbus_ack(con, msg); |
| exit(EXIT_FAILURE); |
| } else if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL, |
| "Disconnected")) { |
| dhcpcd_stop_interfaces(dhcpcd_ctx); |
| exit(EXIT_FAILURE); |
| } |
| return get_dbus_error(con, msg, S_EINVAL, S_ARGS); |
| #undef IsMethod |
| } |
| |
| static void |
| dbus_handle_event(DBusWatch *watch, int flags) |
| { |
| dbus_watch_handle((DBusWatch *)watch, flags); |
| |
| if (connection != NULL) { |
| dbus_connection_ref(connection); |
| while (dbus_connection_dispatch(connection) == |
| DBUS_DISPATCH_DATA_REMAINS) |
| ; |
| dbus_connection_unref(connection); |
| } |
| } |
| |
| static void |
| dbus_read_event(void *watch) |
| { |
| dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_READABLE); |
| } |
| |
| static void |
| dbus_write_event(void *watch) |
| { |
| dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_WRITABLE); |
| } |
| |
| static dbus_bool_t |
| add_watch(DBusWatch *watch, __unused void *data) |
| { |
| int fd, flags; |
| void (*read_event)(void *) = NULL; |
| void *read_arg = NULL; |
| void (*write_event)(void *) = NULL; |
| void *write_arg = NULL; |
| |
| fd = dbus_watch_get_unix_fd(watch); |
| flags = dbus_watch_get_flags(watch); |
| if (flags & DBUS_WATCH_READABLE) { |
| read_event = dbus_read_event; |
| read_arg = watch; |
| } |
| if (flags & DBUS_WATCH_WRITABLE) { |
| write_event = dbus_write_event; |
| write_arg = watch; |
| } |
| |
| if (eloop_event_add(dhcpcd_ctx->eloop, fd, read_event, read_arg, |
| write_event, write_arg) == 0) |
| return TRUE; |
| return FALSE; |
| } |
| |
| static void |
| remove_watch(DBusWatch *watch, __unused void *data) |
| { |
| int fd, flags; |
| int write_only = 0; |
| fd = dbus_watch_get_unix_fd(watch); |
| flags = dbus_watch_get_flags(watch); |
| if (!(flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) |
| write_only = 1; |
| eloop_event_delete(dhcpcd_ctx->eloop, fd, write_only); |
| } |
| |
| static DBusHandlerResult |
| dhcpcd_dbus_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) |
| { |
| const char *service = NULL; |
| const char *old_owner = NULL; |
| const char *new_owner = NULL; |
| |
| if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, |
| "NameOwnerChanged")) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| if (!dbus_message_get_args(msg, NULL, |
| DBUS_TYPE_STRING, &service, |
| DBUS_TYPE_STRING, &old_owner, |
| DBUS_TYPE_STRING, &new_owner, |
| DBUS_TYPE_INVALID)) { |
| syslog(LOG_ERR, |
| "Invalid arguments for NameOwnerChanged signal"); |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| if (strcmp(service, "org.chromium.flimflam") == 0 && |
| strlen(new_owner) == 0) { |
| syslog(LOG_INFO, "exiting because flimflamd has died"); |
| dhcpcd_stop_interfaces(dhcpcd_ctx); |
| exit(EXIT_FAILURE); |
| } |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| |
| int |
| rpc_init(struct dhcpcd_ctx *ctx) |
| { |
| DBusObjectPathVTable vt = { |
| NULL, &msg_handler, NULL, NULL, NULL, NULL |
| }; |
| DBusError err; |
| int ret; |
| |
| dhcpcd_ctx = ctx; |
| |
| dbus_error_init(&err); |
| connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err); |
| if (connection == NULL) { |
| if (dbus_error_is_set(&err)) |
| syslog(LOG_ERR, "%s", err.message); |
| else |
| syslog(LOG_ERR, "failed to get a dbus connection"); |
| return -1; |
| } |
| atexit(rpc_close); |
| |
| if (!dbus_connection_set_watch_functions(connection, |
| add_watch, remove_watch, NULL, NULL, NULL)) |
| { |
| syslog(LOG_ERR, "dbus: failed to set watch functions"); |
| return -1; |
| } |
| if (!dbus_connection_register_object_path(connection, |
| SERVICE_PATH, &vt, NULL)) |
| { |
| syslog(LOG_ERR, "dbus: failed to register object path"); |
| return -1; |
| } |
| dbus_connection_add_filter(connection, dhcpcd_dbus_filter, NULL, NULL); |
| dbus_bus_add_match(connection, service_watch_rule, &err); |
| if (dbus_error_is_set(&err)) { |
| syslog(LOG_ERR, "Cannot add rule: %s", err.message); |
| return -1; |
| } |
| return 0; |
| } |
| |
| void |
| rpc_close(void) |
| { |
| if (connection) { |
| dbus_bus_remove_match(connection, service_watch_rule, NULL); |
| dbus_connection_remove_filter(connection, |
| dhcpcd_dbus_filter, |
| NULL); |
| dbus_connection_unref(connection); |
| connection = NULL; |
| } |
| } |
| |
| void |
| rpc_signal_status(const char *status) |
| { |
| DBusMessage *msg; |
| DBusMessageIter args; |
| int pid = getpid(); |
| |
| syslog(LOG_INFO, "status changed to %s", status); |
| |
| msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, |
| "StatusChanged"); |
| if (msg == NULL) { |
| syslog(LOG_ERR, "failed to make a status changed message"); |
| return; |
| } |
| dbus_message_iter_init_append(msg, &args); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); |
| dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &status); |
| if (!dbus_connection_send(connection, msg, NULL)) |
| syslog(LOG_ERR, "failed to send status to dbus"); |
| dbus_message_unref(msg); |
| } |
| |
| |
| int |
| rpc_update_ipv4(struct interface *ifp) |
| { |
| struct dhcp_state *state = D_STATE(ifp); |
| if (state->new != NULL) { |
| /* push state over d-bus */ |
| dbus_send_message(ifp, state->reason, "new_", state->new); |
| rpc_signal_status("Bound"); |
| } else { |
| rpc_signal_status("Release"); |
| } |
| return 0; |
| } |
| |
| #ifdef INET6 |
| int |
| rpc_update_ipv6(struct interface *ifp) |
| { |
| struct dhcp6_state *state = D6_STATE(ifp); |
| if (state->new != NULL) { |
| /* push state over d-bus */ |
| dbus_send_dhcpv6_message(ifp, state->reason, "new_", |
| state->new, state->new_len); |
| rpc_signal_status("Bound6"); |
| } else { |
| rpc_signal_status("Release6"); |
| } |
| return 0; |
| } |
| #endif |
| |
| int |
| rpc_notify_unicast_arp(struct interface *ifp) { |
| struct dhcp_state *state = D_STATE(ifp); |
| return dbus_send_message(ifp, "GATEWAY-ARP", "saved_", state->offer); |
| } |