Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
/mnet/ -> peer.php (source)

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   1  <?php
   2  /**
   3   * An object to represent lots of information about an RPC-peer machine
   4   *
   5   * @author  Donal McMullan  donal@catalyst.net.nz
   6   * @version 0.0.1
   7   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
   8   * @package mnet
   9   */
  10  
  11  require_once($CFG->libdir . '/filelib.php'); // download_file_content() used here
  12  
  13  class mnet_peer {
  14  
  15      /** No SSL verification. */
  16      const SSL_NONE = 0;
  17  
  18      /** SSL verification for host. */
  19      const SSL_HOST = 1;
  20  
  21      /** SSL verification for host and peer. */
  22      const SSL_HOST_AND_PEER = 2;
  23  
  24      var $id                 = 0;
  25      var $wwwroot            = '';
  26      var $ip_address         = '';
  27      var $name               = '';
  28      var $public_key         = '';
  29      var $public_key_expires = 0;
  30      var $last_connect_time  = 0;
  31      var $last_log_id        = 0;
  32      var $force_theme        = 0;
  33      var $theme              = '';
  34      var $applicationid      = 1; // Default of 1 == Moodle
  35      var $keypair            = array();
  36      var $error              = array();
  37      var $bootstrapped       = false; // set when the object is populated
  38  
  39      /** @var int $sslverification The level of SSL verification to apply. */
  40      public $sslverification = self::SSL_HOST_AND_PEER;
  41  
  42      /** @var int deleted status. */
  43      public $deleted;
  44  
  45      /** @var stdClass data from mnet_application table in DB. */
  46      public $application;
  47  
  48      /**
  49       * Current SSL public key
  50       *
  51       * MNet need to compare the remote machine's SSL Cert and the public key to warn users of any mismatch.
  52       * The property is the remote machine's SSL Cert.
  53       *
  54       * @see admin/mnet/peers.php
  55       * @var string
  56       */
  57      public $currentkey;
  58  
  59      /*
  60       * Fetch information about a peer identified by wwwroot
  61       * If information does not preexist in db, collect it together based on
  62       * supplied information
  63       *
  64       * @param string $wwwroot - address of peer whose details we want
  65       * @param string $pubkey - to use if we add a record to db for new peer
  66       * @param int $application - table id - what kind of peer are we talking to
  67       * @return bool - indication of success or failure
  68       */
  69      function bootstrap($wwwroot, $pubkey, $application) {
  70          global $DB;
  71  
  72          if (substr($wwwroot, -1, 1) == '/') {
  73              $wwwroot = substr($wwwroot, 0, -1);
  74          }
  75  
  76          // If a peer record already exists for this address,
  77          // load that info and return
  78          if ($this->set_wwwroot($wwwroot)) {
  79              return true;
  80          }
  81  
  82          $hostname = mnet_get_hostname_from_uri($wwwroot);
  83          // Get the IP address for that host - if this fails, it will return the hostname string
  84          $ip_address = gethostbyname($hostname);
  85  
  86          // Couldn't find the IP address?
  87          if ($ip_address === $hostname && !preg_match('/^\d+\.\d+\.\d+.\d+$/',$hostname)) {
  88              throw new moodle_exception('noaddressforhost', 'mnet', '', $hostname);
  89          }
  90  
  91          $this->name = $wwwroot;
  92  
  93          // TODO: In reality, this will be prohibitively slow... need another
  94          // default - maybe blank string
  95          $homepage = download_file_content($wwwroot);
  96          if (!empty($homepage)) {
  97              $count = preg_match("@<title>(.*)</title>@siU", $homepage, $matches);
  98              if ($count > 0) {
  99                  $this->name = $matches[1];
 100              }
 101          }
 102  
 103          $this->wwwroot              = $wwwroot;
 104          $this->ip_address           = $ip_address;
 105          $this->deleted              = 0;
 106  
 107          $this->application = $DB->get_record('mnet_application', array('name'=>$application));
 108          if (empty($this->application)) {
 109              $this->application = $DB->get_record('mnet_application', array('name'=>'moodle'));
 110          }
 111  
 112          $this->applicationid = $this->application->id;
 113  
 114          if(empty($pubkey)) {
 115              $this->public_key           = clean_param(mnet_get_public_key($this->wwwroot, $this->application), PARAM_PEM);
 116          } else {
 117              $this->public_key           = clean_param($pubkey, PARAM_PEM);
 118          }
 119          $this->public_key_expires   = $this->check_common_name($this->public_key);
 120          $this->last_connect_time    = 0;
 121          $this->last_log_id          = 0;
 122          if ($this->public_key_expires == false) {
 123              $this->public_key == '';
 124              return false;
 125          }
 126          $this->bootstrapped = true;
 127          return true;
 128      }
 129  
 130      /*
 131       * Delete mnet peer
 132       * the peer is marked as deleted in the database
 133       * we delete current sessions.
 134       * @return bool - success
 135       */
 136      function delete() {
 137          global $DB;
 138  
 139          if ($this->deleted) {
 140              return true;
 141          }
 142  
 143          $this->delete_all_sessions();
 144  
 145          $this->deleted = 1;
 146          return $this->commit();
 147      }
 148  
 149      function count_live_sessions() {
 150          global $DB;
 151          $obj = $this->delete_expired_sessions();
 152          return $DB->count_records('mnet_session', array('mnethostid'=>$this->id));
 153      }
 154  
 155      function delete_expired_sessions() {
 156          global $DB;
 157          $now = time();
 158          return $DB->delete_records_select('mnet_session', " mnethostid = ? AND expires < ? ", array($this->id, $now));
 159      }
 160  
 161      function delete_all_sessions() {
 162          global $CFG, $DB;
 163          // TODO: Expires each PHP session individually
 164          $sessions = $DB->get_records('mnet_session', array('mnethostid'=>$this->id));
 165  
 166          if (count($sessions) > 0 && file_exists($CFG->dirroot.'/auth/mnet/auth.php')) {
 167              require_once($CFG->dirroot.'/auth/mnet/auth.php');
 168              $auth = new auth_plugin_mnet();
 169              $auth->end_local_sessions($sessions);
 170          }
 171  
 172          $deletereturn = $DB->delete_records('mnet_session', array('mnethostid'=>$this->id));
 173          return true;
 174      }
 175  
 176      function check_common_name($key) {
 177          $credentials = $this->check_credentials($key);
 178          return $credentials['validTo_time_t'];
 179      }
 180  
 181      function check_credentials($key) {
 182          $credentials = openssl_x509_parse($key);
 183          if ($credentials == false) {
 184              $this->error[] = array('code' => 3, 'text' => get_string("nonmatchingcert", 'mnet', array('subject' => '','host' => '')));
 185              return false;
 186          } elseif (array_key_exists('subjectAltName', $credentials['subject']) && $credentials['subject']['subjectAltName'] != $this->wwwroot) {
 187              $a['subject'] = $credentials['subject']['subjectAltName'];
 188              $a['host'] = $this->wwwroot;
 189              $this->error[] = array('code' => 5, 'text' => get_string("nonmatchingcert", 'mnet', $a));
 190              return false;
 191          } else if ($credentials['subject']['CN'] !== substr($this->wwwroot, 0, 64)) {
 192              $a['subject'] = $credentials['subject']['CN'];
 193              $a['host'] = $this->wwwroot;
 194              $this->error[] = array('code' => 4, 'text' => get_string("nonmatchingcert", 'mnet', $a));
 195              return false;
 196          } else {
 197              if (array_key_exists('subjectAltName', $credentials['subject'])) {
 198                  $credentials['wwwroot'] = $credentials['subject']['subjectAltName'];
 199              } else {
 200                  $credentials['wwwroot'] = $credentials['subject']['CN'];
 201              }
 202              return $credentials;
 203          }
 204      }
 205  
 206      function commit() {
 207          global $DB;
 208          $obj = new stdClass();
 209  
 210          $obj->wwwroot               = $this->wwwroot;
 211          $obj->ip_address            = $this->ip_address;
 212          $obj->name                  = $this->name;
 213          $obj->public_key            = $this->public_key;
 214          $obj->public_key_expires    = $this->public_key_expires;
 215          $obj->deleted               = $this->deleted;
 216          $obj->last_connect_time     = $this->last_connect_time;
 217          $obj->last_log_id           = $this->last_log_id;
 218          $obj->force_theme           = $this->force_theme;
 219          $obj->theme                 = $this->theme;
 220          $obj->applicationid         = $this->applicationid;
 221          $obj->sslverification       = $this->sslverification;
 222  
 223          if (isset($this->id) && $this->id > 0) {
 224              $obj->id = $this->id;
 225              return $DB->update_record('mnet_host', $obj);
 226          } else {
 227              $this->id = $DB->insert_record('mnet_host', $obj);
 228              return $this->id > 0;
 229          }
 230      }
 231  
 232      function touch() {
 233          $this->last_connect_time = time();
 234          $this->commit();
 235      }
 236  
 237      function set_name($newname) {
 238          if (is_string($newname) && strlen($newname <= 80)) {
 239              $this->name = $newname;
 240              return true;
 241          }
 242          return false;
 243      }
 244  
 245      function set_applicationid($applicationid) {
 246          if (is_numeric($applicationid) && $applicationid == intval($applicationid)) {
 247              $this->applicationid = $applicationid;
 248              return true;
 249          }
 250          return false;
 251      }
 252  
 253      /**
 254       * Load information from db about an mnet peer into this object's properties
 255       *
 256       * @param string $wwwroot - address of peer whose details we want to load
 257       * @return bool - indication of success or failure
 258       */
 259      function set_wwwroot($wwwroot) {
 260          global $CFG, $DB;
 261  
 262          $hostinfo = $DB->get_record('mnet_host', array('wwwroot'=>$wwwroot));
 263  
 264          if ($hostinfo != false) {
 265              $this->populate($hostinfo);
 266              return true;
 267          }
 268          return false;
 269      }
 270  
 271      function set_id($id) {
 272          global $CFG, $DB;
 273  
 274          if (clean_param($id, PARAM_INT) != $id) {
 275              $this->error[] = ['code' => 1, 'text' => 'Your id ('.$id.') is not legal'];
 276              return false;
 277          }
 278  
 279          $sql = "
 280                  SELECT
 281                      h.*
 282                  FROM
 283                      {mnet_host} h
 284                  WHERE
 285                      h.id = ?";
 286  
 287          if ($hostinfo = $DB->get_record_sql($sql, array($id))) {
 288              $this->populate($hostinfo);
 289              return true;
 290          }
 291          return false;
 292      }
 293  
 294      /**
 295       * Several methods can be used to get an 'mnet_host' record. They all then
 296       * send it to this private method to populate this object's attributes.
 297       *
 298       * @param   object  $hostinfo   A database record from the mnet_host table
 299       * @return  void
 300       */
 301      function populate($hostinfo) {
 302          global $DB;
 303          $this->id                   = $hostinfo->id;
 304          $this->wwwroot              = $hostinfo->wwwroot;
 305          $this->ip_address           = $hostinfo->ip_address;
 306          $this->name                 = $hostinfo->name;
 307          $this->deleted              = $hostinfo->deleted;
 308          $this->public_key           = $hostinfo->public_key;
 309          $this->public_key_expires   = $hostinfo->public_key_expires;
 310          $this->last_connect_time    = $hostinfo->last_connect_time;
 311          $this->last_log_id          = $hostinfo->last_log_id;
 312          $this->force_theme          = $hostinfo->force_theme;
 313          $this->theme                = $hostinfo->theme;
 314          $this->applicationid        = $hostinfo->applicationid;
 315          $this->sslverification      = $hostinfo->sslverification;
 316          $this->application = $DB->get_record('mnet_application', array('id'=>$this->applicationid));
 317          $this->bootstrapped = true;
 318      }
 319  
 320      /**
 321       * Get public key.
 322       *
 323       * @deprecated since Moodle 4.3
 324       * @todo MDL-78304 Final deprecation.
 325       */
 326      function get_public_key() {
 327          debugging('Function get_public_key() is deprecated.', DEBUG_DEVELOPER);
 328          if (isset($this->public_key_ref)) return $this->public_key_ref;
 329          $this->public_key_ref = openssl_pkey_get_public($this->public_key);
 330          return $this->public_key_ref;
 331      }
 332  }