From 1f9d32d74d05e9e16e4249ea685866799369880f Mon Sep 17 00:00:00 2001 From: Julien VAUBOURG Date: Sun, 16 Nov 2014 23:27:36 +0100 Subject: [PATCH] * Automatic detection of the interface * Automatic detection of wifi n * Checking inputs --- README.md | 9 +-- TODO | 1 + conf/init_ynh-hotspot | 33 ++++++++-- conf/ipv6_compressed | 3 + conf/ipv6_expanded | 3 + manifest.json | 9 --- scripts/install | 62 ++++++++++++++---- scripts/remove | 11 +--- sources/controller.php | 111 ++++++++++++++++++++++++++------ sources/views/settings.html.php | 6 +- 10 files changed, 187 insertions(+), 61 deletions(-) create mode 100644 conf/ipv6_compressed create mode 100644 conf/ipv6_expanded diff --git a/README.md b/README.md index 4bd0f82..fb65a48 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -# Hotspot Wifi +# Wifi Hotspot ## Overview **Warning: work in progress** -**Warning: currently, there is no checking on input parameters, so be careful** - Hotspot wifi app for [YunoHost](http://yunohost.org/). * Broadcast your own Wifi internet access in addition to your self-hosted web services. @@ -14,10 +12,9 @@ Hotspot wifi app for [YunoHost](http://yunohost.org/). ## Features * WPA2 encryption -* 802.11n if your antenna is compliant +* 802.11n compliant * IPv6 compliant (with a delegated prefix) * Automatic clients configuration (IPv6 and IPv4) * Announce DNS resolvers (IPv6 and IPv4) -* Set an IPv6 from your delegated prefix (*prefix::1*) on the server, to use for the AAAA records -* The internet provider can be a 3/4G connection with tethering +* Set an IPv6 from your delegated prefix (*prefix::42*) on the server, to use for the AAAA records * Web interface ([screenshot](https://raw.githubusercontent.com/jvaubourg/hotspot_ynh/master/screenshot.png)) diff --git a/TODO b/TODO index c283e46..0e366b0 100644 --- a/TODO +++ b/TODO @@ -2,3 +2,4 @@ ** in bash install script (empty parameters, passphrase size and allowed characters with WPA2, is Wifi device exist) ** in PHP controller (empty parameters, parameters format, compressed IPv6, NAT address format, passphrase size and allowed characters with WPA2) * Translate PHP interface in French +** Add require for slapd for the service start diff --git a/conf/init_ynh-hotspot b/conf/init_ynh-hotspot index 93a0b4d..5444f86 100644 --- a/conf/init_ynh-hotspot +++ b/conf/init_ynh-hotspot @@ -1,7 +1,7 @@ #!/bin/bash ### BEGIN INIT INFO # Provides: ynh-hotspot -# Required-Start: $network $remote_fs $syslog +# Required-Start: $network $remote_fs $syslog $all # Required-Stop: $network $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 @@ -12,6 +12,10 @@ # Functions ## State functions +has_vpnclient_app() { + [ -e /tmp/.ynh-vpnclient-started ] +} + has_ip6delegatedprefix() { [ "${ynh_ip6_net}" != none ] } @@ -77,6 +81,7 @@ set_ip4nataddr() { } set_ip6addr() { + ip address delete "${ynh_ip6_addr}/64" dev tun0 &> /dev/null ip address add "${ynh_ip6_addr}/64" dev "${ynh_wifi_device}" } @@ -223,7 +228,14 @@ case "$1" in if is_running; then echo "Already started" else - echo "Starting..." + echo "[hotspot] Starting..." + touch /tmp/.ynh-hotspot-started + + if [ "${new_internet_device}" == tun0 ]; then + moulinette_set vpnclient yes + else + moulinette_set vpnclient no + fi # Set NDP proxy if has_ip6delegatedprefix && ! is_ndproxy_set; then @@ -282,13 +294,20 @@ case "$1" in echo "Run dhcpd" start_dhcpd fi + + # Update dynamic settings + moulinette_set internet_device "${new_internet_device}" fi - # Update dynamic settings - moulinette_set internet_device "${new_internet_device}" + # Restart php5-fpm at the first start (it needs to be restarted after the slapd start) + if [ ! -e /tmp/.ynh-hotspot-boot ]; then + touch /tmp/.ynh-hotspot-boot + service php5-fpm restart + fi ;; stop) - echo "Stopping..." + echo "[hotspot] Stopping..." + rm /tmp/.ynh-hotspot-started if has_ip6delegatedprefix && is_ndproxy_set; then echo "Unset NDP proxy" @@ -329,6 +348,10 @@ case "$1" in echo "Stop dhcpd" stop_dhcpd fi + + if has_vpnclient_app; then + service ynh-vpnclient start + fi ;; status) exitcode=0 diff --git a/conf/ipv6_compressed b/conf/ipv6_compressed new file mode 100644 index 0000000..28c03d6 --- /dev/null +++ b/conf/ipv6_compressed @@ -0,0 +1,3 @@ +#!/bin/bash + +sipcalc "${1}" | grep Compressed | awk '{ print $NF; }' diff --git a/conf/ipv6_expanded b/conf/ipv6_expanded new file mode 100644 index 0000000..8be7777 --- /dev/null +++ b/conf/ipv6_expanded @@ -0,0 +1,3 @@ +#!/bin/bash + +sipcalc "${1}" | grep Expanded | awk '{ print $NF; }' diff --git a/manifest.json b/manifest.json index f950616..576cb45 100644 --- a/manifest.json +++ b/manifest.json @@ -48,15 +48,6 @@ }, "example": "VhegT8oev0jZI" }, - { - "name": "wifi_device", - "ask": { - "en": "Select the wifi antenna interface", - "fr": "Sélectionnez l'interface correspondant à l'antenne wifi" - }, - "example": "wlan0", - "default": "wlan0" - }, { "name": "ip6_net", "ask": { diff --git a/scripts/install b/scripts/install index ff82d85..471ef1b 100644 --- a/scripts/install +++ b/scripts/install @@ -1,16 +1,31 @@ -#wifiadmin!/bin/bash +#!/bin/bash # Retrieve arguments domain=${1} url_path=${2} wifi_ssid=${3} wifi_passphrase=${4} -wifi_device=${5} -ip6_net=${6} +ip6_net=${5} # Check arguments -# TODO +if [ -z "${wifi_ssid}" -o -z "${wifi_passphrase}" ]; then + echo "ERROR: Your Wifi Hotspot needs a name and a password" >&2 + exit 1 +fi +wifi_passphrase_length="$(echo -n "${wifi_passphrase}" | wc -c)" +if [ "${wifi_passphrase_length}" -lt 8 -o "${wifi_passphrase_length}" -gt 63 ]; then + echo "ERROR: Your password must from 8 to 63 characters (WPA2 passphrase)" >&2 + exit 1 +fi + +echo "${wifi_passphrase}" | grep -qP '[^[:print:]]' +if [ $? -eq 0 ]; then + echo "ERROR: Only printable ASCII characters are permitted in your password (WPA2 passphrase)" >&2 + exit 1 +fi + +# Check domain/path availability sudo yunohost app checkurl ${domain}${url_path} -a hotspot if [ ! $? -eq 0 ]; then exit 1 @@ -19,20 +34,40 @@ fi # Install packages # TODO: Replace isc-dhcp-server by dnsmasq (currently negotiating with the YunoHost team to # also replace bind9 by dnsmasq) +#sudo apt-get update sudo apt-get --assume-yes --force-yes install hostapd radvd isc-dhcp-server iptables php5-fpm -# Install extra packages -sudo apt-get --assume-yes --force-yes install sipcalc +# Extra packages +sudo apt-get --assume-yes --force-yes install sipcalc iwconfig # Compute extra arguments if [ -z "${ip6_net}" ]; then ip6_net=none ip6_addr=none else - ip6_expanded_net=$(sipcalc "${ip6_net}" | grep Expanded | awk '{ print $NF; }') - ip6_net=$(sipcalc "${ip6_net}" | grep Compressed | awk '{ print $NF; }') - ip6_addr=$(echo "$(echo "${ip6_expanded_net}" | cut -d: -f1-7):1") - ip6_addr=$(sipcalc "${ip6_addr}" | grep Compressed | awk '{ print $NF; }') + ip6_net=$(bash ../conf/ipv6_expanded "${ip6_net}") + + if [ -z "${ip6_net}" ]; then + echo "ERROR: The IPv6 Delegated Prefix format looks bad" >&2 + exit 1 + fi + + ip6_addr="$(echo "${ip6_net}" | cut -d: -f1-7):42" + ip6_net=$(bash ../conf/ipv6_compressed "${ip6_net}") + ip6_addr=$(bash ../conf/ipv6_compressed "${ip6_addr}") +fi + +wifi_device=$(sudo iwconfig 2>&1 | grep 802.11 | head -n1 | awk '{ print $1 }') +wifi_n=0 + +if [ -z "${wifi_device}" ]; then + echo "ERROR: No wifi interface found" >&2 + exit 1 +fi + +sudo iwconfig "${wifi_device}" | grep -q 'n *ESSID' +if [ $? -eq 0 ]; then + wifi_n=1 fi # Save arguments @@ -40,7 +75,7 @@ sudo yunohost app setting hotspot wifi_ssid -v "${wifi_ssid}" sudo yunohost app setting hotspot wifi_passphrase -v "${wifi_passphrase}" sudo yunohost app setting hotspot wifi_device -v "${wifi_device}" sudo yunohost app setting hotspot wifi_channel -v 6 -sudo yunohost app setting hotspot wifi_n -v 0 +sudo yunohost app setting hotspot wifi_n -v "${wifi_n}" sudo yunohost app setting hotspot ip6_addr -v "${ip6_addr}" sudo yunohost app setting hotspot ip6_net -v "${ip6_net}" sudo yunohost app setting hotspot ip6_dns0 -v 2001:913::8 @@ -48,6 +83,11 @@ sudo yunohost app setting hotspot ip6_dns1 -v 2001:910:800::12 sudo yunohost app setting hotspot ip4_dns0 -v 80.67.188.188 sudo yunohost app setting hotspot ip4_dns1 -v 80.67.169.12 sudo yunohost app setting hotspot ip4_nat_prefix -v 10.0.242 +sudo yunohost app setting hotspot vpnclient -v no + +# Install IPv6 scripts +sudo install -o root -g root -m 0755 ../conf/ipv6_expanded /usr/local/bin/ +sudo install -o root -g root -m 0755 ../conf/ipv6_compressed /usr/local/bin/ # Copy confs sudo install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/ diff --git a/scripts/remove b/scripts/remove index 9ce282f..649e6ea 100644 --- a/scripts/remove +++ b/scripts/remove @@ -7,6 +7,7 @@ domain=$(sudo yunohost app setting hotspot domain) sudo service ynh-hotspot stop sudo yunohost service remove ynh-hotspot sudo rm -f /etc/init.d/ynh-hotspot +sudo rm -f /tmp/.ynh-hotspot-boot # Remove confs sudo rm -f /etc/hostapd/hostapd.conf{.tpl,} /etc/radvd.conf{.tpl,} /etc/dhcp/dhcpd.conf{.tpl,} @@ -24,17 +25,9 @@ sudo rm -rf /var/www/wifiadmin/ # Remove user sudo userdel -f wifiadmin -# Restart vpnclient service if installed to set the IPv6 address -# A new start will fix the address without unsetting all stuff -sudo yunohost app list -f vpnclient --json | grep -q '"installed": true' -if [ "$?" -eq 0 ]; then - sudo service ynh-vpnclient start -fi - - # Remove packets # The yunohost policy is currently to not uninstall packets (dependency problems) ## sudo apt-get --assume-yes --force-yes remove hostapd radvd isc-dhcp-server iptables -## sudo apt-get --assume-yes --force-yes remove sipcalc +## sudo apt-get --assume-yes --force-yes remove sipcalc iwconfig exit 0 diff --git a/sources/controller.php b/sources/controller.php index c3ea133..9ad2fa2 100644 --- a/sources/controller.php +++ b/sources/controller.php @@ -1,11 +1,11 @@ $dev\n"; - } + $active = ($dev == $wifi_device) ? 'class="active"' : ''; + $devs_list .= "
  • $dev
  • \n"; } } @@ -55,7 +65,78 @@ dispatch('/', function() { }); dispatch_put('/settings', function() { + exec('ip link show '.escapeshellarg($_POST['wifi_device']), $output, $retcode); + $wifi_device_exists = ($retcode == 0); + $ip6_net = empty($_POST['ip6_net']) ? 'none' : $_POST['ip6_net']; + $ip6_addr = 'none'; + + try { + if(empty($_POST['wifi_ssid']) || empty($_POST['wifi_passphrase']) || empty($_POST['wifi_channel'])) { + throw new Exception(T_('Your Wifi Hotspot needs a name, a password and a channel')); + } + + if(strlen($_POST['wifi_passphrase']) < 8 || strlen($_POST['wifi_passphrase']) > 63) { + throw new Exception(T_('Your password must from 8 to 63 characters (WPA2 passphrase)')); + } + + if(preg_match('/[^[:print:]]/', $_POST['wifi_passphrase'])) { + throw new Exception(T_('Only printable ASCII characters are permitted in your password')); + } + + if(!$wifi_device_exists) { + throw new Exception(T_('The wifi antenna interface seems not exist on the system')); + } + + if($ip6_net != 'none') { + $ip6_net = ipv6_expanded($ip6_net); + + if(empty($ip6_net)) { + throw new Exception(T_('The IPv6 Delegated Prefix format looks bad')); + } + + $ip6_blocs = explode(':', $ip6_net); + $ip6_addr = "${ip6_blocs[0]}:${ip6_blocs[1]}:${ip6_blocs[2]}:${ip6_blocs[3]}:${ip6_blocs[4]}:${ip6_blocs[5]}:${ip6_blocs[6]}:42"; + + $ip6_net = ipv6_compressed($ip6_net); + $ip6_addr = ipv6_compressed($ip6_addr); + } + + $ip6_dns0 = ipv6_expanded($ip6_dns0); + + if(empty($_POST['ip6_dns0'])) { + throw new Exception(T_('The format of the first IPv6 DNS Resolver looks bad')); + } + + $ip6_dns0 = ipv6_compressed($ip6_dns0); + $ip6_dns1 = ipv6_expanded($ip6_dns1); + + if(empty($_POST['ip6_dns1'])) { + throw new Exception(T_('The format of the second IPv6 DNS Resolver looks bad')); + } + + $ip6_dns1 = ipv6_compressed($ip6_dns1); + + if(inet_pton($_POST['ip4_dns0']) === false) { + throw new Exception(T_('The format of the first IPv4 DNS Resolver looks bad')); + } + + if(inet_pton($_POST['ip4_dns1']) === false) { + throw new Exception(T_('The format of the second IPv4 DNS Resolver looks bad')); + } + + if(inet_pton("${_POST['ip4_nat_prefix']}.0") === false) { + throw new Exception(T_('The format of the IPv4 NAT Prefix (/24) looks bad : x.x.x expected)')); + } + + if(filter_var("${_POST['ip4_nat_prefix']}.0", FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) !== false) { + throw new Exception(T_('The IPv4 NAT Prefix must be from a private range')); + } + + } catch(Exception $e) { + flash('error', $e->getMessage().T_(' (configuration not updated).')); + goto redirect; + } stop_service(); @@ -65,20 +146,13 @@ dispatch_put('/settings', function() { moulinette_set('wifi_n', isset($_POST['wifi_n']) ? 1 : 0); moulinette_set('wifi_device', $_POST['wifi_device']); moulinette_set('ip6_net', $ip6_net); + moulinette_set('ip6_addr', $ip6_addr); moulinette_set('ip6_dns0', $_POST['ip6_dns0']); moulinette_set('ip6_dns1', $_POST['ip6_dns1']); moulinette_set('ip4_nat_prefix', $_POST['ip4_nat_prefix']); moulinette_set('ip4_dns0', $_POST['ip4_dns0']); moulinette_set('ip4_dns1', $_POST['ip4_dns1']); - # TODO: format ip6_net - if($ip6_net == 'none') { - moulinette_set('ip6_addr', 'none'); - } else { - $ip6_addr = "${ip6_net}1"; - moulinette_set('ip6_addr', $ip6_addr); - } - $retcode = start_service(); if($retcode == 0) { @@ -87,6 +161,7 @@ dispatch_put('/settings', function() { flash('error', T_('Configuration updated but service reload failed')); } + redirect: redirect_to('/'); }); diff --git a/sources/views/settings.html.php b/sources/views/settings.html.php index 2f93749..1c8b1fd 100644 --- a/sources/views/settings.html.php +++ b/sources/views/settings.html.php @@ -14,14 +14,14 @@
    - +
    - +
    @@ -84,7 +84,7 @@