From: Alexandre Chataignon <***@mobirider.com>
---
This is Alexandre's original patch. I have only changed the title
slightly.
src/connman.h | 4 ++-
src/dnsproxy.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/technology.c | 7 ++++-
src/tethering.c | 22 ++++++++++++++--
4 files changed, 108 insertions(+), 4 deletions(-)
diff --git a/src/connman.h b/src/connman.h
index 35eb3f5..7b3265f 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -619,7 +619,7 @@ int __connman_tethering_init(void);
void __connman_tethering_cleanup(void);
const char *__connman_tethering_get_bridge(void);
-void __connman_tethering_set_enabled(void);
+void __connman_tethering_set_enabled(bool captive_dns);
void __connman_tethering_set_disabled(void);
int __connman_private_network_request(DBusMessage *msg, const char *owner);
@@ -930,6 +930,8 @@ int __connman_dnsproxy_add_listener(int index);
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_enable_captive(uint32_t ip) ;
+int __connman_dnsproxy_disable_captive(void) ;
int __connman_6to4_probe(struct connman_service *service);
void __connman_6to4_remove(struct connman_ipconfig *ipconfig);
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index a1eda55..f72b5cc 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -76,6 +76,14 @@ struct domain_hdr {
#else
#error "Unknown byte order"
#endif
+struct a_dns_answer {
+ uint16_t ptr;
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength;
+ uint32_t ip;
+} __attribute__ ((packed)) ;
struct partial_reply {
uint16_t len;
@@ -216,6 +224,7 @@ static GHashTable *listener_table = NULL;
static time_t next_refresh;
static GHashTable *partial_tcp_req_table;
static guint cache_timer = 0;
+static uint32_t captive_ip = 0;
static guint16 get_id(void)
{
@@ -502,6 +511,58 @@ static void send_response(int sk, unsigned char *buf, int len,
}
}
+static void send_response_A(int sk, unsigned char *req, int len,
+ uint32_t ip,
+ const struct sockaddr *to, socklen_t tolen,
+ int protocol)
+{
+ struct domain_hdr *hdr;
+ struct a_dns_answer* ans;
+ int err, offset = protocol_offset(protocol);
+
+ DBG("sk %d", sk);
+
+ if (offset < 0)
+ return;
+
+ if (len < 12)
+ return;
+
+ size_t new_len = len+sizeof(struct a_dns_answer) ;
+ char* new_buf ;
+ new_buf = g_malloc(new_len) ;
+ memcpy(new_buf, req, len) ;
+
+ hdr = (void *) (new_buf + offset);
+
+ DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
+
+ hdr->qr = 1;
+ hdr->rcode = ns_r_noerror;
+
+ hdr->ancount = htons(1);
+ hdr->nscount = 0;
+ hdr->arcount = 0;
+
+ /* Create A answer */
+ /* Other attributes */
+ ans = (void *) (new_buf + len);
+ ans->ptr = htons(0xc00c) ;/* 11....1100 -> ptr to 12th octet = query */
+ ans->type = htons(ns_t_a);
+ ans->class = htons(ns_c_in);
+ ans->ttl = 0;
+ ans->rdlength = htons(4);
+ ans->ip = htonl(ip);
+
+ err = sendto(sk, new_buf, new_len, MSG_NOSIGNAL, to, tolen);
+ g_free(new_buf) ;
+ if (err < 0) {
+ connman_error("Failed to send DNS response to %d: %s",
+ sk, strerror(errno));
+ return;
+ }
+}
+
static int get_req_udp_socket(struct request_data *req)
{
GIOChannel *channel;
@@ -3429,6 +3490,14 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+ /* If captive mode, all is redirected to tether ip */
+ if (captive_ip) {
+ send_response_A(sk, buf, len,
+ captive_ip,
+ client_addr, *client_addr_len, IPPROTO_UDP);
+ return true ;
+ }
+
err = parse_request(buf, len, query, sizeof(query));
if (err < 0 || (g_slist_length(server_list) == 0)) {
send_response(sk, buf, len, client_addr,
@@ -3885,3 +3954,13 @@ void __connman_dnsproxy_cleanup(void)
g_hash_table_destroy(partial_tcp_req_table);
}
+
+int __connman_dnsproxy_enable_captive(uint32_t ip) {
+ captive_ip = ip ;
+ return 0;
+}
+
+int __connman_dnsproxy_disable_captive(void) {
+ captive_ip = 0 ;
+ return 0;
+}
diff --git a/src/technology.c b/src/technology.c
index 55303a0..c6475b8 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -66,6 +66,7 @@ struct connman_technology {
*/
char *tethering_ident;
char *tethering_passphrase;
+ bool tethering_captive;
bool enable_persistent; /* Save the tech state */
@@ -224,7 +225,7 @@ void connman_technology_tethering_notify(struct connman_technology *technology,
tethering_changed(technology);
if (enabled)
- __connman_tethering_set_enabled();
+ __connman_tethering_set_enabled(technology->tethering_captive);
else
__connman_tethering_set_disabled();
}
@@ -428,6 +429,10 @@ static void technology_load(struct connman_technology *technology)
technology->tethering_passphrase = g_key_file_get_string(keyfile,
identifier, "Tethering.Passphrase", NULL);
+
+ technology->tethering_captive = g_key_file_get_boolean(keyfile,
+ identifier, "Tethering.Captive", false);
+
done:
g_free(identifier);
diff --git a/src/tethering.c b/src/tethering.c
index ceeec74..9187178 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -56,6 +56,7 @@ static char *private_network_primary_dns = NULL;
static char *private_network_secondary_dns = NULL;
static volatile int tethering_enabled;
+static volatile bool captive_enabled;
static GDHCPServer *tethering_dhcp_server = NULL;
static struct connman_ippool *dhcp_ippool = NULL;
static DBusConnection *connection;
@@ -178,10 +179,10 @@ static void tethering_restart(struct connman_ippool *pool, void *user_data)
{
DBG("pool %p", pool);
__connman_tethering_set_disabled();
- __connman_tethering_set_enabled();
+ __connman_tethering_set_enabled(captive_enabled);
}
-void __connman_tethering_set_enabled(void)
+void __connman_tethering_set_enabled(bool captive_dns)
{
int index;
int err;
@@ -193,6 +194,7 @@ void __connman_tethering_set_enabled(void)
const char *dns;
unsigned char prefixlen;
char **ns;
+ captive_enabled = captive_dns;
DBG("enabled %d", tethering_enabled + 1);
@@ -279,6 +281,19 @@ void __connman_tethering_set_enabled(void)
return;
}
+ if (captive_dns) {
+ err = __connman_dnsproxy_enable_captive(htonl(inet_addr(gateway)));
+ if (err < 0) {
+ connman_error("Cannot enable captive DNS");
+ dhcp_server_stop(tethering_dhcp_server);
+ __connman_bridge_disable(BRIDGE_NAME);
+ __connman_ippool_unref(dhcp_ippool);
+ __connman_bridge_remove(BRIDGE_NAME);
+ __sync_fetch_and_sub(&tethering_enabled, 1);
+ return;
+ }
+ }
+
err = __connman_ipv6pd_setup(BRIDGE_NAME);
if (err < 0 && err != -EINPROGRESS)
DBG("Cannot setup IPv6 prefix delegation %d/%s", err,
@@ -302,6 +317,7 @@ void __connman_tethering_set_disabled(void)
__connman_dnsproxy_remove_listener(index);
__connman_nat_disable(BRIDGE_NAME);
+ __connman_dnsproxy_disable_captive();
dhcp_server_stop(tethering_dhcp_server);
@@ -398,6 +414,7 @@ static void remove_private_network(gpointer user_data)
__connman_nat_disable(BRIDGE_NAME);
connman_rtnl_remove_watch(pn->iface_watch);
__connman_ippool_unref(pn->pool);
+ __connman_dnsproxy_disable_captive();
if (pn->watch > 0) {
g_dbus_remove_watch(connection, pn->watch);
@@ -548,6 +565,7 @@ void __connman_tethering_cleanup(void)
__connman_bridge_disable(BRIDGE_NAME);
__connman_bridge_remove(BRIDGE_NAME);
__connman_nat_disable(BRIDGE_NAME);
+ __connman_dnsproxy_disable_captive();
}
if (!connection)
--
2.1.4