761 lines
24 KiB
C
761 lines
24 KiB
C
/*
|
|
* dhcpcd - DHCP client daemon
|
|
* Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
|
|
* 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);
|
|
}
|