diff --git a/ALL_README.md b/ALL_README.md
index 152f2e7..e3c80d2 100644
--- a/ALL_README.md
+++ b/ALL_README.md
@@ -5,4 +5,6 @@
- [Irakurri README euskaraz](README_eu.md)
- [Lire le README en français](README_fr.md)
- [Le o README en galego](README_gl.md)
+- [Baca README dalam bahasa bahasa Indonesia](README_id.md)
+- [Прочитать README на русский](README_ru.md)
- [阅读中文(简体)的 README](README_zh_Hans.md)
diff --git a/README.md b/README.md
index 9b1e364..4ad5759 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ It shall NOT be edited by hand.
# Wifi Hotspot for YunoHost
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/README_es.md b/README_es.md
index f4260a6..c64cda5 100644
--- a/README_es.md
+++ b/README_es.md
@@ -5,7 +5,7 @@ No se debe editar a mano.
# Wifi Hotspot para Yunohost
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/README_eu.md b/README_eu.md
index 107765e..1b8c91c 100644
--- a/README_eu.md
+++ b/README_eu.md
@@ -5,7 +5,7 @@ EZ editatu eskuz.
# Wifi Hotspot YunoHost-erako
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/README_fr.md b/README_fr.md
index b7b7eae..ded2b7b 100644
--- a/README_fr.md
+++ b/README_fr.md
@@ -5,7 +5,7 @@ Il NE doit PAS être modifié à la main.
# Wifi Hotspot pour YunoHost
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/README_gl.md b/README_gl.md
index 8689b94..ebb0229 100644
--- a/README_gl.md
+++ b/README_gl.md
@@ -5,7 +5,7 @@ NON debe editarse manualmente.
# Wifi Hotspot para YunoHost
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/README_id.md b/README_id.md
new file mode 100644
index 0000000..b66023b
--- /dev/null
+++ b/README_id.md
@@ -0,0 +1,47 @@
+
+
+# Wifi Hotspot untuk YunoHost
+
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
+
+[](https://install-app.yunohost.org/?app=hotspot)
+
+*[Baca README ini dengan bahasa yang lain.](./ALL_README.md)*
+
+> *Paket ini memperbolehkan Anda untuk memasang Wifi Hotspot secara cepat dan mudah pada server YunoHost.*
+> *Bila Anda tidak mempunyai YunoHost, silakan berkonsultasi dengan [panduan](https://yunohost.org/install) untuk mempelajari bagaimana untuk memasangnya.*
+
+## Ringkasan
+
+* Broadcast a Wi-Fi access point from your self-hosted server
+* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi
+
+
+**Versi terkirim:** 2.3.1~ynh1
+
+## Tangkapan Layar
+
+
+
+## Dokumentasi dan sumber daya
+
+- Website aplikasi resmi:
+- Gudang YunoHost:
+- Laporkan bug:
+
+## Info developer
+
+Silakan kirim pull request ke [`testing` branch](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing).
+
+Untuk mencoba branch `testing`, silakan dilanjutkan seperti:
+
+```bash
+sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug
+atau
+sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug
+```
+
+**Info lebih lanjut mengenai pemaketan aplikasi:**
diff --git a/README_ru.md b/README_ru.md
new file mode 100644
index 0000000..99995c7
--- /dev/null
+++ b/README_ru.md
@@ -0,0 +1,47 @@
+
+
+# Wifi Hotspot для YunoHost
+
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
+
+[](https://install-app.yunohost.org/?app=hotspot)
+
+*[Прочтите этот README на других языках.](./ALL_README.md)*
+
+> *Этот пакет позволяет Вам установить Wifi Hotspot быстро и просто на YunoHost-сервер.*
+> *Если у Вас нет YunoHost, пожалуйста, посмотрите [инструкцию](https://yunohost.org/install), чтобы узнать, как установить его.*
+
+## Обзор
+
+* Broadcast a Wi-Fi access point from your self-hosted server
+* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi
+
+
+**Поставляемая версия:** 2.3.1~ynh1
+
+## Снимки экрана
+
+
+
+## Документация и ресурсы
+
+- Официальный веб-сайт приложения:
+- Магазин YunoHost:
+- Сообщите об ошибке:
+
+## Информация для разработчиков
+
+Пришлите Ваш запрос на слияние в [ветку `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing).
+
+Чтобы попробовать ветку `testing`, пожалуйста, сделайте что-то вроде этого:
+
+```bash
+sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug
+или
+sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug
+```
+
+**Больше информации о пакетировании приложений:**
diff --git a/README_zh_Hans.md b/README_zh_Hans.md
index 2c93b7d..50a09ed 100644
--- a/README_zh_Hans.md
+++ b/README_zh_Hans.md
@@ -5,7 +5,7 @@
# YunoHost 上的 Wifi Hotspot
-[](https://dash.yunohost.org/appci/app/hotspot)  
+[](https://ci-apps.yunohost.org/ci/apps/hotspot/)  
[](https://install-app.yunohost.org/?app=hotspot)
diff --git a/conf/captiveportal_fakedns.service b/conf/captiveportal_fakedns.service
new file mode 100644
index 0000000..aeb7647
--- /dev/null
+++ b/conf/captiveportal_fakedns.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=YunoHost Wifi Captive Portal
+Requires=network.target
+After=network.target
+
+[Service]
+Type=oneshot
+User=root
+ExecStart=/usr/local/bin/captiveportal_fakedns
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/conf/nginx.conf b/conf/nginx.conf
new file mode 100644
index 0000000..534161e
--- /dev/null
+++ b/conf/nginx.conf
@@ -0,0 +1,6 @@
+location / {
+ if ($remote_addr ~ "^__IP4_NAT_PREFIX__.\d+$") {
+ return 302 __CAPTIVE_PORTAL_URL__;
+ }
+ return 302 https://$http_host/yunohost/admin;
+}
diff --git a/config_panel.toml b/config_panel.toml
index b03b3bd..798a963 100644
--- a/config_panel.toml
+++ b/config_panel.toml
@@ -108,3 +108,15 @@ name = "Configuration"
pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$'
pattern.error = "Not an ip"
+ [main.hotspot.captive_portal]
+ ask = "Captive portal"
+ type = "boolean"
+ visible = "advanced"
+ help = "Activate the captive portal mode"
+
+ [main.hotspot.captive_portal_url]
+ ask = "Local captive portal URL"
+ type = "string"
+ visible = "advanced && captive_portal"
+ help = "Local URL on which redirect onto when the user mac address is not yet allowed"
+
diff --git a/hooks/post_iptables_rules b/hooks/post_iptables_rules
new file mode 100644
index 0000000..01e3966
--- /dev/null
+++ b/hooks/post_iptables_rules
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device)
+captive_portal=$(ynh_app_setting_get --app=$app --key=captive_portal)
+ip4_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix)
+ip6_prefix=$(ynh_app_setting_get --app=$app --key=ip6_net)
+
+iptables -w -N hotspot_fwd
+ip6tables -w -N hotspot_fwd
+
+if [[ "${captive_portal}" != "1" ]]
+then
+ exit 0
+fi
+
+for iptables_cmd in iptables ip6tables;
+do
+ if [[ "${iptables_cmd}" == "iptables" ]]; then
+ prefix="${ip4_prefix}"
+ ip="${ip4_prefix}.1"
+ subnet="${ip4_prefix}.0/24"
+ else
+ prefix="${ip6_prefix}"
+ ip="${ip6_prefix}1"
+ subnet="${ip6_prefix}1/64"
+ fi
+
+ mac_addresses=$(grep "${prefix}" /etc/hostapd/$app/allowed.csv | cut -d, -f3)
+
+ # Allow to request 4253 port
+ $iptables_cmd -w -A INPUT -i "${wifi_device}" -m udp -p udp --dport 4253 -j ACCEPT
+
+ # Drop all packets going on external internet
+ $iptables_cmd -w -A hotspot_fwd -s "${subnet}" -j DROP
+
+ # Force to use the fakeDNS
+ $iptables_cmd -w -A PREROUTING -i "${wifi_device}" -s "${subnet}" -p udp --dport 53 -j DNAT --to-destination "${ip}:4253"
+
+ # Make things working with DoH
+ # Warning: this rules to ssupport DoH let info in nginx logs on which website the user try to access...
+ # Only activating 80 and not 443 reduces a bit the issues.
+ # A better approach could be to list all ips used by domains dedicated to captive portal detection.
+ $iptables_cmd -w -A PREROUTING -i "${wifi_device}" -s "${subnet}" -p tcp --dport 80 -j DNAT --to-destination "${ip}:80"
+ #$iptables_cmd -w -A PREROUTING -i "${wifi_device}" -s "${subnet}" -p tcp --dport 443 -j DNAT --to-destination "${ip}:443"
+
+ # Maybe needed, maybe not (i din't need this when vpn is activated)
+ #$iptables_cmd -t nat -A POSTROUTING -o "${wifi_device}" -j MASQUERADE
+
+ # Allow specific mac adress to use external internet
+ for mac in ${mac_addresses}; do
+ $iptables_cmd -w -I hotspot_fwd 1 -s "${subnet}" -m mac --mac-source "${mac}" -j ACCEPT
+ $iptables_cmd -t nat -w -I PREROUTING 1 -i "${wifi_device}" -s "${subnet}" -m mac --mac-source "${mac}" -j ACCEPT
+ done
+
+ $iptables_cmd -w -I FORWARD 1 -i "${wifi_device}" -j hotspot_fwd
+done
+exit 0
diff --git a/scripts/backup b/scripts/backup
index d01e26d..27f8b14 100644
--- a/scripts/backup
+++ b/scripts/backup
@@ -24,12 +24,15 @@ ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory
ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory
ynh_backup --src_path="/usr/local/bin/$service_name"
+ynh_backup --src_path="/usr/local/bin/captiveportal_fakedns"
+ynh_backup --src_path="/usr/local/bin/captiveportal_allow"
ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/90-$service_name"
ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/90-$service_name"
ynh_backup --src_path="/etc/systemd/system/$service_name.service"
ynh_backup --src_path="/etc/systemd/system/hostapd@$app.service"
+ynh_backup --src_path="/etc/systemd/system/captiveportal_fakedns.service"
#=================================================
# END OF SCRIPT
diff --git a/scripts/config b/scripts/config
index d1ef487..4ca4533 100644
--- a/scripts/config
+++ b/scripts/config
@@ -196,6 +196,22 @@ ynh_app_config_apply() {
yunohost service stop $service_name
_ynh_app_config_apply
+
+ # Activate captive portal or not
+ captive_portal=$(ynh_app_setting_get --app=$app --key=captive_portal)
+ captive_portal_url=$(ynh_app_setting_get --app=$app --key=captive_portal_url)
+ domain=$(cut -d'/' -f 3 <<< "${captive_portal_url}")
+ if [[ "${captive_portal}" -eq 1 ]]; then
+ ynh_add_nginx_config
+ ynh_systemd_action --service_name=captiveportal_fakedns --action="start" --log_path=systemd
+ else
+ ynh_remove_nginx_config
+ ynh_systemd_action --service_name=captiveportal_fakedns --action="stop" --log_path=systemd
+ fi
+
+ # Start vpn client
+ ynh_print_info --message="Starting hotspot service if needed"
+ /usr/local/bin/ynh-hotspot start
if [[ "${service_enabled}" -eq 1 ]]; then
configure_hostapd
diff --git a/scripts/install b/scripts/install
index 451d9ad..24ab84f 100644
--- a/scripts/install
+++ b/scripts/install
@@ -83,6 +83,8 @@ ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}"
ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}"
ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}"
ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}"
+ynh_app_setting_set --app=$app --key=captive_portal --value=0
+ynh_app_setting_set --app=$app --key=captive_portal_url --value=""
if [[ -z "$wifi_device" ]]; then
ynh_app_setting_set --app=$app --key=service_enabled --value=0
@@ -105,10 +107,17 @@ chown root: /etc/hostapd/$app/
mkdir -pm 0755 /etc/dnsmasq.$app/
chown root: /etc/dnsmasq.$app/
+touch /etc/hostapd/$app/allowed.csv
+
# Copy init script
ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name"
chmod 0755 "/usr/local/bin/$service_name"
+ynh_add_config --template="../sources/captiveportal_fakedns" --destination="/usr/local/bin/captiveportal_fakedns"
+chmod 0755 "/usr/local/bin/captiveportal_fakedns"
+ynh_add_config --template="../sources/captiveportal_allow" --destination="/usr/local/bin/captiveportal_allow"
+chmod 0755 "/usr/local/bin/captiveportal_allow"
+
# Copy openvpn scripts
mkdir -pm 0755 /etc/openvpn/scripts
mkdir -pm 0755 /etc/openvpn/scripts/route-up.d
@@ -140,11 +149,14 @@ ynh_script_progression --message="Configuring systemd service..."
# Create a dedicated systemd config
ynh_add_systemd_config --service=$service_name
+
# Create custom systemd config for hostapd to handle multiple wifi devices
ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_hostapd.service"
-
yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock
+ynh_add_systemd_config --service="captiveportal_fakedns" --template="../conf/captiveportal_fakedns.service"
+yunohost service add captiveportal_fakedns --description "Captive portal dns service" --test_status "systemctl is-active captiveportal_fakedns"
+
#=================================================
# START SYSTEMD SERVICE
#=================================================
diff --git a/scripts/remove b/scripts/remove
index edaa762..3680307 100644
--- a/scripts/remove
+++ b/scripts/remove
@@ -20,8 +20,13 @@ ynh_script_progression --message="Removing $app service"
yunohost service stop $service_name
yunohost service remove $service_name
+ynh_script_progression --message="Removing $app captiveportal_fakedns service"
+yunohost service stop captiveportal_fakedns
+yunohost service remove captiveportal_fakedns
+
ynh_remove_systemd_config --service=$service_name
ynh_remove_systemd_config --service="hostapd@$app"
+ynh_remove_systemd_config --service=captiveportal_fakedns
#=================================================
# REMOVE APP MAIN DIR
@@ -31,6 +36,9 @@ ynh_script_progression --message="Removing $app configurations..."
ynh_secure_remove --file="/etc/openvpn/scripts/route-up.d/90-${service_name}"
ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-${service_name}"
ynh_secure_remove --file="/usr/local/bin/$service_name"
+ynh_secure_remove --file="/usr/local/bin/captiveportal_fakedns"
+ynh_secure_remove --file="/usr/local/bin/captiveportal_allow"
+ynh_secure_remove --file="/etc/hotspot"
for FILE in $(ls /tmp/.${service_name}-* 2>/dev/null); do
ynh_secure_remove --file="$FILE"
diff --git a/scripts/restore b/scripts/restore
index 6a60840..d3a612d 100644
--- a/scripts/restore
+++ b/scripts/restore
@@ -43,6 +43,7 @@ ynh_script_progression --message="Restoring configurations ..."
ynh_restore
yunohost service add "$service_name" --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock
+yunohost service add captiveportal_fakedns --description "Captive portal dns service" --test_status "systemctl is-active captiveportal_fakedns"
#=================================================
# START SYSTEMD SERVICE
diff --git a/scripts/upgrade b/scripts/upgrade
index fb92cc1..d41a8f7 100644
--- a/scripts/upgrade
+++ b/scripts/upgrade
@@ -45,6 +45,15 @@ if [ -z ${service_name:-} ]; then
ynh_app_setting_set --app=$app --key=service_name --value=$service_name
fi
+if [ -z ${captive_portal:-} ]; then
+ captive_portal=0
+ ynh_app_setting_set --app=$app --key=captive_portal --value=$captive_portal
+fi
+if [ -z ${captive_portal_url:-} ]; then
+ captive_portal_url=""
+ ynh_app_setting_set --app=$app --key=captive_portal_url --value=$captive_portal_url
+fi
+
if [[ -n "${multissid:-}" ]] && [[ "${multissid}" -gt 1 ]]; then
wifi_ssid=$(cut -d'|' -f 1 <<< ${wifi_ssid})
wifi_secure=$(cut -d'|' -f 1 <<< ${wifi_secure})
@@ -157,6 +166,8 @@ chown root: /etc/hostapd/$app/
mkdir -pm 0755 /etc/dnsmasq.$app/
chown root: /etc/dnsmasq.$app/
+touch /etc/hostapd/$app/allowed.csv
+
if [[ -n "${wifi_device:-}" ]]; then
configure_hostapd
configure_dhcp
@@ -170,6 +181,11 @@ fi
ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name"
chmod 0755 "/usr/local/bin/$service_name"
+ynh_add_config --template="../sources/captiveportal_fakedns" --destination="/usr/local/bin/captiveportal_fakedns"
+chmod 0755 "/usr/local/bin/captiveportal_fakedns"
+ynh_add_config --template="../sources/captiveportal_allow" --destination="/usr/local/bin/captiveportal_allow"
+chmod 0755 "/usr/local/bin/captiveportal_allow"
+
# Copy openvpn scripts
mkdir -pm 0755 /etc/openvpn/scripts
mkdir -pm 0755 /etc/openvpn/scripts/route-up.d
@@ -191,6 +207,9 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host
yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock
+ynh_add_systemd_config --service="captiveportal_fakedns" --template="../conf/captiveportal_fakedns.service"
+yunohost service add captiveportal_fakedns --description "Captive portal dns service" --test_status "systemctl is-active captiveportal_fakedns"
+
#=================================================
# START SYSTEMD SERVICE
#=================================================
diff --git a/sources/captiveportal_allow b/sources/captiveportal_allow
new file mode 100644
index 0000000..321869a
--- /dev/null
+++ b/sources/captiveportal_allow
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+date=$(date +"%Y-%m-%d %T")
+ip=$1
+mac=$(arp -a $ip | cut -d" " -f4 | head -n1)
+interface=$(grep wifi_device /etc/yunohost/apps/hotspot/settings.yml | cut -d: -f2 | sed "s/[ ']//g")
+if ! grep $mac /etc/hostapd/$app/allowed.csv ; then
+ echo "$date,$ip,$mac" >> /etc/hostapd/$app/allowed.csv
+ iptables -w -I hotspot_fwd 1 -s $ip -m mac --mac-source $mac -j ACCEPT
+ iptables -t nat -w -I PREROUTING 1 -i $interface -s $ip -m mac --mac-source $mac -j ACCEPT
+fi
diff --git a/sources/captiveportal_fakedns b/sources/captiveportal_fakedns
new file mode 100644
index 0000000..e6304b5
--- /dev/null
+++ b/sources/captiveportal_fakedns
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Net::DNS::Nameserver;
+use IO::Socket::INET;
+
+# This idea was stolen from Net::Address::IP::Local::connected_to()
+sub get_local_ip_address {
+ my $socket = IO::Socket::INET->new(
+ Proto => 'udp',
+ PeerAddr => '198.41.0.4', # a.root-servers.net
+ PeerPort => '53', # DNS
+ );
+
+ # A side-effect of making a socket connection is that our IP address
+ # is available from the 'sockhost' method
+ my $local_ip_address = $socket->sockhost;
+
+ return $local_ip_address;
+}
+
+my $ip4_addr = get_local_ip_address();
+
+sub reply_handler {
+ my ($qname, $qclass, $qtype, $peerhost,$query,$conn) = @_;
+ my ($rcode, @ans, @auth, @add);
+
+ if($qtype eq "A") {
+ my ($ttl, $rdata) = (1, $ip4_addr);
+ my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $rdata");
+ push @ans, $rr;
+ $rcode = "NOERROR";
+
+ } else {
+ $rcode = "NXDOMAIN";
+ }
+
+ return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
+}
+
+my $ns = new Net::DNS::Nameserver(
+ LocalPort => 4253,
+ LocalAddr => '0.0.0.0',
+ ReplyHandler => \&reply_handler,
+ Verbose => 0
+ ) || die "Couldn't create fake nameserver object.\n";
+
+$ns->main_loop;
+
+exit 0;
diff --git a/sources/index.php b/sources/index.php
new file mode 100644
index 0000000..c5f05db
--- /dev/null
+++ b/sources/index.php
@@ -0,0 +1,2 @@
+