summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/agent-connman.c14
-rw-r--r--src/agent.c24
-rw-r--r--src/bridge.c3
-rw-r--r--src/clock.c12
-rw-r--r--src/config.c6
-rw-r--r--src/connection.c23
-rw-r--r--src/connman.h32
-rw-r--r--src/dbus.c26
-rw-r--r--src/dhcp.c5
-rw-r--r--src/dhcpv6.c2
-rw-r--r--src/dns-systemd-resolved.c4
-rw-r--r--src/dnsproxy.c1994
-rw-r--r--src/inet.c669
-rw-r--r--src/ipaddress.c12
-rw-r--r--src/ipconfig.c243
-rw-r--r--src/iptables.c6
-rw-r--r--src/main.c191
-rw-r--r--src/main.conf25
-rw-r--r--src/manager.c3
-rw-r--r--src/network.c52
-rw-r--r--src/ntp.c2
-rw-r--r--src/peer.c7
-rw-r--r--src/provider.c110
-rw-r--r--src/resolver.c33
-rw-r--r--src/rtnl.c145
-rw-r--r--src/service.c637
-rw-r--r--src/session.c2
-rw-r--r--src/shared/util.c1
-rw-r--r--src/technology.c70
-rw-r--r--src/tethering.c3
-rw-r--r--src/timeserver.c194
-rw-r--r--src/timezone.c105
-rw-r--r--src/wispr.c246
33 files changed, 3088 insertions, 1813 deletions
diff --git a/src/agent-connman.c b/src/agent-connman.c
index fca7cc1f..2bd33e04 100644
--- a/src/agent-connman.c
+++ b/src/agent-connman.c
@@ -865,3 +865,17 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer,
return -EINPROGRESS;
}
+
+bool __connman_agent_is_request_pending(struct connman_service *service,
+ const char *dbus_sender)
+{
+ void *agent;
+
+ /* Default agent will be returned if no dbus_sender */
+ agent = connman_agent_get_info(dbus_sender, NULL, NULL);
+
+ if (!service || !agent)
+ return false;
+
+ return connman_agent_queue_search(service, agent);
+}
diff --git a/src/agent.c b/src/agent.c
index d6113af7..23517d9b 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -257,6 +257,28 @@ int connman_agent_queue_message(void *user_context,
return err;
}
+bool connman_agent_queue_search(void *user_context, void *agent_data)
+{
+ struct connman_agent *agent = agent_data;
+ struct connman_agent_request *queue_data;
+ GList *iter;
+
+ if (!agent || !user_context)
+ return false;
+
+ if (agent->pending && agent->pending->user_context == user_context)
+ return true;
+
+ for (iter = agent->queue; iter; iter = iter->next) {
+ queue_data = iter->data;
+
+ if (queue_data && queue_data->user_context == user_context)
+ return true;
+ }
+
+ return false;
+}
+
static void set_default_agent(void)
{
struct connman_agent *agent = NULL;
@@ -366,9 +388,9 @@ static void report_error_reply(DBusMessage *reply, void *user_data)
retry = true;
}
+out:
report_error->callback(report_error->user_context, retry,
report_error->user_data);
-out:
g_free(report_error);
}
diff --git a/src/bridge.c b/src/bridge.c
index cd2d9cee..df19a6af 100644
--- a/src/bridge.c
+++ b/src/bridge.c
@@ -122,7 +122,8 @@ int __connman_bridge_enable(const char *name, const char *ip_address,
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- ip_address, NULL, prefix_len, broadcast);
+ ip_address, NULL, prefix_len, broadcast,
+ false);
if (err < 0)
return err;
diff --git a/src/clock.c b/src/clock.c
index 0fde2c34..54ac274a 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -173,6 +173,7 @@ static DBusMessage *get_properties(DBusConnection *conn,
{
DBusMessage *reply;
DBusMessageIter array, dict;
+ dbus_bool_t is_synced;
struct timeval tv;
const char *str;
@@ -210,6 +211,10 @@ static DBusMessage *get_properties(DBusConnection *conn,
connman_dbus_dict_append_array(&dict, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ is_synced = __connman_timeserver_is_synced();
+ connman_dbus_dict_append_basic(&dict, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
+
connman_dbus_dict_close(&array, &dict);
return reply;
@@ -258,12 +263,14 @@ static DBusMessage *set_property(DBusConnection *conn,
if (settimeofday(&tv, NULL) < 0)
return __connman_error_invalid_arguments(msg);
+ __connman_timeserver_set_synced(false);
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Time",
DBUS_TYPE_UINT64, &newval);
} else if (g_str_equal(name, "TimeUpdates")) {
const char *strval;
enum time_updates newval;
+ struct connman_service *service;
if (type != DBUS_TYPE_STRING)
return __connman_error_invalid_arguments(msg);
@@ -283,6 +290,9 @@ static DBusMessage *set_property(DBusConnection *conn,
connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "TimeUpdates",
DBUS_TYPE_STRING, &strval);
+
+ service = connman_service_get_default();
+ __connman_timeserver_conf_update(service);
} else if (g_str_equal(name, "Timezone")) {
const char *strval;
@@ -362,6 +372,8 @@ static DBusMessage *set_property(DBusConnection *conn,
connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
CONNMAN_CLOCK_INTERFACE, "Timeservers",
DBUS_TYPE_STRING, append_timeservers, NULL);
+ } else if (g_str_equal(name, "TimeserverSynced")) {
+ return __connman_error_permission_denied(msg);
} else
return __connman_error_invalid_property(msg);
diff --git a/src/config.c b/src/config.c
index 62023b10..33fdc737 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1106,8 +1106,7 @@ static char *config_pem_fsid(const char *pem_file)
static void provision_service_wifi(struct connman_config_service *config,
struct connman_service *service,
- struct connman_network *network,
- const void *ssid, unsigned int ssid_len)
+ struct connman_network *network)
{
if (config->eap)
__connman_service_set_string(service, "EAP", config->eap);
@@ -1418,8 +1417,7 @@ static int try_provision_service(struct connman_config_service *config,
config->timeservers);
if (type == CONNMAN_SERVICE_TYPE_WIFI) {
- provision_service_wifi(config, service, network,
- ssid, ssid_len);
+ provision_service_wifi(config, service, network);
}
__connman_service_mark_dirty();
diff --git a/src/connection.c b/src/connection.c
index 303e9922..9d2c6961 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1066,6 +1066,29 @@ int __connman_connection_get_vpn_index(int phy_index)
return -1;
}
+int __connman_connection_get_vpn_phy_index(int vpn_index)
+{
+ GHashTableIter iter;
+ gpointer value, key;
+
+ g_hash_table_iter_init(&iter, gateway_hash);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct gateway_data *data = value;
+
+ if (data->index != vpn_index)
+ continue;
+
+ if (data->ipv4_gateway)
+ return data->ipv4_gateway->vpn_phy_index;
+
+ if (data->ipv6_gateway)
+ return data->ipv6_gateway->vpn_phy_index;
+ }
+
+ return -1;
+}
+
int __connman_connection_init(void)
{
int err;
diff --git a/src/connman.h b/src/connman.h
index 3bdc0dc7..b955d98b 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -126,6 +126,8 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer,
bool wps_requested,
const char *dbus_sender,
void *user_data);
+bool __connman_agent_is_request_pending(struct connman_service *service,
+ const char *dbus_sender);
#include <connman/log.h>
@@ -138,8 +140,6 @@ void __connman_log_enable(struct connman_debug_desc *start,
#include <connman/backtrace.h>
-#include <connman/option.h>
-
#include <connman/setting.h>
#include <connman/plugin.h>
@@ -159,7 +159,8 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family,
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast);
+ const char *broadcast,
+ bool is_p2p);
int __connman_inet_get_interface_address(int index, int family, void *address);
int __connman_inet_get_interface_ll_address(int index, int family, void *address);
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address);
@@ -302,6 +303,7 @@ struct connman_ipaddress {
char *peer;
char *broadcast;
char *gateway;
+ bool is_p2p; /* P2P connection or VPN, broadcast is excluded. */
};
struct connman_ipconfig_ops {
@@ -449,7 +451,12 @@ char **__connman_timeserver_system_get();
GSList *__connman_timeserver_add_list(GSList *server_list,
const char *timeserver);
GSList *__connman_timeserver_get_all(struct connman_service *service);
-int __connman_timeserver_sync(struct connman_service *service);
+void __connman_timeserver_sync(struct connman_service *service,
+ enum connman_timeserver_sync_reason reason);
+void __connman_timeserver_conf_update(struct connman_service *service);
+
+bool __connman_timeserver_is_synced(void);
+void __connman_timeserver_set_synced(bool status);
enum __connman_dhcpv6_status {
CONNMAN_DHCPV6_STATUS_FAIL = 0,
@@ -502,6 +509,7 @@ int __connman_connection_gateway_add(struct connman_service *service,
void __connman_connection_gateway_remove(struct connman_service *service,
enum connman_ipconfig_type type);
int __connman_connection_get_vpn_index(int phy_index);
+int __connman_connection_get_vpn_phy_index(int vpn_index);
bool __connman_connection_update_gateway(void);
@@ -600,6 +608,7 @@ void __connman_network_set_device(struct connman_network *network,
int __connman_network_connect(struct connman_network *network);
int __connman_network_disconnect(struct connman_network *network);
+int __connman_network_forget(struct connman_network *network);
int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig);
int __connman_network_enable_ipconfig(struct connman_network *network,
@@ -609,6 +618,7 @@ const char *__connman_network_get_type(struct connman_network *network);
const char *__connman_network_get_group(struct connman_network *network);
const char *__connman_network_get_ident(struct connman_network *network);
bool __connman_network_get_weakness(struct connman_network *network);
+bool __connman_network_native_autoconnect(struct connman_network *network);
int __connman_config_init();
void __connman_config_cleanup(void);
@@ -656,6 +666,8 @@ void __connman_provider_list(DBusMessageIter *iter, void *user_data);
bool __connman_provider_is_immutable(struct connman_provider *provider);
int __connman_provider_create_and_connect(DBusMessage *msg);
const char * __connman_provider_get_ident(struct connman_provider *provider);
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider);
int __connman_provider_indicate_state(struct connman_provider *provider,
enum connman_provider_state state);
int __connman_provider_indicate_error(struct connman_provider *provider,
@@ -670,6 +682,8 @@ int __connman_provider_init(void);
int __connman_service_init(void);
void __connman_service_cleanup(void);
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before);
int __connman_service_load_modifiable(struct connman_service *service);
void __connman_service_list_struct(DBusMessageIter *iter);
@@ -720,8 +734,9 @@ int __connman_service_set_mdns(struct connman_service *service,
void __connman_service_set_string(struct connman_service *service,
const char *key, const char *value);
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type);
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success);
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
enum connman_service_state new_state,
enum connman_ipconfig_type type);
@@ -737,7 +752,6 @@ int __connman_service_indicate_default(struct connman_service *service);
int __connman_service_connect(struct connman_service *service,
enum connman_service_connect_reason reason);
int __connman_service_disconnect(struct connman_service *service);
-int __connman_service_disconnect_all(void);
void __connman_service_set_active_session(bool enable, GSList *list);
void __connman_service_auto_connect(enum connman_service_connect_reason reason);
bool __connman_service_remove(struct connman_service *service);
@@ -779,6 +793,9 @@ void __connman_service_set_pac(struct connman_service *service,
bool __connman_service_is_hidden(struct connman_service *service);
bool __connman_service_is_split_routing(struct connman_service *service);
bool __connman_service_index_is_split_routing(int index);
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool split_routing);
+void __connman_service_split_routing_changed(struct connman_service *service);
int __connman_service_get_index(struct connman_service *service);
void __connman_service_set_hidden(struct connman_service *service);
void __connman_service_set_hostname(struct connman_service *service,
@@ -970,6 +987,7 @@ void __connman_dnsproxy_remove_listener(int index);
int __connman_dnsproxy_append(int index, const char *domain, const char *server);
int __connman_dnsproxy_remove(int index, const char *domain, const char *server);
int __connman_dnsproxy_set_mdns(int index, bool enabled);
+void __connman_dnsproxy_set_listen_port(unsigned int port);
int __connman_6to4_probe(struct connman_service *service);
void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
diff --git a/src/dbus.c b/src/dbus.c
index d80a46ce..74b3157b 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -246,9 +246,7 @@ dbus_bool_t connman_dbus_property_changed_basic(const char *path,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_basic(&iter, key, type, val);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_dict(const char *path,
@@ -268,9 +266,7 @@ dbus_bool_t connman_dbus_property_changed_dict(const char *path,
dbus_message_iter_init_append(signal, &iter);
connman_dbus_property_append_dict(&iter, key, function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_property_changed_array(const char *path,
@@ -291,9 +287,7 @@ dbus_bool_t connman_dbus_property_changed_array(const char *path,
connman_dbus_property_append_array(&iter, key, type,
function, user_data);
- g_dbus_send_message(connection, signal);
-
- return TRUE;
+ return g_dbus_send_message(connection, signal);
}
dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
@@ -319,9 +313,7 @@ dbus_bool_t connman_dbus_setting_changed_basic(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
@@ -348,9 +340,7 @@ dbus_bool_t connman_dbus_setting_changed_dict(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
@@ -377,9 +367,7 @@ dbus_bool_t connman_dbus_setting_changed_array(const char *owner,
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, msg);
-
- return TRUE;
+ return g_dbus_send_message(connection, msg);
}
dbus_bool_t __connman_dbus_append_objpath_dict_array(DBusMessage *msg,
@@ -699,7 +687,7 @@ int __connman_dbus_init(DBusConnection *conn)
connection = conn;
- return 0;
+ return g_dbus_attach_object_manager(conn);
}
void __connman_dbus_cleanup(void)
diff --git a/src/dhcp.c b/src/dhcp.c
index 42e9f417..18dbab27 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -422,8 +422,7 @@ static bool apply_lease_available_on_network(GDHCPClient *dhcp_client,
g_free(dhcp->pac);
dhcp->pac = pac;
- __connman_ipconfig_set_proxy_autoconfig(dhcp->ipconfig,
- dhcp->pac);
+ __connman_service_set_proxy_autoconfig(service, dhcp->pac);
}
if (connman_setting_get_bool("Enable6to4"))
@@ -616,7 +615,7 @@ static int dhcp_initialize(struct connman_dhcp *dhcp)
g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
- vendor_class_id = connman_option_get_string("VendorClassID");
+ vendor_class_id = connman_setting_get_string("VendorClassID");
if (vendor_class_id)
g_dhcp_client_set_send(dhcp_client, G_DHCP_VENDOR_CLASS_ID,
vendor_class_id);
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 2d5f8f6a..8b683599 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -945,7 +945,7 @@ static void do_dad(GDHCPClient *dhcp_client, struct connman_dhcpv6 *dhcp)
ref_own_address(user_data);
- if (inet_pton(AF_INET6, address, &addr) < 0) {
+ if (inet_pton(AF_INET6, address, &addr) != 1) {
DBG("Invalid IPv6 address %s %d/%s", address,
-errno, strerror(errno));
goto fail;
diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c
index 5fe306c3..912ab3fe 100644
--- a/src/dns-systemd-resolved.c
+++ b/src/dns-systemd-resolved.c
@@ -106,7 +106,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
if (type == AF_INET) {
result = inet_pton(type, server, ipv4_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv4 address: %s",
server);
return;
@@ -128,7 +128,7 @@ static void setlinkdns_append(DBusMessageIter *iter, void *user_data)
&byte_array);
} else if (type == AF_INET6) {
result = inet_pton(type, server, ipv6_bytes);
- if (!result) {
+ if (result != 1) {
DBG("Failed to parse IPv6 address: %s", server);
return;
}
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index a7bf87a1..7ebffbc0 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -3,6 +3,7 @@
* Connection Manager
*
* Copyright (C) 2007-2014 Intel Corporation. All rights reserved.
+ * Copyright (C) 2022 Matthias Gerstner of SUSE. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -41,7 +42,13 @@
#include "connman.h"
-#define debug(fmt...) do { } while (0)
+#ifdef DNSPROXY_DEBUG
+# define debug(fmt...) do { fprintf(stderr, fmt); fprintf(stderr, "\n"); } while (0)
+#else
+# define debug(fmt...) do { } while (0)
+#endif
+
+#define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(a[0])
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct domain_hdr {
@@ -172,11 +179,17 @@ struct cache_data {
struct cache_entry {
char *key;
bool want_refresh;
- int hits;
+ size_t hits;
struct cache_data *ipv4;
struct cache_data *ipv6;
};
+struct cache_timeout {
+ time_t current_time;
+ time_t max_timeout;
+ bool try_harder;
+};
+
struct domain_question {
uint16_t type;
uint16_t class;
@@ -214,38 +227,91 @@ struct domain_rr {
*/
#define MAX_CACHE_SIZE 256
+#define DNS_HEADER_SIZE sizeof(struct domain_hdr)
+#define DNS_HEADER_TCP_EXTRA_BYTES 2
+#define DNS_TCP_HEADER_SIZE DNS_HEADER_SIZE + DNS_HEADER_TCP_EXTRA_BYTES
+#define DNS_QUESTION_SIZE sizeof(struct domain_question)
+#define DNS_RR_SIZE sizeof(struct domain_rr)
+#define DNS_QTYPE_QCLASS_SIZE sizeof(struct qtype_qclass)
+
+enum dns_type {
+ /* IPv4 address 32-bit */
+ DNS_TYPE_A = ns_t_a,
+ /* IPv6 address 128-bit */
+ DNS_TYPE_AAAA = ns_t_aaaa,
+ /* alias to another name */
+ DNS_TYPE_CNAME = ns_t_cname,
+ /* start of a zone of authority */
+ DNS_TYPE_SOA = ns_t_soa
+};
+
+enum dns_class {
+ DNS_CLASS_IN = ns_c_in,
+ DNS_CLASS_ANY = ns_c_any /* only valid for QCLASS fields */
+};
+
static int cache_size;
static GHashTable *cache;
static int cache_refcount;
-static GSList *server_list = NULL;
-static GSList *request_list = NULL;
-static GHashTable *listener_table = NULL;
+static GSList *server_list;
+static GSList *request_list;
+static GHashTable *listener_table;
static time_t next_refresh;
static GHashTable *partial_tcp_req_table;
-static guint cache_timer = 0;
+static guint cache_timer;
+static in_port_t dns_listen_port = 53;
+/* we can keep using the same resolve's */
+static GResolv *ipv4_resolve;
+static GResolv *ipv6_resolve;
static guint16 get_id(void)
{
uint64_t rand;
+ /* TODO: return code is ignored, should we rather abort() on error? */
__connman_util_get_random(&rand);
return rand;
}
-static int protocol_offset(int protocol)
+static size_t protocol_offset(int protocol)
{
switch (protocol) {
case IPPROTO_UDP:
return 0;
case IPPROTO_TCP:
- return 2;
+ return DNS_HEADER_TCP_EXTRA_BYTES;
default:
- return -EINVAL;
+ /* this should never happen */
+ abort();
}
+}
+static const char* protocol_label(int protocol)
+{
+ switch(protocol) {
+ case IPPROTO_UDP:
+ return "UDP";
+ case IPPROTO_TCP:
+ return "TCP";
+ default:
+ return "BAD_PROTOCOL";
+ }
+}
+
+static int socket_type(int protocol, int extra_flags)
+{
+ switch (protocol) {
+ case IPPROTO_UDP:
+ return SOCK_DGRAM | extra_flags;
+ case IPPROTO_TCP:
+ return SOCK_STREAM | extra_flags;
+ default:
+ /* this should never happen */
+ abort();
+ }
}
/*
@@ -271,9 +337,7 @@ static time_t round_down_ttl(time_t end_time, int ttl)
static struct request_data *find_request(guint16 id)
{
- GSList *list;
-
- for (list = request_list; list; list = list->next) {
+ for (GSList *list = request_list; list; list = list->next) {
struct request_data *req = list->data;
if (req->dstid == id || req->altid == id)
@@ -287,11 +351,9 @@ static struct server_data *find_server(int index,
const char *server,
int protocol)
{
- GSList *list;
-
debug("index %d server %s proto %d", index, server, protocol);
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (index < 0 && data->index < 0 &&
@@ -312,10 +374,6 @@ static struct server_data *find_server(int index,
return NULL;
}
-/* we can keep using the same resolve's */
-static GResolv *ipv4_resolve;
-static GResolv *ipv6_resolve;
-
static void dummy_resolve_func(GResolvResultStatus status,
char **results, gpointer user_data)
{
@@ -353,95 +411,86 @@ static void refresh_dns_entry(struct cache_entry *entry, char *name)
age = 4;
}
- entry->hits -= age;
- if (entry->hits < 0)
+ if (entry->hits > age)
+ entry->hits -= age;
+ else
entry->hits = 0;
}
-static int dns_name_length(unsigned char *buf)
+static size_t dns_name_length(const unsigned char *buf)
{
if ((buf[0] & NS_CMPRSFLGS) == NS_CMPRSFLGS) /* compressed name */
return 2;
- return strlen((char *)buf) + 1;
+ return strlen((const char *)buf) + 1;
}
-static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
+static void update_cached_ttl(unsigned char *ptr, int len, int new_ttl)
{
- unsigned char *c;
- uint16_t w;
- int l;
+ size_t name_len;
+ const uint32_t raw_ttl = ntohl((uint32_t)new_ttl);
+
+ if (new_ttl < 0)
+ return;
/* skip the header */
- c = buf + 12;
- len -= 12;
+ ptr += DNS_HEADER_SIZE;
+ len -= DNS_HEADER_SIZE;
+
+ if (len < DNS_QUESTION_SIZE + 1)
+ return;
- /* skip the query, which is a name and 2 16 bit words */
- l = dns_name_length(c);
- c += l;
- len -= l;
- c += 4;
- len -= 4;
+ /* skip the query, which is a name and a struct domain_question */
+ name_len = dns_name_length(ptr);
+
+ ptr += name_len + DNS_QUESTION_SIZE;
+ len -= name_len + DNS_QUESTION_SIZE;
/* now we get the answer records */
while (len > 0) {
+ struct domain_rr *rr = NULL;
+ size_t rr_len;
+
/* first a name */
- l = dns_name_length(c);
- c += l;
- len -= l;
- if (len < 0)
- break;
- /* then type + class, 2 bytes each */
- c += 4;
- len -= 4;
+ name_len = dns_name_length(ptr);
+ ptr += name_len;
+ len -= name_len;
if (len < 0)
break;
- /* now the 4 byte TTL field */
- c[0] = new_ttl >> 24 & 0xff;
- c[1] = new_ttl >> 16 & 0xff;
- c[2] = new_ttl >> 8 & 0xff;
- c[3] = new_ttl & 0xff;
- c += 4;
- len -= 4;
- if (len < 0)
+ rr = (void*)ptr;
+ if (len < sizeof(*rr))
+ /* incomplete record */
break;
- /* now the 2 byte rdlen field */
- w = c[0] << 8 | c[1];
- c += w + 2;
- len -= w + 2;
+ /* update the TTL field */
+ memcpy(&rr->ttl, &raw_ttl, sizeof(raw_ttl));
+
+ /* skip to the next record */
+ rr_len = sizeof(*rr) + ntohs(rr->rdlen);
+ ptr += rr_len;
+ len -= rr_len;
}
}
-static void send_cached_response(int sk, unsigned char *buf, int len,
+static void send_cached_response(int sk, const unsigned char *ptr, size_t len,
const struct sockaddr *to, socklen_t tolen,
int protocol, int id, uint16_t answers, int ttl)
{
- struct domain_hdr *hdr;
- unsigned char *ptr = buf;
- int err, offset, dns_len, adj_len = len - 2;
-
+ struct domain_hdr *hdr = NULL;
+ int err;
+ const size_t offset = protocol_offset(protocol);
/*
* The cached packet contains always the TCP offset (two bytes)
* so skip them for UDP.
*/
- switch (protocol) {
- case IPPROTO_UDP:
- ptr += 2;
- len -= 2;
- dns_len = len;
- offset = 0;
- break;
- case IPPROTO_TCP:
- offset = 2;
- dns_len = ptr[0] * 256 + ptr[1];
- break;
- default:
- return;
- }
+ const size_t skip_bytes = offset ? 0 : DNS_HEADER_TCP_EXTRA_BYTES;
+ ptr += skip_bytes;
+ len -= skip_bytes;
+ const size_t dns_len = protocol == IPPROTO_UDP ? len : ntohs(*((uint16_t*)ptr));
+
- if (len < 12)
+ if (len < DNS_HEADER_SIZE)
return;
hdr = (void *) (ptr + offset);
@@ -456,22 +505,21 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
/* if this is a negative reply, we are authoritative */
if (answers == 0)
hdr->aa = 1;
- else
+ else {
+ const int adj_len = len - 2;
update_cached_ttl((unsigned char *)hdr, adj_len, ttl);
+ }
- debug("sk %d id 0x%04x answers %d ptr %p length %d dns %d",
+ debug("sk %d id 0x%04x answers %d ptr %p length %zd dns %zd",
sk, hdr->id, answers, ptr, len, dns_len);
err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
connman_error("Cannot send cached DNS response: %s",
strerror(errno));
- return;
}
-
- if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) ||
- (dns_len != len && protocol == IPPROTO_UDP))
- debug("Packet length mismatch, sent %d wanted %d dns %d",
+ else if (err != len || dns_len != (len - offset))
+ debug("Packet length mismatch, sent %d wanted %zd dns %zd",
err, len, dns_len);
}
@@ -480,20 +528,19 @@ static void send_response(int sk, unsigned char *buf, size_t len,
int protocol)
{
struct domain_hdr *hdr;
- int err, offset = protocol_offset(protocol);
+ int err;
+ const size_t offset = protocol_offset(protocol);
+ const size_t send_size = DNS_HEADER_SIZE + offset;
debug("sk %d", sk);
- if (offset < 0)
- return;
-
- if (len < sizeof(*hdr) + offset)
+ if (len < send_size)
return;
hdr = (void *) (buf + offset);
if (offset) {
buf[0] = 0;
- buf[1] = sizeof(*hdr);
+ buf[1] = DNS_HEADER_SIZE;
}
debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
@@ -506,11 +553,10 @@ static void send_response(int sk, unsigned char *buf, size_t len,
hdr->nscount = 0;
hdr->arcount = 0;
- err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen);
+ err = sendto(sk, buf, send_size, MSG_NOSIGNAL, to, tolen);
if (err < 0) {
connman_error("Failed to send DNS response to %d: %s",
sk, strerror(errno));
- return;
}
}
@@ -544,7 +590,7 @@ static gboolean request_timeout(gpointer user_data)
{
struct request_data *req = user_data;
struct sockaddr *sa;
- int sk;
+ int sk = -1;
if (!req)
return FALSE;
@@ -559,7 +605,9 @@ static gboolean request_timeout(gpointer user_data)
} else if (req->protocol == IPPROTO_TCP) {
sk = req->client_sk;
sa = NULL;
- } else
+ }
+
+ if (sk < 0)
goto out;
if (req->resplen > 0 && req->resp) {
@@ -568,22 +616,18 @@ static gboolean request_timeout(gpointer user_data)
* "not found" result), so send that back to client instead
* of more fatal server failed error.
*/
- if (sk >= 0)
- sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
- sa, req->sa_len);
+ sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
+ sa, req->sa_len);
} else if (req->request) {
/*
* There was not reply from server at all.
*/
- struct domain_hdr *hdr;
-
- hdr = (void *)(req->request + protocol_offset(req->protocol));
+ struct domain_hdr *hdr = (void *)(req->request + protocol_offset(req->protocol));
hdr->id = req->srcid;
- if (sk >= 0)
- send_response(sk, req->request, req->request_len,
- sa, req->sa_len, req->protocol);
+ send_response(sk, req->request, req->request_len,
+ sa, req->sa_len, req->protocol);
}
/*
@@ -603,73 +647,92 @@ out:
return FALSE;
}
-static int append_query(unsigned char *buf, unsigned int size,
- const char *query, const char *domain)
+static int append_data(unsigned char *buf, size_t size, const char *data)
{
unsigned char *ptr = buf;
- int len;
+ size_t len;
- debug("query %s domain %s", query, domain);
+ while (true) {
+ const char *dot = strchr(data, '.');
+ len = dot ? dot - data : strlen(data);
- while (query) {
- const char *tmp;
-
- tmp = strchr(query, '.');
- if (!tmp) {
- len = strlen(query);
- if (len == 0)
- break;
- *ptr = len;
- memcpy(ptr + 1, query, len);
- ptr += len + 1;
+ if (len == 0)
break;
- }
+ else if (size < len + 1)
+ return -1;
- *ptr = tmp - query;
- memcpy(ptr + 1, query, tmp - query);
- ptr += tmp - query + 1;
+ *ptr = len;
+ memcpy(ptr + 1, data, len);
+ ptr += len + 1;
+ size -= len + 1;
+
+ if (!dot)
+ break;
- query = tmp + 1;
+ data = dot + 1;
}
- while (domain) {
- const char *tmp;
+ return ptr - buf;
+}
- tmp = strchr(domain, '.');
- if (!tmp) {
- len = strlen(domain);
- if (len == 0)
- break;
- *ptr = len;
- memcpy(ptr + 1, domain, len);
- ptr += len + 1;
- break;
- }
+static int append_query(unsigned char *buf, size_t size,
+ const char *query, const char *domain)
+{
+ size_t added;
+ size_t left_size = size;
+ int res;
- *ptr = tmp - domain;
- memcpy(ptr + 1, domain, tmp - domain);
- ptr += tmp - domain + 1;
+ debug("query %s domain %s", query, domain);
- domain = tmp + 1;
- }
+ res = append_data(buf, left_size, query);
+ if (res < 0)
+ return -1;
+ left_size -= res;
- *ptr++ = 0x00;
+ res = append_data(buf + res, left_size, domain);
+ if (res < 0)
+ return -1;
+ left_size -= res;
- return ptr - buf;
+ if (left_size == 0)
+ return -1;
+
+ added = size - left_size;
+ *(buf + added) = 0x00;
+
+ return added;
}
-static bool cache_check_is_valid(struct cache_data *data,
- time_t current_time)
+static bool cache_check_is_valid(struct cache_data *data, time_t current_time)
{
if (!data)
return false;
-
- if (data->cache_until < current_time)
+ else if (data->cache_until < current_time)
return false;
return true;
}
+static void cache_free_ipv4(struct cache_entry *entry)
+{
+ if (!entry->ipv4)
+ return;
+
+ g_free(entry->ipv4->data);
+ g_free(entry->ipv4);
+ entry->ipv4 = NULL;
+}
+
+static void cache_free_ipv6(struct cache_entry *entry)
+{
+ if (!entry->ipv6)
+ return;
+
+ g_free(entry->ipv6->data);
+ g_free(entry->ipv6);
+ entry->ipv6 = NULL;
+}
+
/*
* remove stale cached entries so that they can be refreshed
*/
@@ -677,76 +740,65 @@ static void cache_enforce_validity(struct cache_entry *entry)
{
time_t current_time = time(NULL);
- if (!cache_check_is_valid(entry->ipv4, current_time)
- && entry->ipv4) {
+ if (entry->ipv4 && !cache_check_is_valid(entry->ipv4, current_time)) {
debug("cache timeout \"%s\" type A", entry->key);
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- entry->ipv4 = NULL;
-
+ cache_free_ipv4(entry);
}
- if (!cache_check_is_valid(entry->ipv6, current_time)
- && entry->ipv6) {
+ if (entry->ipv6 && !cache_check_is_valid(entry->ipv6, current_time)) {
debug("cache timeout \"%s\" type AAAA", entry->key);
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- entry->ipv6 = NULL;
+ cache_free_ipv6(entry);
}
}
-static uint16_t cache_check_validity(char *question, uint16_t type,
+static bool cache_check_validity(const char *question, uint16_t type,
struct cache_entry *entry)
{
- time_t current_time = time(NULL);
- bool want_refresh = false;
-
- /*
- * if we have a popular entry, we want a refresh instead of
- * total destruction of the entry.
- */
- if (entry->hits > 2)
- want_refresh = true;
+ struct cache_data *cached_ip = NULL, *other_ip = NULL;
+ const time_t current_time = time(NULL);
+ bool want_refresh;
cache_enforce_validity(entry);
switch (type) {
- case 1: /* IPv4 */
- if (!cache_check_is_valid(entry->ipv4, current_time)) {
- debug("cache %s \"%s\" type A", entry->ipv4 ?
- "timeout" : "entry missing", question);
-
- if (want_refresh)
- entry->want_refresh = true;
+ case DNS_TYPE_A: /* IPv4 */
+ cached_ip = entry->ipv4;
+ other_ip = entry->ipv6;
+ break;
- /*
- * We do not remove cache entry if there is still
- * valid IPv6 entry found in the cache.
- */
- if (!cache_check_is_valid(entry->ipv6, current_time) && !want_refresh) {
- g_hash_table_remove(cache, question);
- type = 0;
- }
- }
+ case DNS_TYPE_AAAA: /* IPv6 */
+ cached_ip = entry->ipv6;
+ other_ip = entry->ipv4;
break;
+ default:
+ return false;
+ }
- case 28: /* IPv6 */
- if (!cache_check_is_valid(entry->ipv6, current_time)) {
- debug("cache %s \"%s\" type AAAA", entry->ipv6 ?
- "timeout" : "entry missing", question);
+ /*
+ * if we have a popular entry, we want a refresh instead of
+ * total destruction of the entry.
+ */
+ want_refresh = entry->hits > 2 ? true : false;
- if (want_refresh)
- entry->want_refresh = true;
+ if (!cache_check_is_valid(cached_ip, current_time)) {
+ debug("cache %s \"%s\" type %s",
+ cached_ip ? "timeout" : "entry missing",
+ question,
+ cached_ip == entry->ipv4 ? "A" : "AAAA");
- if (!cache_check_is_valid(entry->ipv4, current_time) && !want_refresh) {
- g_hash_table_remove(cache, question);
- type = 0;
- }
+ if (want_refresh)
+ entry->want_refresh = true;
+ /*
+ * We do not remove cache entry if there is still a
+ * valid entry for another IP version found in the cache.
+ */
+ else if (!cache_check_is_valid(other_ip, current_time)) {
+ g_hash_table_remove(cache, question);
+ return false;
}
- break;
}
- return type;
+ return true;
}
static void cache_element_destroy(gpointer value)
@@ -756,19 +808,13 @@ static void cache_element_destroy(gpointer value)
if (!entry)
return;
- if (entry->ipv4) {
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- }
-
- if (entry->ipv6) {
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- }
+ cache_free_ipv4(entry);
+ cache_free_ipv6(entry);
g_free(entry->key);
g_free(entry);
+ /* TODO: this would be a worrying condition. Does this ever happen? */
if (--cache_size < 0)
cache_size = 0;
}
@@ -782,6 +828,7 @@ static gboolean try_remove_cache(gpointer user_data)
g_hash_table_destroy(cache);
cache = NULL;
+ cache_size = 0;
}
return FALSE;
@@ -789,36 +836,28 @@ static gboolean try_remove_cache(gpointer user_data)
static void create_cache(void)
{
- if (__sync_fetch_and_add(&cache_refcount, 1) == 0)
+ if (__sync_fetch_and_add(&cache_refcount, 1) == 0) {
cache = g_hash_table_new_full(g_str_hash,
g_str_equal,
NULL,
cache_element_destroy);
+ cache_size = 0;
+ }
}
-static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
+static struct cache_entry *cache_check(gpointer request, uint16_t *qtype, int proto)
{
- char *question;
- struct cache_entry *entry;
- struct domain_question *q;
- uint16_t type;
- int offset, proto_offset;
-
if (!request)
return NULL;
- proto_offset = protocol_offset(proto);
- if (proto_offset < 0)
- return NULL;
-
- question = request + proto_offset + 12;
-
- offset = strlen(question) + 1;
- q = (void *) (question + offset);
- type = ntohs(q->type);
+ const char *question = request + protocol_offset(proto) + DNS_HEADER_SIZE;
+ const size_t offset = strlen(question) + 1;
+ const struct domain_question *q = (void *) (question + offset);
+ const uint16_t type = ntohs(q->type);
+ struct cache_entry *entry;
/* We only cache either A (1) or AAAA (28) requests */
- if (type != 1 && type != 28)
+ if (type != DNS_TYPE_A && type != DNS_TYPE_AAAA)
return NULL;
if (!cache) {
@@ -830,8 +869,7 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
if (!entry)
return NULL;
- type = cache_check_validity(question, type, entry);
- if (type == 0)
+ if (!cache_check_validity(question, type, entry))
return NULL;
*qtype = type;
@@ -846,20 +884,19 @@ static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
* format so that we can cache the wire format string directly.
*/
static int get_name(int counter,
- unsigned char *pkt, unsigned char *start, unsigned char *max,
+ const unsigned char *pkt, const unsigned char *start, const unsigned char *max,
unsigned char *output, int output_max, int *output_len,
- unsigned char **end, char *name, size_t max_name, int *name_len)
+ const unsigned char **end, char *name, size_t max_name, int *name_len)
{
- unsigned char *p;
+ const unsigned char *p = start;
/* Limit recursion to 10 (this means up to 10 labels in domain name) */
if (counter > 10)
return -EINVAL;
- p = start;
while (*p) {
if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
- uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
+ const uint16_t offset = (*p & 0x3F) * 256 + *(p + 1);
if (offset >= max - pkt)
return -ENOBUFS;
@@ -875,11 +912,9 @@ static int get_name(int counter,
if (pkt + label_len > max)
return -ENOBUFS;
-
- if (*output_len > output_max)
+ else if (*output_len > output_max)
return -ENOBUFS;
-
- if ((*name_len + 1 + label_len + 1) > max_name)
+ else if ((*name_len + 1 + label_len + 1) > max_name)
return -ENOBUFS;
/*
@@ -908,25 +943,25 @@ static int get_name(int counter,
return 0;
}
-static int parse_rr(unsigned char *buf, unsigned char *start,
- unsigned char *max,
- unsigned char *response, unsigned int *response_size,
- uint16_t *type, uint16_t *class, int *ttl, int *rdlen,
- unsigned char **end,
+static int parse_rr(const unsigned char *buf, const unsigned char *start,
+ const unsigned char *max,
+ unsigned char *response, size_t *response_size,
+ uint16_t *type, uint16_t *class, int *ttl, uint16_t *rdlen,
+ const unsigned char **end,
char *name, size_t max_name)
{
struct domain_rr *rr;
- int err, offset;
+ size_t offset;
int name_len = 0, output_len = 0, max_rsp = *response_size;
+ int err = get_name(0, buf, start, max, response, max_rsp,
+ &output_len, end, name, max_name, &name_len);
- err = get_name(0, buf, start, max, response, max_rsp,
- &output_len, end, name, max_name, &name_len);
if (err < 0)
return err;
offset = output_len;
- if ((unsigned int) offset > *response_size)
+ if (offset > *response_size)
return -ENOBUFS;
rr = (void *) (*end);
@@ -942,31 +977,28 @@ static int parse_rr(unsigned char *buf, unsigned char *start,
if (*ttl < 0)
return -EINVAL;
- memcpy(response + offset, *end, sizeof(struct domain_rr));
+ memcpy(response + offset, *end, DNS_RR_SIZE);
- offset += sizeof(struct domain_rr);
- *end += sizeof(struct domain_rr);
+ offset += DNS_RR_SIZE;
+ *end += DNS_RR_SIZE;
- if ((unsigned int) (offset + *rdlen) > *response_size)
+ if ((offset + *rdlen) > *response_size)
return -ENOBUFS;
memcpy(response + offset, *end, *rdlen);
*end += *rdlen;
-
*response_size = offset + *rdlen;
return 0;
}
-static bool check_alias(GSList *aliases, char *name)
+static bool check_alias(GSList *aliases, const char *name)
{
- GSList *list;
-
if (aliases) {
- for (list = aliases; list; list = list->next) {
- int len = strlen((char *)list->data);
- if (strncmp((char *)list->data, name, len) == 0)
+ for (GSList *list = aliases; list; list = list->next) {
+ const char *cmpname = (const char*)list->data;
+ if (strncmp(cmpname, name, NS_MAXDNAME) == 0)
return true;
}
}
@@ -974,55 +1006,67 @@ static bool check_alias(GSList *aliases, char *name)
return false;
}
-static int parse_response(unsigned char *buf, int buflen,
- char *question, int qlen,
+/*
+ * Parses the DNS response packet found in 'buf' consisting of 'buflen' bytes.
+ *
+ * The parsed question label, response type and class, ttl and number of
+ * answer sections are output parameters. The response output buffer will
+ * receive all matching resource records to be cached.
+ *
+ * Return value is < 0 on error (negative errno) or zero on success.
+ */
+static int parse_response(const unsigned char *buf, size_t buflen,
+ char *question, size_t qlen,
uint16_t *type, uint16_t *class, int *ttl,
- unsigned char *response, unsigned int *response_len,
+ unsigned char *response, size_t *response_len,
uint16_t *answers)
{
struct domain_hdr *hdr = (void *) buf;
struct domain_question *q;
- unsigned char *ptr;
- uint16_t qdcount = ntohs(hdr->qdcount);
- uint16_t ancount = ntohs(hdr->ancount);
- int err, i;
- uint16_t qtype, qclass;
- unsigned char *next = NULL;
- unsigned int maxlen = *response_len;
- GSList *aliases = NULL, *list;
- char name[NS_MAXDNAME + 1];
-
- if (buflen < 12)
+ uint16_t qtype;
+ int err = -ENOMSG;
+ uint16_t ancount, qclass;
+ GSList *aliases = NULL;
+ const size_t maxlen = *response_len;
+
+ *response_len = 0;
+ *answers = 0;
+
+ if (buflen < DNS_HEADER_SIZE)
return -EINVAL;
+ const uint16_t qdcount = ntohs(hdr->qdcount);
+ const unsigned char *ptr = buf + DNS_HEADER_SIZE;
+ const unsigned char *eptr = buf + buflen;
+
debug("qr %d qdcount %d", hdr->qr, qdcount);
/* We currently only cache responses where question count is 1 */
if (hdr->qr != 1 || qdcount != 1)
return -EINVAL;
- ptr = buf + sizeof(struct domain_hdr);
-
- strncpy(question, (char *) ptr, qlen);
+ /*
+ * NOTE: currently the *caller* ensures that the `question' buffer is
+ * always zero terminated.
+ */
+ strncpy(question, (const char *) ptr, MIN(qlen, buflen - DNS_HEADER_SIZE));
qlen = strlen(question);
ptr += qlen + 1; /* skip \0 */
+ if ((eptr - ptr) < DNS_QUESTION_SIZE)
+ return -EINVAL;
+
q = (void *) ptr;
qtype = ntohs(q->type);
/* We cache only A and AAAA records */
- if (qtype != 1 && qtype != 28)
+ if (qtype != DNS_TYPE_A && qtype != DNS_TYPE_AAAA)
return -ENOMSG;
- qclass = ntohs(q->class);
-
- ptr += 2 + 2; /* ptr points now to answers */
+ ptr += DNS_QUESTION_SIZE; /* advance to answers section */
- err = -ENOMSG;
- *response_len = 0;
- *answers = 0;
-
- memset(name, 0, sizeof(name));
+ ancount = ntohs(hdr->ancount);
+ qclass = ntohs(q->class);
/*
* We have a bunch of answers (like A, AAAA, CNAME etc) to
@@ -1030,7 +1074,8 @@ static int parse_response(unsigned char *buf, int buflen,
* resource records. Only A and AAAA records are cached, all
* the other records in answers are skipped.
*/
- for (i = 0; i < ancount; i++) {
+ for (uint16_t i = 0; i < ancount; i++) {
+ char name[NS_MAXDNAME + 1] = {0};
/*
* Get one address at a time to this buffer.
* The max size of the answer is
@@ -1038,23 +1083,27 @@ static int parse_response(unsigned char *buf, int buflen,
* 4 (ttl) + 2 (rdlen) + addr (16 or 4) = 28
* for A or AAAA record.
* For CNAME the size can be bigger.
+ * TODO: why are we using the MAXCDNAME constant as buffer
+ * size then?
*/
- unsigned char rsp[NS_MAXCDNAME];
- unsigned int rsp_len = sizeof(rsp) - 1;
- int ret, rdlen;
-
- memset(rsp, 0, sizeof(rsp));
+ unsigned char rsp[NS_MAXCDNAME] = {0};
+ size_t rsp_len = sizeof(rsp) - 1;
+ const unsigned char *next = NULL;
+ uint16_t rdlen;
- ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
+ int ret = parse_rr(buf, ptr, buf + buflen, rsp, &rsp_len,
type, class, ttl, &rdlen, &next, name,
sizeof(name) - 1);
if (ret != 0) {
err = ret;
- goto out;
+ break;
}
+ /* set pointer to the next RR for the next iteration */
+ ptr = next;
+
/*
- * Now rsp contains compressed or uncompressed resource
+ * Now rsp contains a compressed or an uncompressed resource
* record. Next we check if this record answers the question.
* The name var contains the uncompressed label.
* One tricky bit is the CNAME records as they alias
@@ -1066,8 +1115,6 @@ static int parse_response(unsigned char *buf, int buflen,
* looking for.
*/
if (*class != qclass) {
- ptr = next;
- next = NULL;
continue;
}
@@ -1092,7 +1139,7 @@ static int parse_response(unsigned char *buf, int buflen,
* address of ipv6.l.google.com. For caching purposes this
* should not cause any issues.
*/
- if (*type == 5 && strncmp(question, name, qlen) == 0) {
+ if (*type == DNS_TYPE_CNAME && strncmp(question, name, qlen) == 0) {
/*
* So now the alias answered the question. This is
* not very useful from caching point of view as
@@ -1100,7 +1147,7 @@ static int parse_response(unsigned char *buf, int buflen,
* question. We need to find the real A/AAAA record
* of the alias and cache that.
*/
- unsigned char *end = NULL;
+ const unsigned char *end = NULL;
int name_len = 0, output_len = 0;
memset(rsp, 0, sizeof(rsp));
@@ -1116,8 +1163,6 @@ static int parse_response(unsigned char *buf, int buflen,
name, sizeof(name) - 1, &name_len);
if (ret != 0) {
/* just ignore the error at this point */
- ptr = next;
- next = NULL;
continue;
}
@@ -1129,12 +1174,8 @@ static int parse_response(unsigned char *buf, int buflen,
*/
aliases = g_slist_prepend(aliases, g_strdup(name));
- ptr = next;
- next = NULL;
continue;
- }
-
- if (*type == qtype) {
+ } else if (*type == qtype) {
/*
* We found correct type (A or AAAA)
*/
@@ -1151,7 +1192,7 @@ static int parse_response(unsigned char *buf, int buflen,
*/
if (*response_len + rsp_len > maxlen) {
err = -ENOBUFS;
- goto out;
+ break;
}
memcpy(response + *response_len, rsp, rsp_len);
*response_len += rsp_len;
@@ -1159,31 +1200,21 @@ static int parse_response(unsigned char *buf, int buflen,
err = 0;
}
}
-
- ptr = next;
- next = NULL;
}
-out:
- for (list = aliases; list; list = list->next)
+ for (GSList *list = aliases; list; list = list->next)
g_free(list->data);
g_slist_free(aliases);
return err;
}
-struct cache_timeout {
- time_t current_time;
- int max_timeout;
- int try_harder;
-};
-
static gboolean cache_check_entry(gpointer key, gpointer value,
gpointer user_data)
{
struct cache_timeout *data = user_data;
struct cache_entry *entry = value;
- int max_timeout;
+ time_t max_timeout;
/* Scale the number of hits by half as part of cache aging */
@@ -1224,14 +1255,14 @@ static gboolean cache_check_entry(gpointer key, gpointer value,
static void cache_cleanup(void)
{
- static int max_timeout;
- struct cache_timeout data;
+ static time_t max_timeout;
+ struct cache_timeout data = {
+ .current_time = time(NULL),
+ .max_timeout = 0,
+ .try_harder = false
+ };
int count = 0;
- data.current_time = time(NULL);
- data.max_timeout = 0;
- data.try_harder = 0;
-
/*
* In the first pass, we only remove entries that have timed out.
* We use a cache of the first time to expire to do this only
@@ -1248,7 +1279,7 @@ static void cache_cleanup(void)
* we also expire entries with a low hit count,
* while aging the hit count at the same time.
*/
- data.try_harder = 1;
+ data.try_harder = true;
if (count == 0)
count = g_hash_table_foreach_remove(cache, cache_check_entry,
&data);
@@ -1278,23 +1309,11 @@ static gboolean cache_invalidate_entry(gpointer key, gpointer value,
entry->want_refresh = true;
/* delete the cached data */
- if (entry->ipv4) {
- g_free(entry->ipv4->data);
- g_free(entry->ipv4);
- entry->ipv4 = NULL;
- }
-
- if (entry->ipv6) {
- g_free(entry->ipv6->data);
- g_free(entry->ipv6);
- entry->ipv6 = NULL;
- }
+ cache_free_ipv4(entry);
+ cache_free_ipv6(entry);
/* keep the entry if we want it refreshed, delete it otherwise */
- if (entry->want_refresh)
- return FALSE;
- else
- return TRUE;
+ return entry->want_refresh ? FALSE : TRUE;
}
/*
@@ -1315,25 +1334,24 @@ static void cache_invalidate(void)
static void cache_refresh_entry(struct cache_entry *entry)
{
-
cache_enforce_validity(entry);
- if (entry->hits > 2 && !entry->ipv4)
- entry->want_refresh = true;
- if (entry->hits > 2 && !entry->ipv6)
+ if (entry->hits > 2 && (!entry->ipv4 || !entry->ipv6))
entry->want_refresh = true;
if (entry->want_refresh) {
- char *c;
char dns_name[NS_MAXDNAME + 1];
+ char *c;
+
entry->want_refresh = false;
/* turn a DNS name into a hostname with dots */
strncpy(dns_name, entry->key, NS_MAXDNAME);
c = dns_name;
- while (c && *c) {
- int jump;
- jump = *c;
+ while (*c) {
+ /* fetch the size of the current component and replace
+ it by a dot */
+ int jump = *c;
*c = '.';
c += jump + 1;
}
@@ -1359,43 +1377,44 @@ static void cache_refresh(void)
g_hash_table_foreach(cache, cache_refresh_iterator, NULL);
}
-static int reply_query_type(unsigned char *msg, int len)
+static int reply_query_type(const unsigned char *msg, int len)
{
- unsigned char *c;
- int l;
- int type;
-
/* skip the header */
- c = msg + sizeof(struct domain_hdr);
- len -= sizeof(struct domain_hdr);
+ const unsigned char *c = msg + DNS_HEADER_SIZE;
+ int type;
+ len -= DNS_HEADER_SIZE;
if (len < 0)
return 0;
- /* now the query, which is a name and 2 16 bit words */
- l = dns_name_length(c);
- c += l;
+ /* now the query, which is a name and 2 16 bit words for type and class */
+ c += dns_name_length(c);
+
type = c[0] << 8 | c[1];
return type;
}
-static int cache_update(struct server_data *srv, unsigned char *msg,
- unsigned int msg_len)
+/*
+ * update the cache with the DNS reply found in msg
+ */
+static int cache_update(struct server_data *srv, const unsigned char *msg, size_t msg_len)
{
- int offset = protocol_offset(srv->protocol);
- int err, qlen, ttl = 0;
+ const size_t offset = protocol_offset(srv->protocol);
+ int err, ttl = 0;
+ uint16_t *lenhdr;
+ size_t qlen;
+ bool is_new_entry = false;
uint16_t answers = 0, type = 0, class = 0;
struct domain_hdr *hdr = (void *)(msg + offset);
- struct domain_question *q;
+ struct domain_question *q = NULL;
struct cache_entry *entry;
struct cache_data *data;
char question[NS_MAXDNAME + 1];
unsigned char response[NS_MAXDNAME + 1];
- unsigned char *ptr;
- unsigned int rsplen;
- bool new_entry = true;
- time_t current_time;
+ unsigned char *ptr = NULL;
+ size_t rsplen = sizeof(response) - 1;
+ const time_t current_time = time(NULL);
if (cache_size >= MAX_CACHE_SIZE) {
cache_cleanup();
@@ -1403,18 +1422,13 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
return 0;
}
- current_time = time(NULL);
-
/* don't do a cache refresh more than twice a minute */
if (next_refresh < current_time) {
cache_refresh();
next_refresh = current_time + 30;
}
- if (offset < 0)
- return 0;
-
- debug("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
+ debug("offset %zd hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
/* Continue only if response code is 0 (=ok) */
if (hdr->rcode != ns_r_noerror)
@@ -1423,9 +1437,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
if (!cache)
create_cache();
- rsplen = sizeof(response) - 1;
question[sizeof(question) - 1] = '\0';
-
err = parse_response(msg + offset, msg_len - offset,
question, sizeof(question) - 1,
&type, &class, &ttl,
@@ -1438,26 +1450,29 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
*/
if ((err == -ENOMSG || err == -ENOBUFS) &&
reply_query_type(msg + offset,
- msg_len - offset) == 28) {
+ msg_len - offset) == DNS_TYPE_AAAA) {
entry = g_hash_table_lookup(cache, question);
if (entry && entry->ipv4 && !entry->ipv6) {
- int cache_offset = 0;
+ struct cache_data *data = g_try_new(struct cache_data, 1);
- data = g_try_new(struct cache_data, 1);
if (!data)
return -ENOMEM;
data->inserted = entry->ipv4->inserted;
data->type = type;
data->answers = ntohs(hdr->ancount);
data->timeout = entry->ipv4->timeout;
- if (srv->protocol == IPPROTO_UDP)
- cache_offset = 2;
- data->data_len = msg_len + cache_offset;
- data->data = ptr = g_malloc(data->data_len);
- ptr[0] = (data->data_len - 2) / 256;
- ptr[1] = (data->data_len - 2) - ptr[0] * 256;
- if (srv->protocol == IPPROTO_UDP)
- ptr += 2;
+ data->data_len = msg_len +
+ (offset ? 0 : DNS_HEADER_TCP_EXTRA_BYTES);
+ data->data = g_malloc(data->data_len);
+ ptr = data->data;
+ if (srv->protocol == IPPROTO_UDP) {
+ /* add the two bytes length header also for
+ * UDP responses */
+ lenhdr = (void*)ptr;
+ *lenhdr = htons(data->data_len -
+ DNS_HEADER_TCP_EXTRA_BYTES);
+ ptr += DNS_HEADER_TCP_EXTRA_BYTES;
+ }
data->valid_until = entry->ipv4->valid_until;
data->cache_until = entry->ipv4->cache_until;
memcpy(ptr, msg, msg_len);
@@ -1466,9 +1481,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
* we will get a "hit" when we serve the response
* out of the cache
*/
- entry->hits--;
- if (entry->hits < 0)
- entry->hits = 0;
+ entry->hits = entry->hits ? entry->hits - 1 : 0;
return 0;
}
}
@@ -1476,8 +1489,6 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
if (err < 0 || ttl == 0)
return 0;
- qlen = strlen(question);
-
/*
* If the cache contains already data, check if the
* type of the cached data is the same and do not add
@@ -1485,7 +1496,11 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
* This is needed so that we can cache both A and AAAA
* records for the same name.
*/
+
entry = g_hash_table_lookup(cache, question);
+ data = NULL;
+ is_new_entry = !entry;
+
if (!entry) {
entry = g_try_new(struct cache_entry, 1);
if (!entry)
@@ -1502,37 +1517,28 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
entry->want_refresh = false;
entry->hits = 0;
- if (type == 1)
- entry->ipv4 = data;
- else
- entry->ipv6 = data;
} else {
- if (type == 1 && entry->ipv4)
+ if (type == DNS_TYPE_A && entry->ipv4)
return 0;
-
- if (type == 28 && entry->ipv6)
+ else if (type == DNS_TYPE_AAAA && entry->ipv6)
return 0;
data = g_try_new(struct cache_data, 1);
if (!data)
return -ENOMEM;
- if (type == 1)
- entry->ipv4 = data;
- else
- entry->ipv6 = data;
-
/*
* compensate for the hit we'll get for serving
* the response out of the cache
*/
- entry->hits--;
- if (entry->hits < 0)
- entry->hits = 0;
-
- new_entry = false;
+ entry->hits = entry->hits ? entry->hits - 1 : 0;
}
+ if (type == DNS_TYPE_A)
+ entry->ipv4 = data;
+ else
+ entry->ipv6 = data;
+
if (ttl < MIN_CACHE_TTL)
ttl = MIN_CACHE_TTL;
@@ -1540,14 +1546,21 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
data->type = type;
data->answers = answers;
data->timeout = ttl;
+ data->valid_until = current_time + ttl;
+
+ qlen = strlen(question);
/*
- * The "2" in start of the length is the TCP offset. We allocate it
- * here even for UDP packet because it simplifies the sending
- * of cached packet.
+ * We allocate the extra TCP header bytes here even for UDP packet
+ * because it simplifies the sending of cached packet.
*/
- data->data_len = 2 + 12 + qlen + 1 + 2 + 2 + rsplen;
- data->data = ptr = g_malloc(data->data_len);
- data->valid_until = current_time + ttl;
+ data->data_len = DNS_TCP_HEADER_SIZE + qlen + 1 + 2 + 2 + rsplen;
+ data->data = g_malloc(data->data_len);
+ if (!data->data) {
+ g_free(entry->key);
+ g_free(data);
+ g_free(entry);
+ return -ENOMEM;
+ }
/*
* Restrict the cached DNS record TTL to some sane value
@@ -1558,45 +1571,39 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
data->cache_until = round_down_ttl(current_time + ttl, ttl);
- if (!data->data) {
- g_free(entry->key);
- g_free(data);
- g_free(entry);
- return -ENOMEM;
- }
+ ptr = data->data;
/*
* We cache the two extra bytes at the start of the message
- * in a TCP packet. When sending UDP packet, we skip the first
+ * in a TCP packet. When sending UDP packet, we pad the first
* two bytes. This way we do not need to know the format
* (UDP/TCP) of the cached message.
*/
- if (srv->protocol == IPPROTO_UDP)
- memcpy(ptr + 2, msg, offset + 12);
- else
- memcpy(ptr, msg, offset + 12);
+ lenhdr = (void*)ptr;
+ *lenhdr = htons(data->data_len - DNS_HEADER_TCP_EXTRA_BYTES);
+ ptr += DNS_HEADER_TCP_EXTRA_BYTES;
- ptr[0] = (data->data_len - 2) / 256;
- ptr[1] = (data->data_len - 2) - ptr[0] * 256;
- if (srv->protocol == IPPROTO_UDP)
- ptr += 2;
+ memcpy(ptr, hdr, DNS_HEADER_SIZE);
+ ptr += DNS_HEADER_SIZE;
- memcpy(ptr + offset + 12, question, qlen + 1); /* copy also the \0 */
+ memcpy(ptr, question, qlen + 1); /* copy also the \0 */
+ ptr += qlen + 1;
- q = (void *) (ptr + offset + 12 + qlen + 1);
+ q = (void *)ptr;
q->type = htons(type);
q->class = htons(class);
- memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question),
- response, rsplen);
+ ptr += DNS_QUESTION_SIZE;
- if (new_entry) {
+ memcpy(ptr, response, rsplen);
+
+ if (is_new_entry) {
g_hash_table_replace(cache, entry->key, entry);
cache_size++;
}
debug("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u "
"dns len %u",
- cache_size, new_entry ? "new " : "old ",
+ cache_size, is_new_entry ? "new " : "old ",
question, type, ttl,
sizeof(*entry) + sizeof(*data) + data->data_len + qlen,
data->data_len,
@@ -1607,38 +1614,41 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
return 0;
}
-static int ns_resolv(struct server_data *server, struct request_data *req,
- gpointer request, gpointer name)
+/*
+ * attempts to answer the given request from cached replies.
+ *
+ * returns:
+ * > 0 on cache hit (answer is already sent out to client)
+ * == 0 on cache miss
+ * < 0 on error condition (errno)
+ */
+static int ns_try_resolv_from_cache(
+ struct request_data *req, gpointer request, const char *lookup)
{
- GList *list;
- int sk, err, type = 0;
- char *dot, *lookup = (char *) name;
- struct cache_entry *entry;
+ uint16_t type = 0;
+ int ttl_left;
+ struct cache_data *data;
+ struct cache_entry *entry = cache_check(request, &type, req->protocol);
+ if (!entry)
+ return 0;
- entry = cache_check(request, &type, req->protocol);
- if (entry) {
- int ttl_left = 0;
- struct cache_data *data;
+ debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
- debug("cache hit %s type %s", lookup, type == 1 ? "A" : "AAAA");
- if (type == 1)
- data = entry->ipv4;
- else
- data = entry->ipv6;
+ data = type == DNS_TYPE_A ? entry->ipv4 : entry->ipv6;
- if (data) {
- ttl_left = data->valid_until - time(NULL);
- entry->hits++;
- }
+ if (!data)
+ return 0;
- if (data && req->protocol == IPPROTO_TCP) {
+ ttl_left = data->valid_until - time(NULL);
+ entry->hits++;
+
+ switch(req->protocol) {
+ case IPPROTO_TCP:
send_cached_response(req->client_sk, data->data,
data->data_len, NULL, 0, IPPROTO_TCP,
req->srcid, data->answers, ttl_left);
return 1;
- }
-
- if (data && req->protocol == IPPROTO_UDP) {
+ case IPPROTO_UDP: {
int udp_sk = get_req_udp_socket(req);
if (udp_sk < 0)
@@ -1652,6 +1662,24 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
}
}
+ return -EINVAL;
+}
+
+static int ns_resolv(struct server_data *server, struct request_data *req,
+ gpointer request, gpointer name)
+{
+ int sk = -1;
+ const char *lookup = (const char *)name;
+ int err = ns_try_resolv_from_cache(req, request, lookup);
+
+ if (err > 0)
+ /* cache hit */
+ return 1;
+ else if (err != 0)
+ /* error other than cache miss, don't continue */
+ return err;
+
+ /* forward request to real DNS server */
sk = g_io_channel_unix_get_fd(server->channel);
err = sendto(sk, request, req->request_len, MSG_NOSIGNAL,
@@ -1667,54 +1695,51 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
req->numserv++;
/* If we have more than one dot, we don't add domains */
- dot = strchr(lookup, '.');
- if (dot && dot != lookup + strlen(lookup) - 1)
- return 0;
+ {
+ const char *dot = strchr(lookup, '.');
+ if (dot && dot != lookup + strlen(lookup) - 1)
+ return 0;
+ }
if (server->domains && server->domains->data)
req->append_domain = true;
- for (list = server->domains; list; list = list->next) {
- char *domain;
+ for (GList *list = server->domains; list; list = list->next) {
+ int domlen, altlen;
unsigned char alt[1024];
- struct domain_hdr *hdr = (void *) &alt;
- int altlen, domlen, offset;
-
- domain = list->data;
+ const char *domain = list->data;
+ const size_t offset = protocol_offset(server->protocol);
+ struct domain_hdr *hdr = (void *) (&alt[0] + offset);
if (!domain)
continue;
- offset = protocol_offset(server->protocol);
- if (offset < 0)
- return offset;
-
domlen = strlen(domain) + 1;
+
if (domlen < 5)
return -EINVAL;
- alt[offset] = req->altid & 0xff;
- alt[offset + 1] = req->altid >> 8;
+ memcpy(alt + offset, &req->altid, sizeof(req->altid));
- memcpy(alt + offset + 2, request + offset + 2, 10);
+ memcpy(alt + offset + 2, request + offset + 2, DNS_HEADER_SIZE - 2);
hdr->qdcount = htons(1);
- altlen = append_query(alt + offset + 12, sizeof(alt) - 12,
+ altlen = append_query(alt + offset + DNS_HEADER_SIZE, sizeof(alt) - DNS_HEADER_SIZE - offset,
name, domain);
if (altlen < 0)
return -EINVAL;
- altlen += 12;
+ altlen += DNS_HEADER_SIZE;
+ altlen += offset;
- memcpy(alt + offset + altlen,
- request + offset + altlen - domlen,
- req->request_len - altlen - offset + domlen);
+ memcpy(alt + altlen,
+ request + altlen - domlen,
+ req->request_len - altlen + domlen);
if (server->protocol == IPPROTO_TCP) {
- int req_len = req->request_len + domlen - 2;
-
- alt[0] = (req_len >> 8) & 0xff;
- alt[1] = req_len & 0xff;
+ uint16_t req_len = req->request_len + domlen - DNS_HEADER_TCP_EXTRA_BYTES;
+ uint16_t *len_hdr = (void*)alt;
+ *len_hdr = htons(req_len);
}
debug("req %p dstid 0x%04x altid 0x%04x", req, req->dstid,
@@ -1730,17 +1755,17 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
return 0;
}
-static char *convert_label(char *start, char *end, char *ptr, char *uptr,
+static bool convert_label(const char *start, const char *end, const char *ptr, char *uptr,
int remaining_len, int *used_comp, int *used_uncomp)
{
- int pos, comp_pos;
+ int comp_pos;
char name[NS_MAXLABEL];
- pos = dn_expand((u_char *)start, (u_char *)end, (u_char *)ptr,
+ const int pos = dn_expand((const u_char *)start, (const u_char *)end, (const u_char *)ptr,
name, NS_MAXLABEL);
if (pos < 0) {
debug("uncompress error [%d/%s]", errno, strerror(errno));
- goto out;
+ return false;
}
/*
@@ -1750,23 +1775,21 @@ static char *convert_label(char *start, char *end, char *ptr, char *uptr,
comp_pos = dn_comp(name, (u_char *)uptr, remaining_len, NULL, NULL);
if (comp_pos < 0) {
debug("compress error [%d/%s]", errno, strerror(errno));
- goto out;
+ return false;
}
*used_comp = pos;
*used_uncomp = comp_pos;
- return ptr;
-
-out:
- return NULL;
+ return true;
}
-static char *uncompress(int16_t field_count, char *start, char *end,
- char *ptr, char *uncompressed, int uncomp_len,
+static const char* uncompress(int16_t field_count, const char *start, const char *end,
+ const char *ptr, char *uncompressed, int uncomp_len,
char **uncompressed_ptr)
{
char *uptr = *uncompressed_ptr; /* position in result buffer */
+ char * const uncomp_end = uncompressed + uncomp_len - 1;
debug("count %d ptr %p end %p uptr %p", field_count, ptr, end, uptr);
@@ -1780,21 +1803,22 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
&pos, &comp_pos))
- goto out;
+ return NULL;
/*
* Copy the uncompressed resource record, type, class and \0 to
* tmp buffer.
*/
- ulen = strlen(name);
- strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
+ ulen = strlen(name) + 1;
+ if ((uptr + ulen) > uncomp_end)
+ return NULL;
+ memcpy(uptr, name, ulen);
debug("pos %d ulen %d left %d name %s", pos, ulen,
- (int)(uncomp_len - (uptr - uncompressed)), uptr);
+ (int)(uncomp_end - (uptr + ulen)), uptr);
uptr += ulen;
- *uptr++ = '\0';
ptr += pos;
@@ -1802,13 +1826,17 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* We copy also the fixed portion of the result (type, class,
* ttl, address length and the address)
*/
+ if ((uptr + NS_RRFIXEDSZ) > uncomp_end) {
+ debug("uncompressed data too large for buffer");
+ return NULL;
+ }
memcpy(uptr, ptr, NS_RRFIXEDSZ);
dns_type = uptr[0] << 8 | uptr[1];
dns_class = uptr[2] << 8 | uptr[3];
- if (dns_class != ns_c_in)
- goto out;
+ if (dns_class != DNS_CLASS_IN)
+ return NULL;
ptr += NS_RRFIXEDSZ;
uptr += NS_RRFIXEDSZ;
@@ -1818,11 +1846,11 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* Typically this portion is also compressed
* so we need to uncompress it also when necessary.
*/
- if (dns_type == ns_t_cname) {
+ if (dns_type == DNS_TYPE_CNAME) {
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
uptr[-2] = comp_pos << 8;
uptr[-1] = comp_pos & 0xff;
@@ -1830,19 +1858,19 @@ static char *uncompress(int16_t field_count, char *start, char *end,
uptr += comp_pos;
ptr += pos;
- } else if (dns_type == ns_t_a || dns_type == ns_t_aaaa) {
+ } else if (dns_type == DNS_TYPE_A || dns_type == DNS_TYPE_AAAA) {
dlen = uptr[-2] << 8 | uptr[-1];
- if (ptr + dlen > end) {
+ if ((ptr + dlen) > end || (uptr + dlen) > uncomp_end) {
debug("data len %d too long", dlen);
- goto out;
+ return NULL;
}
memcpy(uptr, ptr, dlen);
uptr += dlen;
ptr += dlen;
- } else if (dns_type == ns_t_soa) {
+ } else if (dns_type == DNS_TYPE_SOA) {
int total_len = 0;
char *len_ptr;
@@ -1850,7 +1878,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
total_len += comp_pos;
len_ptr = &uptr[-2];
@@ -1861,7 +1889,7 @@ static char *uncompress(int16_t field_count, char *start, char *end,
if (!convert_label(start, end, ptr, uptr,
uncomp_len - (uptr - uncompressed),
&pos, &comp_pos))
- goto out;
+ return NULL;
total_len += comp_pos;
ptr += pos;
@@ -1872,6 +1900,10 @@ static char *uncompress(int16_t field_count, char *start, char *end,
* refresh interval, retry interval, expiration
* limit and minimum ttl). They are 20 bytes long.
*/
+ if ((uptr + 20) > uncomp_end || (ptr + 20) > end) {
+ debug("soa record too long");
+ return NULL;
+ }
memcpy(uptr, ptr, 20);
uptr += 20;
ptr += 20;
@@ -1888,239 +1920,300 @@ static char *uncompress(int16_t field_count, char *start, char *end,
}
return ptr;
-
-out:
- return NULL;
}
-static int strip_domains(char *name, char *answers, int maxlen)
+/*
+ * removes the qualified domain name part from the given answer sections
+ * starting at 'answers', consisting of 'length' bytes.
+ *
+ * 'name' points the start of the unqualified host label including the leading
+ * length octet.
+ *
+ * returns the new (possibly shorter) length of remaining payload in the
+ * answers buffer, or a negative (errno) value to indicate error conditions.
+ */
+static int strip_domains(const char *name, char *answers, size_t length)
{
uint16_t data_len;
- int name_len = strlen(name);
- char *ptr, *start = answers, *end = answers + maxlen;
+ struct domain_rr *rr;
+ /* length of the name label including the length header octet */
+ const size_t name_len = strlen(name);
+ const char *end = answers + length;
- while (maxlen > 0) {
- ptr = strstr(answers, name);
+ while (answers < end) {
+ char *ptr = strstr(answers, name);
if (ptr) {
char *domain = ptr + name_len;
+ /* this now points to the domain part length octet. */
if (*domain) {
- int domain_len = strlen(domain);
+ /*
+ * length of the rest of the labels up to the
+ * null label (zero byte).
+ */
+ const size_t domain_len = strlen(domain);
+ char *remaining = domain + domain_len;
- memmove(answers + name_len,
- domain + domain_len,
- end - (domain + domain_len));
+ /*
+ * now shift the rest of the answer sections
+ * to the left to get rid of the domain label
+ * part
+ */
+ memmove(ptr + name_len,
+ remaining,
+ end - remaining);
end -= domain_len;
- maxlen -= domain_len;
+ length -= domain_len;
}
}
- answers += strlen(answers) + 1;
- answers += 2 + 2 + 4; /* skip type, class and ttl fields */
-
- data_len = answers[0] << 8 | answers[1];
- answers += 2; /* skip the length field */
+ /* skip to the next answer section */
- if (answers + data_len > end)
+ /* the labels up to the root null label */
+ answers += strlen(answers) + 1;
+ /* the fixed part of the RR */
+ rr = (void*)answers;
+ if (answers + sizeof(*rr) > end)
return -EINVAL;
-
+ data_len = htons(rr->rdlen);
+ /* skip the rest of the RR */
+ answers += sizeof(*rr);
answers += data_len;
- maxlen -= answers - ptr;
}
- return end - start;
+ if (answers > end)
+ return -EINVAL;
+
+ return length;
}
-static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
- struct server_data *data)
+/*
+ * Removes domain names from replies, if one has been appended during
+ * forwarding to the real DNS server.
+ *
+ * Returns:
+ * < 0 on error (abort processing reply)
+ * == 0 if the reply should be forwarded unmodified
+ * > 0 returns a new reply buffer in *new_reply on success. The return value
+ * indicates the new length of the data in *new_reply.
+ */
+static int dns_reply_fixup_domains(
+ const char *reply, size_t reply_len,
+ const size_t offset,
+ struct request_data *req,
+ char **new_reply)
{
- struct domain_hdr *hdr;
- struct request_data *req;
- int dns_id, sk, err, offset = protocol_offset(protocol);
+ char uncompressed[NS_MAXDNAME];
+ char *uptr, *answers;
+ size_t fixed_len;
+ int new_an_len;
+ const struct domain_hdr *hdr = (void *)(reply + offset);
+ const char *eom = reply + reply_len;
+ const uint16_t header_len = offset + DNS_HEADER_SIZE;
+ /* full header plus at least one byte for the hostname length */
+ if (reply_len < header_len + 1)
+ return -EINVAL;
- if (offset < 0)
- return offset;
+ const uint16_t section_counts[] = {
+ hdr->ancount,
+ hdr->nscount,
+ hdr->arcount
+ };
- hdr = (void *)(reply + offset);
- dns_id = reply[offset] | reply[offset + 1] << 8;
+ /*
+ * length octet of the hostname.
+ * ->hostname.domain.net
+ */
+ const char *ptr = reply + header_len;
+ const uint8_t host_len = *ptr;
+ const char *domain = ptr + host_len + 1;
+ if (domain >= eom)
+ return -EINVAL;
- debug("Received %d bytes (id 0x%04x)", reply_len, dns_id);
+ const uint16_t domain_len = host_len ? strnlen(domain, eom - domain) : 0;
- req = find_request(dns_id);
- if (!req)
+ /*
+ * If the query type is anything other than A or AAAA, then bail out
+ * and pass the message as is. We only want to deal with IPv4 or IPv6
+ * addresses.
+ */
+ const struct qtype_qclass *qtc = (void*)(domain + domain_len + 1);
+ if (((const char*)(qtc + 1)) > eom)
return -EINVAL;
- debug("req %p dstid 0x%04x altid 0x%04x rcode %d",
- req, req->dstid, req->altid, hdr->rcode);
+ const uint16_t dns_type = ntohs(qtc->qtype);
+ const uint16_t dns_class = ntohs(qtc->qclass);
- reply[offset] = req->srcid & 0xff;
- reply[offset + 1] = req->srcid >> 8;
+ if (domain_len == 0) {
+ /* nothing to do */
+ return 0;
+ }
- req->numresp++;
+ /* TODO: This condition looks wrong. It should probably be
+ *
+ * (dns_type != A && dns_type != AAAA) || dns_class != IN
+ *
+ * doing so, however, changes the behaviour of dnsproxy, e.g. MX
+ * records will be passed back to the client, but without the
+ * adjustment of the appended domain name.
+ */
+ if (dns_type != DNS_TYPE_A && dns_type != DNS_TYPE_AAAA &&
+ dns_class != DNS_CLASS_IN) {
+ debug("Pass msg dns type %d class %d", dns_type, dns_class);
+ return 0;
+ }
- if (hdr->rcode == ns_r_noerror || !req->resp) {
- unsigned char *new_reply = NULL;
+ /*
+ * Remove the domain name and replace it by the end of reply. Check if
+ * the domain is really there before trying to copy the data. We also
+ * need to uncompress the answers if necessary. The domain_len can be
+ * 0 because if the original query did not contain a domain name, then
+ * we are sending two packets, first without the domain name and the
+ * second packet with domain name. The append_domain is set to true
+ * even if we sent the first packet without domain name. In this case
+ * we end up in this branch.
+ */
- /*
- * If the domain name was append
- * remove it before forwarding the reply.
- * If there were more than one question, then this
- * domain name ripping can be hairy so avoid that
- * and bail out in that that case.
- *
- * The reason we are doing this magic is that if the
- * user's DNS client tries to resolv hostname without
- * domain part, it also expects to get the result without
- * a domain name part.
- */
- if (req->append_domain && ntohs(hdr->qdcount) == 1) {
- uint16_t domain_len = 0;
- uint16_t header_len;
- uint16_t dns_type, dns_class;
- uint8_t host_len, dns_type_pos;
- char uncompressed[NS_MAXDNAME], *uptr;
- char *ptr, *eom = (char *)reply + reply_len;
+ /* NOTE: length checks up and including to qtype_qclass have already
+ been done above */
- /*
- * ptr points to the first char of the hostname.
- * ->hostname.domain.net
- */
- header_len = offset + sizeof(struct domain_hdr);
- ptr = (char *)reply + header_len;
+ /*
+ * First copy host (without domain name) into tmp buffer.
+ */
+ uptr = &uncompressed[0];
+ memcpy(uptr, ptr, host_len + 1);
- host_len = *ptr;
- if (host_len > 0)
- domain_len = strnlen(ptr + 1 + host_len,
- reply_len - header_len);
+ uptr[host_len + 1] = '\0'; /* host termination */
+ uptr += host_len + 2;
- /*
- * If the query type is anything other than A or AAAA,
- * then bail out and pass the message as is.
- * We only want to deal with IPv4 or IPv6 addresses.
- */
- dns_type_pos = host_len + 1 + domain_len + 1;
-
- dns_type = ptr[dns_type_pos] << 8 |
- ptr[dns_type_pos + 1];
- dns_class = ptr[dns_type_pos + 2] << 8 |
- ptr[dns_type_pos + 3];
- if (dns_type != ns_t_a && dns_type != ns_t_aaaa &&
- dns_class != ns_c_in) {
- debug("Pass msg dns type %d class %d",
- dns_type, dns_class);
- goto pass;
- }
+ /*
+ * Copy type and class fields of the question.
+ */
+ memcpy(uptr, qtc, sizeof(*qtc));
- /*
- * Remove the domain name and replace it by the end
- * of reply. Check if the domain is really there
- * before trying to copy the data. We also need to
- * uncompress the answers if necessary.
- * The domain_len can be 0 because if the original
- * query did not contain a domain name, then we are
- * sending two packets, first without the domain name
- * and the second packet with domain name.
- * The append_domain is set to true even if we sent
- * the first packet without domain name. In this
- * case we end up in this branch.
- */
- if (domain_len > 0) {
- int len = host_len + 1;
- int new_len, fixed_len;
- char *answers;
+ /*
+ * ptr points to answers after this
+ */
+ ptr = (void*)(qtc + 1);
+ uptr += sizeof(*qtc);
+ answers = uptr;
+ fixed_len = answers - uncompressed;
- /*
- * First copy host (without domain name) into
- * tmp buffer.
- */
- uptr = &uncompressed[0];
- memcpy(uptr, ptr, len);
+ /*
+ * We then uncompress the result to buffer so that we can rip off the
+ * domain name part from the question. First answers, then name server
+ * (authority) information, and finally additional record info.
+ */
- uptr[len] = '\0'; /* host termination */
- uptr += len + 1;
+ for (size_t i = 0; i < NUM_ARRAY_ELEMENTS(section_counts); i++) {
+ ptr = uncompress(ntohs(section_counts[i]), reply + offset, eom,
+ ptr, uncompressed, NS_MAXDNAME, &uptr);
+ if (!ptr) {
+ /* failed to uncompress, pass on as is
+ * (TODO: good idea?) */
+ return 0;
+ }
+ }
- /*
- * Copy type and class fields of the question.
- */
- ptr += len + domain_len + 1;
- memcpy(uptr, ptr, NS_QFIXEDSZ);
+ /*
+ * The uncompressed buffer now contains an almost valid response.
+ * Final step is to get rid of the domain name because at least glibc
+ * gethostbyname() implementation does extra checks and expects to
+ * find an answer without domain name if we asked a query without
+ * domain part. Note that glibc getaddrinfo() works differently and
+ * accepts FQDN in answer
+ */
+ new_an_len = strip_domains(uncompressed, answers, uptr - answers);
+ if (new_an_len < 0) {
+ debug("Corrupted packet");
+ return -EINVAL;
+ }
- /*
- * ptr points to answers after this
- */
- ptr += NS_QFIXEDSZ;
- uptr += NS_QFIXEDSZ;
- answers = uptr;
- fixed_len = answers - uncompressed;
+ /*
+ * Because we have now uncompressed the answers we might have to
+ * create a bigger buffer to hold all that data.
+ *
+ * TODO: only create a bigger buffer if actually necessary, pass
+ * allocation size of input buffer via additional parameter.
+ */
- /*
- * We then uncompress the result to buffer
- * so that we can rip off the domain name
- * part from the question. First answers,
- * then name server (authority) information,
- * and finally additional record info.
- */
+ reply_len = header_len + new_an_len + fixed_len;
- ptr = uncompress(ntohs(hdr->ancount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
-
- ptr = uncompress(ntohs(hdr->nscount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
-
- ptr = uncompress(ntohs(hdr->arcount),
- (char *)reply + offset, eom,
- ptr, uncompressed, NS_MAXDNAME,
- &uptr);
- if (!ptr)
- goto out;
+ *new_reply = g_try_malloc(reply_len);
+ if (!*new_reply)
+ return -ENOMEM;
- /*
- * The uncompressed buffer now contains almost
- * valid response. Final step is to get rid of
- * the domain name because at least glibc
- * gethostbyname() implementation does extra
- * checks and expects to find an answer without
- * domain name if we asked a query without
- * domain part. Note that glibc getaddrinfo()
- * works differently and accepts FQDN in answer
- */
- new_len = strip_domains(uncompressed, answers,
- uptr - answers);
- if (new_len < 0) {
- debug("Corrupted packet");
- return -EINVAL;
- }
+ memcpy(*new_reply, reply, header_len);
+ memcpy(*new_reply + header_len, uncompressed, new_an_len + fixed_len);
- /*
- * Because we have now uncompressed the answers
- * we might have to create a bigger buffer to
- * hold all that data.
- */
+ return reply_len;
+}
+
+static struct request_data* lookup_request(
+ const unsigned char *reply, size_t len, int protocol)
+{
+ const size_t offset = protocol_offset(protocol);
+ struct request_data *req;
+ struct domain_hdr *hdr = (void *)(reply + offset);
+
+ debug("Received %zd bytes (id 0x%04x)", len, hdr->id);
- reply_len = header_len + new_len + fixed_len;
+ if (len < DNS_HEADER_SIZE + offset)
+ return NULL;
+
+ req = find_request(hdr->id);
+
+ if (!req)
+ return NULL;
+
+ debug("req %p dstid 0x%04x altid 0x%04x rcode %d",
+ req, req->dstid, req->altid, hdr->rcode);
+
+ req->numresp++;
+
+ return req;
+}
+
+static int forward_dns_reply(char *reply, size_t reply_len, int protocol,
+ struct server_data *data, struct request_data *req)
+{
+ const size_t offset = protocol_offset(protocol);
+ struct domain_hdr *hdr = (void *)(reply + offset);
+ int err, sk;
- new_reply = g_try_malloc(reply_len);
- if (!new_reply)
- return -ENOMEM;
+ /* replace with original request ID from our client */
+ hdr->id = req->srcid;
- memcpy(new_reply, reply, header_len);
- memcpy(new_reply + header_len, uncompressed,
- new_len + fixed_len);
+ if (hdr->rcode == ns_r_noerror || !req->resp) {
+ /*
+ * If the domain name was appended remove it before forwarding
+ * the reply. If there were more than one question, then this
+ * domain name ripping can be hairy so avoid that and bail out
+ * in that that case.
+ *
+ * The reason we are doing this magic is that if the user's
+ * DNS client tries to resolv hostname without domain part, it
+ * also expects to get the result without a domain name part.
+ */
+ char *new_reply = NULL;
+ if (req->append_domain && ntohs(hdr->qdcount) == 1) {
+ const int fixup_res = dns_reply_fixup_domains(
+ reply, reply_len,
+ offset, req, &new_reply);
+ if (fixup_res < 0) {
+ /* error occured */
+ return fixup_res;
+ } else if (fixup_res > 0 && new_reply) {
+ /* new reply length */
+ reply_len = fixup_res;
reply = new_reply;
+ } else {
+ /* keep message as is */
}
}
- pass:
g_free(req->resp);
req->resplen = 0;
@@ -2131,12 +2224,11 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
memcpy(req->resp, reply, reply_len);
req->resplen = reply_len;
- cache_update(data, reply, reply_len);
+ cache_update(data, (unsigned char*)reply, reply_len);
g_free(new_reply);
}
-out:
if (req->numresp < req->numserv) {
if (hdr->rcode > ns_r_noerror) {
return -EINVAL;
@@ -2156,6 +2248,9 @@ out:
err = sendto(sk, req->resp, req->resplen, 0,
&req->sa, req->sa_len);
} else {
+ const uint16_t tcp_len = htons(req->resplen - DNS_HEADER_TCP_EXTRA_BYTES);
+ /* correct TCP message length */
+ memcpy(req->resp, &tcp_len, sizeof(tcp_len));
sk = req->client_sk;
err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
}
@@ -2166,8 +2261,6 @@ out:
else
debug("proto %d sent %d bytes to %d", protocol, err, sk);
- destroy_request_data(req);
-
return err;
}
@@ -2231,8 +2324,10 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
unsigned char buf[4096];
- int sk, len;
+ int sk, res;
+ ssize_t len;
struct server_data *data = user_data;
+ struct request_data *req;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with UDP server %s", data->server);
@@ -2241,11 +2336,22 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
}
sk = g_io_channel_unix_get_fd(channel);
-
len = recv(sk, buf, sizeof(buf), 0);
- if (len >= 12)
- forward_dns_reply(buf, len, IPPROTO_UDP, data);
+ if (len <= 0)
+ return TRUE;
+
+ req = lookup_request(buf, len, IPPROTO_UDP);
+
+ if (!req)
+ /* invalid / corrupt request */
+ return TRUE;
+
+ res = forward_dns_reply((char*)buf, len, IPPROTO_UDP, data, req);
+
+ /* on success or no further responses are expected, destroy the req */
+ if (res == 0 || req->numresp >= req->numserv)
+ destroy_request_data(req);
return TRUE;
}
@@ -2253,10 +2359,9 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
- int sk;
+ struct request_data *req;
struct server_data *server = user_data;
-
- sk = g_io_channel_unix_get_fd(channel);
+ int sk = g_io_channel_unix_get_fd(channel);
if (sk == 0)
return FALSE;
@@ -2274,14 +2379,13 @@ hangup:
list = request_list;
while (list) {
- struct request_data *req = list->data;
struct domain_hdr *hdr;
+ req = list->data;
list = list->next;
if (req->protocol == IPPROTO_UDP)
continue;
-
- if (!req->request)
+ else if (!req->request)
continue;
/*
@@ -2292,7 +2396,7 @@ hangup:
if (req->numserv && --(req->numserv))
continue;
- hdr = (void *) (req->request + 2);
+ hdr = (void *)(req->request + DNS_HEADER_TCP_EXTRA_BYTES);
hdr->id = req->srcid;
send_response(req->client_sk, req->request,
req->request_len, NULL, 0, IPPROTO_TCP);
@@ -2306,17 +2410,14 @@ hangup:
}
if ((condition & G_IO_OUT) && !server->connected) {
- GSList *list;
- GList *domains;
bool no_request_sent = true;
- struct server_data *udp_server;
-
- udp_server = find_server(server->index, server->server,
- IPPROTO_UDP);
+ struct server_data *udp_server = find_server(
+ server->index, server->server,
+ IPPROTO_UDP);
if (udp_server) {
- for (domains = udp_server->domains; domains;
+ for (GList *domains = udp_server->domains; domains;
domains = domains->next) {
- char *dom = domains->data;
+ const char *dom = domains->data;
debug("Adding domain %s to %s",
dom, server->server);
@@ -2326,17 +2427,26 @@ hangup:
}
}
+ /*
+ * Remove the G_IO_OUT flag from the watch, otherwise we end
+ * up in a busy loop, because the socket is constantly writable.
+ *
+ * There seems to be no better way in g_io to do that than
+ * re-adding the watch.
+ */
+ g_source_remove(server->watch);
+ server->watch = g_io_add_watch(server->channel,
+ G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+ tcp_server_event, server);
+
server->connected = true;
server_list = g_slist_append(server_list, server);
- if (server->timeout > 0) {
- g_source_remove(server->timeout);
- server->timeout = 0;
- }
-
- for (list = request_list; list; ) {
- struct request_data *req = list->data;
+ /* don't advance the list in the for loop, because we might
+ * need to delete elements while iterating through it */
+ for (GSList *list = request_list; list; ) {
int status;
+ req = list->data;
if (req->protocol == IPPROTO_UDP) {
list = list->next;
@@ -2356,9 +2466,7 @@ hangup:
request_list = g_slist_remove(request_list, req);
destroy_request_data(req);
continue;
- }
-
- if (status < 0) {
+ } else if (status < 0) {
list = list->next;
continue;
}
@@ -2381,12 +2489,12 @@ hangup:
} else if (condition & G_IO_IN) {
struct partial_reply *reply = server->incoming_reply;
int bytes_recv;
+ int res;
if (!reply) {
- unsigned char reply_len_buf[2];
uint16_t reply_len;
- bytes_recv = recv(sk, reply_len_buf, 2, MSG_PEEK);
+ bytes_recv = recv(sk, &reply_len, sizeof(reply_len), MSG_PEEK);
if (!bytes_recv) {
goto hangup;
} else if (bytes_recv < 0) {
@@ -2396,11 +2504,12 @@ hangup:
connman_error("DNS proxy error %s",
strerror(errno));
goto hangup;
- } else if (bytes_recv < 2)
+ } else if (bytes_recv < sizeof(reply_len))
return TRUE;
- reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
- reply_len += 2;
+ /* the header contains the length of the message
+ * excluding the two length bytes */
+ reply_len = ntohs(reply_len) + DNS_HEADER_TCP_EXTRA_BYTES;
debug("TCP reply %d bytes from %d", reply_len, sk);
@@ -2409,6 +2518,8 @@ hangup:
return TRUE;
reply->len = reply_len;
+ /* we only peeked the two length bytes, so we have to
+ receive the complete message below proper. */
reply->received = 0;
server->incoming_reply = reply;
@@ -2431,15 +2542,30 @@ hangup:
reply->received += bytes_recv;
}
- forward_dns_reply(reply->buf, reply->received, IPPROTO_TCP,
- server);
+ req = lookup_request(reply->buf, reply->received, IPPROTO_TCP);
+
+ if (!req)
+ /* invalid / corrupt request */
+ return TRUE;
+
+ res = forward_dns_reply((char*)reply->buf, reply->received, IPPROTO_TCP, server, req);
g_free(reply);
server->incoming_reply = NULL;
- destroy_server(server);
+ /* on success or if no further responses are expected close
+ * connection */
+ if (res == 0 || req->numresp >= req->numserv) {
+ destroy_request_data(req);
+ destroy_server(server);
+ return FALSE;
+ }
- return FALSE;
+ /*
+ * keep the TCP connection open, there are more
+ * requests to be answered
+ */
+ return TRUE;
}
return TRUE;
@@ -2449,7 +2575,7 @@ static gboolean tcp_idle_timeout(gpointer user_data)
{
struct server_data *server = user_data;
- debug("");
+ debug("\n");
if (!server)
return FALSE;
@@ -2461,15 +2587,15 @@ static gboolean tcp_idle_timeout(gpointer user_data)
static int server_create_socket(struct server_data *data)
{
- int sk, err;
+ int err;
char *interface;
+ int sk = socket(data->server_addr->sa_family,
+ data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
+ data->protocol);
debug("index %d server %s proto %d", data->index,
data->server, data->protocol);
- sk = socket(data->server_addr->sa_family,
- data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
- data->protocol);
if (sk < 0) {
err = errno;
connman_error("Failed to create server %s socket",
@@ -2540,9 +2666,7 @@ static int server_create_socket(struct server_data *data)
static void enable_fallback(bool enable)
{
- GSList *list;
-
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->index != -1)
@@ -2557,17 +2681,30 @@ static void enable_fallback(bool enable)
}
}
+static unsigned int get_enabled_server_number(void)
+{
+ GSList *list;
+ unsigned int result = 0;
+
+ for (list = server_list; list; list = list->next) {
+ struct server_data *data = list->data;
+
+ if (data->index != -1 && data->enabled == true)
+ result++;
+ }
+ return result;
+}
+
static struct server_data *create_server(int index,
const char *domain, const char *server,
int protocol)
{
- struct server_data *data;
+ struct server_data *data = g_try_new0(struct server_data, 1);
struct addrinfo hints, *rp;
int ret;
DBG("index %d server %s", index, server);
- data = g_try_new0(struct server_data, 1);
if (!data) {
connman_error("Failed to allocate server %s data", server);
return NULL;
@@ -2580,20 +2717,7 @@ static struct server_data *create_server(int index,
data->protocol = protocol;
memset(&hints, 0, sizeof(hints));
-
- switch (protocol) {
- case IPPROTO_UDP:
- hints.ai_socktype = SOCK_DGRAM;
- break;
-
- case IPPROTO_TCP:
- hints.ai_socktype = SOCK_STREAM;
- break;
-
- default:
- destroy_server(data);
- return NULL;
- }
+ hints.ai_socktype = socket_type(protocol, 0);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
@@ -2645,6 +2769,9 @@ static struct server_data *create_server(int index,
DBG("Adding DNS server %s", data->server);
enable_fallback(false);
+ } else if (data->index == -1 && get_enabled_server_number() == 0) {
+ data->enabled = true;
+ DBG("Adding fallback DNS server %s", data->server);
}
server_list = g_slist_append(server_list, data);
@@ -2656,9 +2783,7 @@ static struct server_data *create_server(int index,
static bool resolv(struct request_data *req,
gpointer request, gpointer name)
{
- GSList *list;
-
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->protocol == IPPROTO_TCP) {
@@ -2687,26 +2812,22 @@ static bool resolv(struct request_data *req,
static void update_domain(int index, const char *domain, bool append)
{
- GSList *list;
-
DBG("index %d domain %s", index, domain);
if (!domain)
return;
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
- GList *dom_list;
- char *dom;
+ char *dom = NULL;
bool dom_found = false;
if (data->index < 0)
continue;
-
- if (data->index != index)
+ else if (data->index != index)
continue;
- for (dom_list = data->domains; dom_list;
+ for (GList *dom_list = data->domains; dom_list;
dom_list = dom_list->next) {
dom = dom_list->data;
@@ -2739,9 +2860,7 @@ static void remove_domain(int index, const char *domain)
static void flush_requests(struct server_data *server)
{
- GSList *list;
-
- list = request_list;
+ GSList *list = request_list;
while (list) {
struct request_data *req = list->data;
@@ -2769,22 +2888,20 @@ int __connman_dnsproxy_append(int index, const char *domain,
const char *server)
{
struct server_data *data;
-
DBG("index %d server %s", index, server);
- if (!server && !domain)
- return -EINVAL;
-
if (!server) {
- append_domain(index, domain);
-
- return 0;
+ if (!domain) {
+ return -EINVAL;
+ } else {
+ append_domain(index, domain);
+ return 0;
+ }
}
if (g_str_equal(server, "127.0.0.1"))
return -ENODEV;
-
- if (g_str_equal(server, "::1"))
+ else if (g_str_equal(server, "::1"))
return -ENODEV;
data = find_server(index, server, IPPROTO_UDP);
@@ -2802,11 +2919,9 @@ int __connman_dnsproxy_append(int index, const char *domain,
return 0;
}
-static void remove_server(int index, const char *domain,
- const char *server, int protocol)
+static void remove_server(int index, const char *server, int protocol)
{
struct server_data *data;
- GSList *list;
data = find_server(index, server, protocol);
if (!data)
@@ -2814,14 +2929,8 @@ static void remove_server(int index, const char *domain,
destroy_server(data);
- for (list = server_list; list; list = list->next) {
- struct server_data *data = list->data;
-
- if (data->index != -1 && data->enabled == true)
- return;
- }
-
- enable_fallback(true);
+ if (get_enabled_server_number() == 0)
+ enable_fallback(true);
}
int __connman_dnsproxy_remove(int index, const char *domain,
@@ -2829,34 +2938,31 @@ int __connman_dnsproxy_remove(int index, const char *domain,
{
DBG("index %d server %s", index, server);
- if (!server && !domain)
- return -EINVAL;
-
if (!server) {
- remove_domain(index, domain);
-
- return 0;
+ if (!domain) {
+ return -EINVAL;
+ } else {
+ remove_domain(index, domain);
+ return 0;
+ }
}
if (g_str_equal(server, "127.0.0.1"))
return -ENODEV;
-
- if (g_str_equal(server, "::1"))
+ else if (g_str_equal(server, "::1"))
return -ENODEV;
- remove_server(index, domain, server, IPPROTO_UDP);
- remove_server(index, domain, server, IPPROTO_TCP);
+ remove_server(index, server, IPPROTO_UDP);
+ remove_server(index, server, IPPROTO_TCP);
return 0;
}
static void dnsproxy_offline_mode(bool enabled)
{
- GSList *list;
-
DBG("enabled %d", enabled);
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (!enabled) {
@@ -2874,9 +2980,8 @@ static void dnsproxy_offline_mode(bool enabled)
static void dnsproxy_default_changed(struct connman_service *service)
{
- bool server_enabled = false;
- GSList *list;
- int index;
+ bool any_server_enabled = false;
+ int index, vpn_index;
DBG("service %p", service);
@@ -2893,20 +2998,30 @@ static void dnsproxy_default_changed(struct connman_service *service)
if (index < 0)
return;
- for (list = server_list; list; list = list->next) {
+ /*
+ * In case non-split-routed VPN is set as split routed the DNS servers
+ * the VPN must be enabled as well, when the transport becomes the
+ * default service.
+ */
+ vpn_index = __connman_connection_get_vpn_index(index);
+
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->index == index) {
DBG("Enabling DNS server %s", data->server);
data->enabled = true;
- server_enabled = true;
+ any_server_enabled = true;
+ } else if (data->index == vpn_index) {
+ DBG("Enabling DNS server of VPN %s", data->server);
+ data->enabled = true;
} else {
DBG("Disabling DNS server %s", data->server);
data->enabled = false;
}
}
- if (!server_enabled)
+ if (!any_server_enabled)
enable_fallback(true);
cache_refresh();
@@ -2954,47 +3069,59 @@ static const struct connman_notifier dnsproxy_notifier = {
.service_state_changed = dnsproxy_service_state_changed,
};
-static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
-
+/*
+ * Parses the given request buffer. `buf´ is expected to be the start of the
+ * domain_hdr structure i.e. the TCP length header is not handled by this
+ * function.
+ * Returns the ascii string dot representation of the query in `name´, which
+ * must be able to hold `size´ bytes.
+ *
+ * Returns < 0 on error (errno) or zero on success.
+ */
static int parse_request(unsigned char *buf, size_t len,
- char *name, unsigned int size)
+ char *name, size_t size)
{
+ static const unsigned char OPT_EDNS0_TYPE[2] = { 0x00, 0x29 };
struct domain_hdr *hdr = (void *) buf;
- uint16_t qdcount = ntohs(hdr->qdcount);
- uint16_t ancount = ntohs(hdr->ancount);
- uint16_t nscount = ntohs(hdr->nscount);
- uint16_t arcount = ntohs(hdr->arcount);
- unsigned char *ptr;
- unsigned int remain, used = 0;
-
- if (len < sizeof(*hdr) + sizeof(struct qtype_qclass) ||
- hdr->qr || qdcount != 1 || ancount || nscount) {
- DBG("Dropped DNS request qr %d with len %zd qdcount %d "
- "ancount %d nscount %d", hdr->qr, len, qdcount, ancount,
- nscount);
+ uint16_t qdcount, ancount, nscount, arcount;
+ unsigned char *ptr = buf + DNS_HEADER_SIZE;
+ size_t remain = len - DNS_HEADER_SIZE;
+ size_t used = 0;
+ if (len < DNS_HEADER_SIZE + DNS_QTYPE_QCLASS_SIZE) {
+ DBG("Dropped DNS request with short length %zd", len);
return -EINVAL;
}
if (!name || !size)
return -EINVAL;
+ qdcount = ntohs(hdr->qdcount);
+ ancount = ntohs(hdr->ancount);
+ nscount = ntohs(hdr->nscount);
+ arcount = ntohs(hdr->arcount);
+
+ if (hdr->qr || qdcount != 1 || ancount || nscount) {
+ DBG("Dropped DNS request with bad flags/counts qr %d "
+ "with len %zd qdcount %d ancount %d nscount %d",
+ hdr->qr, len, qdcount, ancount, nscount);
+
+ return -EINVAL;
+ }
+
debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
hdr->id, hdr->qr, hdr->opcode,
qdcount, arcount);
name[0] = '\0';
- ptr = buf + sizeof(struct domain_hdr);
- remain = len - sizeof(struct domain_hdr);
-
+ /* parse DNS query string into `name' out parameter */
while (remain > 0) {
uint8_t label_len = *ptr;
if (label_len == 0x00) {
- uint8_t class;
- struct qtype_qclass *q =
- (struct qtype_qclass *)(ptr + 1);
+ struct qtype_qclass *q = (struct qtype_qclass *)(ptr + 1);
+ uint16_t class;
if (remain < sizeof(*q)) {
DBG("Dropped malformed DNS query");
@@ -3002,7 +3129,7 @@ static int parse_request(unsigned char *buf, size_t len,
}
class = ntohs(q->qclass);
- if (class != 1 && class != 255) {
+ if (class != DNS_CLASS_IN && class != DNS_CLASS_ANY) {
DBG("Dropped non-IN DNS class %d", class);
return -EINVAL;
}
@@ -3019,18 +3146,17 @@ static int parse_request(unsigned char *buf, size_t len,
strcat(name, ".");
used += label_len + 1;
-
ptr += label_len + 1;
remain -= label_len + 1;
}
- if (arcount && remain >= sizeof(struct domain_rr) + 1 && !ptr[0] &&
- ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) {
+ if (arcount && remain >= DNS_RR_SIZE + 1 && !ptr[0] &&
+ ptr[1] == OPT_EDNS0_TYPE[0] && ptr[2] == OPT_EDNS0_TYPE[1]) {
struct domain_rr *edns0 = (struct domain_rr *)(ptr + 1);
DBG("EDNS0 buffer size %u", ntohs(edns0->class));
} else if (!arcount && remain) {
- DBG("DNS request with %d garbage bytes", remain);
+ DBG("DNS request with %zd garbage bytes", remain);
}
debug("query %s", name);
@@ -3067,7 +3193,7 @@ static void client_reset(struct tcp_partial_client_data *client)
client->buf_end = 0;
}
-static unsigned int get_msg_len(unsigned char *buf)
+static size_t get_msg_len(const unsigned char *buf)
{
return buf[0]<<8 | buf[1];
}
@@ -3078,15 +3204,14 @@ static bool read_tcp_data(struct tcp_partial_client_data *client,
{
char query[TCP_MAX_BUF_LEN];
struct request_data *req;
- int client_sk, err;
- unsigned int msg_len;
- GSList *list;
+ struct domain_hdr *hdr;
+ int client_sk = g_io_channel_unix_get_fd(client->channel);
+ int err;
+ size_t msg_len;
bool waiting_for_connect = false;
- int qtype = 0;
+ uint16_t qtype = 0;
struct cache_entry *entry;
- client_sk = g_io_channel_unix_get_fd(client->channel);
-
if (read_len == 0) {
debug("client %d closed, pending %d bytes",
client_sk, client->buf_end);
@@ -3099,34 +3224,36 @@ static bool read_tcp_data(struct tcp_partial_client_data *client,
client->buf_end += read_len;
- if (client->buf_end < 2)
+ /* we need at least the message length header */
+ if (client->buf_end < DNS_HEADER_TCP_EXTRA_BYTES)
return true;
msg_len = get_msg_len(client->buf);
if (msg_len > TCP_MAX_BUF_LEN) {
- debug("client %d sent too much data %d", client_sk, msg_len);
+ debug("client %d sent too much data %zd", client_sk, msg_len);
g_hash_table_remove(partial_tcp_req_table,
GINT_TO_POINTER(client_sk));
return false;
}
read_another:
- debug("client %d msg len %d end %d past end %d", client_sk, msg_len,
+ debug("client %d msg len %zd end %d past end %zd", client_sk, msg_len,
client->buf_end, client->buf_end - (msg_len + 2));
if (client->buf_end < (msg_len + 2)) {
- debug("client %d still missing %d bytes",
+ debug("client %d still missing %zd bytes",
client_sk,
msg_len + 2 - client->buf_end);
return true;
}
- debug("client %d all data %d received", client_sk, msg_len);
+ debug("client %d all data %zd received", client_sk, msg_len);
- err = parse_request(client->buf + 2, msg_len,
- query, sizeof(query));
+ err = parse_request(client->buf + DNS_HEADER_TCP_EXTRA_BYTES,
+ msg_len, query, sizeof(query));
if (err < 0 || (g_slist_length(server_list) == 0)) {
- send_response(client_sk, client->buf, msg_len + 2,
+ send_response(client_sk, client->buf,
+ msg_len + DNS_HEADER_TCP_EXTRA_BYTES,
NULL, 0, IPPROTO_TCP);
return true;
}
@@ -3141,13 +3268,15 @@ read_another:
req->protocol = IPPROTO_TCP;
req->family = client->family;
- req->srcid = client->buf[2] | (client->buf[3] << 8);
+ hdr = (void*)(client->buf + DNS_HEADER_TCP_EXTRA_BYTES);
+
+ memcpy(&req->srcid, &hdr->id, sizeof(req->srcid));
req->dstid = get_id();
req->altid = get_id();
- req->request_len = msg_len + 2;
+ req->request_len = msg_len + DNS_HEADER_TCP_EXTRA_BYTES;
- client->buf[2] = req->dstid & 0xff;
- client->buf[3] = req->dstid >> 8;
+ /* replace ID the request for forwarding */
+ memcpy(&hdr->id, &req->dstid, sizeof(hdr->id));
req->numserv = 0;
req->ifdata = client->ifdata;
@@ -3159,17 +3288,12 @@ read_another:
*/
entry = cache_check(client->buf, &qtype, IPPROTO_TCP);
if (entry) {
- int ttl_left = 0;
- struct cache_data *data;
-
- debug("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
- if (qtype == 1)
- data = entry->ipv4;
- else
- data = entry->ipv6;
+ debug("cache hit %s type %s", query, qtype == DNS_TYPE_A ? "A" : "AAAA");
+ struct cache_data *data = qtype == DNS_TYPE_A ?
+ entry->ipv4 : entry->ipv6;
if (data) {
- ttl_left = data->valid_until - time(NULL);
+ int ttl_left = data->valid_until - time(NULL);
entry->hits++;
send_cached_response(client_sk, data->data,
@@ -3182,7 +3306,7 @@ read_another:
debug("data missing, ignoring cache for this query");
}
- for (list = server_list; list; list = list->next) {
+ for (GSList *list = server_list; list; list = list->next) {
struct server_data *data = list->data;
if (data->protocol != IPPROTO_UDP || !data->enabled)
@@ -3233,8 +3357,8 @@ read_another:
request_list = g_slist_append(request_list, req);
out:
- if (client->buf_end > (msg_len + 2)) {
- debug("client %d buf %p -> %p end %d len %d new %d",
+ if (client->buf_end > (msg_len + DNS_HEADER_TCP_EXTRA_BYTES)) {
+ debug("client %d buf %p -> %p end %d len %d new %zd",
client_sk,
client->buf + msg_len + 2,
client->buf, client->buf_end,
@@ -3250,7 +3374,7 @@ out:
*/
msg_len = get_msg_len(client->buf);
if ((msg_len + 2) == client->buf_end) {
- debug("client %d reading another %d bytes", client_sk,
+ debug("client %d reading another %zd bytes", client_sk,
msg_len + 2);
goto read_another;
}
@@ -3276,15 +3400,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
gpointer user_data)
{
struct tcp_partial_client_data *client = user_data;
- struct sockaddr_in6 client_addr6;
- socklen_t client_addr6_len = sizeof(client_addr6);
- struct sockaddr_in client_addr4;
- socklen_t client_addr4_len = sizeof(client_addr4);
- void *client_addr;
- socklen_t *client_addr_len;
- int len, client_sk;
-
- client_sk = g_io_channel_unix_get_fd(channel);
+ int client_sk = g_io_channel_unix_get_fd(channel);
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
g_hash_table_remove(partial_tcp_req_table,
@@ -3294,6 +3410,13 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
return FALSE;
}
+ struct sockaddr_in6 client_addr6;
+ socklen_t client_addr6_len = sizeof(client_addr6);
+ struct sockaddr_in client_addr4;
+ socklen_t client_addr4_len = sizeof(client_addr4);
+ void *client_addr;
+ socklen_t *client_addr_len;
+
switch (client->family) {
case AF_INET:
client_addr = &client_addr4;
@@ -3310,7 +3433,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
return FALSE;
}
- len = recvfrom(client_sk, client->buf + client->buf_end,
+ const int len = recvfrom(client_sk, client->buf + client->buf_end,
TCP_MAX_BUF_LEN - client->buf_end, 0,
client_addr, client_addr_len);
if (len < 0) {
@@ -3330,9 +3453,7 @@ static gboolean tcp_client_event(GIOChannel *channel, GIOCondition condition,
static gboolean client_timeout(gpointer user_data)
{
struct tcp_partial_client_data *client = user_data;
- int sock;
-
- sock = g_io_channel_unix_get_fd(client->channel);
+ int sock = g_io_channel_unix_get_fd(client->channel);
debug("client %d timeout pending %d bytes", sock, client->buf_end);
@@ -3345,8 +3466,12 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
struct listener_data *ifdata, int family,
guint *listener_watch)
{
- int sk, client_sk, len;
- unsigned int msg_len;
+ int sk = -1, client_sk = -1;
+ int recv_len;
+ size_t msg_len;
+ fd_set readfds;
+ struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
+
struct tcp_partial_client_data *client;
struct sockaddr_in6 client_addr6;
socklen_t client_addr6_len = sizeof(client_addr6);
@@ -3354,8 +3479,6 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
socklen_t client_addr4_len = sizeof(client_addr4);
void *client_addr;
socklen_t *client_addr_len;
- struct timeval tv;
- fd_set readfds;
debug("condition 0x%02x channel %p ifdata %p family %d",
condition, channel, ifdata, family);
@@ -3380,29 +3503,27 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
client_addr_len = &client_addr6_len;
}
- tv.tv_sec = tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(sk, &readfds);
+ /* TODO: check select return code */
select(sk + 1, &readfds, NULL, NULL, &tv);
- if (FD_ISSET(sk, &readfds)) {
- client_sk = accept(sk, client_addr, client_addr_len);
- debug("client %d accepted", client_sk);
- } else {
+ if (!FD_ISSET(sk, &readfds)) {
debug("No data to read from master %d, waiting.", sk);
return true;
}
+ client_sk = accept(sk, client_addr, client_addr_len);
if (client_sk < 0) {
connman_error("Accept failure on TCP listener");
*listener_watch = 0;
return false;
}
+ debug("client %d accepted", client_sk);
fcntl(client_sk, F_SETFL, O_NONBLOCK);
- client = g_hash_table_lookup(partial_tcp_req_table,
- GINT_TO_POINTER(client_sk));
+ client = g_hash_table_lookup(partial_tcp_req_table, GINT_TO_POINTER(client_sk));
if (!client) {
client = g_try_new0(struct tcp_partial_client_data, 1);
if (!client) {
@@ -3446,8 +3567,8 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
* proceed normally, otherwise read the bits until everything
* is received or timeout occurs.
*/
- len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0);
- if (len < 0) {
+ recv_len = recv(client_sk, client->buf, TCP_MAX_BUF_LEN, 0);
+ if (recv_len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
debug("client %d no data to read, waiting", client_sk);
return true;
@@ -3460,15 +3581,15 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
return true;
}
- if (len < 2) {
+ if (recv_len < DNS_HEADER_TCP_EXTRA_BYTES) {
debug("client %d not enough data to read, waiting", client_sk);
- client->buf_end += len;
+ client->buf_end += recv_len;
return true;
}
msg_len = get_msg_len(client->buf);
if (msg_len > TCP_MAX_BUF_LEN) {
- debug("client %d invalid message length %u ignoring packet",
+ debug("client %d invalid message length %zd ignoring packet",
client_sk, msg_len);
g_hash_table_remove(partial_tcp_req_table,
GINT_TO_POINTER(client_sk));
@@ -3479,15 +3600,15 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
* The packet length bytes do not contain the total message length,
* that is the reason to -2 below.
*/
- if (msg_len != (unsigned int)(len - 2)) {
- debug("client %d sent %d bytes but expecting %u pending %d",
- client_sk, len, msg_len + 2, msg_len + 2 - len);
+ if (msg_len != (size_t)(recv_len - DNS_HEADER_TCP_EXTRA_BYTES)) {
+ debug("client %d sent %d bytes but expecting %zd pending %zd",
+ client_sk, recv_len, msg_len + 2, msg_len + 2 - recv_len);
- client->buf_end += len;
+ client->buf_end += recv_len;
return true;
}
- return read_tcp_data(client, client_addr, *client_addr_len, len);
+ return read_tcp_data(client, client_addr, *client_addr_len, recv_len);
}
static gboolean tcp4_listener_event(GIOChannel *channel, GIOCondition condition,
@@ -3514,14 +3635,16 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
{
unsigned char buf[768];
char query[512];
- struct request_data *req;
+ struct request_data *req = NULL;
+ struct domain_hdr *hdr = NULL;
+ int sk = -1, err, len;
+
struct sockaddr_in6 client_addr6;
socklen_t client_addr6_len = sizeof(client_addr6);
struct sockaddr_in client_addr4;
socklen_t client_addr4_len = sizeof(client_addr4);
void *client_addr;
socklen_t *client_addr_len;
- int sk, err, len;
if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
connman_error("Error with UDP listener channel");
@@ -3529,8 +3652,6 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
return false;
}
- sk = g_io_channel_unix_get_fd(channel);
-
if (family == AF_INET) {
client_addr = &client_addr4;
client_addr_len = &client_addr4_len;
@@ -3540,6 +3661,7 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
}
memset(client_addr, 0, *client_addr_len);
+ sk = g_io_channel_unix_get_fd(channel);
len = recvfrom(sk, buf, sizeof(buf), 0, client_addr, client_addr_len);
if (len < 2)
return true;
@@ -3563,13 +3685,14 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
req->protocol = IPPROTO_UDP;
req->family = family;
- req->srcid = buf[0] | (buf[1] << 8);
+ hdr = (void*)buf;
+
+ req->srcid = hdr->id;
req->dstid = get_id();
req->altid = get_id();
req->request_len = len;
- buf[0] = req->dstid & 0xff;
- buf[1] = req->dstid >> 8;
+ hdr->id = req->dstid;
req->numserv = 0;
req->ifdata = ifdata;
@@ -3610,42 +3733,26 @@ static gboolean udp6_listener_event(GIOChannel *channel, GIOCondition condition,
static GIOChannel *get_listener(int family, int protocol, int index)
{
- GIOChannel *channel;
- const char *proto;
+ GIOChannel *channel = NULL;
union {
struct sockaddr sa;
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
} s;
socklen_t slen;
- int sk, type;
+ const char *proto = protocol_label(protocol);
+ const int type = socket_type(protocol, SOCK_CLOEXEC);
char *interface;
+ int sk = socket(family, type, protocol);
debug("family %d protocol %d index %d", family, protocol, index);
- switch (protocol) {
- case IPPROTO_UDP:
- proto = "UDP";
- type = SOCK_DGRAM | SOCK_CLOEXEC;
- break;
-
- case IPPROTO_TCP:
- proto = "TCP";
- type = SOCK_STREAM | SOCK_CLOEXEC;
- break;
-
- default:
- return NULL;
- }
-
- sk = socket(family, type, protocol);
- if (sk < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
- connman_error("No IPv6 support");
- return NULL;
- }
-
if (sk < 0) {
- connman_error("Failed to create %s listener socket", proto);
+ if (family == AF_INET6 && errno == EAFNOSUPPORT) {
+ connman_error("No IPv6 support");
+ } else {
+ connman_error("Failed to create %s listener socket", proto);
+ }
return NULL;
}
@@ -3666,7 +3773,7 @@ static GIOChannel *get_listener(int family, int protocol, int index)
if (family == AF_INET6) {
memset(&s.sin6, 0, sizeof(s.sin6));
s.sin6.sin6_family = AF_INET6;
- s.sin6.sin6_port = htons(53);
+ s.sin6.sin6_port = htons(dns_listen_port);
slen = sizeof(s.sin6);
if (__connman_inet_get_interface_address(index,
@@ -3683,7 +3790,7 @@ static GIOChannel *get_listener(int family, int protocol, int index)
} else if (family == AF_INET) {
memset(&s.sin, 0, sizeof(s.sin));
s.sin.sin_family = AF_INET;
- s.sin.sin_port = htons(53);
+ s.sin.sin_port = htons(dns_listen_port);
slen = sizeof(s.sin);
if (__connman_inet_get_interface_address(index,
@@ -3704,7 +3811,6 @@ static GIOChannel *get_listener(int family, int protocol, int index)
}
if (protocol == IPPROTO_TCP) {
-
if (listen(sk, 10) < 0) {
connman_error("Failed to listen on TCP socket %d/%s",
-errno, strerror(errno));
@@ -3818,9 +3924,7 @@ static void destroy_tcp_listener(struct listener_data *ifdata)
static int create_listener(struct listener_data *ifdata)
{
- int err, index;
-
- err = create_dns_listener(IPPROTO_UDP, ifdata);
+ int err = create_dns_listener(IPPROTO_UDP, ifdata);
if ((err & UDP_FAILED) == UDP_FAILED)
return -EIO;
@@ -3830,7 +3934,7 @@ static int create_listener(struct listener_data *ifdata)
return -EIO;
}
- index = connman_inet_ifindex("lo");
+ int index = connman_inet_ifindex("lo");
if (ifdata->index == index) {
if ((err & IPv6_FAILED) != IPv6_FAILED)
__connman_resolvfile_append(index, NULL, "::1");
@@ -3844,16 +3948,14 @@ static int create_listener(struct listener_data *ifdata)
static void destroy_listener(struct listener_data *ifdata)
{
- int index;
- GSList *list;
+ int index = connman_inet_ifindex("lo");
- index = connman_inet_ifindex("lo");
if (ifdata->index == index) {
__connman_resolvfile_remove(index, NULL, "127.0.0.1");
__connman_resolvfile_remove(index, NULL, "::1");
}
- for (list = request_list; list; list = list->next) {
+ for (GSList *list = request_list; list; list = list->next) {
struct request_data *req = list->data;
debug("Dropping request (id 0x%04x -> 0x%04x)",
@@ -3914,7 +4016,6 @@ int __connman_dnsproxy_add_listener(int index)
void __connman_dnsproxy_remove_listener(int index)
{
struct listener_data *ifdata;
-
DBG("index %d", index);
if (!listener_table)
@@ -3967,17 +4068,15 @@ int __connman_dnsproxy_init(void)
return err;
err = connman_notifier_register(&dnsproxy_notifier);
- if (err < 0)
- goto destroy;
-
- return 0;
+ if (err < 0) {
+ __connman_dnsproxy_remove_listener(index);
+ g_hash_table_destroy(listener_table);
+ g_hash_table_destroy(partial_tcp_req_table);
-destroy:
- __connman_dnsproxy_remove_listener(index);
- g_hash_table_destroy(listener_table);
- g_hash_table_destroy(partial_tcp_req_table);
+ return err;
+ }
- return err;
+ return 0;
}
int __connman_dnsproxy_set_mdns(int index, bool enabled)
@@ -4012,3 +4111,8 @@ void __connman_dnsproxy_cleanup(void)
if (ipv6_resolve)
g_resolv_unref(ipv6_resolve);
}
+
+void __connman_dnsproxy_set_listen_port(unsigned int port)
+{
+ dns_listen_port = port;
+}
diff --git a/src/inet.c b/src/inet.c
index 4c341438..4039a73c 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -79,7 +79,8 @@ int __connman_inet_modify_address(int cmd, int flags,
const char *address,
const char *peer,
unsigned char prefixlen,
- const char *broadcast)
+ const char *broadcast,
+ bool is_p2p)
{
uint8_t request[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
@@ -94,8 +95,9 @@ int __connman_inet_modify_address(int cmd, int flags,
int sk, err;
DBG("cmd %#x flags %#x index %d family %d address %s peer %s "
- "prefixlen %hhu broadcast %s", cmd, flags, index, family,
- address, peer, prefixlen, broadcast);
+ "prefixlen %hhu broadcast %s p2p %s", cmd, flags, index,
+ family, address, peer, prefixlen, broadcast,
+ is_p2p ? "true" : "false");
if (!address)
return -EINVAL;
@@ -119,17 +121,11 @@ int __connman_inet_modify_address(int cmd, int flags,
ifaddrmsg->ifa_index = index;
if (family == AF_INET) {
- if (inet_pton(AF_INET, address, &ipv4_addr) < 1)
+ if (inet_pton(AF_INET, address, &ipv4_addr) != 1)
return -1;
- if (broadcast)
- inet_pton(AF_INET, broadcast, &ipv4_bcast);
- else
- ipv4_bcast.s_addr = ipv4_addr.s_addr |
- htonl(0xfffffffflu >> prefixlen);
-
if (peer) {
- if (inet_pton(AF_INET, peer, &ipv4_dest) < 1)
+ if (inet_pton(AF_INET, peer, &ipv4_dest) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
@@ -149,16 +145,27 @@ int __connman_inet_modify_address(int cmd, int flags,
if (err < 0)
return err;
- err = __connman_inet_rtnl_addattr_l(header,
- sizeof(request),
- IFA_BROADCAST,
- &ipv4_bcast,
- sizeof(ipv4_bcast));
- if (err < 0)
- return err;
+ /*
+ * Broadcast address must not be added for P2P / VPN as
+ * getifaddrs() cannot interpret destination address.
+ */
+ if (!is_p2p) {
+ if (broadcast)
+ inet_pton(AF_INET, broadcast, &ipv4_bcast);
+ else
+ ipv4_bcast.s_addr = ipv4_addr.s_addr |
+ htonl(0xfffffffflu >> prefixlen);
+ err = __connman_inet_rtnl_addattr_l(header,
+ sizeof(request),
+ IFA_BROADCAST,
+ &ipv4_bcast,
+ sizeof(ipv4_bcast));
+ if (err < 0)
+ return err;
+ }
} else if (family == AF_INET6) {
- if (inet_pton(AF_INET6, address, &ipv6_addr) < 1)
+ if (inet_pton(AF_INET6, address, &ipv6_addr) != 1)
return -1;
err = __connman_inet_rtnl_addattr_l(header,
@@ -189,13 +196,46 @@ done:
return err;
}
+static bool is_addr_unspec(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return in4->sin_addr.s_addr == INADDR_ANY;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
+static bool is_addr_ll(int family, struct sockaddr *addr)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ switch (family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in*) addr;
+ return (in4->sin_addr.s_addr & IN_CLASSB_NET) ==
+ ((in_addr_t) htonl(0xa9fe0000));
+ case AF_INET6:
+ in6 = (struct sockaddr_in6*) addr;
+ return IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr);
+ default:
+ return false;
+ }
+}
+
bool __connman_inet_is_any_addr(const char *address, int family)
{
bool ret = false;
struct addrinfo hints;
struct addrinfo *result = NULL;
- struct sockaddr_in6 *in6 = NULL;
- struct sockaddr_in *in4 = NULL;
if (!address || !*address)
goto out;
@@ -208,14 +248,7 @@ bool __connman_inet_is_any_addr(const char *address, int family)
goto out;
if (result) {
- if (result->ai_family == AF_INET6) {
- in6 = (struct sockaddr_in6*)result->ai_addr;
- ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr);
- } else if (result->ai_family == AF_INET) {
- in4 = (struct sockaddr_in*)result->ai_addr;
- ret = in4->sin_addr.s_addr == INADDR_ANY;
- }
-
+ ret = is_addr_unspec(result->ai_family, result->ai_addr);
freeaddrinfo(result);
}
@@ -409,18 +442,20 @@ int connman_inet_set_ipv6_address(int index,
int err;
unsigned char prefix_len;
const char *address;
+ bool is_p2p;
if (!ipaddress->local)
return 0;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -434,6 +469,7 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
if (!ipaddress->local)
return -1;
@@ -442,12 +478,13 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -456,10 +493,17 @@ int connman_inet_set_address(int index, struct connman_ipaddress *ipaddress)
return 0;
}
-int connman_inet_clear_ipv6_address(int index, const char *address,
- int prefix_len)
+int connman_inet_clear_ipv6_address(int index,
+ struct connman_ipaddress *ipaddress)
{
int err;
+ int prefix_len;
+ const char *address;
+ bool is_p2p;
+
+ address = ipaddress->local;
+ prefix_len = ipaddress->prefixlen;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d", index, address, prefix_len);
@@ -467,7 +511,7 @@ int connman_inet_clear_ipv6_address(int index, const char *address,
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET6,
- address, NULL, prefix_len, NULL);
+ address, NULL, prefix_len, NULL, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -481,11 +525,13 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
int err;
unsigned char prefix_len;
const char *address, *broadcast, *peer;
+ bool is_p2p;
prefix_len = ipaddress->prefixlen;
address = ipaddress->local;
broadcast = ipaddress->broadcast;
peer = ipaddress->peer;
+ is_p2p = ipaddress->is_p2p;
DBG("index %d address %s prefix_len %d peer %s broadcast %s", index,
address, prefix_len, peer, broadcast);
@@ -494,7 +540,7 @@ int connman_inet_clear_address(int index, struct connman_ipaddress *ipaddress)
return -EINVAL;
err = __connman_inet_modify_address(RTM_DELADDR, 0, index, AF_INET,
- address, peer, prefix_len, broadcast);
+ address, peer, prefix_len, broadcast, is_p2p);
if (err < 0) {
connman_error("%s: %s", __func__, strerror(-err));
return err;
@@ -661,7 +707,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
@@ -711,7 +757,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
rt.rtmsg_dst_len = prefix_len;
- if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) < 0) {
+ if (inet_pton(AF_INET6, host, &rt.rtmsg_dst) != 1) {
err = -errno;
goto out;
}
@@ -727,7 +773,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
*/
if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) &&
- inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0)
+ inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) == 1)
rt.rtmsg_flags |= RTF_GATEWAY;
rt.rtmsg_metric = 1;
@@ -770,7 +816,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
memset(&rt, 0, sizeof(rt));
- if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) < 0) {
+ if (inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) != 1) {
err = -errno;
goto out;
}
@@ -1066,54 +1112,161 @@ out:
return err;
}
-bool connman_inet_compare_subnet(int index, const char *host)
+#define ADDR_TYPE_MAX 4
+
+struct interface_address {
+ int index;
+ int family;
+ bool allow_unspec;
+ /* Applies only to ADDR_TYPE_IPADDR in ipaddrs */
+ bool require_ll;
+ /* Real types must be in_addr for AF_INET and in6_addr for AF_INET6 */
+ void *ipaddrs[ADDR_TYPE_MAX];
+};
+
+enum ipaddr_type {
+ ADDR_TYPE_IPADDR = 0,
+ ADDR_TYPE_NETMASK,
+ ADDR_TYPE_BRDADDR,
+ ADDR_TYPE_DSTADDR
+};
+
+static int get_interface_addresses(struct interface_address *if_addr)
{
- struct ifreq ifr;
- struct in_addr _host_addr;
- in_addr_t host_addr, netmask_addr, if_addr;
- struct sockaddr_in *netmask, *addr;
- int sk;
+ struct ifaddrs *ifaddr;
+ struct ifaddrs *ifa;
+ struct sockaddr *addrs[ADDR_TYPE_MAX] = { 0 };
+ struct sockaddr_in *addr_in;
+ struct sockaddr_in6 *addr_in6;
+ char name[IF_NAMESIZE] = { 0 };
+ size_t len;
+ int err = -ENOENT;
+ int i;
- DBG("host %s", host);
+ if (!if_addr)
+ return -EINVAL;
- if (!host)
- return false;
+ if (!if_indextoname(if_addr->index, name))
+ return -EINVAL;
- if (inet_aton(host, &_host_addr) == 0)
- return false;
- host_addr = _host_addr.s_addr;
+ DBG("index %d interface %s", if_addr->index, name);
- sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return false;
+ if (getifaddrs(&ifaddr) < 0) {
+ connman_error("Cannot get addresses err %d/%s", errno,
+ strerror(errno));
+ return -errno;
+ }
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- close(sk);
- return false;
+ if (g_strcmp0(ifa->ifa_name, name) ||
+ ifa->ifa_addr->sa_family !=
+ if_addr->family)
+ continue;
+
+
+ if (if_addr->ipaddrs[ADDR_TYPE_IPADDR]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ if (if_addr->require_ll && !is_addr_ll(if_addr->family,
+ ifa->ifa_addr))
+ continue;
+
+ addrs[ADDR_TYPE_IPADDR] = ifa->ifa_addr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_NETMASK]) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_netmask))
+ continue;
+
+ addrs[ADDR_TYPE_NETMASK] = ifa->ifa_netmask;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_BRDADDR] &&
+ (ifa->ifa_flags & IFF_BROADCAST)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_broadaddr))
+ continue;
+
+ addrs[ADDR_TYPE_BRDADDR] = ifa->ifa_ifu.ifu_broadaddr;
+ }
+
+ if (if_addr->ipaddrs[ADDR_TYPE_DSTADDR] &&
+ (ifa->ifa_flags & IFF_POINTOPOINT)) {
+ if (!if_addr->allow_unspec && is_addr_unspec(
+ if_addr->family,
+ ifa->ifa_ifu.ifu_dstaddr))
+ continue;
+
+ addrs[ADDR_TYPE_DSTADDR] = ifa->ifa_ifu.ifu_dstaddr;
+ }
+
+ err = 0;
+
+ break;
}
- if (ioctl(sk, SIOCGIFNETMASK, &ifr) < 0) {
- close(sk);
- return false;
+ if (err)
+ goto out;
+
+ for (i = 0; i < ADDR_TYPE_MAX; i++) {
+ if (!addrs[i])
+ continue;
+
+ switch (if_addr->family) {
+ case AF_INET:
+ len = sizeof(struct in_addr);
+ addr_in = (struct sockaddr_in*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in->sin_addr, len);
+ break;
+ case AF_INET6:
+ len = sizeof(struct in6_addr);
+ addr_in6 = (struct sockaddr_in6*) addrs[i];
+ memcpy(if_addr->ipaddrs[i], &addr_in6->sin6_addr, len);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
}
- netmask = (struct sockaddr_in *)&ifr.ifr_netmask;
- netmask_addr = netmask->sin_addr.s_addr;
+out:
+ freeifaddrs(ifaddr);
+ return err;
+}
- if (ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
- close(sk);
+bool connman_inet_compare_subnet(int index, const char *host)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr iaddr = { 0 };
+ struct in_addr imask = { 0 };
+ struct in_addr haddr = { 0 };
+
+ DBG("host %s", host);
+
+ if (!host)
return false;
- }
- close(sk);
+ if (inet_pton(AF_INET, host, &haddr) != 1)
+ return false;
+
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- addr = (struct sockaddr_in *)&ifr.ifr_addr;
- if_addr = addr->sin_addr.s_addr;
+ if (get_interface_addresses(&if_addr))
+ return false;
- return ((if_addr & netmask_addr) == (host_addr & netmask_addr));
+ return (iaddr.s_addr & imask.s_addr) == (haddr.s_addr & imask.s_addr);
}
static bool mem_mask_equal(const void *a, const void *b,
@@ -1134,47 +1287,23 @@ static bool mem_mask_equal(const void *a, const void *b,
bool connman_inet_compare_ipv6_subnet(int index, const char *host)
{
- struct ifaddrs *ifaddr, *ifa;
- bool rv = false;
- char name[IF_NAMESIZE];
- struct in6_addr haddr;
+ struct interface_address addr = { 0 };
+ struct in6_addr iaddr = { 0 };
+ struct in6_addr imask = { 0 };
+ struct in6_addr haddr = { 0 };
- if (inet_pton(AF_INET6, host, &haddr) <= 0)
+ if (inet_pton(AF_INET6, host, &haddr) != 1)
return false;
- if (!if_indextoname(index, name))
- return false;
-
- DBG("index %d interface %s", index, name);
+ addr.index = index;
+ addr.family = AF_INET6;
+ addr.ipaddrs[ADDR_TYPE_IPADDR] = &iaddr;
+ addr.ipaddrs[ADDR_TYPE_NETMASK] = &imask;
- if (getifaddrs(&ifaddr) < 0) {
- DBG("Cannot get addresses err %d/%s", errno, strerror(errno));
+ if (get_interface_addresses(&addr))
return false;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- struct sockaddr_in6 *iaddr;
- struct sockaddr_in6 *imask;
-
- if (!ifa->ifa_addr)
- continue;
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 ||
- ifa->ifa_addr->sa_family != AF_INET6)
- continue;
-
- iaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
- imask = (struct sockaddr_in6 *)ifa->ifa_netmask;
-
- rv = mem_mask_equal(&iaddr->sin6_addr, &haddr,
- &imask->sin6_addr,
- sizeof(haddr));
- goto out;
- }
-
-out:
- freeifaddrs(ifaddr);
- return rv;
+ return mem_mask_equal(&iaddr, &haddr, &imask, sizeof(haddr));
}
int connman_inet_remove_from_bridge(int index, const char *bridge)
@@ -1707,13 +1836,6 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
return 0;
}
-static inline void ipv6_addr_advert_mult(const struct in6_addr *addr,
- struct in6_addr *advert)
-{
- ipv6_addr_set(advert, htonl(0xFF020000), 0, htonl(0x2),
- htonl(0xFF000000) | addr->s6_addr32[3]);
-}
-
#define MSG_SIZE_SEND 1452
static int inc_len(int len, int inc)
@@ -2147,98 +2269,156 @@ GSList *__connman_inet_ipv6_get_prefixes(struct nd_router_advert *hdr,
return prefixes;
}
-static int get_dest_addr(int family, int index, char *buf, int len)
+int connman_inet_get_dest_addr(int index, char **dest)
{
- struct ifreq ifr;
- void *addr;
- int sk;
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- sk = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (sk < 0)
- return -errno;
+ if (!dest)
+ return -EINVAL;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = index;
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
- DBG("SIOCGIFNAME (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
- DBG("SIOCGIFFLAGS (%d/%s)", errno, strerror(errno));
- close(sk);
- return -errno;
- }
+ if (inet_ntop(AF_INET, &dstaddr, buf, INET_ADDRSTRLEN))
+ *dest = g_strdup(buf);
- if ((ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
- close(sk);
- errno = EINVAL;
- return -errno;
- }
+ DBG("destination %s", *dest);
- DBG("index %d %s", index, ifr.ifr_name);
+ return *dest && **dest ? 0 : -ENOENT;
+}
- if (ioctl(sk, SIOCGIFDSTADDR, &ifr) < 0) {
- connman_error("Get destination address failed (%s)",
- strerror(errno));
- close(sk);
- return -errno;
- }
+int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+{
+ struct interface_address if_addr = { 0 };
+ struct in_addr dstaddr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- close(sk);
+ if (!dest)
+ return -EINVAL;
- switch (family) {
- case AF_INET:
- addr = &((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr;
- break;
- case AF_INET6:
- addr = &((struct sockaddr_in6 *)&ifr.ifr_dstaddr)->sin6_addr;
- break;
- default:
- errno = EINVAL;
- return -errno;
- }
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.allow_unspec = true;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dstaddr;
- if (!inet_ntop(family, addr, buf, len)) {
- DBG("error %d/%s", errno, strerror(errno));
- return -errno;
- }
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ if (inet_ntop(AF_INET6, &dstaddr, buf, INET6_ADDRSTRLEN))
+ *dest = g_strdup(buf);
+
+ DBG("destination %s", *dest);
+
+ return *dest && **dest ? 0 : -ENOENT;
}
-int connman_inet_get_dest_addr(int index, char **dest)
+/* destination is optional */
+int connman_inet_get_route_addresses(int index, char **network, char **netmask,
+ char **destination)
{
- char addr[INET_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in_addr addr = { 0 };
+ struct in_addr mask = { 0 };
+ struct in_addr dest = { 0 };
+ struct in_addr nw_addr = { 0 };
+ char buf[INET_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET, index, addr, INET_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network || !netmask)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ nw_addr.s_addr = (addr.s_addr & mask.s_addr);
+
+ if (inet_ntop(AF_INET, &nw_addr, buf, INET_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &mask, buf, INET_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET, &dest, buf, INET_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
-int connman_inet_ipv6_get_dest_addr(int index, char **dest)
+int connman_inet_ipv6_get_route_addresses(int index, char **network,
+ char **netmask, char **destination)
{
- char addr[INET6_ADDRSTRLEN];
- int ret;
+ struct interface_address if_addr = { 0 };
+ struct in6_addr addr = { 0 };
+ struct in6_addr mask = { 0 };
+ struct in6_addr dest = { 0 };
+ struct in6_addr nw_addr = { 0 };
+ char buf[INET6_ADDRSTRLEN] = { 0 };
+ int err;
- ret = get_dest_addr(PF_INET6, index, addr, INET6_ADDRSTRLEN);
- if (ret < 0)
- return ret;
+ if (!network)
+ return -EINVAL;
- *dest = g_strdup(addr);
+ if_addr.index = index;
+ if_addr.family = AF_INET6;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = &addr;
+ if_addr.ipaddrs[ADDR_TYPE_NETMASK] = &mask;
+ if_addr.ipaddrs[ADDR_TYPE_DSTADDR] = &dest;
- DBG("destination %s", *dest);
+ err = get_interface_addresses(&if_addr);
+ if (err)
+ return err;
- return 0;
+ ipv6_addr_set(&nw_addr, addr.s6_addr32[0] & mask.s6_addr32[0],
+ addr.s6_addr32[1] & mask.s6_addr32[1],
+ addr.s6_addr32[2] & mask.s6_addr32[2],
+ addr.s6_addr32[3] & mask.s6_addr32[3]);
+
+ if (inet_ntop(AF_INET6, &nw_addr, buf, INET6_ADDRSTRLEN))
+ *network = g_strdup(buf);
+
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &mask, buf, INET6_ADDRSTRLEN))
+ *netmask = g_strdup(buf);
+
+ if (destination) {
+ memset(&buf, 0, INET6_ADDRSTRLEN);
+
+ if (inet_ntop(AF_INET6, &dest, buf, INET6_ADDRSTRLEN))
+ *destination = g_strdup(buf);
+ }
+
+ DBG("network %s netmask %s destination %s", *network, *netmask,
+ destination ? *destination : NULL);
+
+ return *network && **network && *netmask && **netmask ? 0 : -ENOENT;
}
int __connman_inet_rtnl_open(struct __connman_inet_rtnl_handle *rth)
@@ -2835,58 +3015,30 @@ bool connman_inet_is_ipv6_supported()
return true;
}
-int __connman_inet_get_interface_address(int index, int family, void *address)
+/*
+ * Omits checking of the gateway matching the actual gateway IP since both
+ * connmand and vpnd use inet.c, getting the route is via ipconfig and ipconfig
+ * is different for both. Gateway is left here for possible future use.
+ *
+ * Gateway can be NULL and connection.c then assigns 0.0.0.0 address or ::,
+ * depending on IP family.
+ */
+bool connman_inet_is_default_route(int family, const char *host,
+ const char *gateway, const char *netmask)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
-
- DBG("index %d interface %s", index, name);
-
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
+ return __connman_inet_is_any_addr(host, family) &&
+ __connman_inet_is_any_addr(netmask, family);
+}
- } else {
- err = -EINVAL;
- goto out;
- }
+int __connman_inet_get_interface_address(int index, int family, void *address)
+{
+ struct interface_address if_addr = { 0 };
- err = 0;
- break;
- }
- }
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address)
@@ -3020,7 +3172,7 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex,
ret = inet_pton(family, dst ? dst : gateway, buf);
g_free(dst);
- if (ret <= 0)
+ if (ret != 1)
return -EINVAL;
memset(&rth, 0, sizeof(rth));
@@ -3095,61 +3247,14 @@ int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex,
int __connman_inet_get_interface_ll_address(int index, int family,
void *address)
{
- struct ifaddrs *ifaddr, *ifa;
- int err = -ENOENT;
- char name[IF_NAMESIZE];
-
- if (!if_indextoname(index, name))
- return -EINVAL;
+ struct interface_address if_addr = { 0 };
- DBG("index %d interface %s", index, name);
+ if_addr.index = index;
+ if_addr.family = family;
+ if_addr.require_ll = true;
+ if_addr.ipaddrs[ADDR_TYPE_IPADDR] = address;
- if (getifaddrs(&ifaddr) < 0) {
- err = -errno;
- DBG("Cannot get addresses err %d/%s", err, strerror(-err));
- return err;
- }
-
- for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
-
- if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) == 0 &&
- ifa->ifa_addr->sa_family == family) {
- if (family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *)
- ifa->ifa_addr;
- if (in4->sin_addr.s_addr == INADDR_ANY)
- continue;
- if ((in4->sin_addr.s_addr & IN_CLASSB_NET) !=
- ((in_addr_t) 0xa9fe0000))
- continue;
- memcpy(address, &in4->sin_addr,
- sizeof(struct in_addr));
- } else if (family == AF_INET6) {
- struct sockaddr_in6 *in6 =
- (struct sockaddr_in6 *)ifa->ifa_addr;
- if (memcmp(&in6->sin6_addr, &in6addr_any,
- sizeof(struct in6_addr)) == 0)
- continue;
- if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
- continue;
-
- memcpy(address, &in6->sin6_addr,
- sizeof(struct in6_addr));
- } else {
- err = -EINVAL;
- goto out;
- }
-
- err = 0;
- break;
- }
- }
-
-out:
- freeifaddrs(ifaddr);
- return err;
+ return get_interface_addresses(&if_addr);
}
int __connman_inet_get_address_netmask(int ifindex,
@@ -3303,7 +3408,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file,
addrstr[len] = '\0';
err = inet_pton(AF_INET, addrstr, addr);
- if (err <= 0) {
+ if (err != 1) {
connman_error("%s: Cannot convert to numeric addr \"%s\"\n",
__func__, addrstr);
err = -1;
diff --git a/src/ipaddress.c b/src/ipaddress.c
index d63d95c3..201d8345 100644
--- a/src/ipaddress.c
+++ b/src/ipaddress.c
@@ -70,10 +70,19 @@ struct connman_ipaddress *connman_ipaddress_alloc(int family)
ipaddress->peer = NULL;
ipaddress->broadcast = NULL;
ipaddress->gateway = NULL;
+ ipaddress->is_p2p = false;
return ipaddress;
}
+void connman_ipaddress_set_p2p(struct connman_ipaddress *ipaddress, bool value)
+{
+ if (!ipaddress)
+ return;
+
+ ipaddress->is_p2p = value;
+}
+
void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
{
if (!ipaddress)
@@ -95,7 +104,7 @@ static bool check_ipv6_address(const char *address)
return false;
err = inet_pton(AF_INET6, address, buf);
- if (err > 0)
+ if (err == 1)
return true;
return false;
@@ -223,6 +232,7 @@ connman_ipaddress_copy(struct connman_ipaddress *ipaddress)
copy->peer = g_strdup(ipaddress->peer);
copy->broadcast = g_strdup(ipaddress->broadcast);
copy->gateway = g_strdup(ipaddress->gateway);
+ copy->is_p2p = ipaddress->is_p2p;
return copy;
}
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 915c0823..34b1724a 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -258,153 +258,165 @@ static const char *scope2str(unsigned char scope)
return "";
}
-static bool get_ipv6_state(gchar *ifname)
+#define PROC_IPV4_CONF_PREFIX "/proc/sys/net/ipv4/conf"
+#define PROC_IPV6_CONF_PREFIX "/proc/sys/net/ipv6/conf"
+
+static int read_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int *value)
{
- int disabled;
gchar *path;
FILE *f;
- bool enabled = false;
-
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
+ int err;
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return enabled;
+ return -ENOMEM;
+ errno = 0;
f = fopen(path, "r");
+ if (!f) {
+ err = -errno;
+ } else {
+ errno = 0; /* Avoid stale errno values with fscanf */
- g_free(path);
+ err = fscanf(f, "%d", value);
+ if (err <= 0 && errno)
+ err = -errno;
- if (f) {
- if (fscanf(f, "%d", &disabled) > 0)
- enabled = !disabled;
fclose(f);
}
- return enabled;
+ if (err <= 0)
+ connman_error("failed to read %s", path);
+
+ g_free(path);
+
+ return err;
+}
+
+static int read_ipv4_conf_value(const char *ifname, const char *suffix,
+ int *value)
+{
+ return read_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
}
-static void set_ipv6_state(gchar *ifname, bool enable)
+static int read_ipv6_conf_value(const char *ifname, const char *suffix,
+ int *value)
{
+ return read_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
+}
+
+static int write_conf_value(const char *prefix, const char *ifname,
+ const char *suffix, int value) {
gchar *path;
FILE *f;
+ int rval;
- if (!ifname)
- path = g_strdup("/proc/sys/net/ipv6/conf/all/disable_ipv6");
- else
- path = g_strdup_printf(
- "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname);
-
+ path = g_build_filename(prefix, ifname ? ifname : "all", suffix, NULL);
if (!path)
- return;
+ return -ENOMEM;
f = fopen(path, "r+");
+ if (!f) {
+ rval = -errno;
+ } else {
+ rval = fprintf(f, "%d", value);
+ fclose(f);
+ }
+
+ if (rval <= 0)
+ connman_error("failed to set %s value %d", path, value);
g_free(path);
- if (!f)
- return;
+ return rval;
+}
- if (!enable)
- fprintf(f, "1");
- else
- fprintf(f, "0");
+static int write_ipv4_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV4_CONF_PREFIX, ifname, suffix, value);
+}
- fclose(f);
+static int write_ipv6_conf_value(const char *ifname, const char *suffix,
+ int value)
+{
+ return write_conf_value(PROC_IPV6_CONF_PREFIX, ifname, suffix, value);
}
-static int get_ipv6_privacy(gchar *ifname)
+static bool get_ipv6_state(gchar *ifname)
{
- gchar *path;
- FILE *f;
- int value;
+ int disabled;
+ bool enabled = false;
- if (!ifname)
- return 0;
+ if (read_ipv6_conf_value(ifname, "disable_ipv6", &disabled) > 0)
+ enabled = !disabled;
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
+ return enabled;
+}
- if (!path)
- return 0;
+static int set_ipv6_state(gchar *ifname, bool enable)
+{
+ int disabled = enable ? 0 : 1;
- f = fopen(path, "r");
+ DBG("%s %d", ifname, disabled);
- g_free(path);
+ return write_ipv6_conf_value(ifname, "disable_ipv6", disabled);
+}
- if (!f)
+static int get_ipv6_privacy(gchar *ifname)
+{
+ int value;
+
+ if (!ifname)
return 0;
- if (fscanf(f, "%d", &value) <= 0)
+ if (read_ipv6_conf_value(ifname, "use_tempaddr", &value) < 0)
value = 0;
- fclose(f);
-
return value;
}
/* Enable the IPv6 privacy extension for stateless address autoconfiguration.
* The privacy extension is described in RFC 3041 and RFC 4941
*/
-static void set_ipv6_privacy(gchar *ifname, int value)
+static int set_ipv6_privacy(gchar *ifname, int value)
{
- gchar *path;
- FILE *f;
-
if (!ifname)
- return;
-
- path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/use_tempaddr",
- ifname);
-
- if (!path)
- return;
+ return -EINVAL;
if (value < 0)
value = 0;
- f = fopen(path, "r+");
-
- g_free(path);
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
- fclose(f);
+ return write_ipv6_conf_value(ifname, "use_tempaddr", value);
}
static int get_rp_filter(void)
{
- FILE *f;
- int value = -EINVAL, tmp;
-
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
+ int value;
- if (f) {
- if (fscanf(f, "%d", &tmp) == 1)
- value = tmp;
- fclose(f);
- }
+ if (read_ipv4_conf_value(NULL, "rp_filter", &value) < 0)
+ value = -EINVAL;
return value;
}
-static void set_rp_filter(int value)
+static int set_rp_filter(int value)
{
- FILE *f;
-
- f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
-
- if (!f)
- return;
-
- fprintf(f, "%d", value);
+ /* 0 = no validation, 1 = strict mode, 2 = loose mode */
+ switch (value) {
+ case -1:
+ value = 0;
+ /* fall through */
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
- fclose(f);
+ return write_ipv4_conf_value(NULL, "rp_filter", value);
}
int __connman_ipconfig_set_rp_filter()
@@ -696,6 +708,25 @@ static inline gint check_duplicate_address(gconstpointer a, gconstpointer b)
return g_strcmp0(addr1->local, addr2->local);
}
+static bool is_index_p2p_service(int index)
+{
+ struct connman_service *service;
+ enum connman_service_type type;
+
+ service = __connman_service_lookup_from_index(index);
+ if (!service)
+ return false;
+
+ type = connman_service_get_type(service);
+ switch (type) {
+ case CONNMAN_SERVICE_TYPE_P2P:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ return true;
+ default:
+ return false;
+ }
+}
+
int __connman_ipconfig_newaddr(int index, int family, const char *label,
unsigned char prefixlen, const char *address)
{
@@ -718,6 +749,9 @@ int __connman_ipconfig_newaddr(int index, int family, const char *label,
ipaddress->prefixlen = prefixlen;
ipaddress->local = g_strdup(address);
+ if (is_index_p2p_service(index))
+ connman_ipaddress_set_p2p(ipaddress, true);
+
if (g_slist_find_custom(ipdevice->address_list, ipaddress,
check_duplicate_address)) {
connman_ipaddress_free(ipaddress);
@@ -1186,6 +1220,15 @@ void __connman_ipconfig_set_prefixlen(struct connman_ipconfig *ipconfig,
ipconfig->address->prefixlen = prefixlen;
}
+static void ipconfig_set_p2p(int index, struct connman_ipconfig *ipconfig)
+{
+ if (!is_index_p2p_service(index))
+ return;
+
+ connman_ipaddress_set_p2p(ipconfig->address, true);
+ connman_ipaddress_set_p2p(ipconfig->system, true);
+}
+
static struct connman_ipconfig *create_ipv6config(int index)
{
struct connman_ipconfig *ipv6config;
@@ -1217,6 +1260,8 @@ static struct connman_ipconfig *create_ipv6config(int index)
ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+ ipconfig_set_p2p(index, ipv6config);
+
DBG("ipconfig %p index %d method %s", ipv6config, index,
__connman_ipconfig_method2string(ipv6config->method));
@@ -1255,6 +1300,8 @@ struct connman_ipconfig *__connman_ipconfig_create(int index,
ipconfig->system = connman_ipaddress_alloc(AF_INET);
+ ipconfig_set_p2p(index, ipconfig);
+
DBG("ipconfig %p index %d", ipconfig, index);
return ipconfig;
@@ -1451,10 +1498,8 @@ int __connman_ipconfig_address_unset(struct connman_ipconfig *ipconfig)
err = connman_inet_clear_address(ipconfig->index,
ipconfig->address);
else if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6)
- err = connman_inet_clear_ipv6_address(
- ipconfig->index,
- ipconfig->address->local,
- ipconfig->address->prefixlen);
+ err = connman_inet_clear_ipv6_address(ipconfig->index,
+ ipconfig->address);
else
err = -EINVAL;
@@ -1548,6 +1593,9 @@ static void disable_ipv6(struct connman_ipconfig *ipconfig)
ifname = connman_inet_ifname(ipconfig->index);
+ if (!ifname)
+ return;
+
set_ipv6_state(ifname, false);
g_free(ifname);
@@ -1567,6 +1615,9 @@ static void enable_ipv6(struct connman_ipconfig *ipconfig)
ifname = connman_inet_ifname(ipconfig->index);
+ if (!ifname)
+ return;
+
if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO)
set_ipv6_privacy(ifname, ipconfig->ipv6_privacy_config);
@@ -1647,6 +1698,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV6 &&
@@ -1657,6 +1711,9 @@ int __connman_ipconfig_enable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
@@ -1719,6 +1776,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv4->system);
__connman_ipconfig_unref(ipdevice->config_ipv4);
ipdevice->config_ipv4 = NULL;
+
+ g_free(ipdevice->ipv4_gateway);
+ ipdevice->ipv4_gateway = NULL;
+
return 0;
}
@@ -1728,6 +1789,10 @@ int __connman_ipconfig_disable(struct connman_ipconfig *ipconfig)
connman_ipaddress_clear(ipdevice->config_ipv6->system);
__connman_ipconfig_unref(ipdevice->config_ipv6);
ipdevice->config_ipv6 = NULL;
+
+ g_free(ipdevice->ipv6_gateway);
+ ipdevice->ipv6_gateway = NULL;
+
return 0;
}
diff --git a/src/iptables.c b/src/iptables.c
index 47ea1c2d..664b27f1 100644
--- a/src/iptables.c
+++ b/src/iptables.c
@@ -2931,7 +2931,7 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip,
if (!tokens)
return -1;
- if (!inet_pton(AF_INET, tokens[0], ip)) {
+ if (inet_pton(AF_INET, tokens[0], ip) != 1) {
err = -1;
goto out;
}
@@ -2972,7 +2972,7 @@ static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip,
if (!tokens)
return -1;
- if (!inet_pton(AF_INET6, tokens[0], ip)) {
+ if (inet_pton(AF_INET6, tokens[0], ip) != 1) {
err = -1;
goto out;
}
@@ -3383,7 +3383,7 @@ static int parse_rule_spec(struct connman_iptables *table,
break;
if (invert)
- ctx->ip->invflags |= IP6T_INV_DSTIP;
+ ctx->ipv6->invflags |= IP6T_INV_DSTIP;
}
break;
diff --git a/src/main.c b/src/main.c
index 2371771f..ae4a450d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -44,6 +44,18 @@
#define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000)
#define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000)
+#define DEFAULT_ONLINE_CHECK_IPV4_URL "http://ipv4.connman.net/online/status.html"
+#define DEFAULT_ONLINE_CHECK_IPV6_URL "http://ipv6.connman.net/online/status.html"
+
+/*
+ * We set the integer to 1 sec so that we have a chance to get
+ * necessary IPv6 router advertisement messages that might have
+ * DNS data etc.
+ */
+#define DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL 1
+#define DEFAULT_ONLINE_CHECK_MAX_INTERVAL 12
+#define DEFAULT_LOCALTIME "/etc/localtime"
+
#define MAINFILE "main.conf"
#define CONFIGMAINFILE CONFIGDIR "/" MAINFILE
@@ -66,6 +78,7 @@ static char *default_blacklist[] = {
"ifb",
"ve-",
"vb-",
+ "ham",
NULL
};
@@ -88,9 +101,17 @@ static struct {
bool enable_6to4;
char *vendor_class_id;
bool enable_online_check;
+ bool enable_online_to_ready_transition;
+ char *online_check_ipv4_url;
+ char *online_check_ipv6_url;
+ unsigned int online_check_initial_interval;
+ unsigned int online_check_max_interval;
bool auto_connect_roaming_services;
bool acd;
bool use_gateways_as_timeservers;
+ char *localtime;
+ bool regdom_follows_timezone;
+ char *resolv_conf;
} connman_settings = {
.bg_scan = true,
.pref_timeservers = NULL,
@@ -110,9 +131,16 @@ static struct {
.enable_6to4 = false,
.vendor_class_id = NULL,
.enable_online_check = true,
+ .enable_online_to_ready_transition = false,
+ .online_check_ipv4_url = NULL,
+ .online_check_ipv6_url = NULL,
+ .online_check_initial_interval = DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL,
+ .online_check_max_interval = DEFAULT_ONLINE_CHECK_MAX_INTERVAL,
.auto_connect_roaming_services = false,
.acd = false,
.use_gateways_as_timeservers = false,
+ .localtime = NULL,
+ .resolv_conf = NULL,
};
#define CONF_BG_SCAN "BackgroundScanning"
@@ -133,14 +161,23 @@ static struct {
#define CONF_ENABLE_6TO4 "Enable6to4"
#define CONF_VENDOR_CLASS_ID "VendorClassID"
#define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck"
+#define CONF_ENABLE_ONLINE_TO_READY_TRANSITION "EnableOnlineToReadyTransition"
+#define CONF_ONLINE_CHECK_IPV4_URL "OnlineCheckIPv4URL"
+#define CONF_ONLINE_CHECK_IPV6_URL "OnlineCheckIPv6URL"
+#define CONF_ONLINE_CHECK_INITIAL_INTERVAL "OnlineCheckInitialInterval"
+#define CONF_ONLINE_CHECK_MAX_INTERVAL "OnlineCheckMaxInterval"
#define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices"
#define CONF_ACD "AddressConflictDetection"
#define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers"
+#define CONF_LOCALTIME "Localtime"
+#define CONF_REGDOM_FOLLOWS_TIMEZONE "RegdomFollowsTimezone"
+#define CONF_RESOLV_CONF "ResolvConf"
static const char *supported_options[] = {
CONF_BG_SCAN,
CONF_PREF_TIMESERVERS,
CONF_AUTO_CONNECT_TECHS,
+ CONF_FAVORITE_TECHS,
CONF_ALWAYS_CONNECTED_TECHS,
CONF_PREFERRED_TECHS,
CONF_FALLBACK_NAMESERVERS,
@@ -155,9 +192,17 @@ static const char *supported_options[] = {
CONF_ENABLE_6TO4,
CONF_VENDOR_CLASS_ID,
CONF_ENABLE_ONLINE_CHECK,
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION,
+ CONF_ONLINE_CHECK_IPV4_URL,
+ CONF_ONLINE_CHECK_IPV6_URL,
+ CONF_ONLINE_CHECK_INITIAL_INTERVAL,
+ CONF_ONLINE_CHECK_MAX_INTERVAL,
CONF_AUTO_CONNECT_ROAMING_SERVICES,
CONF_ACD,
CONF_USE_GATEWAYS_AS_TIMESERVERS,
+ CONF_LOCALTIME,
+ CONF_REGDOM_FOLLOWS_TIMEZONE,
+ CONF_RESOLV_CONF,
NULL
};
@@ -280,17 +325,23 @@ static void parse_config(GKeyFile *config)
char **interfaces;
char **str_list;
char **tethering;
- char *vendor_class_id;
+ char *string;
gsize len;
- int timeout;
+ int integer;
if (!config) {
connman_settings.auto_connect =
- parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+ parse_service_types(default_auto_connect,
+ CONF_ARRAY_SIZE(default_auto_connect));
connman_settings.favorite_techs =
- parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs));
+ parse_service_types(default_favorite_techs,
+ CONF_ARRAY_SIZE(default_favorite_techs));
connman_settings.blacklisted_interfaces =
g_strdupv(default_blacklist);
+ connman_settings.online_check_ipv4_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+ connman_settings.online_check_ipv6_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
return;
}
@@ -320,6 +371,8 @@ static void parse_config(GKeyFile *config)
connman_settings.auto_connect =
parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect));
+ g_strfreev(str_list);
+
g_clear_error(&error);
str_list = __connman_config_get_string_list(config, "General",
@@ -369,17 +422,17 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_INPUTREQ, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_inputreq = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_inputreq = integer * 1000;
g_clear_error(&error);
- timeout = g_key_file_get_integer(config, "General",
+ integer = g_key_file_get_integer(config, "General",
CONF_TIMEOUT_BROWSERLAUNCH, &error);
- if (!error && timeout >= 0)
- connman_settings.timeout_browserlaunch = timeout * 1000;
+ if (!error && integer >= 0)
+ connman_settings.timeout_browserlaunch = integer * 1000;
g_clear_error(&error);
@@ -440,10 +493,10 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
- vendor_class_id = __connman_config_get_string(config, "General",
+ string = __connman_config_get_string(config, "General",
CONF_VENDOR_CLASS_ID, &error);
if (!error)
- connman_settings.vendor_class_id = vendor_class_id;
+ connman_settings.vendor_class_id = string;
g_clear_error(&error);
@@ -458,6 +511,61 @@ static void parse_config(GKeyFile *config)
g_clear_error(&error);
boolean = __connman_config_get_bool(config, "General",
+ CONF_ENABLE_ONLINE_TO_READY_TRANSITION, &error);
+ if (!error) {
+ connman_settings.enable_online_to_ready_transition = boolean;
+ }
+
+ g_clear_error(&error);
+
+ string = __connman_config_get_string(config, "General",
+ CONF_ONLINE_CHECK_IPV4_URL, &error);
+ if (!error)
+ connman_settings.online_check_ipv4_url = string;
+ else
+ connman_settings.online_check_ipv4_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV4_URL);
+
+ g_clear_error(&error);
+
+ string = __connman_config_get_string(config, "General",
+ CONF_ONLINE_CHECK_IPV6_URL, &error);
+ if (!error)
+ connman_settings.online_check_ipv6_url = string;
+ else
+ connman_settings.online_check_ipv6_url =
+ g_strdup(DEFAULT_ONLINE_CHECK_IPV6_URL);
+
+
+ g_clear_error(&error);
+
+ integer = g_key_file_get_integer(config, "General",
+ CONF_ONLINE_CHECK_INITIAL_INTERVAL, &error);
+ if (!error && integer >= 0)
+ connman_settings.online_check_initial_interval = integer;
+
+ g_clear_error(&error);
+
+ integer = g_key_file_get_integer(config, "General",
+ CONF_ONLINE_CHECK_MAX_INTERVAL, &error);
+ if (!error && integer >= 0)
+ connman_settings.online_check_max_interval = integer;
+
+ g_clear_error(&error);
+
+ if (connman_settings.online_check_initial_interval < 1 ||
+ connman_settings.online_check_initial_interval >
+ connman_settings.online_check_max_interval) {
+ connman_warn("Incorrect online check intervals [%u, %u]",
+ connman_settings.online_check_initial_interval,
+ connman_settings.online_check_max_interval);
+ connman_settings.online_check_initial_interval =
+ DEFAULT_ONLINE_CHECK_INITIAL_INTERVAL;
+ connman_settings.online_check_max_interval =
+ DEFAULT_ONLINE_CHECK_MAX_INTERVAL;
+ }
+
+ boolean = __connman_config_get_bool(config, "General",
CONF_AUTO_CONNECT_ROAMING_SERVICES, &error);
if (!error)
connman_settings.auto_connect_roaming_services = boolean;
@@ -476,6 +584,29 @@ static void parse_config(GKeyFile *config)
connman_settings.use_gateways_as_timeservers = boolean;
g_clear_error(&error);
+
+ string = __connman_config_get_string(config, "General",
+ CONF_LOCALTIME, &error);
+ if (!error)
+ connman_settings.localtime = string;
+ else
+ g_free(string);
+
+ g_clear_error(&error);
+
+ boolean = __connman_config_get_bool(config, "General",
+ CONF_REGDOM_FOLLOWS_TIMEZONE, &error);
+ if (!error)
+ connman_settings.regdom_follows_timezone = boolean;
+
+ string = __connman_config_get_string(config, "General",
+ CONF_RESOLV_CONF, &error);
+ if (!error)
+ connman_settings.resolv_conf = string;
+ else
+ g_free(string);
+
+ g_clear_error(&error);
}
static int config_init(const char *file)
@@ -648,11 +779,17 @@ static GOptionEntry options[] = {
{ NULL },
};
-const char *connman_option_get_string(const char *key)
+char *connman_setting_get_string(const char *key)
{
if (g_str_equal(key, CONF_VENDOR_CLASS_ID))
return connman_settings.vendor_class_id;
+ if (g_str_equal(key, CONF_ONLINE_CHECK_IPV4_URL))
+ return connman_settings.online_check_ipv4_url;
+
+ if (g_str_equal(key, CONF_ONLINE_CHECK_IPV6_URL))
+ return connman_settings.online_check_ipv6_url;
+
if (g_strcmp0(key, "wifi") == 0) {
if (!option_wifi)
return "nl80211,wext";
@@ -660,6 +797,10 @@ const char *connman_option_get_string(const char *key)
return option_wifi;
}
+ if (g_str_equal(key, CONF_LOCALTIME))
+ return connman_settings.localtime ?
+ connman_settings.localtime : DEFAULT_LOCALTIME;
+
return NULL;
}
@@ -686,6 +827,9 @@ bool connman_setting_get_bool(const char *key)
if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK))
return connman_settings.enable_online_check;
+ if (g_str_equal(key, CONF_ENABLE_ONLINE_TO_READY_TRANSITION))
+ return connman_settings.enable_online_to_ready_transition;
+
if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES))
return connman_settings.auto_connect_roaming_services;
@@ -695,9 +839,26 @@ bool connman_setting_get_bool(const char *key)
if (g_str_equal(key, CONF_USE_GATEWAYS_AS_TIMESERVERS))
return connman_settings.use_gateways_as_timeservers;
+ if (g_str_equal(key, CONF_REGDOM_FOLLOWS_TIMEZONE))
+ return connman_settings.regdom_follows_timezone;
+
+ if (g_str_equal(key, CONF_RESOLV_CONF))
+ return connman_settings.resolv_conf;
+
return false;
}
+unsigned int connman_setting_get_uint(const char *key)
+{
+ if (g_str_equal(key, CONF_ONLINE_CHECK_INITIAL_INTERVAL))
+ return connman_settings.online_check_initial_interval;
+
+ if (g_str_equal(key, CONF_ONLINE_CHECK_MAX_INTERVAL))
+ return connman_settings.online_check_max_interval;
+
+ return 0;
+}
+
char **connman_setting_get_string_list(const char *key)
{
if (g_str_equal(key, CONF_PREF_TIMESERVERS))
@@ -919,6 +1080,10 @@ int main(int argc, char *argv[])
g_strfreev(connman_settings.fallback_nameservers);
g_strfreev(connman_settings.blacklisted_interfaces);
g_strfreev(connman_settings.tethering_technologies);
+ g_free(connman_settings.vendor_class_id);
+ g_free(connman_settings.online_check_ipv4_url);
+ g_free(connman_settings.online_check_ipv6_url);
+ g_free(connman_settings.localtime);
g_free(option_debug);
g_free(option_wifi);
diff --git a/src/main.conf b/src/main.conf
index 14965e12..ddd57996 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -125,6 +125,31 @@
# Default value is true.
# EnableOnlineCheck = false
+# Urls (IPv4 and IPv6 respectively) used during the online status check.
+# Please refer to the README for more detailed information.
+# Default values are http://ipv4.connman.net/online/status.html and
+# http://ipv6.connman.net/online/status.html respectively.
+# OnlineCheckIPv4URL= http://ipv4.connman.net/online/status.html
+# OnlineCheckIPv6URL= http://ipv6.connman.net/online/status.html
+
+# Range of intervals between two online check requests.
+# Please refer to the README for more detailed information.
+# Default values are 1 and 12 respectively.
+# OnlineCheckInitialInterval = 1
+# OnlineCheckMaxInterval = 12
+
+# WARNING: Experimental feature!!!
+# In addition to EnableOnlineCheck setting, enable or disable use of HTTP GET
+# to detect the loss of end-to-end connectivity.
+# If this setting is false, when the default service transitions to ONLINE
+# state, the HTTP GET request is no more called until next cycle, initiated
+# by a transition of the default service to DISCONNECT state.
+# If this setting is true, the HTTP GET request keeps beeing called to guarantee
+# that end-to-end connectivity is still successful. If not, the default service
+# will transition to READY state, enabling another service to become the
+# default one, in replacement.
+# EnableOnlineToReadyTransition = false
+
# List of technologies with AutoConnect = true which are always connected
# regardless of PreferredTechnologies setting. Default value is empty and
# will connect a technology only if it is at a higher preference than any
diff --git a/src/manager.c b/src/manager.c
index 3bf8f4e4..892d3a42 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -600,6 +600,9 @@ static const GDBusSignalTable manager_signals[] = {
{ GDBUS_SIGNAL("PeersChanged",
GDBUS_ARGS({ "changed", "a(oa{sv})" },
{ "removed", "ao" })) },
+ { GDBUS_SIGNAL("TetheringClientsChanged",
+ GDBUS_ARGS({ "registered", "as" },
+ { "removed", "as" })) },
{ },
};
diff --git a/src/network.c b/src/network.c
index f2ab16bd..e3e02d16 100644
--- a/src/network.c
+++ b/src/network.c
@@ -940,7 +940,7 @@ static void set_disconnected(struct connman_network *network)
{
struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
enum connman_ipconfig_method ipv4_method, ipv6_method;
- enum connman_service_state state;
+ enum connman_service_state state_ipv4, state_ipv6;
struct connman_service *service;
service = connman_service_lookup_from_network(network);
@@ -1006,18 +1006,18 @@ static void set_disconnected(struct connman_network *network)
* or in failure. It does not make sense to go to disconnect
* state if we were not connected.
*/
- state = __connman_service_ipconfig_get_state(service,
+ state_ipv4 = __connman_service_ipconfig_get_state(service,
CONNMAN_IPCONFIG_TYPE_IPV4);
- if (state != CONNMAN_SERVICE_STATE_IDLE &&
- state != CONNMAN_SERVICE_STATE_FAILURE)
+ if (state_ipv4 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_DISCONNECT,
CONNMAN_IPCONFIG_TYPE_IPV4);
- state = __connman_service_ipconfig_get_state(service,
+ state_ipv6 = __connman_service_ipconfig_get_state(service,
CONNMAN_IPCONFIG_TYPE_IPV6);
- if (state != CONNMAN_SERVICE_STATE_IDLE &&
- state != CONNMAN_SERVICE_STATE_FAILURE)
+ if (state_ipv6 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv6 != CONNMAN_SERVICE_STATE_FAILURE)
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_DISCONNECT,
CONNMAN_IPCONFIG_TYPE_IPV6);
@@ -1041,11 +1041,15 @@ static void set_disconnected(struct connman_network *network)
}
}
- __connman_service_ipconfig_indicate_state(service,
+ if (state_ipv4 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv4 != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV4);
- __connman_service_ipconfig_indicate_state(service,
+ if (state_ipv6 != CONNMAN_SERVICE_STATE_IDLE &&
+ state_ipv6 != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_IDLE,
CONNMAN_IPCONFIG_TYPE_IPV6);
@@ -1797,8 +1801,6 @@ int __connman_network_connect(struct connman_network *network)
if (!network->device)
return -ENODEV;
- __connman_device_disconnect(network->device);
-
network->connecting = true;
err = network->driver->connect(network);
@@ -1850,6 +1852,19 @@ int __connman_network_disconnect(struct connman_network *network)
return err;
}
+int __connman_network_forget(struct connman_network *network)
+{
+ DBG("network %p", network);
+
+ if (!network->driver)
+ return -EUNATCH;
+
+ if (network->driver->forget)
+ return network->driver->forget(network);
+
+ return 0;
+}
+
int __connman_network_clear_ipconfig(struct connman_network *network,
struct connman_ipconfig *ipconfig)
{
@@ -2112,6 +2127,21 @@ int connman_network_set_wifi_channel(struct connman_network *network,
return 0;
}
+int connman_network_set_autoconnect(struct connman_network *network,
+ bool autoconnect)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return 0;
+ return network->driver->set_autoconnect(network, autoconnect);
+}
+
+bool __connman_network_native_autoconnect(struct connman_network *network)
+{
+ if (!network->driver || !network->driver->set_autoconnect)
+ return false;
+ return true;
+}
+
uint16_t connman_network_get_wifi_channel(struct connman_network *network)
{
return network->wifi.channel;
diff --git a/src/ntp.c b/src/ntp.c
index e7fee22a..5d0df9e5 100644
--- a/src/ntp.c
+++ b/src/ntp.c
@@ -577,7 +577,7 @@ int __connman_ntp_start(char *server, __connman_ntp_cb_t callback,
return -EINVAL;
if (ntp_data) {
- connman_warn("ntp_data is not NULL (timerserver %s)",
+ connman_warn("ntp_data is not NULL (timeserver %s)",
ntp_data->timeserver);
free_ntp_data(ntp_data);
}
diff --git a/src/peer.c b/src/peer.c
index 2102f119..bad5c841 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -154,7 +154,7 @@ static int start_dhcp_server(struct connman_peer *peer)
err = __connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
- gateway, NULL, prefixlen, broadcast);
+ gateway, NULL, prefixlen, broadcast, true);
if (err < 0)
goto error;
@@ -983,7 +983,10 @@ void connman_peer_add_service(struct connman_peer *peer,
service = g_malloc0(sizeof(struct _peer_service));
service->type = type;
- service->data = g_memdup(data, data_length * sizeof(unsigned char));
+ if (data_length > 0) {
+ service->data = g_malloc(data_length * sizeof(unsigned char));
+ memcpy(service->data, data, data_length * sizeof(unsigned char));
+ }
service->length = data_length;
peer->services = g_slist_prepend(peer->services, service);
diff --git a/src/provider.c b/src/provider.c
index 7d663e0c..e2091846 100644
--- a/src/provider.c
+++ b/src/provider.c
@@ -53,6 +53,7 @@ void __connman_provider_append_properties(struct connman_provider *provider,
DBusMessageIter *iter)
{
const char *host, *domain, *type;
+ dbus_bool_t split_routing;
if (!provider->driver || !provider->driver->get_property)
return;
@@ -72,6 +73,12 @@ void __connman_provider_append_properties(struct connman_provider *provider,
if (type)
connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
&type);
+
+ if (provider->vpn_service) {
+ split_routing = connman_provider_is_split_routing(provider);
+ connman_dbus_dict_append_basic(iter, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing);
+ }
}
struct connman_provider *
@@ -435,6 +442,15 @@ const char *__connman_provider_get_ident(struct connman_provider *provider)
return provider->identifier;
}
+const char * __connman_provider_get_transport_ident(
+ struct connman_provider *provider)
+{
+ if (provider && provider && provider->driver && provider->driver->get_property)
+ return provider->driver->get_property(provider, "Transport");
+
+ return NULL;
+}
+
int connman_provider_set_string(struct connman_provider *provider,
const char *key, const char *value)
{
@@ -597,6 +613,100 @@ void connman_provider_set_autoconnect(struct connman_provider *provider,
__connman_service_save(provider->vpn_service);
}
+bool connman_provider_is_split_routing(struct connman_provider *provider)
+{
+ if (!provider || !provider->vpn_service)
+ return false;
+
+ return __connman_service_is_split_routing(provider->vpn_service);
+}
+
+int connman_provider_set_split_routing(struct connman_provider *provider,
+ bool split_routing)
+{
+ struct connman_service *service;
+ enum connman_ipconfig_type type;
+ int service_index;
+ int vpn_index;
+ bool service_split_routing;
+ int err = 0;
+
+ DBG("");
+
+ if (!provider || !provider->vpn_service)
+ return -EINVAL;
+
+ service_split_routing = __connman_service_is_split_routing(
+ provider->vpn_service);
+
+ if (service_split_routing == split_routing) {
+ DBG("split_routing already set %s",
+ split_routing ? "true" : "false");
+ return -EALREADY;
+ }
+
+ switch (provider->family) {
+ case AF_INET:
+ type = CONNMAN_IPCONFIG_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ type = CONNMAN_IPCONFIG_TYPE_IPV6;
+ break;
+ case AF_UNSPEC:
+ type = CONNMAN_IPCONFIG_TYPE_ALL;
+ break;
+ default:
+ type = CONNMAN_IPCONFIG_TYPE_UNKNOWN;
+ }
+
+ if (!__connman_service_is_connected_state(provider->vpn_service,
+ type)) {
+ DBG("%p VPN not connected", provider->vpn_service);
+ goto save;
+ }
+
+ vpn_index = __connman_service_get_index(provider->vpn_service);
+ service_index = __connman_connection_get_vpn_phy_index(vpn_index);
+ service = __connman_service_lookup_from_index(service_index);
+ if (!service)
+ goto save;
+
+ if (split_routing)
+ err = __connman_service_move(service, provider->vpn_service,
+ true);
+ else
+ err = __connman_service_move(provider->vpn_service, service,
+ true);
+
+ if (err) {
+ connman_warn("cannot move service %p and VPN %p error %d",
+ service, provider->vpn_service, err);
+
+ /*
+ * In case of error notify vpnd about the current split routing
+ * state.
+ */
+ __connman_service_split_routing_changed(provider->vpn_service);
+ goto out;
+ }
+
+save:
+ __connman_service_set_split_routing(provider->vpn_service,
+ split_routing);
+ __connman_service_save(provider->vpn_service);
+
+out:
+ return err;
+}
+
+int connman_provider_get_family(struct connman_provider *provider)
+{
+ if (!provider)
+ return AF_UNSPEC;
+
+ return provider->family;
+}
+
static void unregister_provider(gpointer data)
{
struct connman_provider *provider = data;
diff --git a/src/resolver.c b/src/resolver.c
index 618353fd..4ab51d6b 100644
--- a/src/resolver.c
+++ b/src/resolver.c
@@ -103,6 +103,7 @@ static int resolvfile_export(void)
int fd, err;
unsigned int count;
mode_t old_umask;
+ const char *resolv_conf;
content = g_string_new("# Generated by Connection Manager\n");
@@ -161,15 +162,33 @@ static int resolvfile_export(void)
old_umask = umask(022);
- fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd < 0) {
- connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" "
- "falling back to "RESOLV_CONF_ETC);
+ resolv_conf = connman_setting_get_string("ResolvConf");
+ /*
+ * TODO: This is mainly for backward compatibility. In some future version,
+ * "ResolvConf" setting should default to RESOLV_CONF_STATEDIR or
+ * RESOLV_CONF_ETC and this branch can be removed.
+ */
+ if (resolv_conf == NULL) {
+ fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" "
+ "falling back to "RESOLV_CONF_ETC);
- fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ err = -errno;
+ goto done;
+ }
+ }
+ } else if (resolv_conf[0] == '\0' || strcmp(resolv_conf, "/dev/null") == 0) {
+ err = 0;
+ goto done;
+ } else {
+ fd = open(resolv_conf, O_RDWR | O_CREAT | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
err = -errno;
goto done;
diff --git a/src/rtnl.c b/src/rtnl.c
index dfe6bb69..e8a8325e 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -177,6 +177,9 @@ static void read_uevent(struct interface_data *interface)
} else if (strcmp(line + 8, "bond") == 0) {
interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
+ } else if (strcmp(line + 8, "dsa") == 0) {
+ interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET;
+ interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET;
} else {
interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN;
@@ -883,7 +886,7 @@ static inline void print_attr(struct rtattr *attr, const char *name)
print(" attr %d (len %d)\n", attr->rta_type, len);
}
-static void rtnl_link(struct nlmsghdr *hdr)
+static void rtnl_link(struct nlmsghdr *hdr, bool *has_master)
{
struct ifinfomsg *msg;
struct rtattr *attr;
@@ -925,6 +928,7 @@ static void rtnl_link(struct nlmsghdr *hdr)
print_attr(attr, "priority");
break;
case IFLA_MASTER:
+ *has_master = true;
print_attr(attr, "master");
break;
case IFLA_WIRELESS:
@@ -957,22 +961,32 @@ static void rtnl_link(struct nlmsghdr *hdr)
static void rtnl_newlink(struct nlmsghdr *hdr)
{
+ bool has_master = false;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- rtnl_link(hdr);
+ rtnl_link(hdr, &has_master);
if (hdr->nlmsg_type == IFLA_WIRELESS)
connman_warn_once("Obsolete WEXT WiFi driver detected");
+ /* ignore RTM_NEWLINK when adding interface to bridge */
+ if (has_master)
+ return;
+
process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
msg->ifi_change, msg, IFA_PAYLOAD(hdr));
}
static void rtnl_dellink(struct nlmsghdr *hdr)
{
+ bool has_master = false;
struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
- rtnl_link(hdr);
+ rtnl_link(hdr, &has_master);
+
+ /* ignore RTM_DELLINK when removing interface from bridge */
+ if (has_master)
+ return;
process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
msg->ifi_change, msg, IFA_PAYLOAD(hdr));
@@ -1305,75 +1319,71 @@ static const char *type2string(uint16_t type)
static GIOChannel *channel = NULL;
static guint channel_watch = 0;
-struct rtnl_request {
- struct nlmsghdr hdr;
- struct rtgenmsg msg;
-};
-#define RTNL_REQUEST_SIZE (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+#define RTNL_REQUEST_SIZE (NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(struct rtgenmsg)))
static GSList *request_list = NULL;
static guint32 request_seq = 0;
-static struct rtnl_request *find_request(guint32 seq)
+static struct nlmsghdr *find_request(guint32 seq)
{
GSList *list;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr = list->data;
- if (req->hdr.nlmsg_seq == seq)
- return req;
+ if (hdr->nlmsg_seq == seq)
+ return hdr;
}
return NULL;
}
-static int send_request(struct rtnl_request *req)
+static int send_request(struct nlmsghdr *hdr)
{
struct sockaddr_nl addr;
int sk;
DBG("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
sk = g_io_channel_unix_get_fd(channel);
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- return sendto(sk, req, req->hdr.nlmsg_len, 0,
+ return sendto(sk, hdr, hdr->nlmsg_len, 0,
(struct sockaddr *) &addr, sizeof(addr));
}
-static int queue_request(struct rtnl_request *req)
+static int queue_request(struct nlmsghdr *hdr)
{
- request_list = g_slist_append(request_list, req);
+ request_list = g_slist_append(request_list, hdr);
if (g_slist_length(request_list) > 1)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static int process_response(guint32 seq)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
DBG("seq %d", seq);
- req = find_request(seq);
- if (req) {
- request_list = g_slist_remove(request_list, req);
- g_free(req);
+ hdr = find_request(seq);
+ if (hdr) {
+ request_list = g_slist_remove(request_list, hdr);
+ g_free(hdr);
}
- req = g_slist_nth_data(request_list, 0);
- if (!req)
+ hdr = g_slist_nth_data(request_list, 0);
+ if (!hdr)
return 0;
- return send_request(req);
+ return send_request(hdr);
}
static void rtnl_message(void *buf, size_t len)
@@ -1471,62 +1481,65 @@ static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, gpointer data
static int send_getlink(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETLINK;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETLINK;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static int send_getaddr(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETADDR;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETADDR;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- return queue_request(req);
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
+
+ return queue_request(hdr);
}
static int send_getroute(void)
{
- struct rtnl_request *req;
+ struct nlmsghdr *hdr;
+ struct rtgenmsg *msg;
DBG("");
- req = g_try_malloc0(RTNL_REQUEST_SIZE);
- if (!req)
- return -ENOMEM;
+ hdr = g_malloc0(RTNL_REQUEST_SIZE);
+
+ hdr->nlmsg_len = RTNL_REQUEST_SIZE;
+ hdr->nlmsg_type = RTM_GETROUTE;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = request_seq++;
- req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
- req->hdr.nlmsg_type = RTM_GETROUTE;
- req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req->hdr.nlmsg_pid = 0;
- req->hdr.nlmsg_seq = request_seq++;
- req->msg.rtgen_family = AF_INET;
+ msg = (struct rtgenmsg *) NLMSG_DATA(hdr);
+ msg->rtgen_family = AF_INET;
- return queue_request(req);
+ return queue_request(hdr);
}
static gboolean update_timeout_cb(gpointer user_data)
@@ -1670,14 +1683,14 @@ void __connman_rtnl_cleanup(void)
update_list = NULL;
for (list = request_list; list; list = list->next) {
- struct rtnl_request *req = list->data;
+ struct nlmsghdr *hdr= list->data;
DBG("%s len %d type %d flags 0x%04x seq %d",
- type2string(req->hdr.nlmsg_type),
- req->hdr.nlmsg_len, req->hdr.nlmsg_type,
- req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+ type2string(hdr->nlmsg_type),
+ hdr->nlmsg_len, hdr->nlmsg_type,
+ hdr->nlmsg_flags, hdr->nlmsg_seq);
- g_free(req);
+ g_free(hdr);
list->data = NULL;
}
diff --git a/src/service.c b/src/service.c
index 2f497d10..06d02322 100644
--- a/src/service.c
+++ b/src/service.c
@@ -49,11 +49,15 @@ static DBusConnection *connection = NULL;
static GList *service_list = NULL;
static GHashTable *service_hash = NULL;
+static GHashTable *passphrase_requested = NULL;
static GSList *counter_list = NULL;
static unsigned int autoconnect_id = 0;
static unsigned int vpn_autoconnect_id = 0;
static struct connman_service *current_default = NULL;
static bool services_dirty = false;
+static bool enable_online_to_ready_transition = false;
+static unsigned int online_check_initial_interval = 0;
+static unsigned int online_check_max_interval = 0;
struct connman_stats {
bool valid;
@@ -133,9 +137,10 @@ struct connman_service {
char *pac;
bool wps;
bool wps_advertizing;
- guint online_timeout;
- int online_check_interval_ipv4;
- int online_check_interval_ipv6;
+ guint online_timeout_ipv4;
+ guint online_timeout_ipv6;
+ unsigned int online_check_interval_ipv4;
+ unsigned int online_check_interval_ipv6;
bool do_split_routing;
bool new_service;
bool hidden_service;
@@ -151,6 +156,7 @@ static struct connman_ipconfig *create_ip6config(struct connman_service *service
int index);
static void dns_changed(struct connman_service *service);
static void vpn_auto_connect(void);
+static void trigger_autoconnect(struct connman_service *service);
struct find_data {
const char *path;
@@ -192,6 +198,8 @@ static const char *reason2string(enum connman_service_connect_reason reason)
return "auto";
case CONNMAN_SERVICE_CONNECT_REASON_SESSION:
return "session";
+ case CONNMAN_SERVICE_CONNECT_REASON_NATIVE:
+ return "native";
}
return "unknown";
@@ -367,7 +375,26 @@ static enum connman_service_proxy_method string2proxymethod(const char *method)
return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
}
-static void set_split_routing(struct connman_service *service, bool value)
+void __connman_service_split_routing_changed(struct connman_service *service)
+{
+ dbus_bool_t split_routing;
+
+ if (!service->path)
+ return;
+
+ if (!allow_property_changed(service))
+ return;
+
+ split_routing = service->do_split_routing;
+ if (!connman_dbus_property_changed_basic(service->path,
+ CONNMAN_SERVICE_INTERFACE, "SplitRouting",
+ DBUS_TYPE_BOOLEAN, &split_routing))
+ connman_warn("cannot send SplitRouting property change on %s",
+ service->identifier);
+}
+
+void __connman_service_set_split_routing(struct connman_service *service,
+ bool value)
{
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
return;
@@ -378,6 +405,12 @@ static void set_split_routing(struct connman_service *service, bool value)
service->order = 0;
else
service->order = 10;
+
+ /*
+ * In order to make sure the value is propagated also when loading the
+ * VPN service signal the value regardless of the value change.
+ */
+ __connman_service_split_routing_changed(service);
}
int __connman_service_load_modifiable(struct connman_service *service)
@@ -400,9 +433,10 @@ int __connman_service_load_modifiable(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
/* fall through */
case CONNMAN_SERVICE_TYPE_WIFI:
@@ -456,9 +490,10 @@ static int service_load(struct connman_service *service)
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_VPN:
- set_split_routing(service, g_key_file_get_boolean(keyfile,
- service->identifier,
- "SplitRouting", NULL));
+ __connman_service_set_split_routing(service,
+ g_key_file_get_boolean(keyfile,
+ service->identifier,
+ "SplitRouting", NULL));
autoconnect = g_key_file_get_boolean(keyfile,
service->identifier, "AutoConnect", &error);
@@ -545,8 +580,10 @@ static int service_load(struct connman_service *service)
str = g_key_file_get_string(keyfile,
service->identifier, "Passphrase", NULL);
if (str) {
+ char *dec = g_strcompress(str);
+ g_free(str);
g_free(service->passphrase);
- service->passphrase = str;
+ service->passphrase = dec;
}
if (service->ipconfig_ipv4)
@@ -709,9 +746,12 @@ static int service_save(struct connman_service *service)
g_free(str);
}
- if (service->passphrase && strlen(service->passphrase) > 0)
+ if (service->passphrase && strlen(service->passphrase) > 0) {
+ char *enc = g_strescape(service->passphrase, NULL);
g_key_file_set_string(keyfile, service->identifier,
- "Passphrase", service->passphrase);
+ "Passphrase", enc);
+ g_free(enc);
+ }
if (service->ipconfig_ipv4)
__connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
@@ -1400,12 +1440,16 @@ static bool check_proxy_setup(struct connman_service *service)
static void cancel_online_check(struct connman_service *service)
{
- if (service->online_timeout == 0)
- return;
-
- g_source_remove(service->online_timeout);
- service->online_timeout = 0;
- connman_service_unref(service);
+ if (service->online_timeout_ipv4) {
+ g_source_remove(service->online_timeout_ipv4);
+ service->online_timeout_ipv4 = 0;
+ connman_service_unref(service);
+ }
+ if (service->online_timeout_ipv6) {
+ g_source_remove(service->online_timeout_ipv6);
+ service->online_timeout_ipv6 = 0;
+ connman_service_unref(service);
+ }
}
static void start_online_check(struct connman_service *service,
@@ -1416,8 +1460,14 @@ static void start_online_check(struct connman_service *service,
"Default service remains in READY state.");
return;
}
+ enable_online_to_ready_transition =
+ connman_setting_get_bool("EnableOnlineToReadyTransition");
+ online_check_initial_interval =
+ connman_setting_get_uint("OnlineCheckInitialInterval");
+ online_check_max_interval =
+ connman_setting_get_uint("OnlineCheckMaxInterval");
- if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) {
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV6 || check_proxy_setup(service)) {
cancel_online_check(service);
__connman_service_wispr_start(service, type);
}
@@ -1432,7 +1482,8 @@ static void address_updated(struct connman_service *service,
nameserver_add_all(service, type);
start_online_check(service, type);
- __connman_timeserver_sync(service);
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_ADDRESS_UPDATE);
}
}
@@ -1551,6 +1602,25 @@ bool __connman_service_index_is_default(int index)
return __connman_service_get_index(service) == index;
}
+static void start_wispr_when_connected(struct connman_service *service)
+{
+ if (!connman_setting_get_bool("EnableOnlineCheck")) {
+ connman_info("Online check disabled. "
+ "Default service remains in READY state.");
+ return;
+ }
+
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ if (__connman_service_is_connected_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6))
+ __connman_service_wispr_start(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
static void default_changed(void)
{
struct connman_service *service = connman_service_get_default();
@@ -1575,6 +1645,8 @@ static void default_changed(void)
connman_setting_get_bool("AllowDomainnameUpdates"))
__connman_utsname_set_domainname(service->domainname);
+ start_wispr_when_connected(service);
+
/*
* Connect VPN automatically when new default service
* is set and connected, unless new default is VPN
@@ -1693,6 +1765,8 @@ bool connman_service_set_autoconnect(struct connman_service *service,
service->autoconnect = autoconnect;
autoconnect_changed(service);
+ connman_network_set_autoconnect(service->network, autoconnect);
+
return true;
}
@@ -2525,7 +2599,6 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
case CONNMAN_SERVICE_TYPE_UNKNOWN:
case CONNMAN_SERVICE_TYPE_SYSTEM:
case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_P2P:
break;
case CONNMAN_SERVICE_TYPE_CELLULAR:
@@ -2536,6 +2609,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
connman_dbus_dict_append_dict(dict, "Ethernet",
append_ethernet, service);
break;
+ case CONNMAN_SERVICE_TYPE_VPN:
case CONNMAN_SERVICE_TYPE_WIFI:
case CONNMAN_SERVICE_TYPE_ETHERNET:
case CONNMAN_SERVICE_TYPE_BLUETOOTH:
@@ -3167,6 +3241,9 @@ int __connman_service_check_passphrase(enum connman_service_security security,
return 0;
}
+static void set_error(struct connman_service *service,
+ enum connman_service_error error);
+
int __connman_service_set_passphrase(struct connman_service *service,
const char *passphrase)
{
@@ -3191,6 +3268,10 @@ int __connman_service_set_passphrase(struct connman_service *service,
connman_network_set_string(service->network, "WiFi.Passphrase",
service->passphrase);
+ if (service->hidden_service &&
+ service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY)
+ set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
+
return 0;
}
@@ -3428,15 +3509,28 @@ static void do_auto_connect(struct connman_service *service,
return;
/*
+ * Only user interaction should get VPN or WIFI connected in failure
+ * state.
+ */
+ if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
+ reason != CONNMAN_SERVICE_CONNECT_REASON_USER &&
+ (service->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service->type == CONNMAN_SERVICE_TYPE_WIFI))
+ return;
+
+ /*
+ * Do not use the builtin auto connect, instead rely on the
+ * native auto connect feature of the service.
+ */
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE)
+ return;
+
+ /*
* Run service auto connect for other than VPN services. Afterwards
* start also VPN auto connect process.
*/
if (service->type != CONNMAN_SERVICE_TYPE_VPN)
__connman_service_auto_connect(reason);
- /* Only user interaction should get VPN connected in failure state. */
- else if (service->state == CONNMAN_SERVICE_STATE_FAILURE &&
- reason != CONNMAN_SERVICE_CONNECT_REASON_USER)
- return;
vpn_auto_connect();
}
@@ -3515,14 +3609,6 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
return err;
}
-/*
- * We set the timeout to 1 sec so that we have a chance to get
- * necessary IPv6 router advertisement messages that might have
- * DNS data etc.
- */
-#define ONLINE_CHECK_INITIAL_INTERVAL 1
-#define ONLINE_CHECK_MAX_INTERVAL 12
-
void __connman_service_wispr_start(struct connman_service *service,
enum connman_ipconfig_type type)
{
@@ -3530,17 +3616,14 @@ void __connman_service_wispr_start(struct connman_service *service,
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
service->online_check_interval_ipv4 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
else
service->online_check_interval_ipv6 =
- ONLINE_CHECK_INITIAL_INTERVAL;
+ online_check_initial_interval;
__connman_wispr_start(service, type);
}
-static void set_error(struct connman_service *service,
- enum connman_service_error error);
-
static DBusMessage *set_property(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -3665,13 +3748,7 @@ static DBusMessage *set_property(DBusConnection *conn,
nameserver_add_all(service, CONNMAN_IPCONFIG_TYPE_ALL);
dns_configuration_changed(service);
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV4))
- __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
-
- if (__connman_service_is_connected_state(service,
- CONNMAN_IPCONFIG_TYPE_IPV6))
- __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6);
+ start_wispr_when_connected(service);
service_save(service);
} else if (g_str_equal(name, "Timeservers.Configuration")) {
@@ -3717,9 +3794,7 @@ static DBusMessage *set_property(DBusConnection *conn,
service_save(service);
timeservers_configuration_changed(service);
-
- if (service == connman_service_get_default())
- __connman_timeserver_sync(service);
+ __connman_timeserver_conf_update(service);
} else if (g_str_equal(name, "Domains.Configuration")) {
DBusMessageIter entry;
@@ -4151,6 +4226,7 @@ static bool auto_connect_service(GList *services,
bool ignore[MAX_CONNMAN_SERVICE_TYPES] = { };
bool autoconnecting = false;
GList *list;
+ int index;
DBG("preferred %d sessions %d reason %s", preferred, active_count,
reason2string(reason));
@@ -4166,6 +4242,17 @@ static bool auto_connect_service(GList *services,
continue;
}
+ if (service->connect_reason ==
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
+ DBG("service %p uses native autonnect, skip", service);
+ continue;
+ }
+
+ index = __connman_service_get_index(service);
+ if (g_hash_table_lookup(passphrase_requested,
+ GINT_TO_POINTER(index)))
+ return true;
+
if (service->pending ||
is_connecting(service->state) ||
is_connected(service->state)) {
@@ -4486,7 +4573,10 @@ static DBusMessage *connect_service(DBusConnection *conn,
DBG("service %p", service);
- if (service->pending)
+ /* Hidden services do not keep the pending msg, check it from agent */
+ if (service->pending || (service->hidden &&
+ __connman_agent_is_request_pending(service,
+ dbus_message_get_sender(msg))))
return __connman_error_in_progress(msg);
index = __connman_service_get_index(service);
@@ -4495,7 +4585,7 @@ static DBusMessage *connect_service(DBusConnection *conn,
struct connman_service *temp = list->data;
if (!is_connecting(temp->state) && !is_connected(temp->state))
- break;
+ continue;
if (service == temp)
continue;
@@ -4555,6 +4645,8 @@ bool __connman_service_remove(struct connman_service *service)
return false;
__connman_service_disconnect(service);
+ if (service->network)
+ __connman_network_forget(service->network);
g_free(service->passphrase);
service->passphrase = NULL;
@@ -4647,15 +4739,11 @@ static void apply_relevant_default_downgrade(struct connman_service *service)
struct connman_service *def_service;
def_service = connman_service_get_default();
- if (!def_service)
+ if (!def_service || def_service != service ||
+ def_service->state != CONNMAN_SERVICE_STATE_ONLINE)
return;
- if (def_service == service &&
- def_service->state == CONNMAN_SERVICE_STATE_ONLINE) {
- def_service->state = CONNMAN_SERVICE_STATE_READY;
- __connman_notifier_leave_online(def_service->type);
- state_changed(def_service);
- }
+ downgrade_state(def_service);
}
static void switch_default_service(struct connman_service *default_service,
@@ -4762,27 +4850,22 @@ static void service_schedule_changed(void)
services_notify->id = g_timeout_add(100, service_send_changed, NULL);
}
-static DBusMessage *move_service(DBusConnection *conn,
- DBusMessage *msg, void *user_data,
- bool before)
+int __connman_service_move(struct connman_service *service,
+ struct connman_service *target, bool before)
{
- struct connman_service *service = user_data;
- struct connman_service *target;
- const char *path;
enum connman_ipconfig_method target4, target6;
enum connman_ipconfig_method service4, service6;
DBG("service %p", service);
- dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
+ if (!service)
+ return -EINVAL;
if (!service->favorite)
- return __connman_error_not_supported(msg);
+ return -EOPNOTSUPP;
- target = find_service(path);
if (!target || !target->favorite || target == service)
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
if (target->type == CONNMAN_SERVICE_TYPE_VPN) {
/*
@@ -4793,14 +4876,14 @@ static DBusMessage *move_service(DBusConnection *conn,
connman_info("Cannot move service. "
"No routes defined for provider %s",
__connman_provider_get_ident(target->provider));
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
- set_split_routing(target, true);
+ __connman_service_set_split_routing(target, true);
} else
- set_split_routing(target, false);
+ __connman_service_set_split_routing(target, false);
- set_split_routing(service, false);
+ __connman_service_set_split_routing(service, false);
target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
target6 = __connman_ipconfig_get_method(target->ipconfig_ipv6);
@@ -4823,7 +4906,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (service6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4831,7 +4914,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (service4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4839,7 +4922,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (target6 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv6,
service->state_ipv6))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4847,7 +4930,7 @@ static DBusMessage *move_service(DBusConnection *conn,
if (target4 != CONNMAN_IPCONFIG_METHOD_OFF) {
if (!check_suitable_state(target->state_ipv4,
service->state_ipv4))
- return __connman_error_invalid_service(msg);
+ return -EINVAL;
}
}
@@ -4870,6 +4953,39 @@ static DBusMessage *move_service(DBusConnection *conn,
service_schedule_changed();
+ return 0;
+}
+
+static DBusMessage *move_service(DBusConnection *conn,
+ DBusMessage *msg, void *user_data,
+ bool before)
+{
+ struct connman_service *service = user_data;
+ struct connman_service *target;
+ const char *path;
+ int err;
+
+ DBG("service %p", service);
+
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ target = find_service(path);
+
+ err = __connman_service_move(service, target, before);
+ switch (err) {
+ case 0:
+ break;
+ case -EINVAL:
+ return __connman_error_invalid_service(msg);
+ case -EOPNOTSUPP:
+ return __connman_error_not_supported(msg);
+ default:
+ connman_warn("unsupported error code %d in move_service()",
+ err);
+ break;
+ }
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
@@ -5190,6 +5306,59 @@ void connman_service_unref_debug(struct connman_service *service,
g_hash_table_remove(service_hash, service->identifier);
}
+static gint service_compare(gconstpointer a, gconstpointer b);
+
+static gint service_compare_vpn(struct connman_service *a,
+ struct connman_service *b)
+{
+ struct connman_provider *provider;
+ struct connman_service *service;
+ struct connman_service *transport;
+ const char *ident;
+ bool reverse;
+
+ if (a->provider) {
+ provider = a->provider;
+ service = b;
+ reverse = false;
+ } else if (b->provider) {
+ provider = b->provider;
+ service = a;
+ reverse = true;
+ } else {
+ return 0;
+ }
+
+ ident = __connman_provider_get_transport_ident(provider);
+ transport = connman_service_lookup_from_identifier(ident);
+ if (!transport)
+ return 0;
+
+ if (reverse)
+ return service_compare(service, transport);
+
+ return service_compare(transport, service);
+}
+
+static gint service_compare_preferred(struct connman_service *service_a,
+ struct connman_service *service_b)
+{
+ unsigned int *tech_array;
+ int i;
+
+ tech_array = connman_setting_get_uint_list("PreferredTechnologies");
+ if (tech_array) {
+ for (i = 0; tech_array[i]; i++) {
+ if (tech_array[i] == service_a->type)
+ return -1;
+
+ if (tech_array[i] == service_b->type)
+ return 1;
+ }
+ }
+ return 0;
+}
+
static gint service_compare(gconstpointer a, gconstpointer b)
{
struct connman_service *service_a = (void *) a;
@@ -5204,11 +5373,26 @@ static gint service_compare(gconstpointer a, gconstpointer b)
b_connected = is_connected(state_b);
if (a_connected && b_connected) {
+ int rval;
+
+ /* Compare the VPN transport and the service */
+ if ((service_a->type == CONNMAN_SERVICE_TYPE_VPN ||
+ service_b->type == CONNMAN_SERVICE_TYPE_VPN) &&
+ service_b->type != service_a->type) {
+ rval = service_compare_vpn(service_a, service_b);
+ if (rval)
+ return rval;
+ }
+
if (service_a->order > service_b->order)
return -1;
if (service_a->order < service_b->order)
return 1;
+
+ rval = service_compare_preferred(service_a, service_b);
+ if (rval)
+ return rval;
}
if (state_a != state_b) {
@@ -5239,20 +5423,11 @@ static gint service_compare(gconstpointer a, gconstpointer b)
return 1;
if (service_a->type != service_b->type) {
- unsigned int *tech_array;
- int i;
+ int rval;
- tech_array = connman_setting_get_uint_list(
- "PreferredTechnologies");
- if (tech_array) {
- for (i = 0; tech_array[i]; i++) {
- if (tech_array[i] == service_a->type)
- return -1;
-
- if (tech_array[i] == service_b->type)
- return 1;
- }
- }
+ rval = service_compare_preferred(service_a, service_b);
+ if (rval)
+ return rval;
if (service_a->type == CONNMAN_SERVICE_TYPE_ETHERNET)
return -1;
@@ -5454,6 +5629,9 @@ int __connman_service_set_favorite_delayed(struct connman_service *service,
service->favorite = favorite;
favorite_changed(service);
+ /* If native autoconnect is in use, the favorite state may affect the
+ * autoconnect state, so it needs to be rerun. */
+ trigger_autoconnect(service);
if (!delay_ordering) {
@@ -5596,6 +5774,7 @@ static void report_error_cb(void *user_context, bool retry,
__connman_service_clear_error(service);
service_complete(service);
+ service_list_sort();
__connman_connection_update_gateway();
}
}
@@ -5642,6 +5821,7 @@ static void request_input_cb(struct connman_service *service,
struct connman_device *device;
const char *security;
int err = 0;
+ int index;
DBG("RequestInput return, %p", service);
@@ -5667,14 +5847,18 @@ static void request_input_cb(struct connman_service *service,
goto done;
}
- if (service->hidden && name_len > 0 && name_len <= 32) {
- device = connman_network_get_device(service->network);
- security = connman_network_get_string(service->network,
- "WiFi.Security");
- err = __connman_device_request_hidden_scan(device,
- name, name_len,
- identity, passphrase,
- security, user_data);
+ if (service->hidden) {
+ if (name_len > 0 && name_len <= 32) {
+ device = connman_network_get_device(service->network);
+ security = connman_network_get_string(service->network,
+ "WiFi.Security");
+ err = __connman_device_request_hidden_scan(device,
+ name, name_len,
+ identity, passphrase,
+ security, user_data);
+ } else {
+ err = -EINVAL;
+ }
if (err < 0)
__connman_service_return_error(service, -err,
user_data);
@@ -5700,6 +5884,10 @@ static void request_input_cb(struct connman_service *service,
err = __connman_service_set_passphrase(service, passphrase);
done:
+ index = __connman_service_get_index(service);
+ g_hash_table_remove(passphrase_requested,
+ GINT_TO_POINTER(index));
+
if (err >= 0) {
/* We forget any previous error. */
set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN);
@@ -5753,27 +5941,14 @@ static int service_update_preferred_order(struct connman_service *default_servic
struct connman_service *new_service,
enum connman_service_state new_state)
{
- unsigned int *tech_array;
- int i;
-
- if (!default_service || default_service == new_service ||
- default_service->state != new_state)
+ if (!default_service || default_service == new_service)
return 0;
- tech_array = connman_setting_get_uint_list("PreferredTechnologies");
- if (tech_array) {
-
- for (i = 0; tech_array[i] != 0; i += 1) {
- if (default_service->type == tech_array[i])
- return -EALREADY;
-
- if (new_service->type == tech_array[i]) {
- switch_default_service(default_service,
- new_service);
- __connman_connection_update_gateway();
- return 0;
- }
- }
+ if (service_compare_preferred(default_service, new_service) > 0) {
+ switch_default_service(default_service,
+ new_service);
+ __connman_connection_update_gateway();
+ return 0;
}
return -EALREADY;
@@ -5867,6 +6042,15 @@ static int service_indicate_state(struct connman_service *service)
break;
case CONNMAN_SERVICE_STATE_IDLE:
+ if (old_state == CONNMAN_SERVICE_STATE_FAILURE &&
+ service->connect_reason ==
+ CONNMAN_SERVICE_CONNECT_REASON_NATIVE &&
+ service->error ==
+ CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+ __connman_service_clear_error(service);
+ service_complete(service);
+ }
+
if (old_state != CONNMAN_SERVICE_STATE_DISCONNECT)
__connman_service_disconnect(service);
@@ -5909,12 +6093,12 @@ static int service_indicate_state(struct connman_service *service)
service->new_service = false;
- default_changed();
-
def_service = connman_service_get_default();
service_update_preferred_order(def_service, service, new_state);
+ default_changed();
+
__connman_service_set_favorite(service, true);
reply_pending(service, 0);
@@ -5983,12 +6167,14 @@ static int service_indicate_state(struct connman_service *service)
break;
case CONNMAN_SERVICE_STATE_FAILURE:
- if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+ if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+ service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
connman_agent_report_error(service, service->path,
error2string(service->error),
report_error_cb,
get_dbus_sender(service),
NULL);
+ goto notifier;
}
service_complete(service);
break;
@@ -5998,6 +6184,7 @@ static int service_indicate_state(struct connman_service *service)
__connman_connection_update_gateway();
+notifier:
if ((old_state == CONNMAN_SERVICE_STATE_ONLINE &&
new_state != CONNMAN_SERVICE_STATE_READY) ||
(old_state == CONNMAN_SERVICE_STATE_READY &&
@@ -6156,20 +6343,20 @@ static void service_rp_filter(struct connman_service *service,
static void redo_wispr(struct connman_service *service,
enum connman_ipconfig_type type)
{
- service->online_timeout = 0;
- connman_service_unref(service);
-
DBG("Retrying %s WISPr for %p %s",
__connman_ipconfig_type2string(type),
service, service->name);
__connman_wispr_start(service, type);
+ connman_service_unref(service);
}
static gboolean redo_wispr_ipv4(gpointer user_data)
{
struct connman_service *service = user_data;
+ service->online_timeout_ipv4 = 0;
+
redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV4);
return FALSE;
@@ -6179,16 +6366,24 @@ static gboolean redo_wispr_ipv6(gpointer user_data)
{
struct connman_service *service = user_data;
+ service->online_timeout_ipv6 = 0;
+
redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV6);
return FALSE;
}
-int __connman_service_online_check_failed(struct connman_service *service,
- enum connman_ipconfig_type type)
+void __connman_service_online_check(struct connman_service *service,
+ enum connman_ipconfig_type type,
+ bool success)
{
GSourceFunc redo_func;
- int *interval;
+ unsigned int *interval;
+ enum connman_service_state current_state;
+ int timeout;
+
+ DBG("service %p type %s success %d\n",
+ service, __connman_ipconfig_type2string(type), success);
if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
interval = &service->online_check_interval_ipv4;
@@ -6198,19 +6393,37 @@ int __connman_service_online_check_failed(struct connman_service *service,
redo_func = redo_wispr_ipv6;
}
+ if(!enable_online_to_ready_transition)
+ goto redo_func;
+
+ if (success) {
+ *interval = online_check_max_interval;
+ } else {
+ current_state = service->state;
+ downgrade_state(service);
+ if (current_state != service->state)
+ *interval = online_check_initial_interval;
+ if (service != connman_service_get_default()) {
+ return;
+ }
+ }
+
+redo_func:
DBG("service %p type %s interval %d", service,
__connman_ipconfig_type2string(type), *interval);
- service->online_timeout = g_timeout_add_seconds(*interval * *interval,
+ timeout = g_timeout_add_seconds(*interval * *interval,
redo_func, connman_service_ref(service));
+ if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+ service->online_timeout_ipv4 = timeout;
+ else
+ service->online_timeout_ipv6 = timeout;
/* Increment the interval for the next time, set a maximum timeout of
- * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds.
+ * online_check_max_interval seconds * online_check_max_interval seconds.
*/
- if (*interval < ONLINE_CHECK_MAX_INTERVAL)
+ if (*interval < online_check_max_interval)
(*interval)++;
-
- return EAGAIN;
}
int __connman_service_ipconfig_indicate_state(struct connman_service *service,
@@ -6317,7 +6530,8 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
if (!is_connected(old_state) && is_connected(new_state))
nameserver_add_all(service, type);
- __connman_timeserver_sync(service);
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_STATE_UPDATE);
return service_indicate_state(service);
}
@@ -6533,6 +6747,7 @@ static int service_connect(struct connman_service *service)
int __connman_service_connect(struct connman_service *service,
enum connman_service_connect_reason reason)
{
+ int index;
int err;
DBG("service %p state %s connect reason %s -> %s",
@@ -6567,6 +6782,12 @@ int __connman_service_connect(struct connman_service *service,
__connman_service_clear_error(service);
+ if (service->network && service->autoconnect &&
+ __connman_network_native_autoconnect(service->network)) {
+ DBG("service %p switch connecting reason to native", service);
+ reason = CONNMAN_SERVICE_CONNECT_REASON_NATIVE;
+ }
+
err = service_connect(service);
DBG("service %p err %d", service, err);
@@ -6590,7 +6811,8 @@ int __connman_service_connect(struct connman_service *service,
service->provider)
connman_provider_disconnect(service->provider);
- if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) {
+ if (reason == CONNMAN_SERVICE_CONNECT_REASON_USER ||
+ reason == CONNMAN_SERVICE_CONNECT_REASON_NATIVE) {
if (err == -ENOKEY || err == -EPERM) {
DBusMessage *pending = NULL;
const char *dbus_sender = get_dbus_sender(service);
@@ -6606,6 +6828,13 @@ int __connman_service_connect(struct connman_service *service,
service->pending = NULL;
}
+ if (service->hidden_service &&
+ service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY) {
+ __connman_service_indicate_error(service,
+ CONNMAN_SERVICE_ERROR_INVALID_KEY);
+ return err;
+ }
+
err = __connman_agent_request_passphrase_input(service,
request_input_cb,
dbus_sender,
@@ -6613,6 +6842,13 @@ int __connman_service_connect(struct connman_service *service,
if (service->hidden && err != -EINPROGRESS)
service->pending = pending;
+ if (err == -EINPROGRESS) {
+ index = __connman_service_get_index(service);
+ g_hash_table_replace(passphrase_requested,
+ GINT_TO_POINTER(index),
+ GINT_TO_POINTER(true));
+ }
+
return err;
}
}
@@ -6665,36 +6901,6 @@ int __connman_service_disconnect(struct connman_service *service)
return err;
}
-int __connman_service_disconnect_all(void)
-{
- struct connman_service *service;
- GSList *services = NULL, *list;
- GList *iter;
-
- DBG("");
-
- for (iter = service_list; iter; iter = iter->next) {
- service = iter->data;
-
- if (!is_connected(service->state))
- break;
-
- services = g_slist_prepend(services, service);
- }
-
- for (list = services; list; list = list->next) {
- struct connman_service *service = list->data;
-
- service->ignore = true;
-
- __connman_service_disconnect(service);
- }
-
- g_slist_free(services);
-
- return 0;
-}
-
/**
* lookup_by_identifier:
* @identifier: service identifier
@@ -6810,14 +7016,14 @@ static int service_register(struct connman_service *service)
DBG("path %s", service->path);
- if (__connman_config_provision_service(service) < 0)
- service_load(service);
-
g_dbus_register_interface(connection, service->path,
CONNMAN_SERVICE_INTERFACE,
service_methods, service_signals,
NULL, service, NULL);
+ if (__connman_config_provision_service(service) < 0)
+ service_load(service);
+
service_list_sort();
__connman_connection_update_gateway();
@@ -7222,6 +7428,50 @@ static void update_from_network(struct connman_service *service,
service_list_sort();
}
+static void trigger_autoconnect(struct connman_service *service)
+{
+ struct connman_device *device;
+ bool native;
+
+ if (!service->favorite)
+ return;
+
+ native = __connman_network_native_autoconnect(service->network);
+ if (native && service->autoconnect) {
+ DBG("trigger native autoconnect");
+ connman_network_set_autoconnect(service->network, true);
+ return;
+ }
+
+ device = connman_network_get_device(service->network);
+ if (device && connman_device_get_scanning(device, CONNMAN_SERVICE_TYPE_UNKNOWN))
+ return;
+
+ switch (service->type) {
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_P2P:
+ break;
+
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ if (service->autoconnect) {
+ __connman_service_connect(service,
+ CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+
+ /* fall through */
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO);
+ break;
+ }
+}
+
/**
* __connman_service_create_from_network:
* @network: network structure
@@ -7231,7 +7481,6 @@ static void update_from_network(struct connman_service *service,
struct connman_service * __connman_service_create_from_network(struct connman_network *network)
{
struct connman_service *service;
- struct connman_device *device;
const char *ident, *group;
char *name;
unsigned int *auto_connect_types, *favorite_types;
@@ -7261,8 +7510,19 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
if (__connman_network_get_weakness(network))
return service;
+ index = connman_network_get_index(network);
+
if (service->path) {
update_from_network(service, network);
+
+ if (service->ipconfig_ipv4)
+ __connman_ipconfig_set_index(service->ipconfig_ipv4,
+ index);
+
+ if (service->ipconfig_ipv6)
+ __connman_ipconfig_set_index(service->ipconfig_ipv6,
+ index);
+
__connman_connection_update_gateway();
return service;
}
@@ -7293,49 +7553,21 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne
update_from_network(service, network);
- index = connman_network_get_index(network);
-
if (!service->ipconfig_ipv4)
service->ipconfig_ipv4 = create_ip4config(service, index,
CONNMAN_IPCONFIG_METHOD_DHCP);
+ else
+ __connman_ipconfig_set_index(service->ipconfig_ipv4, index);
if (!service->ipconfig_ipv6)
service->ipconfig_ipv6 = create_ip6config(service, index);
+ else
+ __connman_ipconfig_set_index(service->ipconfig_ipv6, index);
service_register(service);
service_schedule_added(service);
- if (service->favorite) {
- device = connman_network_get_device(service->network);
- if (device && !connman_device_get_scanning(device,
- CONNMAN_SERVICE_TYPE_UNKNOWN)) {
-
- switch (service->type) {
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_P2P:
- break;
-
- case CONNMAN_SERVICE_TYPE_GADGET:
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- if (service->autoconnect) {
- __connman_service_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
-
- /* fall through */
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- do_auto_connect(service,
- CONNMAN_SERVICE_CONNECT_REASON_AUTO);
- break;
- }
- }
- }
+ trigger_autoconnect(service);
__connman_notifier_service_add(service, service->name);
@@ -7601,6 +7833,8 @@ int __connman_service_init(void)
service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, service_free);
+ passphrase_requested = g_hash_table_new(g_direct_hash, g_direct_equal);
+
services_notify = g_new0(struct _services_notify, 1);
services_notify->remove = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, NULL);
@@ -7633,6 +7867,9 @@ void __connman_service_cleanup(void)
g_hash_table_destroy(service_hash);
service_hash = NULL;
+ g_hash_table_destroy(passphrase_requested);
+ passphrase_requested = NULL;
+
g_slist_free(counter_list);
counter_list = NULL;
diff --git a/src/session.c b/src/session.c
index 2a1dd9aa..eeefe3f2 100644
--- a/src/session.c
+++ b/src/session.c
@@ -1804,7 +1804,7 @@ static void session_activate(struct connman_session *session)
struct connman_service *service;
struct connman_service_info *info;
GSList *service_list = NULL;
- enum connman_service_state state = CONNMAN_SESSION_STATE_DISCONNECTED;
+ enum connman_service_state state = CONNMAN_SERVICE_STATE_DISCONNECT;
g_hash_table_iter_init(&iter, service_hash);
diff --git a/src/shared/util.c b/src/shared/util.c
index 73c24aef..bda2d2b3 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
+#include <string.h>
#include "src/shared/util.h"
diff --git a/src/technology.c b/src/technology.c
index 4e053fc9..5c469111 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -66,6 +66,7 @@ struct connman_technology {
*/
char *tethering_ident;
char *tethering_passphrase;
+ int tethering_freq;
bool enable_persistent; /* Save the tech state */
@@ -185,10 +186,19 @@ static void technology_save(struct connman_technology *technology)
"Tethering.Identifier",
technology->tethering_ident);
- if (technology->tethering_passphrase)
+ if (technology->tethering_passphrase) {
+ char *enc = g_strescape(technology->tethering_passphrase, NULL);
g_key_file_set_string(keyfile, identifier,
- "Tethering.Passphrase",
- technology->tethering_passphrase);
+ "Tethering.Passphrase", enc);
+ g_free(enc);
+ }
+
+ if (technology->tethering_freq == 0)
+ technology->tethering_freq = 2412;
+
+ g_key_file_set_integer(keyfile, identifier,
+ "Tethering.Freq",
+ technology->tethering_freq);
done:
g_free(identifier);
@@ -262,8 +272,7 @@ static int set_tethering(struct connman_technology *technology,
if (!driver || !driver->set_tethering)
continue;
- err = driver->set_tethering(technology, ident, passphrase,
- bridge, enabled);
+ err = driver->set_tethering(technology, bridge, enabled);
if (result == -EINPROGRESS)
continue;
@@ -354,25 +363,32 @@ enum connman_service_type connman_technology_get_type
return technology->type;
}
-bool connman_technology_get_wifi_tethering(const char **ssid,
- const char **psk)
+bool connman_technology_get_wifi_tethering(const struct connman_technology *technology,
+ const char **ssid, const char **psk,
+ int *freq)
{
- struct connman_technology *technology;
+ bool force = true;
if (!ssid || !psk)
return false;
*ssid = *psk = NULL;
- technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+ /* Workaround for the neard plugin */
+ if (!technology) {
+ technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+ force = false;
+ }
+
if (!technology)
return false;
- if (!technology->tethering)
+ if (!force && !technology->tethering)
return false;
*ssid = technology->tethering_ident;
*psk = technology->tethering_passphrase;
+ *freq = technology->tethering_freq;
return true;
}
@@ -390,6 +406,7 @@ static void technology_load(struct connman_technology *technology)
gchar *identifier;
GError *error = NULL;
bool enable, need_saving = false;
+ char *enc;
DBG("technology %p", technology);
@@ -436,8 +453,14 @@ static void technology_load(struct connman_technology *technology)
technology->tethering_ident = g_key_file_get_string(keyfile,
identifier, "Tethering.Identifier", NULL);
- technology->tethering_passphrase = g_key_file_get_string(keyfile,
+ enc = g_key_file_get_string(keyfile,
identifier, "Tethering.Passphrase", NULL);
+ if (enc)
+ technology->tethering_passphrase = g_strcompress(enc);
+
+ technology->tethering_freq = g_key_file_get_integer(keyfile,
+ identifier, "Tethering.Freq", NULL);
+
done:
g_free(identifier);
@@ -549,6 +572,10 @@ static void append_properties(DBusMessageIter *iter,
DBUS_TYPE_STRING,
&technology->tethering_passphrase);
+ connman_dbus_dict_append_basic(&dict, "TetheringFreq",
+ DBUS_TYPE_INT32,
+ &technology->tethering_freq);
+
connman_dbus_dict_close(iter, &dict);
}
@@ -963,6 +990,27 @@ static DBusMessage *set_property(DBusConnection *conn,
DBUS_TYPE_STRING,
&technology->tethering_passphrase);
}
+ } else if (g_str_equal(name, "TetheringFreq")) {
+ dbus_int32_t freq;
+
+ if (type != DBUS_TYPE_INT32)
+ return __connman_error_invalid_arguments(msg);
+
+ dbus_message_iter_get_basic(&value, &freq);
+
+ if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+ return __connman_error_not_supported(msg);
+
+ if (freq >= 0) {
+ technology->tethering_freq = freq;
+ technology_save(technology);
+
+ connman_dbus_property_changed_basic(technology->path,
+ CONNMAN_TECHNOLOGY_INTERFACE,
+ "TetheringFreq",
+ DBUS_TYPE_INT32,
+ &technology->tethering_freq);
+ }
} else if (g_str_equal(name, "Powered")) {
dbus_bool_t enable;
diff --git a/src/tethering.c b/src/tethering.c
index e2687b6e..f930a26b 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -386,7 +386,8 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
if ((__connman_inet_modify_address(RTM_NEWADDR,
NLM_F_REPLACE | NLM_F_ACK, pn->index, AF_INET,
- server_ip, peer_ip, prefixlen, NULL)) < 0) {
+ server_ip, peer_ip, prefixlen, NULL, true))
+ < 0) {
DBG("address setting failed");
return;
}
diff --git a/src/timeserver.c b/src/timeserver.c
index decca153..d23776fa 100644
--- a/src/timeserver.c
+++ b/src/timeserver.c
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <gweb/gresolv.h>
#include <netdb.h>
+#include <sys/time.h>
#include "connman.h"
@@ -40,11 +41,13 @@ static GSList *ts_list = NULL;
static char *ts_current = NULL;
static int ts_recheck_id = 0;
static int ts_backoff_id = 0;
+static bool ts_is_synced = false;
static GResolv *resolv = NULL;
static int resolv_id = 0;
static void sync_next(void);
+static void ts_set_nameservers(struct connman_service *service);
static void resolv_debug(const char *str, void *data)
{
@@ -53,10 +56,26 @@ static void resolv_debug(const char *str, void *data)
static void ntp_callback(bool success, void *user_data)
{
+ dbus_uint64_t timestamp;
+ struct timeval tv;
+
DBG("success %d", success);
- if (!success)
+ __connman_timeserver_set_synced(success);
+ if (!success) {
sync_next();
+ return;
+ }
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ connman_warn("Failed to get current time");
+ }
+
+ timestamp = tv.tv_sec;
+ connman_dbus_property_changed_basic(
+ CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "Time",
+ DBUS_TYPE_UINT64, &timestamp);
}
static void save_timeservers(char **servers)
@@ -165,6 +184,7 @@ static void sync_next(void)
}
__connman_ntp_stop();
+ ts_set_nameservers(ts_service);
while (ts_list) {
ts_current = ts_list->data;
@@ -272,6 +292,7 @@ GSList *__connman_timeserver_get_all(struct connman_service *service)
static gboolean ts_recheck(gpointer user_data)
{
+ struct connman_service *service;
GSList *ts;
ts = __connman_timeserver_get_all(connman_service_get_default());
@@ -287,7 +308,9 @@ static gboolean ts_recheck(gpointer user_data)
g_slist_free_full(ts, g_free);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_TS_CHANGE);
return FALSE;
}
@@ -327,37 +350,34 @@ static void ts_recheck_enable(void)
NULL);
}
-/*
- * This function must be called every time the default service changes, the
- * service timeserver(s) or gateway changes or the global timeserver(s) changes.
- */
-int __connman_timeserver_sync(struct connman_service *default_service)
+static int ts_setup_resolv(struct connman_service *service)
{
- struct connman_service *service;
- char **nameservers;
int i;
- if (default_service)
- service = default_service;
- else
- service = connman_service_get_default();
-
- if (!service)
+ i = __connman_service_get_index(service);
+ if (i < 0)
return -EINVAL;
- if (service == ts_service)
- return -EALREADY;
+ if (resolv) {
+ g_resolv_unref(resolv);
+ resolv = NULL;
+ }
+ resolv = g_resolv_new(i);
if (!resolv)
- return 0;
- /*
- * Before we start creating the new timeserver list we must stop
- * any ongoing ntp query and server resolution.
- */
+ return -ENOMEM;
- __connman_ntp_stop();
+ if (getenv("CONNMAN_RESOLV_DEBUG"))
+ g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+ return 0;
+}
- ts_recheck_disable();
+
+static void ts_set_nameservers(struct connman_service *service)
+{
+ char **nameservers;
+ int i;
if (resolv_id > 0)
g_resolv_cancel_lookup(resolv, resolv_id);
@@ -365,73 +385,121 @@ int __connman_timeserver_sync(struct connman_service *default_service)
g_resolv_flush_nameservers(resolv);
nameservers = connman_service_get_nameservers(service);
- if (!nameservers)
- return -EINVAL;
+ if (nameservers) {
+ for (i = 0; nameservers[i]; i++)
+ g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ g_strfreev(nameservers);
+ }
+}
+
+static void ts_reset(struct connman_service *service)
+{
+ if (!resolv)
+ return;
+
+ __connman_timeserver_set_synced(false);
- g_strfreev(nameservers);
+ /*
+ * Before we start creating the new timeserver list we must stop
+ * any ongoing ntp query and server resolution.
+ */
+
+ __connman_ntp_stop();
+
+ ts_recheck_disable();
+
+ ts_set_nameservers(service);
g_slist_free_full(timeservers_list, g_free);
+ g_slist_free_full(ts_list, g_free);
+ ts_list = NULL;
+
timeservers_list = __connman_timeserver_get_all(service);
__connman_service_timeserver_changed(service, timeservers_list);
if (!timeservers_list) {
DBG("No timeservers set.");
- return 0;
+ return;
}
ts_recheck_enable();
ts_service = service;
timeserver_sync_start();
+}
- return 0;
+void __connman_timeserver_sync(struct connman_service *service,
+ enum connman_timeserver_sync_reason reason)
+{
+ if (!service)
+ return;
+
+ switch (reason) {
+ case CONNMAN_TIMESERVER_SYNC_REASON_START:
+ case CONNMAN_TIMESERVER_SYNC_REASON_STATE_UPDATE:
+ if (ts_service == service)
+ return;
+ break;
+ case CONNMAN_TIMESERVER_SYNC_REASON_ADDRESS_UPDATE:
+ case CONNMAN_TIMESERVER_SYNC_REASON_TS_CHANGE:
+ if (ts_service != service)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ ts_reset(service);
}
-static int timeserver_start(struct connman_service *service)
+void __connman_timeserver_conf_update(struct connman_service *service)
{
- char **nameservers;
- int i;
+ if (!service || (ts_service && ts_service != service))
+ return;
- DBG("service %p", service);
+ ts_reset(service);
+}
- i = __connman_service_get_index(service);
- if (i < 0)
- return -EINVAL;
- nameservers = connman_service_get_nameservers(service);
+bool __connman_timeserver_is_synced(void)
+{
+ return ts_is_synced;
+}
- /* Stop an already ongoing resolution, if there is one */
- if (resolv && resolv_id > 0)
- g_resolv_cancel_lookup(resolv, resolv_id);
+void __connman_timeserver_set_synced(bool status)
+{
+ dbus_bool_t is_synced;
- /* get rid of the old resolver */
- if (resolv) {
- g_resolv_unref(resolv);
- resolv = NULL;
- }
+ if (ts_is_synced == status)
+ return;
- resolv = g_resolv_new(i);
- if (!resolv) {
- g_strfreev(nameservers);
- return -ENOMEM;
- }
+ ts_is_synced = status;
+ is_synced = status;
+ connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+ CONNMAN_CLOCK_INTERFACE, "TimeserverSynced",
+ DBUS_TYPE_BOOLEAN, &is_synced);
+}
- if (getenv("CONNMAN_RESOLV_DEBUG"))
- g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+static int timeserver_start(struct connman_service *service)
+{
+ int rv;
- if (nameservers) {
- for (i = 0; nameservers[i]; i++)
- g_resolv_add_nameserver(resolv, nameservers[i], 53, 0);
+ DBG("service %p", service);
- g_strfreev(nameservers);
- }
+ /* get rid of the old resolver */
+ rv = ts_setup_resolv(service);
+ if (rv)
+ return rv;
+
+ ts_set_nameservers(service);
+
+ __connman_timeserver_sync(service,
+ CONNMAN_TIMESERVER_SYNC_REASON_START);
- return __connman_timeserver_sync(service);
+ return 0;
}
static void timeserver_stop(void)
@@ -458,9 +526,13 @@ static void timeserver_stop(void)
int __connman_timeserver_system_set(char **servers)
{
+ struct connman_service *service;
+
save_timeservers(servers);
- __connman_timeserver_sync(NULL);
+ service = connman_service_get_default();
+ if (service)
+ ts_reset(service);
return 0;
}
diff --git a/src/timezone.c b/src/timezone.c
index cc499097..f8d192df 100644
--- a/src/timezone.c
+++ b/src/timezone.c
@@ -38,9 +38,9 @@
#include "connman.h"
-#define ETC_LOCALTIME "/etc/localtime"
#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
#define USR_SHARE_ZONEINFO "/usr/share/zoneinfo"
+#define USR_SHARE_ZONEINFO_MAP USR_SHARE_ZONEINFO "/zone1970.tab"
static char *read_key_file(const char *pathname, const char *key)
{
@@ -228,18 +228,104 @@ static char *find_origin(void *src_map, struct stat *src_st,
return NULL;
}
+static char *get_timezone_alpha2(const char *zone)
+{
+ GIOChannel *channel;
+ struct stat st;
+ char **tokens;
+ char *line;
+ char *alpha2 = NULL;
+ gsize len;
+ int fd;
+
+ if (!zone)
+ return NULL;
+
+ fd = open(USR_SHARE_ZONEINFO_MAP, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ connman_warn("failed to open zoneinfo map %s",
+ USR_SHARE_ZONEINFO_MAP);
+ return NULL;
+ }
+
+ if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) {
+ connman_warn("zoneinfo map does not exist/not regular file");
+ close(fd);
+ return NULL;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+ if (!channel) {
+ connman_warn("failed to create io channel for %s",
+ USR_SHARE_ZONEINFO_MAP);
+ close(fd);
+ return NULL;
+ }
+
+ DBG("read %s for %s", USR_SHARE_ZONEINFO_MAP, zone);
+ g_io_channel_set_encoding(channel, "UTF-8", NULL);
+
+ while (g_io_channel_read_line(channel, &line, &len, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
+ if (!line || !*line || *line == '#' || *line == '\n') {
+ g_free(line);
+ continue;
+ }
+
+ /* File format: Countrycodes Coordinates TZ Comments */
+ tokens = g_strsplit_set(line, " \t", 4);
+ if (!tokens) {
+ connman_warn("line %s failed to parse", line);
+ g_free(line);
+ continue;
+ }
+
+ if (g_strv_length(tokens) >= 3 && !g_strcmp0(
+ g_strstrip(tokens[2]), zone)) {
+ /*
+ * Multiple country codes can be listed, use the first
+ * 2 chars.
+ */
+ alpha2 = g_strndup(g_strstrip(tokens[0]), 2);
+ }
+
+ g_strfreev(tokens);
+ g_free(line);
+
+ if (alpha2) {
+ if (strlen(alpha2) != 2) {
+ connman_warn("Invalid ISO3166 code %s", alpha2);
+ g_free(alpha2);
+ alpha2 = NULL;
+ } else {
+ DBG("Zone %s ISO3166 country code %s", zone,
+ alpha2);
+ }
+
+ break;
+ }
+ }
+
+ g_io_channel_unref(channel);
+ close(fd);
+
+ return alpha2;
+}
+
char *__connman_timezone_lookup(void)
{
struct stat st;
void *map;
int fd;
char *zone;
+ char *alpha2;
zone = read_key_file(ETC_SYSCONFIG_CLOCK, "ZONE");
DBG("sysconfig zone %s", zone);
- fd = open(ETC_LOCALTIME, O_RDONLY | O_CLOEXEC);
+ fd = open(connman_setting_get_string("Localtime"),
+ O_RDONLY | O_CLOEXEC);
if (fd < 0) {
g_free(zone);
return NULL;
@@ -283,6 +369,15 @@ done:
DBG("localtime zone %s", zone);
+ if (connman_setting_get_bool("RegdomFollowsTimezone")) {
+ alpha2 = get_timezone_alpha2(zone);
+ if (alpha2) {
+ DBG("change regdom to %s", alpha2);
+ connman_technology_set_regdom(alpha2);
+ g_free(alpha2);
+ }
+ }
+
return zone;
}
@@ -338,7 +433,7 @@ int __connman_timezone_change(const char *zone)
return -EIO;
}
- err = write_file(map, &st, ETC_LOCALTIME);
+ err = write_file(map, &st, connman_setting_get_string("Localtime"));
munmap(map, st.st_size);
@@ -432,9 +527,9 @@ int __connman_timezone_init(void)
g_io_channel_unref(channel);
- dirname = g_path_get_dirname(ETC_LOCALTIME);
+ dirname = g_path_get_dirname(connman_setting_get_string("Localtime"));
- wd = inotify_add_watch(fd, dirname, IN_DONT_FOLLOW |
+ wd = inotify_add_watch(fd, dirname, IN_CREATE | IN_DONT_FOLLOW |
IN_CLOSE_WRITE | IN_MOVED_TO);
g_free(dirname);
diff --git a/src/wispr.c b/src/wispr.c
index 41157580..a4372018 100644
--- a/src/wispr.c
+++ b/src/wispr.c
@@ -30,9 +30,6 @@
#include "connman.h"
-#define STATUS_URL_IPV4 "http://ipv4.connman.net/online/status.html"
-#define STATUS_URL_IPV6 "http://ipv6.connman.net/online/status.html"
-
struct connman_wispr_message {
bool has_error;
const char *current_element;
@@ -59,6 +56,7 @@ struct wispr_route {
};
struct connman_wispr_portal_context {
+ int refcount;
struct connman_service *service;
enum connman_ipconfig_type type;
struct connman_wispr_portal *wispr_portal;
@@ -94,12 +92,19 @@ struct connman_wispr_portal {
static bool wispr_portal_web_result(GWebResult *result, gpointer user_data);
-static GHashTable *wispr_portal_list = NULL;
+static GHashTable *wispr_portal_hash = NULL;
+
+static char *online_check_ipv4_url = NULL;
+static char *online_check_ipv6_url = NULL;
+static bool enable_online_to_ready_transition = false;
+
+#define wispr_portal_context_ref(wp_context) \
+ wispr_portal_context_ref_debug(wp_context, __FILE__, __LINE__, __func__)
+#define wispr_portal_context_unref(wp_context) \
+ wispr_portal_context_unref_debug(wp_context, __FILE__, __LINE__, __func__)
static void connman_wispr_message_init(struct connman_wispr_message *msg)
{
- DBG("");
-
msg->has_error = false;
msg->current_element = NULL;
@@ -159,11 +164,6 @@ static void free_wispr_routes(struct connman_wispr_portal_context *wp_context)
static void free_connman_wispr_portal_context(
struct connman_wispr_portal_context *wp_context)
{
- DBG("context %p", wp_context);
-
- if (!wp_context)
- return;
-
if (wp_context->wispr_portal) {
if (wp_context->wispr_portal->ipv4_context == wp_context)
wp_context->wispr_portal->ipv4_context = NULL;
@@ -200,9 +200,38 @@ static void free_connman_wispr_portal_context(
g_free(wp_context);
}
+static struct connman_wispr_portal_context *
+wispr_portal_context_ref_debug(struct connman_wispr_portal_context *wp_context,
+ const char *file, int line, const char *caller)
+{
+ DBG("%p ref %d by %s:%d:%s()", wp_context,
+ wp_context->refcount + 1, file, line, caller);
+
+ __sync_fetch_and_add(&wp_context->refcount, 1);
+
+ return wp_context;
+}
+
+static void wispr_portal_context_unref_debug(
+ struct connman_wispr_portal_context *wp_context,
+ const char *file, int line, const char *caller)
+{
+ if (!wp_context)
+ return;
+
+ DBG("%p ref %d by %s:%d:%s()", wp_context,
+ wp_context->refcount - 1, file, line, caller);
+
+ if (__sync_fetch_and_sub(&wp_context->refcount, 1) != 1)
+ return;
+
+ free_connman_wispr_portal_context(wp_context);
+}
+
static struct connman_wispr_portal_context *create_wispr_portal_context(void)
{
- return g_try_new0(struct connman_wispr_portal_context, 1);
+ return wispr_portal_context_ref(
+ g_new0(struct connman_wispr_portal_context, 1));
}
static void free_connman_wispr_portal(gpointer data)
@@ -214,8 +243,8 @@ static void free_connman_wispr_portal(gpointer data)
if (!wispr_portal)
return;
- free_connman_wispr_portal_context(wispr_portal->ipv4_context);
- free_connman_wispr_portal_context(wispr_portal->ipv6_context);
+ wispr_portal_context_unref(wispr_portal->ipv4_context);
+ wispr_portal_context_unref(wispr_portal->ipv6_context);
g_free(wispr_portal);
}
@@ -450,10 +479,11 @@ static void portal_manage_status(GWebResult *result,
&str))
connman_info("Client-Timezone: %s", str);
- free_connman_wispr_portal_context(wp_context);
-
__connman_service_ipconfig_indicate_state(service,
CONNMAN_SERVICE_STATE_ONLINE, type);
+
+ if (enable_online_to_ready_transition)
+ __connman_service_online_check(service, type, true);
}
static bool wispr_route_request(const char *address, int ai_family,
@@ -507,16 +537,20 @@ static bool wispr_route_request(const char *address, int ai_family,
static void wispr_portal_request_portal(
struct connman_wispr_portal_context *wp_context)
{
- DBG("");
+ DBG("wp_context %p %s", wp_context,
+ __connman_ipconfig_type2string(wp_context->type));
+ wispr_portal_context_ref(wp_context);
wp_context->request_id = g_web_request_get(wp_context->web,
wp_context->status_url,
wispr_portal_web_result,
wispr_route_request,
wp_context);
- if (wp_context->request_id == 0)
+ if (wp_context->request_id == 0) {
wispr_portal_error(wp_context);
+ wispr_portal_context_unref(wp_context);
+ }
}
static bool wispr_input(const guint8 **data, gsize *length,
@@ -571,7 +605,7 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service,
if (index < 0)
return;
- wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
GINT_TO_POINTER(index));
if (!wispr_portal)
return;
@@ -581,13 +615,15 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service,
return;
if (!authentication_done) {
- wispr_portal_error(wp_context);
free_wispr_routes(wp_context);
+ wispr_portal_error(wp_context);
+ wispr_portal_context_unref(wp_context);
return;
}
/* Restarting the test */
__connman_service_wispr_start(service, wp_context->type);
+ wispr_portal_context_unref(wp_context);
}
static void wispr_portal_request_wispr_login(struct connman_service *service,
@@ -611,7 +647,7 @@ static void wispr_portal_request_wispr_login(struct connman_service *service,
return;
}
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
return;
}
@@ -663,11 +699,13 @@ static bool wispr_manage_message(GWebResult *result,
wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
+ wispr_portal_context_ref(wp_context);
if (__connman_agent_request_login_input(wp_context->service,
wispr_portal_request_wispr_login,
- wp_context) != -EINPROGRESS)
+ wp_context) != -EINPROGRESS) {
wispr_portal_error(wp_context);
- else
+ wispr_portal_context_unref(wp_context);
+ } else
return true;
break;
@@ -716,6 +754,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
if (length > 0) {
g_web_parser_feed_data(wp_context->wispr_parser,
chunk, length);
+ /* read more data */
return true;
}
@@ -733,6 +772,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
switch (status) {
case 000:
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -744,18 +784,25 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
if (g_web_result_get_header(result, "X-ConnMan-Status",
&str)) {
portal_manage_status(result, wp_context);
- return false;
- } else
+ } else {
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->redirect_url, wp_context);
+ }
break;
+ case 300:
+ case 301:
case 302:
+ case 303:
+ case 307:
+ case 308:
if (!g_web_supports_tls() ||
!g_web_result_get_header(result, "Location",
&redirect)) {
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -766,6 +813,7 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
wp_context->redirect_url = g_strdup(redirect);
+ wispr_portal_context_ref(wp_context);
wp_context->request_id = g_web_request_get(wp_context->web,
redirect, wispr_portal_web_result,
wispr_route_request, wp_context);
@@ -773,15 +821,12 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
goto done;
case 400:
case 404:
- if (__connman_service_online_check_failed(wp_context->service,
- wp_context->type) == 0) {
- wispr_portal_error(wp_context);
- free_connman_wispr_portal_context(wp_context);
- return false;
- }
+ __connman_service_online_check(wp_context->service,
+ wp_context->type, false);
break;
case 505:
+ wispr_portal_context_ref(wp_context);
__connman_agent_request_browser(wp_context->service,
wispr_portal_browser_reply_cb,
wp_context->status_url, wp_context);
@@ -794,26 +839,51 @@ static bool wispr_portal_web_result(GWebResult *result, gpointer user_data)
wp_context->request_id = 0;
done:
wp_context->wispr_msg.message_type = -1;
+ wispr_portal_context_unref(wp_context);
return false;
}
+static char *parse_proxy(const char *proxy)
+{
+ char *proxy_server;
+
+ if (!g_strcmp0(proxy, "DIRECT"))
+ return NULL;
+
+ if (!g_str_has_prefix(proxy, "PROXY "))
+ return NULL;
+
+ proxy_server = g_strdup(proxy + 6);
+
+ /* Use first proxy server */
+ for (char *c = proxy_server; *c != '\0'; ++c) {
+ if (*c == ';') {
+ *c = '\0';
+ break;
+ }
+ }
+
+ g_strstrip(proxy_server);
+
+ return proxy_server;
+}
+
static void proxy_callback(const char *proxy, void *user_data)
{
struct connman_wispr_portal_context *wp_context = user_data;
+ char *proxy_server;
DBG("proxy %s", proxy);
- if (!wp_context)
+ if (!wp_context || !proxy)
return;
wp_context->token = 0;
- if (proxy && g_strcmp0(proxy, "DIRECT") != 0) {
- if (g_str_has_prefix(proxy, "PROXY")) {
- proxy += 5;
- for (; *proxy == ' ' && *proxy != '\0'; proxy++);
- }
- g_web_set_proxy(wp_context->web, proxy);
+ proxy_server = parse_proxy(proxy);
+ if (proxy_server) {
+ g_web_set_proxy(wp_context->web, proxy_server);
+ g_free(proxy_server);
}
g_web_set_accept(wp_context->web, NULL);
@@ -828,6 +898,7 @@ static void proxy_callback(const char *proxy, void *user_data)
xml_wispr_parser_callback, wp_context);
wispr_portal_request_portal(wp_context);
+ wispr_portal_context_unref(wp_context);
}
static gboolean no_proxy_callback(gpointer user_data)
@@ -844,7 +915,6 @@ static gboolean no_proxy_callback(gpointer user_data)
static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
{
enum connman_service_proxy_method proxy_method;
- enum connman_service_type service_type;
char *interface = NULL;
char **nameservers = NULL;
int if_index;
@@ -854,23 +924,6 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
DBG("wispr/portal context %p service %p", wp_context,
wp_context->service);
- service_type = connman_service_get_type(wp_context->service);
-
- switch (service_type) {
- case CONNMAN_SERVICE_TYPE_ETHERNET:
- case CONNMAN_SERVICE_TYPE_WIFI:
- case CONNMAN_SERVICE_TYPE_BLUETOOTH:
- case CONNMAN_SERVICE_TYPE_CELLULAR:
- case CONNMAN_SERVICE_TYPE_GADGET:
- break;
- case CONNMAN_SERVICE_TYPE_UNKNOWN:
- case CONNMAN_SERVICE_TYPE_SYSTEM:
- case CONNMAN_SERVICE_TYPE_GPS:
- case CONNMAN_SERVICE_TYPE_VPN:
- case CONNMAN_SERVICE_TYPE_P2P:
- return -EOPNOTSUPP;
- }
-
interface = connman_service_get_interface(wp_context->service);
if (!interface)
return -EINVAL;
@@ -903,10 +956,10 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
g_web_set_address_family(wp_context->web, AF_INET);
- wp_context->status_url = STATUS_URL_IPV4;
+ wp_context->status_url = online_check_ipv4_url;
} else {
g_web_set_address_family(wp_context->web, AF_INET6);
- wp_context->status_url = STATUS_URL_IPV6;
+ wp_context->status_url = online_check_ipv6_url;
}
for (i = 0; nameservers[i]; i++)
@@ -922,7 +975,7 @@ static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
if (wp_context->token == 0) {
err = -EINVAL;
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
}
} else if (wp_context->timeout == 0) {
wp_context->timeout = g_idle_add(no_proxy_callback, wp_context);
@@ -940,42 +993,58 @@ int __connman_wispr_start(struct connman_service *service,
{
struct connman_wispr_portal_context *wp_context = NULL;
struct connman_wispr_portal *wispr_portal = NULL;
- int index;
+ int index, err;
- DBG("service %p", service);
+ DBG("service %p %s", service,
+ __connman_ipconfig_type2string(type));
- if (!wispr_portal_list)
+ if (!wispr_portal_hash)
return -EINVAL;
+ switch (connman_service_get_type(service)) {
+ case CONNMAN_SERVICE_TYPE_ETHERNET:
+ case CONNMAN_SERVICE_TYPE_WIFI:
+ case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+ case CONNMAN_SERVICE_TYPE_CELLULAR:
+ case CONNMAN_SERVICE_TYPE_GADGET:
+ break;
+ case CONNMAN_SERVICE_TYPE_UNKNOWN:
+ case CONNMAN_SERVICE_TYPE_SYSTEM:
+ case CONNMAN_SERVICE_TYPE_GPS:
+ case CONNMAN_SERVICE_TYPE_VPN:
+ case CONNMAN_SERVICE_TYPE_P2P:
+ return -EOPNOTSUPP;
+ }
+
index = __connman_service_get_index(service);
if (index < 0)
return -EINVAL;
- wispr_portal = g_hash_table_lookup(wispr_portal_list,
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
GINT_TO_POINTER(index));
if (!wispr_portal) {
wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
if (!wispr_portal)
return -ENOMEM;
- g_hash_table_replace(wispr_portal_list,
+ g_hash_table_replace(wispr_portal_hash,
GINT_TO_POINTER(index), wispr_portal);
}
if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
wp_context = wispr_portal->ipv4_context;
- else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
- wp_context = wispr_portal->ipv6_context;
else
- return -EINVAL;
+ wp_context = wispr_portal->ipv6_context;
/* If there is already an existing context, we wipe it */
if (wp_context)
- free_connman_wispr_portal_context(wp_context);
+ wispr_portal_context_unref(wp_context);
wp_context = create_wispr_portal_context();
- if (!wp_context)
- return -ENOMEM;
+ if (!wp_context) {
+ err = -ENOMEM;
+ goto free_wp;
+ }
wp_context->service = service;
wp_context->type = type;
@@ -986,33 +1055,58 @@ int __connman_wispr_start(struct connman_service *service,
else
wispr_portal->ipv6_context = wp_context;
- return wispr_portal_detect(wp_context);
+ err = wispr_portal_detect(wp_context);
+ if (err)
+ goto free_wp;
+ return 0;
+
+free_wp:
+ g_hash_table_remove(wispr_portal_hash, GINT_TO_POINTER(index));
+ return err;
}
void __connman_wispr_stop(struct connman_service *service)
{
+ struct connman_wispr_portal *wispr_portal;
int index;
DBG("service %p", service);
- if (!wispr_portal_list)
+ if (!wispr_portal_hash)
return;
index = __connman_service_get_index(service);
if (index < 0)
return;
- g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
+ wispr_portal = g_hash_table_lookup(wispr_portal_hash,
+ GINT_TO_POINTER(index));
+ if (!wispr_portal)
+ return;
+
+ if ((wispr_portal->ipv4_context &&
+ service == wispr_portal->ipv4_context->service) ||
+ (wispr_portal->ipv6_context &&
+ service == wispr_portal->ipv6_context->service))
+ g_hash_table_remove(wispr_portal_hash, GINT_TO_POINTER(index));
}
int __connman_wispr_init(void)
{
DBG("");
- wispr_portal_list = g_hash_table_new_full(g_direct_hash,
+ wispr_portal_hash = g_hash_table_new_full(g_direct_hash,
g_direct_equal, NULL,
free_connman_wispr_portal);
+ online_check_ipv4_url =
+ connman_setting_get_string("OnlineCheckIPv4URL");
+ online_check_ipv6_url =
+ connman_setting_get_string("OnlineCheckIPv6URL");
+
+ enable_online_to_ready_transition =
+ connman_setting_get_bool("EnableOnlineToReadyTransition");
+
return 0;
}
@@ -1020,6 +1114,6 @@ void __connman_wispr_cleanup(void)
{
DBG("");
- g_hash_table_destroy(wispr_portal_list);
- wispr_portal_list = NULL;
+ g_hash_table_destroy(wispr_portal_hash);
+ wispr_portal_hash = NULL;
}