m_upnp.php 7.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
<?php
/*
  ----------------------------------------------------------------------
  AlternC - Web Hosting System
  Copyright (C) 2000-2012 by the AlternC Development Team.
  https://alternc.org/
  ----------------------------------------------------------------------
  LICENSE
  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License (GPL)
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  To read the license please visit http://www.gnu.org/copyleft/gpl.html
  ----------------------------------------------------------------------
  Purpose of file: Manage UPnP ports forwarding from router
  ----------------------------------------------------------------------
*/

/**
* This class handle UPnP forwarding from a IGD/UPnP compliant router
* you need this only when you are behind a IGD-compliant router
* each class may exports a hook that defined named port/protocol to
* forward to the local IP address of the server.
* this class manage the upnp table 
* and its configuration from admin control panel
*/
class m_upnp {


  /* ----------------------------------------------------------------- */
  /** get the list of current upnp forwards and their status
   * @return array the attributes of all port-forwards
   */
  function get_forward_list() {
    global $db,$err;
    $err->log("upnp","get_forward_list");
    $db->query("SELECT * FROM upnp");
    $res=array();
    while ($db->next_record()) {
      $res[]=$db->Record;
    }
    return $res;
  }


  /* ----------------------------------------------------------------- */
  /** enable a upnp port in the upnp table
   * @param integer the id of the port to enable
   * @return boolean TRUE if the port has been properly forwarded
   * FALSE if an error occurred
   */
  function enable_port($id) {
    global $db,$err;
    $id=intval($id);
    $err->log("upnp","enable_port($id)");
    $db->query("SELECT enabled FROM upnp WHERE id=$id;");
    if (!$db->next_record()) {
      $err->raise("upnp",_("The required port is not currently defined"));
      return false;
    }
    if (!$db->f("enabled")) {
      $db->query("UPDATE upnp SET enabled=1 WHERE id=$id;");
      $err->raise("upnp",_("The specified upnp port is now enabled"));
      return true;
    }
    $err->raise("upnp",_("The specified upnp port is already enabled"));
    return true;
  }


  /* ----------------------------------------------------------------- */
  /** disable a upnp port in the upnp table
   * @param integer the id of the port to disable
   * @return boolean TRUE if the port has been properly forwarded
   * FALSE if an error occurred
   */
  function disable_port($id) {
    global $db,$err;
    $id=intval($id);
    $err->log("upnp","disable_port($id)");
88
    $db->query("SELECT enabled,mandatory FROM upnp WHERE id=$id;");
89 90 91 92
    if (!$db->next_record()) {
      $err->raise("upnp",_("The required port is not currently defined"));
      return false;
    }
93 94 95 96
    if ($db->f("mandatory")) {
      $err->raise("upnp",_("You can't disable that mandatory port forward"));
      return false;
    }
97 98 99 100 101 102 103 104 105
    if ($db->f("enabled")) {
      $db->query("UPDATE upnp SET enabled=0 WHERE id=$id;");
      $err->raise("upnp",_("The specified upnp port is now disabled"));
      return true;
    }
    $err->raise("upnp",_("The specified upnp port is already disabled"));
    return true;
  }

106

107 108 109 110
  /* ----------------------------------------------------------------- */
  /** cron launched every minute to check the status of UPnP forwarding
   */
  function cron() {
Alan Garcia's avatar
Alan Garcia committed
111
    global $hooks,$db,$L_INTERNAL_IP,$PUBLIC_IP;
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    // if we check anything less than 5 minutes ago, or if the upnp table is empty, let's make a check...
    $db->query("SELECT UNIX_TIMESTAMP(lastcheck) AS lc, * FROM upnp ORDER BY lastcheck ASC;");
    $forwards=array();
    $bigcheck=false;
    if (!$db->next_record()) {
      $bigcheck=true;
    } else {
      if ($db->f("lc")+600<time()) { 
	$bigcheck=true;
      }
      do {
	$db->Record["found"]=false;
	$forwards[]=$db->Record;
      } while ($db->next_record());
    }
    
    if ($bigcheck) {
      // Do the check first by calling the hooks & comparing the arrays
      $res=$hooks->invoke("hooks_upnp_list");
      foreach($res as $c=>$tmp) {
Benjamin Sonntag's avatar
Benjamin Sonntag committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	if (is_array($tmp) && count($tmp)) {
	  foreach($tmp as $name=>$v) {
	    
	    // We compare the hook array with the forwards array
	    $found=false;
	    for($i=0;$i<count($forwards);$i++) {
	      if ($forwards[$i]["class"]==$c && 
		  $forwards[$i]["name"]==$name && 
		  $forwards[$i]["protocol"]==$v["protocol"] && 
		  $forwards[$i]["port"]==$v["port"] && 
		  $forwards[$i]["mandatory"]==$v["mandatory"]) {
		// Found it and unchanged
		$forwards[$i]["found"]=true;
		$found=true;
	      }
	    } // compare with forwards class.
	    if (!$found) {
	      // Mark it for creation
	      $db->query("INSERT INTO upnp SET mandatory='".addslashes($v["mandatory"])."', protocol='".addslashes($v["protocol"])."', port='".addslashes($v["port"])."', name='".addslashes($name)."', action='CREATE'");
	      $id=$db->last_id();
	      $forwards[]=array("id"=>$id, "mandatory" => intval($v["mandatory"]), "protocol" => $v["protocol"], "port" => intval($v["port"]), "name" => $name, "action" => "CREATE");
153
	    }
Benjamin Sonntag's avatar
Benjamin Sonntag committed
154 155
	  } // for each port forward in that class
	} 
156
      } // for each hooked class
Benjamin Sonntag's avatar
Benjamin Sonntag committed
157
      // Now We search the "not found" and remove them from the array
158 159 160 161 162 163 164 165
      for($i=0;$i<count($forwards);$i++) {
	if (!$forwards[$i]["found"]) {
	  $forwards[$i]["action"]="DELETING";
	  $db->query("UPDATE upnp SET action='DELETING' WHERE id=".$forwards[$i]["id"].";");
	}
      }
      
    } // bigcheck ?
Benjamin Sonntag's avatar
Benjamin Sonntag committed
166 167 168 169 170
    
    // Ask for the current upnp status of forwarded ports
    $status=array(); $statusout=array(); $bad=false;
    unset($out);
    exec("upnpc -l 2>&1",$res,$out);
Alan Garcia's avatar
Alan Garcia committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    if ( is_array($out) && !empty($out)) {
      foreach($out as $line) {
        // example line:  1 TCP   222->192.168.0.5:22   'libminiupnpc' ''
        if (preg_match("#^ *([0-9]+) (TCP|UDP) *([0-9]+)\-\>([0-9\.]+):([0-9]+) *#",$line,$mat)) {
  	if ($mat[4]==$L_INTERNAL_IP) {
  	  $status[]=array("protocol" => $mat[2], "port" => $mat[3]);
  	} else {
  	  $statusout[]=array("protocol" => $mat[2], "port" => $mat[3], "ip" => $mat[4]);
  	}
        }
        if (preg_match("#No IGD UPnP Device found on the network#",$line)) {
  	$bad=true;
        }
      } // For each line in upnpc -l (check list)
    }
Benjamin Sonntag's avatar
Benjamin Sonntag committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

    // No UPnP peripheral !! maybe you should not have installed AlternC-upnp altogether ? 
    if ($bad) {
      foreach($forwards as $f) {
	if ($f["action"]!="OK") {
	  $db->query("UPDATE upnp SET lastupdate=NOW(), lastcheck=NOW(), result='No UPnP device detected in your network !' WHERE id=".$f["id"].";");
	} else {
	  $db->query("UPDATE upnp SET lastupdate=NOW(), lastcheck=NOW(), WHERE id=".$f["id"].";");
	}
      }
      return;
    }

    // Now, for each forward, we either 
    // * check it (via upnpc -l parsing)
    // * add it (via upnpc -a)
    // * remove it (via upnpc -d)
    foreach($forwards as $f) {
      switch ($f["action"]) {
      case "OK": // check
	$found=false;
	foreach($status as $s) {
	  if ($s["port"]==$f["port"] && $s["protocol"]==$s["protocol"]) {
	    $found=true;
	    $db->query("UPDATE upnp SET lastcheck=NOW() WHERE id=".$f["id"].";");
	  }
	}
	if (!$found) {
	  // Search this protocol+port in the OTHER list ... if found, tell it ...
	}
	break;
      case "CREATE": 
	break;
      case "DELETE":
      case "DELETING":
	break;
      case "DISABLE":
	break;
      case "ENABLE":
	break;
      }
    }


  } // CRON function
231 232
  

233 234


235 236
  

237 238
} /* Class UPnP */