From: Alexandre Chataignon <alexandre.chataig...@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

_______________________________________________
connman mailing list
connman@connman.net
https://lists.connman.net/mailman/listinfo/connman

Reply via email to