Browse Source

* Automatic detection of the interface
* Automatic detection of wifi n
* Checking inputs

Julien VAUBOURG 10 years ago
parent
commit
1f9d32d74d
10 changed files with 187 additions and 61 deletions
  1. 3 6
      README.md
  2. 1 0
      TODO
  3. 28 5
      conf/init_ynh-hotspot
  4. 3 0
      conf/ipv6_compressed
  5. 3 0
      conf/ipv6_expanded
  6. 0 9
      manifest.json
  7. 51 11
      scripts/install
  8. 2 9
      scripts/remove
  9. 93 18
      sources/controller.php
  10. 3 3
      sources/views/settings.html.php

+ 3 - 6
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))

+ 1 - 0
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

+ 28 - 5
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

+ 3 - 0
conf/ipv6_compressed

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+sipcalc "${1}" | grep Compressed | awk '{ print $NF; }'

+ 3 - 0
conf/ipv6_expanded

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+sipcalc "${1}" | grep Expanded | awk '{ print $NF; }'

+ 0 - 9
manifest.json

@@ -49,15 +49,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": {
             "en": "Select your IPv6 delegated prefix (leave empty if your Internet Service Provider does not give you a delegated prefix, but you will not have IPv6)",

+ 51 - 11
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/

+ 2 - 9
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

+ 93 - 18
sources/controller.php

@@ -1,11 +1,11 @@
 <?php
 
 function moulinette_get($var) {
-  return htmlspecialchars(exec("sudo yunohost app setting hotspot ".escapeshellarg($var)));
+  return htmlspecialchars(exec('sudo yunohost app setting hotspot '.escapeshellarg($var)));
 }
 
 function moulinette_set($var, $value) {
-  return exec("sudo yunohost app setting hotspot ".escapeshellarg($var)." -v ".escapeshellarg($value));
+  return exec('sudo yunohost app setting hotspot '.escapeshellarg($var).' -v '.escapeshellarg($value));
 }
 
 function stop_service() {
@@ -18,20 +18,30 @@ function start_service() {
   return $retcode;
 }
 
+function ipv6_expanded($ip) {
+  exec('ipv6_expanded '.escapeshellarg($ip), $output);
+
+  return $output[0];
+}
+
+function ipv6_compressed($ip) {
+  exec('ipv6_compressed '.escapeshellarg($ip), $output);
+
+  return $output[0];
+}
+
 dispatch('/', function() {
-  exec('ip link', $devs);
+  exec('sudo iwconfig', $devs);
   $wifi_device = moulinette_get('wifi_device');
   $devs_list = "";
 
   foreach($devs AS $dev) {
-    if(preg_match('/^[0-9]/', $dev)) {
-      $dev = explode(':', $dev);
-      $dev = trim($dev[1]);
+    if(preg_match('/802.11/', $dev)) {
+      $dev = explode(' ', $dev);
+      $dev = $dev[0];
 
-      if($dev != 'lo') {
-        $active = ($dev == $wifi_device) ? 'class="active"' : '';
-        $devs_list .= "<li $active><a href='#'>$dev</a></li>\n";
-      }
+      $active = ($dev == $wifi_device) ? 'class="active"' : '';
+      $devs_list .= "<li $active><a href='#'>$dev</a></li>\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('/');
 });
 

+ 3 - 3
sources/views/settings.html.php

@@ -14,14 +14,14 @@
 
         <div style="padding: 14px 14px 0 10px">
           <div class="form-group">
-            <label for="wifi_ssid" class="col-sm-3 control-label"><?= T_('SSID') ?></label>
+            <label for="wifi_ssid" class="col-sm-3 control-label"><?= T_('Name (SSID)') ?></label>
             <div class="col-sm-9">
               <input type="text" class="form-control" name="wifi_ssid" id="wifi_ssid" placeholder="myNeutralNetwork" value="<?= $wifi_ssid ?>" />
             </div>
           </div>
   
           <div class="form-group">
-            <label for="wifi_passphrase" class="col-sm-3 control-label"><?= T_('Passphrase (WPA2)') ?></label>
+            <label for="wifi_passphrase" class="col-sm-3 control-label"><?= T_('Password (WPA2)') ?></label>
             <div class="col-sm-9">
               <input type="text" data-toggle="tooltip" data-title="<?= T_('At least 8 characters') ?>" class="form-control" name="wifi_passphrase" id="wifi_passphrase" placeholder="VhegT8oev0jZI" value="<?= $wifi_passphrase ?>" />
             </div>
@@ -84,7 +84,7 @@
             <div class="alert alert-dismissible alert-warning fade in" style="margin: 2px 2px 17px" role="alert">
               <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
               <strong><?= T_('Notice') ?>:</strong> <?= T_("Currently, your wifi clients don't have IPv6 and it's a very bad thing. Ask your Internet Service Provider an IPv6 delegated prefix, or") ?>
-              <a href="http://db.ffdn.org" class="alert-link"><?= T_('change providers') ?></a> !</span>
+              <a href="http://db.ffdn.org" class="alert-link"><?= T_('change providers') ?></a>!
             </div>
           <?php endif; ?>